@mastra/server 1.60.0-alpha.11 → 1.60.0-alpha.13
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.
- package/CHANGELOG.md +20 -0
- package/dist/{a2a-C4JIRtLT.cjs → a2a-BtXFFv-0.cjs} +2 -2
- package/dist/{a2a-C4JIRtLT.cjs.map → a2a-BtXFFv-0.cjs.map} +1 -1
- package/dist/{a2a-B2yosTel.js → a2a-DZhtQeFL.js} +2 -2
- package/dist/{a2a-B2yosTel.js.map → a2a-DZhtQeFL.js.map} +1 -1
- package/dist/{api-schema-manifest-Cq7dyGz7.js → api-schema-manifest-BdBDDuGv.js} +2 -2
- package/dist/{api-schema-manifest-Cq7dyGz7.js.map → api-schema-manifest-BdBDDuGv.js.map} +1 -1
- package/dist/{api-schema-manifest-DBTuPeWn.cjs → api-schema-manifest-CLvx-Pd5.cjs} +2 -2
- package/dist/{api-schema-manifest-DBTuPeWn.cjs.map → api-schema-manifest-CLvx-Pd5.cjs.map} +1 -1
- package/dist/{builder-registry-C-NVj8w5.cjs → builder-registry-Dcd_ApmT.cjs} +2 -2
- package/dist/{builder-registry-C-NVj8w5.cjs.map → builder-registry-Dcd_ApmT.cjs.map} +1 -1
- package/dist/{builder-registry-CqLsr39q.js → builder-registry-DnEa4qnF.js} +2 -2
- package/dist/{builder-registry-CqLsr39q.js.map → builder-registry-DnEa4qnF.js.map} +1 -1
- package/dist/{channels-BnFQESMf.js → channels-BO3Zd38V.js} +2 -2
- package/dist/{channels-BnFQESMf.js.map → channels-BO3Zd38V.js.map} +1 -1
- package/dist/{channels-Bn2aN-6o.cjs → channels-D9WRnKzU.cjs} +2 -2
- package/dist/{channels-Bn2aN-6o.cjs.map → channels-D9WRnKzU.cjs.map} +1 -1
- package/dist/{constants-CutMu4A_.js → constants-0jFCWoex.js} +13 -2
- package/dist/constants-0jFCWoex.js.map +1 -0
- package/dist/{constants-CXPpy-Ef.cjs → constants-4IJp_uzj.cjs} +18 -1
- package/dist/constants-4IJp_uzj.cjs.map +1 -0
- package/dist/{dist-DR_dRdv7.js → dist-0PQDswJk.js} +2692 -288
- package/dist/dist-0PQDswJk.js.map +1 -0
- package/dist/{dist-BrbvwbbJ.cjs → dist-Jy_bgZ7X.cjs} +2691 -287
- package/dist/dist-Jy_bgZ7X.cjs.map +1 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/{helpers-C54OMiDG.cjs → helpers-BWxRRShz.cjs} +2 -2
- package/dist/{helpers-C54OMiDG.cjs.map → helpers-BWxRRShz.cjs.map} +1 -1
- package/dist/{helpers-Da9nPOFL.js → helpers-DBAYsA50.js} +2 -2
- package/dist/{helpers-Da9nPOFL.js.map → helpers-DBAYsA50.js.map} +1 -1
- package/dist/{routes-DHRnM_bB.cjs → routes-B0T6HEoP.cjs} +8 -8
- package/dist/{routes-DHRnM_bB.cjs.map → routes-B0T6HEoP.cjs.map} +1 -1
- package/dist/{routes-DMs0TJQw.js → routes-CT1kQN7A.js} +8 -8
- package/dist/{routes-DMs0TJQw.js.map → routes-CT1kQN7A.js.map} +1 -1
- package/dist/server/auth/index.cjs +2 -2
- package/dist/server/auth/index.js +2 -2
- package/dist/server/constants.d.ts +11 -0
- package/dist/server/constants.d.ts.map +1 -1
- package/dist/server/handlers/a2a.cjs +1 -1
- package/dist/server/handlers/a2a.js +1 -1
- package/dist/server/handlers/agent-builder.cjs +2 -2
- package/dist/server/handlers/agent-builder.js +2 -2
- package/dist/server/handlers/agent-versions.cjs +1 -1
- package/dist/server/handlers/agent-versions.js +1 -1
- package/dist/server/handlers/agents.cjs +3 -3
- package/dist/server/handlers/agents.js +3 -3
- package/dist/server/handlers/auth.cjs +14 -6
- package/dist/server/handlers/auth.cjs.map +1 -1
- package/dist/server/handlers/auth.d.ts +10 -4
- package/dist/server/handlers/auth.d.ts.map +1 -1
- package/dist/server/handlers/auth.js +14 -6
- package/dist/server/handlers/auth.js.map +1 -1
- package/dist/server/handlers/authorship.cjs +1 -1
- package/dist/server/handlers/authorship.js +1 -1
- package/dist/server/handlers/builder-registry.cjs +1 -1
- package/dist/server/handlers/builder-registry.js +1 -1
- package/dist/server/handlers/channels.cjs +1 -1
- package/dist/server/handlers/channels.js +1 -1
- package/dist/server/handlers/conversations.cjs +1 -1
- package/dist/server/handlers/conversations.js +1 -1
- package/dist/server/handlers/datasets.cjs +1 -1
- package/dist/server/handlers/datasets.js +1 -1
- package/dist/server/handlers/logs.cjs +1 -1
- package/dist/server/handlers/logs.js +1 -1
- package/dist/server/handlers/mcp-client-versions.cjs +1 -1
- package/dist/server/handlers/mcp-client-versions.js +1 -1
- package/dist/server/handlers/memory.cjs +1 -1
- package/dist/server/handlers/memory.js +1 -1
- package/dist/server/handlers/observability.cjs +5 -2
- package/dist/server/handlers/observability.cjs.map +1 -1
- package/dist/server/handlers/observability.d.ts.map +1 -1
- package/dist/server/handlers/observability.js +6 -3
- package/dist/server/handlers/observability.js.map +1 -1
- package/dist/server/handlers/plans.cjs +1 -1
- package/dist/server/handlers/plans.js +1 -1
- package/dist/server/handlers/prompt-block-versions.cjs +1 -1
- package/dist/server/handlers/prompt-block-versions.js +1 -1
- package/dist/server/handlers/responses.cjs +1 -1
- package/dist/server/handlers/responses.js +1 -1
- package/dist/server/handlers/responses.storage.cjs +1 -1
- package/dist/server/handlers/responses.storage.js +1 -1
- package/dist/server/handlers/scorer-versions.cjs +1 -1
- package/dist/server/handlers/scorer-versions.js +1 -1
- package/dist/server/handlers/stored-agent-favorites.cjs +1 -1
- package/dist/server/handlers/stored-agent-favorites.js +1 -1
- package/dist/server/handlers/stored-agents.cjs +3 -3
- package/dist/server/handlers/stored-agents.js +3 -3
- package/dist/server/handlers/stored-mcp-clients.cjs +1 -1
- package/dist/server/handlers/stored-mcp-clients.js +1 -1
- package/dist/server/handlers/stored-prompt-blocks.cjs +1 -1
- package/dist/server/handlers/stored-prompt-blocks.js +1 -1
- package/dist/server/handlers/stored-scorers.cjs +1 -1
- package/dist/server/handlers/stored-scorers.js +1 -1
- package/dist/server/handlers/stored-skill-favorites.cjs +1 -1
- package/dist/server/handlers/stored-skill-favorites.js +1 -1
- package/dist/server/handlers/stored-skills.cjs +1 -1
- package/dist/server/handlers/stored-skills.js +1 -1
- package/dist/server/handlers/stored-workspaces.cjs +1 -1
- package/dist/server/handlers/stored-workspaces.js +1 -1
- package/dist/server/handlers/system.cjs +1 -1
- package/dist/server/handlers/system.js +1 -1
- package/dist/server/handlers/tool-providers.cjs +1 -1
- package/dist/server/handlers/tool-providers.js +1 -1
- package/dist/server/handlers/tools.cjs +2 -2
- package/dist/server/handlers/tools.js +2 -2
- package/dist/server/handlers/utils.cjs +1 -1
- package/dist/server/handlers/utils.js +1 -1
- package/dist/server/handlers/validate-agent-instructions.cjs +1 -1
- package/dist/server/handlers/validate-agent-instructions.js +1 -1
- package/dist/server/handlers/workflows.cjs +2 -2
- package/dist/server/handlers/workflows.js +2 -2
- package/dist/server/handlers.cjs +1 -1
- package/dist/server/handlers.js +1 -1
- package/dist/server/schemas/index.cjs +5 -5
- package/dist/server/schemas/index.js +5 -5
- package/dist/server/server-adapter/index.cjs +5 -5
- package/dist/server/server-adapter/index.js +5 -5
- package/dist/{utils-CljhO7OY.js → utils-B2vd77Fi.js} +2 -2
- package/dist/{utils-CljhO7OY.js.map → utils-B2vd77Fi.js.map} +1 -1
- package/dist/{utils-zkjmP1OK.cjs → utils-CmYnjLcY.cjs} +2 -2
- package/dist/{utils-zkjmP1OK.cjs.map → utils-CmYnjLcY.cjs.map} +1 -1
- package/dist/{utils-Xnn91QBW.cjs → utils-DJMUhfvY.cjs} +2 -2
- package/dist/{utils-Xnn91QBW.cjs.map → utils-DJMUhfvY.cjs.map} +1 -1
- package/dist/{utils-qMLbyf2U.js → utils-Hz8Hc2cd.js} +2 -2
- package/dist/{utils-qMLbyf2U.js.map → utils-Hz8Hc2cd.js.map} +1 -1
- package/package.json +5 -5
- package/dist/constants-CXPpy-Ef.cjs.map +0 -1
- package/dist/constants-CutMu4A_.js.map +0 -1
- package/dist/dist-BrbvwbbJ.cjs.map +0 -1
- package/dist/dist-DR_dRdv7.js.map +0 -1
|
@@ -21,8 +21,8 @@ let _mastra_core_features = require("@mastra/core/features");
|
|
|
21
21
|
let _mastra_core_observability = require("@mastra/core/observability");
|
|
22
22
|
let _mastra_core_request_context = require("@mastra/core/request-context");
|
|
23
23
|
let _mastra_core_processors = require("@mastra/core/processors");
|
|
24
|
-
let _mastra_core_workflows = require("@mastra/core/workflows");
|
|
25
24
|
let fs = require("fs");
|
|
25
|
+
let _mastra_core_workflows = require("@mastra/core/workflows");
|
|
26
26
|
let os = require("os");
|
|
27
27
|
let path = require("path");
|
|
28
28
|
let module$1 = require("module");
|
|
@@ -4693,7 +4693,7 @@ function splitLines(text) {
|
|
|
4693
4693
|
return result;
|
|
4694
4694
|
}
|
|
4695
4695
|
//#endregion
|
|
4696
|
-
//#region ../memory/dist/src-
|
|
4696
|
+
//#region ../memory/dist/src-t_4yoTX9.js
|
|
4697
4697
|
var __defProp$3 = Object.defineProperty;
|
|
4698
4698
|
var __exportAll = (all, no_symbols) => {
|
|
4699
4699
|
let target = {};
|
|
@@ -19240,6 +19240,2388 @@ createIdGenerator$3({
|
|
|
19240
19240
|
prefix: "aiobj",
|
|
19241
19241
|
size: 24
|
|
19242
19242
|
});
|
|
19243
|
+
const BUILT_IN_SLUGS = /* @__PURE__ */ new Set([
|
|
19244
|
+
"current-task",
|
|
19245
|
+
"suggested-response",
|
|
19246
|
+
"thread-title"
|
|
19247
|
+
]);
|
|
19248
|
+
const EXTRACTED_VALUES_TAG = "extracted-values";
|
|
19249
|
+
const RESERVED_XML_TAGS = /* @__PURE__ */ new Set([
|
|
19250
|
+
"observations",
|
|
19251
|
+
"observation",
|
|
19252
|
+
EXTRACTED_VALUES_TAG,
|
|
19253
|
+
"thread",
|
|
19254
|
+
"message",
|
|
19255
|
+
"messages",
|
|
19256
|
+
"conversation",
|
|
19257
|
+
"history",
|
|
19258
|
+
"system",
|
|
19259
|
+
"user",
|
|
19260
|
+
"assistant",
|
|
19261
|
+
"tool",
|
|
19262
|
+
...BUILT_IN_SLUGS
|
|
19263
|
+
]);
|
|
19264
|
+
const BUILT_IN_EXTRACTOR_SLUGS = [...BUILT_IN_SLUGS];
|
|
19265
|
+
function isBuiltInExtractorSlug(slug) {
|
|
19266
|
+
return BUILT_IN_SLUGS.has(slug);
|
|
19267
|
+
}
|
|
19268
|
+
function slugifyExtractorName(name) {
|
|
19269
|
+
let normalized = "";
|
|
19270
|
+
let previousWasSeparator = false;
|
|
19271
|
+
for (const char of name.trim().toLowerCase()) {
|
|
19272
|
+
const code = char.charCodeAt(0);
|
|
19273
|
+
if (code >= 97 && code <= 122 || code >= 48 && code <= 57) {
|
|
19274
|
+
normalized += char;
|
|
19275
|
+
previousWasSeparator = false;
|
|
19276
|
+
continue;
|
|
19277
|
+
}
|
|
19278
|
+
if (char === "'" || char === "\"" || char === "`") continue;
|
|
19279
|
+
if (!previousWasSeparator && normalized.length > 0) {
|
|
19280
|
+
normalized += "-";
|
|
19281
|
+
previousWasSeparator = true;
|
|
19282
|
+
}
|
|
19283
|
+
}
|
|
19284
|
+
return normalized.endsWith("-") ? normalized.slice(0, -1) : normalized;
|
|
19285
|
+
}
|
|
19286
|
+
function assertValidSlug(slug, name) {
|
|
19287
|
+
if (!slug) throw new Error(`Extractor name "${name}" must produce a non-empty slug.`);
|
|
19288
|
+
const first = slug.charCodeAt(0);
|
|
19289
|
+
const last = slug.charCodeAt(slug.length - 1);
|
|
19290
|
+
const startsWithLetter = first >= 97 && first <= 122;
|
|
19291
|
+
const endsWithLetterOrNumber = last >= 97 && last <= 122 || last >= 48 && last <= 57;
|
|
19292
|
+
const hasOnlySlugCharacters = [...slug].every((char) => {
|
|
19293
|
+
const code = char.charCodeAt(0);
|
|
19294
|
+
return code >= 97 && code <= 122 || code >= 48 && code <= 57 || char === "-";
|
|
19295
|
+
});
|
|
19296
|
+
if (!startsWithLetter || !endsWithLetterOrNumber || !hasOnlySlugCharacters) throw new Error(`Extractor name "${name}" produced invalid slug "${slug}".`);
|
|
19297
|
+
}
|
|
19298
|
+
var Extractor = class Extractor {
|
|
19299
|
+
name;
|
|
19300
|
+
slug;
|
|
19301
|
+
instructions;
|
|
19302
|
+
schema;
|
|
19303
|
+
mode;
|
|
19304
|
+
includePreviousExtraction;
|
|
19305
|
+
metadataKeyPath;
|
|
19306
|
+
onExtracted;
|
|
19307
|
+
retryStructuredExtractionOnEmptyObject;
|
|
19308
|
+
/** @internal */
|
|
19309
|
+
internal;
|
|
19310
|
+
instructionsConfig;
|
|
19311
|
+
schemaConfig;
|
|
19312
|
+
constructor(config, internal = false) {
|
|
19313
|
+
const name = config.name.trim();
|
|
19314
|
+
const instructions = typeof config.instructions === "string" ? config.instructions.trim() : void 0;
|
|
19315
|
+
const slug = slugifyExtractorName(name);
|
|
19316
|
+
const isHook = config.mode === "hook";
|
|
19317
|
+
if (!name) throw new Error("Extractor name is required.");
|
|
19318
|
+
if (!isHook && !config.instructions) throw new Error(`Extractor "${name}" must include instructions.`);
|
|
19319
|
+
if (instructions !== void 0 && !instructions) throw new Error(`Extractor "${name}" must include instructions.`);
|
|
19320
|
+
if (isHook && !config.onExtracted) throw new Error(`Hook extractor "${name}" must include an onExtracted handler.`);
|
|
19321
|
+
if (isHook && (config.instructions || config.schema)) throw new Error(`Hook extractor "${name}" cannot include instructions or a schema.`);
|
|
19322
|
+
assertValidSlug(slug, name);
|
|
19323
|
+
if (!internal && RESERVED_XML_TAGS.has(slug)) throw new Error(`Extractor slug "${slug}" is reserved by Observational Memory.`);
|
|
19324
|
+
this.name = name;
|
|
19325
|
+
this.slug = slug;
|
|
19326
|
+
this.instructionsConfig = config.instructions ?? "";
|
|
19327
|
+
this.schemaConfig = config.schema;
|
|
19328
|
+
this.instructions = instructions ?? "";
|
|
19329
|
+
this.schema = isHook ? zod.z.unknown() : typeof config.schema === "function" ? zod.z.string() : config.schema ?? zod.z.string();
|
|
19330
|
+
this.mode = isHook ? "hook" : internal || !config.schema ? "inline" : "structured";
|
|
19331
|
+
this.includePreviousExtraction = isHook ? false : config.includePreviousExtraction ?? true;
|
|
19332
|
+
this.metadataKeyPath = isHook ? false : config.metadataKeyPath ?? `extracted.${slug}`;
|
|
19333
|
+
this.onExtracted = config.onExtracted;
|
|
19334
|
+
this.retryStructuredExtractionOnEmptyObject = config.retryStructuredExtractionOnEmptyObject ?? false;
|
|
19335
|
+
this.internal = internal;
|
|
19336
|
+
}
|
|
19337
|
+
async resolve(context) {
|
|
19338
|
+
if (this.mode === "hook") return this;
|
|
19339
|
+
const instructions = typeof this.instructionsConfig === "function" ? (await this.instructionsConfig(context)).trim() : this.instructionsConfig.trim();
|
|
19340
|
+
if (!instructions) throw new Error(`Extractor "${this.name}" must include instructions.`);
|
|
19341
|
+
const schema = typeof this.schemaConfig === "function" ? await this.schemaConfig(context) : this.schemaConfig;
|
|
19342
|
+
return new Extractor({
|
|
19343
|
+
name: this.name,
|
|
19344
|
+
instructions,
|
|
19345
|
+
...schema ? { schema } : {},
|
|
19346
|
+
includePreviousExtraction: this.includePreviousExtraction,
|
|
19347
|
+
metadataKeyPath: this.metadataKeyPath,
|
|
19348
|
+
onExtracted: this.onExtracted,
|
|
19349
|
+
retryStructuredExtractionOnEmptyObject: this.retryStructuredExtractionOnEmptyObject
|
|
19350
|
+
}, this.internal);
|
|
19351
|
+
}
|
|
19352
|
+
};
|
|
19353
|
+
async function resolveExtractors(extractors, context) {
|
|
19354
|
+
return Promise.all(extractors.map((extractor) => extractor.resolve(context)));
|
|
19355
|
+
}
|
|
19356
|
+
function validateExtractorList(extractors) {
|
|
19357
|
+
const seen = /* @__PURE__ */ new Map();
|
|
19358
|
+
for (const extractor of extractors) {
|
|
19359
|
+
assertValidSlug(extractor.slug, extractor.name);
|
|
19360
|
+
if (!extractor.internal && RESERVED_XML_TAGS.has(extractor.slug)) throw new Error(`Extractor slug "${extractor.slug}" is reserved by Observational Memory.`);
|
|
19361
|
+
const previous = seen.get(extractor.slug);
|
|
19362
|
+
if (previous) throw new Error(`Duplicate extractor slug "${extractor.slug}" from "${previous}" and "${extractor.name}".`);
|
|
19363
|
+
seen.set(extractor.slug, extractor.name);
|
|
19364
|
+
}
|
|
19365
|
+
return [...extractors];
|
|
19366
|
+
}
|
|
19367
|
+
function isJsonLike(value) {
|
|
19368
|
+
return /^(?:[\[{"-]|\d|true\b|false\b|null\b)/.test(value.trim());
|
|
19369
|
+
}
|
|
19370
|
+
function candidateValues(raw) {
|
|
19371
|
+
const trimmed = raw.trim();
|
|
19372
|
+
const candidates = [];
|
|
19373
|
+
const add = (value) => {
|
|
19374
|
+
if (!candidates.some((candidate) => Object.is(candidate, value))) candidates.push(value);
|
|
19375
|
+
};
|
|
19376
|
+
if (isJsonLike(trimmed)) try {
|
|
19377
|
+
add(JSON.parse(trimmed));
|
|
19378
|
+
} catch {}
|
|
19379
|
+
add(trimmed);
|
|
19380
|
+
if (!isJsonLike(trimmed)) try {
|
|
19381
|
+
add(JSON.parse(trimmed));
|
|
19382
|
+
} catch {}
|
|
19383
|
+
return candidates;
|
|
19384
|
+
}
|
|
19385
|
+
function parseExtractorValue(extractor, raw) {
|
|
19386
|
+
const failures = [];
|
|
19387
|
+
for (const candidate of candidateValues(raw)) {
|
|
19388
|
+
const parsed = extractor.schema.safeParse(candidate);
|
|
19389
|
+
if (parsed.success) return parsed.data;
|
|
19390
|
+
failures.push(parsed.error.message);
|
|
19391
|
+
}
|
|
19392
|
+
throw new Error(`Extractor "${extractor.slug}" output did not match its schema: ${failures[0] ?? "invalid value"}`);
|
|
19393
|
+
}
|
|
19394
|
+
function parseExtractedValuesObject(raw) {
|
|
19395
|
+
const trimmed = raw.trim();
|
|
19396
|
+
if (!trimmed || trimmed.startsWith("Write only the extracted values JSON object here.")) return;
|
|
19397
|
+
const parsed = JSON.parse(trimmed);
|
|
19398
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`${EXTRACTED_VALUES_TAG} must contain a JSON object.`);
|
|
19399
|
+
return parsed;
|
|
19400
|
+
}
|
|
19401
|
+
function parseExtractedValues(output, extractors) {
|
|
19402
|
+
const values = {};
|
|
19403
|
+
const failures = [];
|
|
19404
|
+
const inlineExtractors = extractors.filter((extractor) => extractor.mode === "inline");
|
|
19405
|
+
const regex = new RegExp(`<${EXTRACTED_VALUES_TAG}>([\\s\\S]*?)<\\/${EXTRACTED_VALUES_TAG}>`, "gi");
|
|
19406
|
+
const matches = [...output.matchAll(regex)];
|
|
19407
|
+
for (const match of matches) try {
|
|
19408
|
+
const extractedValues = parseExtractedValuesObject(match[1] ?? "");
|
|
19409
|
+
if (!extractedValues) continue;
|
|
19410
|
+
for (const extractor of inlineExtractors) {
|
|
19411
|
+
if (!Object.prototype.hasOwnProperty.call(extractedValues, extractor.slug)) continue;
|
|
19412
|
+
const rawValue = extractedValues[extractor.slug];
|
|
19413
|
+
if (rawValue === void 0 || rawValue === null || rawValue === "") continue;
|
|
19414
|
+
const parsed = extractor.schema.safeParse(rawValue);
|
|
19415
|
+
if (parsed.success) values[extractor.slug] = parsed.data;
|
|
19416
|
+
else failures.push({
|
|
19417
|
+
slug: extractor.slug,
|
|
19418
|
+
error: parsed.error.message
|
|
19419
|
+
});
|
|
19420
|
+
}
|
|
19421
|
+
} catch (error) {
|
|
19422
|
+
failures.push({
|
|
19423
|
+
slug: EXTRACTED_VALUES_TAG,
|
|
19424
|
+
error: error instanceof Error ? error.message : String(error)
|
|
19425
|
+
});
|
|
19426
|
+
}
|
|
19427
|
+
for (const extractor of inlineExtractors) {
|
|
19428
|
+
if (Object.prototype.hasOwnProperty.call(values, extractor.slug)) continue;
|
|
19429
|
+
const tagRegex = new RegExp(`<${extractor.slug}>([\\s\\S]*?)<\\/${extractor.slug}>`, "gi");
|
|
19430
|
+
const rawValue = [...output.matchAll(tagRegex)].at(-1)?.[1]?.trim();
|
|
19431
|
+
if (!rawValue) continue;
|
|
19432
|
+
try {
|
|
19433
|
+
values[extractor.slug] = parseExtractorValue(extractor, rawValue);
|
|
19434
|
+
} catch (error) {
|
|
19435
|
+
failures.push({
|
|
19436
|
+
slug: extractor.slug,
|
|
19437
|
+
error: error instanceof Error ? error.message : String(error)
|
|
19438
|
+
});
|
|
19439
|
+
}
|
|
19440
|
+
}
|
|
19441
|
+
return {
|
|
19442
|
+
values,
|
|
19443
|
+
failures
|
|
19444
|
+
};
|
|
19445
|
+
}
|
|
19446
|
+
function stripExtractorSections(output, extractors) {
|
|
19447
|
+
const inlineExtractors = extractors.filter((extractor) => extractor.mode === "inline");
|
|
19448
|
+
let stripped = output.replace(new RegExp(`[ \\t]*<${EXTRACTED_VALUES_TAG}>[\\s\\S]*?<\\/${EXTRACTED_VALUES_TAG}>\\s*`, "gi"), "");
|
|
19449
|
+
for (const extractor of inlineExtractors) stripped = stripped.replace(new RegExp(`[ \\t]*<${extractor.slug}>[\\s\\S]*?<\\/${extractor.slug}>\\s*`, "gi"), "");
|
|
19450
|
+
return stripped;
|
|
19451
|
+
}
|
|
19452
|
+
function buildExtractorOutputSections(extractors) {
|
|
19453
|
+
const inlineExtractors = extractors.filter((extractor) => extractor.mode === "inline");
|
|
19454
|
+
if (inlineExtractors.length === 0) return "";
|
|
19455
|
+
return `Additional optional XML sections:\nIf the observations include information relevant to any of these tags, output that tag after <observations> and include the relevant information.\n${inlineExtractors.map((extractor) => `<${extractor.slug}>
|
|
19456
|
+
${extractor.instructions}
|
|
19457
|
+
Include this section when the observations contain relevant information for <${extractor.slug}>. Write only that information inside the tag.
|
|
19458
|
+
</${extractor.slug}>`).join("\n\n")}`;
|
|
19459
|
+
}
|
|
19460
|
+
function renderPriorValue(value) {
|
|
19461
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
19462
|
+
}
|
|
19463
|
+
function buildExtractorPriorLines(extractors, priorExtractedValues) {
|
|
19464
|
+
if (!priorExtractedValues) return [];
|
|
19465
|
+
const lines = [];
|
|
19466
|
+
for (const extractor of extractors) {
|
|
19467
|
+
if (!extractor.includePreviousExtraction) continue;
|
|
19468
|
+
const value = priorExtractedValues[extractor.slug];
|
|
19469
|
+
if (value === void 0 || value === null || value === "") continue;
|
|
19470
|
+
lines.push(`<${extractor.slug}>\n${renderPriorValue(value)}\n</${extractor.slug}>`);
|
|
19471
|
+
}
|
|
19472
|
+
return lines;
|
|
19473
|
+
}
|
|
19474
|
+
const SUBCONSCIOUS_ACTIVITY_STATE_ID = "subconscious-activity";
|
|
19475
|
+
async function getActivityTarget(store, event, scope) {
|
|
19476
|
+
if (event.recordType === "node") {
|
|
19477
|
+
const node = await store.getNode(event.recordId);
|
|
19478
|
+
if (!node || !(0, _mastra_core_storage.isKnowledgeScopeVisible)(node.scope, scope)) return {
|
|
19479
|
+
id: event.recordId,
|
|
19480
|
+
type: "node"
|
|
19481
|
+
};
|
|
19482
|
+
return {
|
|
19483
|
+
id: node.id,
|
|
19484
|
+
name: node.name,
|
|
19485
|
+
type: "node"
|
|
19486
|
+
};
|
|
19487
|
+
}
|
|
19488
|
+
const record = await store.getKnowledge({
|
|
19489
|
+
id: event.recordId,
|
|
19490
|
+
includeDeleted: true
|
|
19491
|
+
});
|
|
19492
|
+
const node = record ? await store.getNode(record.node) : void 0;
|
|
19493
|
+
if (!node || !(0, _mastra_core_storage.isKnowledgeScopeVisible)(node.scope, scope)) return {
|
|
19494
|
+
id: event.recordId,
|
|
19495
|
+
type: "node"
|
|
19496
|
+
};
|
|
19497
|
+
return {
|
|
19498
|
+
id: node.id,
|
|
19499
|
+
name: node.name,
|
|
19500
|
+
type: "node"
|
|
19501
|
+
};
|
|
19502
|
+
}
|
|
19503
|
+
async function buildSubconsciousActivitySnapshot(input) {
|
|
19504
|
+
const events = await input.store.listActivity({
|
|
19505
|
+
scope: input.scope,
|
|
19506
|
+
limit: input.recentUpdates
|
|
19507
|
+
});
|
|
19508
|
+
const resolvedUpdates = await Promise.all(events.map(async (event) => {
|
|
19509
|
+
const target = await getActivityTarget(input.store, event, input.scope);
|
|
19510
|
+
return {
|
|
19511
|
+
action: event.action,
|
|
19512
|
+
type: event.recordType,
|
|
19513
|
+
name: target.name,
|
|
19514
|
+
targetId: target.id,
|
|
19515
|
+
targetType: target.type,
|
|
19516
|
+
createdAt: event.createdAt.toISOString()
|
|
19517
|
+
};
|
|
19518
|
+
}));
|
|
19519
|
+
const hotByRecord = /* @__PURE__ */ new Map();
|
|
19520
|
+
for (const update of resolvedUpdates) {
|
|
19521
|
+
if (!update.name) continue;
|
|
19522
|
+
const key = `${update.targetType}:${update.targetId}`;
|
|
19523
|
+
const existing = hotByRecord.get(key);
|
|
19524
|
+
if (existing) existing.updates += 1;
|
|
19525
|
+
else hotByRecord.set(key, {
|
|
19526
|
+
type: update.targetType,
|
|
19527
|
+
name: update.name,
|
|
19528
|
+
updates: 1
|
|
19529
|
+
});
|
|
19530
|
+
}
|
|
19531
|
+
const hot = [...hotByRecord.values()].sort((a, b) => b.updates - a.updates || a.name.localeCompare(b.name)).slice(0, input.recentUpdates);
|
|
19532
|
+
const updates = resolvedUpdates.map((update) => ({
|
|
19533
|
+
action: update.action,
|
|
19534
|
+
type: update.type,
|
|
19535
|
+
...update.name ? { name: update.name } : {},
|
|
19536
|
+
createdAt: update.createdAt
|
|
19537
|
+
}));
|
|
19538
|
+
const errors = input.errors?.filter(Boolean).slice(0, input.recentUpdates);
|
|
19539
|
+
return {
|
|
19540
|
+
updates,
|
|
19541
|
+
hot,
|
|
19542
|
+
...errors?.length ? { errors } : {}
|
|
19543
|
+
};
|
|
19544
|
+
}
|
|
19545
|
+
function renderSubconsciousActivity(snapshot) {
|
|
19546
|
+
const lines = snapshot.updates.map((update) => {
|
|
19547
|
+
const target = update.name ? `${update.type} [[${update.name}]]` : update.type;
|
|
19548
|
+
return `- ${update.action}: ${target}`;
|
|
19549
|
+
});
|
|
19550
|
+
const hot = snapshot.hot.map((record) => `[[${record.name}]] (${record.updates})`).join(", ");
|
|
19551
|
+
return [
|
|
19552
|
+
hot ? `Hot: ${hot}` : "Hot: none",
|
|
19553
|
+
"Recent updates:",
|
|
19554
|
+
...lines.length ? lines : ["- none"],
|
|
19555
|
+
...snapshot.errors?.length ? ["Errors:", ...snapshot.errors.map((error) => `- ${error}`)] : []
|
|
19556
|
+
].join("\n");
|
|
19557
|
+
}
|
|
19558
|
+
async function publishSubconsciousError(input) {
|
|
19559
|
+
await input.writer?.custom({
|
|
19560
|
+
type: "data-subconscious-error",
|
|
19561
|
+
data: {
|
|
19562
|
+
error: input.error,
|
|
19563
|
+
agent: input.agent
|
|
19564
|
+
}
|
|
19565
|
+
});
|
|
19566
|
+
if (!input.sendStateSignal) return;
|
|
19567
|
+
const snapshot = {
|
|
19568
|
+
updates: [],
|
|
19569
|
+
hot: [],
|
|
19570
|
+
errors: [input.error]
|
|
19571
|
+
};
|
|
19572
|
+
const contents = renderSubconsciousActivity(snapshot);
|
|
19573
|
+
await input.sendStateSignal({
|
|
19574
|
+
id: SUBCONSCIOUS_ACTIVITY_STATE_ID,
|
|
19575
|
+
mode: "snapshot",
|
|
19576
|
+
cacheKey: (0, crypto$1.createHash)("sha256").update(contents).digest("hex"),
|
|
19577
|
+
tagName: "state",
|
|
19578
|
+
attributes: { id: SUBCONSCIOUS_ACTIVITY_STATE_ID },
|
|
19579
|
+
metadata: { origin: "subconscious" },
|
|
19580
|
+
contents,
|
|
19581
|
+
value: snapshot
|
|
19582
|
+
});
|
|
19583
|
+
}
|
|
19584
|
+
async function publishSubconsciousActivity(input) {
|
|
19585
|
+
if (!input.sendStateSignal) return void 0;
|
|
19586
|
+
const snapshot = await buildSubconsciousActivitySnapshot(input);
|
|
19587
|
+
const contents = renderSubconsciousActivity(snapshot);
|
|
19588
|
+
const cacheKey = (0, crypto$1.createHash)("sha256").update(contents).digest("hex");
|
|
19589
|
+
await input.sendStateSignal({
|
|
19590
|
+
id: SUBCONSCIOUS_ACTIVITY_STATE_ID,
|
|
19591
|
+
mode: "snapshot",
|
|
19592
|
+
cacheKey,
|
|
19593
|
+
tagName: "state",
|
|
19594
|
+
attributes: { id: SUBCONSCIOUS_ACTIVITY_STATE_ID },
|
|
19595
|
+
metadata: { origin: "subconscious" },
|
|
19596
|
+
contents,
|
|
19597
|
+
value: snapshot
|
|
19598
|
+
});
|
|
19599
|
+
return snapshot;
|
|
19600
|
+
}
|
|
19601
|
+
/** Processor id and state-signal id for the pinned-knowledge lane. */
|
|
19602
|
+
const SUBCONSCIOUS_PINS_STATE_ID = "subconscious-pins";
|
|
19603
|
+
/** Snapshot tag the model sees; the delta tag appends `-update`. */
|
|
19604
|
+
const PINNED_SNAPSHOT_TAG = "pinned-knowledge";
|
|
19605
|
+
const PINNED_DELTA_TAG = "pinned-knowledge-update";
|
|
19606
|
+
/** Reserved node holding the pin set. One record, at one fixed scope level. */
|
|
19607
|
+
const PINNED_NODE_NAME = "pinned";
|
|
19608
|
+
const PINNED_NODE_KIND = "system";
|
|
19609
|
+
const PINNED_NODE_SCOPE_LEVEL = "resource";
|
|
19610
|
+
const DEFAULT_PINNED_MAX_CHARACTERS = 2e3;
|
|
19611
|
+
const MAX_PINNED_MAX_CHARACTERS = 8e3;
|
|
19612
|
+
const PIN_IDENTITY = "subconscious:pin";
|
|
19613
|
+
function pinnedNodeScope(scope, maxScope) {
|
|
19614
|
+
return (0, _mastra_core_storage.expandKnowledgeScope)(scope, maxScope === "thread" ? "thread" : PINNED_NODE_SCOPE_LEVEL);
|
|
19615
|
+
}
|
|
19616
|
+
async function resolvePinnedNodeId(store, scope) {
|
|
19617
|
+
return (await store.resolveNode({
|
|
19618
|
+
name: PINNED_NODE_NAME,
|
|
19619
|
+
scope
|
|
19620
|
+
}))?.id;
|
|
19621
|
+
}
|
|
19622
|
+
/** Reuse the node wherever it is visible; otherwise create it. `createNode` is an idempotent upsert on (name, scope). */
|
|
19623
|
+
async function ensurePinnedNodeId(store, scope, maxScope) {
|
|
19624
|
+
const existing = await resolvePinnedNodeId(store, scope);
|
|
19625
|
+
if (existing) return existing;
|
|
19626
|
+
return (await store.createNode({
|
|
19627
|
+
name: PINNED_NODE_NAME,
|
|
19628
|
+
kind: PINNED_NODE_KIND,
|
|
19629
|
+
scope: pinnedNodeScope(scope, maxScope)
|
|
19630
|
+
})).id;
|
|
19631
|
+
}
|
|
19632
|
+
/**
|
|
19633
|
+
* Assembles the current pin set.
|
|
19634
|
+
*
|
|
19635
|
+
* Reads use the FULL visible scope context, never a level-narrowed write scope: visibility is
|
|
19636
|
+
* subset containment, so querying at the node's level would drop pins written at narrower
|
|
19637
|
+
* levels. Deleted records are excluded explicitly.
|
|
19638
|
+
*/
|
|
19639
|
+
async function listPinnedKnowledge(input) {
|
|
19640
|
+
const nodeId = await resolvePinnedNodeId(input.store, input.scope);
|
|
19641
|
+
if (!nodeId) return { pins: [] };
|
|
19642
|
+
const pins = [];
|
|
19643
|
+
let after;
|
|
19644
|
+
do {
|
|
19645
|
+
const page = await input.store.listKnowledgeAbout({
|
|
19646
|
+
node: nodeId,
|
|
19647
|
+
scope: input.scope,
|
|
19648
|
+
after,
|
|
19649
|
+
includeDeleted: false
|
|
19650
|
+
});
|
|
19651
|
+
pins.push(...page.records);
|
|
19652
|
+
after = page.nextCursor;
|
|
19653
|
+
} while (after);
|
|
19654
|
+
return {
|
|
19655
|
+
nodeId,
|
|
19656
|
+
pins
|
|
19657
|
+
};
|
|
19658
|
+
}
|
|
19659
|
+
function totalCharacters(pins) {
|
|
19660
|
+
return pins.reduce((sum, pin) => sum + pin.text.length, 0);
|
|
19661
|
+
}
|
|
19662
|
+
function assertBudget(options, pins, incomingText, replacing) {
|
|
19663
|
+
const kept = replacing ? pins.filter((pin) => pin.id !== replacing.id) : pins;
|
|
19664
|
+
if (!replacing && kept.length >= options.maxPins) throw new Error(`Pin limit reached: the set holds at most ${options.maxPins}. Unpin something first.`);
|
|
19665
|
+
if (totalCharacters(kept) + incomingText.length > options.maxCharacters) throw new Error(`Pin budget exceeded: the pin set is limited to ${options.maxCharacters} characters in total.`);
|
|
19666
|
+
}
|
|
19667
|
+
function clampPinLevel(level) {
|
|
19668
|
+
return level === "org" ? "resource" : level;
|
|
19669
|
+
}
|
|
19670
|
+
function resolveWriteScope$1(options, level) {
|
|
19671
|
+
let effective = clampPinLevel(level ?? options.defaultScope);
|
|
19672
|
+
if (!level && options.maxScope === "thread") effective = "thread";
|
|
19673
|
+
const scope = (0, _mastra_core_storage.expandKnowledgeScope)(options.scope, effective);
|
|
19674
|
+
(0, _mastra_core_storage.assertKnowledgeScopeWithinCeiling)(scope, options.maxScope);
|
|
19675
|
+
return scope;
|
|
19676
|
+
}
|
|
19677
|
+
const scopeLevelSchema$1 = {
|
|
19678
|
+
type: "string",
|
|
19679
|
+
enum: ["resource", "thread"]
|
|
19680
|
+
};
|
|
19681
|
+
/** Shared pin write path used by the tool and capture-time pinning. */
|
|
19682
|
+
async function writePinnedKnowledge(store, options, text, level, metadata) {
|
|
19683
|
+
const { pins } = await listPinnedKnowledge({
|
|
19684
|
+
store,
|
|
19685
|
+
scope: options.scope
|
|
19686
|
+
});
|
|
19687
|
+
assertBudget(options, pins, text);
|
|
19688
|
+
const nodeId = await ensurePinnedNodeId(store, options.scope, options.maxScope);
|
|
19689
|
+
return store.appendKnowledge({
|
|
19690
|
+
node: nodeId,
|
|
19691
|
+
text,
|
|
19692
|
+
scope: resolveWriteScope$1(options, level),
|
|
19693
|
+
sourceThreadId: options.sourceThreadId,
|
|
19694
|
+
maxScope: options.maxScope,
|
|
19695
|
+
metadata,
|
|
19696
|
+
resolutionScope: options.scope,
|
|
19697
|
+
defaultScope: (0, _mastra_core_storage.expandKnowledgeScope)(options.scope, options.defaultScope)
|
|
19698
|
+
});
|
|
19699
|
+
}
|
|
19700
|
+
async function getStore$1(memory) {
|
|
19701
|
+
const store = await memory.storage.getStore("knowledge");
|
|
19702
|
+
if (!store) throw new Error("Pinned knowledge requires a configured knowledge storage domain.");
|
|
19703
|
+
return store;
|
|
19704
|
+
}
|
|
19705
|
+
async function requirePin(store, recordId, options) {
|
|
19706
|
+
const record = await store.getKnowledge({
|
|
19707
|
+
id: recordId,
|
|
19708
|
+
includeDeleted: false
|
|
19709
|
+
});
|
|
19710
|
+
if (!record) throw new Error(`Pin not found: ${recordId}`);
|
|
19711
|
+
const nodeId = await resolvePinnedNodeId(store, options.scope);
|
|
19712
|
+
if (!nodeId || record.node !== nodeId) throw new Error(`Record is not a pin: ${recordId}`);
|
|
19713
|
+
if (!(0, _mastra_core_storage.isKnowledgeScopeVisible)(record.scope, options.scope)) throw new Error("Pin is outside the visible scope.");
|
|
19714
|
+
return record;
|
|
19715
|
+
}
|
|
19716
|
+
/**
|
|
19717
|
+
* Pin lifecycle tools. Pin appends a record on the reserved node; unpin soft-deletes it
|
|
19718
|
+
* (auditable, restorable); edit is remove plus append because knowledge records are immutable,
|
|
19719
|
+
* so an edited pin carries a new record id.
|
|
19720
|
+
*/
|
|
19721
|
+
function createPinnedTools(memory, options) {
|
|
19722
|
+
return {
|
|
19723
|
+
knowledge_pin: (0, _mastra_core_tools.createTool)({
|
|
19724
|
+
id: "knowledge_pin",
|
|
19725
|
+
description: "Pin knowledge that must stay in context every turn without being asked for. Pins cost context permanently; pin only what is unconditionally relevant.",
|
|
19726
|
+
inputSchema: {
|
|
19727
|
+
type: "object",
|
|
19728
|
+
properties: {
|
|
19729
|
+
text: {
|
|
19730
|
+
type: "string",
|
|
19731
|
+
minLength: 1
|
|
19732
|
+
},
|
|
19733
|
+
scope: scopeLevelSchema$1,
|
|
19734
|
+
reason: {
|
|
19735
|
+
type: "string",
|
|
19736
|
+
minLength: 1,
|
|
19737
|
+
description: "One short sentence: why this must stay in context permanently."
|
|
19738
|
+
}
|
|
19739
|
+
},
|
|
19740
|
+
required: ["text"],
|
|
19741
|
+
additionalProperties: false
|
|
19742
|
+
},
|
|
19743
|
+
execute: async (input) => {
|
|
19744
|
+
const value = input;
|
|
19745
|
+
return writePinnedKnowledge(await getStore$1(memory), options, value.text, value.scope, value.reason ? { reason: value.reason } : void 0);
|
|
19746
|
+
}
|
|
19747
|
+
}),
|
|
19748
|
+
knowledge_unpin: (0, _mastra_core_tools.createTool)({
|
|
19749
|
+
id: "knowledge_unpin",
|
|
19750
|
+
description: "Remove a pin. The underlying knowledge record is soft-deleted and drops out of the pinned context.",
|
|
19751
|
+
inputSchema: {
|
|
19752
|
+
type: "object",
|
|
19753
|
+
properties: { recordId: {
|
|
19754
|
+
type: "string",
|
|
19755
|
+
minLength: 1
|
|
19756
|
+
} },
|
|
19757
|
+
required: ["recordId"],
|
|
19758
|
+
additionalProperties: false
|
|
19759
|
+
},
|
|
19760
|
+
execute: async (input) => {
|
|
19761
|
+
const store = await getStore$1(memory);
|
|
19762
|
+
const record = await requirePin(store, input.recordId, options);
|
|
19763
|
+
return store.removeKnowledge({
|
|
19764
|
+
id: record.id,
|
|
19765
|
+
deletedBy: PIN_IDENTITY
|
|
19766
|
+
});
|
|
19767
|
+
}
|
|
19768
|
+
}),
|
|
19769
|
+
knowledge_edit_pin: (0, _mastra_core_tools.createTool)({
|
|
19770
|
+
id: "knowledge_edit_pin",
|
|
19771
|
+
description: "Replace the text of an existing pin. The replacement carries a new knowledge record id.",
|
|
19772
|
+
inputSchema: {
|
|
19773
|
+
type: "object",
|
|
19774
|
+
properties: {
|
|
19775
|
+
recordId: {
|
|
19776
|
+
type: "string",
|
|
19777
|
+
minLength: 1
|
|
19778
|
+
},
|
|
19779
|
+
text: {
|
|
19780
|
+
type: "string",
|
|
19781
|
+
minLength: 1
|
|
19782
|
+
},
|
|
19783
|
+
reason: {
|
|
19784
|
+
type: "string",
|
|
19785
|
+
minLength: 1,
|
|
19786
|
+
description: "One short sentence: why this must stay in context permanently."
|
|
19787
|
+
}
|
|
19788
|
+
},
|
|
19789
|
+
required: ["recordId", "text"],
|
|
19790
|
+
additionalProperties: false
|
|
19791
|
+
},
|
|
19792
|
+
execute: async (input) => {
|
|
19793
|
+
const value = input;
|
|
19794
|
+
const store = await getStore$1(memory);
|
|
19795
|
+
const record = await requirePin(store, value.recordId, options);
|
|
19796
|
+
const { pins } = await listPinnedKnowledge({
|
|
19797
|
+
store,
|
|
19798
|
+
scope: options.scope
|
|
19799
|
+
});
|
|
19800
|
+
assertBudget(options, pins, value.text, record);
|
|
19801
|
+
await store.removeKnowledge({
|
|
19802
|
+
id: record.id,
|
|
19803
|
+
deletedBy: PIN_IDENTITY
|
|
19804
|
+
});
|
|
19805
|
+
return store.appendKnowledge({
|
|
19806
|
+
node: record.node,
|
|
19807
|
+
text: value.text,
|
|
19808
|
+
scope: record.scope,
|
|
19809
|
+
sourceThreadId: options.sourceThreadId,
|
|
19810
|
+
maxScope: record.maxScope,
|
|
19811
|
+
metadata: value.reason ? {
|
|
19812
|
+
...record.metadata,
|
|
19813
|
+
reason: value.reason
|
|
19814
|
+
} : record.metadata,
|
|
19815
|
+
resolutionScope: options.scope,
|
|
19816
|
+
defaultScope: (0, _mastra_core_storage.expandKnowledgeScope)(options.scope, options.defaultScope)
|
|
19817
|
+
});
|
|
19818
|
+
}
|
|
19819
|
+
})
|
|
19820
|
+
};
|
|
19821
|
+
}
|
|
19822
|
+
/**
|
|
19823
|
+
* Resolves the resource rung of a subconscious knowledge scope.
|
|
19824
|
+
*
|
|
19825
|
+
* When the request context carries a `knowledgeResourceId` override, every
|
|
19826
|
+
* scope resolver anchors the resource rung on it instead of the run's
|
|
19827
|
+
* resourceId. This lets a host (for example, the factory) share one knowledge
|
|
19828
|
+
* graph across many runs of the same project while leaving thread identity and
|
|
19829
|
+
* observational memory untouched. Without the override, behavior is unchanged:
|
|
19830
|
+
* the fallback (the run's resourceId) is used as-is.
|
|
19831
|
+
*/
|
|
19832
|
+
function resolveKnowledgeResourceId(requestContext, fallback) {
|
|
19833
|
+
const override = requestContext?.get?.("knowledgeResourceId");
|
|
19834
|
+
if (typeof override === "string" && override.trim()) return override;
|
|
19835
|
+
return fallback;
|
|
19836
|
+
}
|
|
19837
|
+
const CAPTURE_GUIDANCE_PAGE = "capture-guidance";
|
|
19838
|
+
const MAX_CAPTURE_GUIDANCE_LENGTH = 4e3;
|
|
19839
|
+
const SCOPE_ORDER = {
|
|
19840
|
+
org: 0,
|
|
19841
|
+
resource: 1,
|
|
19842
|
+
thread: 2
|
|
19843
|
+
};
|
|
19844
|
+
const subconsciousCaptureSchema = zod.z.object({ nodes: zod.z.array(zod.z.object({
|
|
19845
|
+
name: zod.z.string().trim().min(1),
|
|
19846
|
+
kind: zod.z.string().trim().min(1),
|
|
19847
|
+
scope: zod.z.enum([
|
|
19848
|
+
"org",
|
|
19849
|
+
"resource",
|
|
19850
|
+
"thread"
|
|
19851
|
+
]).optional(),
|
|
19852
|
+
records: zod.z.array(zod.z.object({
|
|
19853
|
+
text: zod.z.string().trim().min(1),
|
|
19854
|
+
scope: zod.z.enum([
|
|
19855
|
+
"org",
|
|
19856
|
+
"resource",
|
|
19857
|
+
"thread"
|
|
19858
|
+
]).optional(),
|
|
19859
|
+
when: zod.z.string().trim().min(1).optional(),
|
|
19860
|
+
reason: zod.z.string().trim().min(1)
|
|
19861
|
+
}))
|
|
19862
|
+
})) });
|
|
19863
|
+
const subconsciousCapturePinningSchema = zod.z.object({ nodes: zod.z.array(zod.z.object({
|
|
19864
|
+
name: zod.z.string().trim().min(1),
|
|
19865
|
+
kind: zod.z.string().trim().min(1),
|
|
19866
|
+
scope: zod.z.enum([
|
|
19867
|
+
"org",
|
|
19868
|
+
"resource",
|
|
19869
|
+
"thread"
|
|
19870
|
+
]).optional(),
|
|
19871
|
+
records: zod.z.array(zod.z.object({
|
|
19872
|
+
text: zod.z.string().trim().min(1),
|
|
19873
|
+
scope: zod.z.enum([
|
|
19874
|
+
"org",
|
|
19875
|
+
"resource",
|
|
19876
|
+
"thread"
|
|
19877
|
+
]).optional(),
|
|
19878
|
+
when: zod.z.string().trim().min(1).optional(),
|
|
19879
|
+
reason: zod.z.string().trim().min(1),
|
|
19880
|
+
pin: zod.z.boolean().optional()
|
|
19881
|
+
}))
|
|
19882
|
+
})) });
|
|
19883
|
+
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.`;
|
|
19884
|
+
const CAPTURE_INSTRUCTIONS = `Extract durable, explicitly stated knowledge from the observations.
|
|
19885
|
+
Return nodes with short stable names, a freeform kind, and knowledge records nested under the node each record is about.
|
|
19886
|
+
Use common kinds such as person, task, event, project, organization, or document when they fit.
|
|
19887
|
+
Set node scope to the narrowest level where that identity and content should be shared. Omit it to use the configured default scope.
|
|
19888
|
+
Knowledge records must be grounded in the conversation, concise, and written as prose. Do not infer unstated information.
|
|
19889
|
+
Wrap every named node mentioned in record text in [[wikilinks]].
|
|
19890
|
+
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.
|
|
19891
|
+
Omit scope when uncertain; omitted record scopes stay private to the current thread.
|
|
19892
|
+
Emit when only when the conversation anchors the referred time. Resolve relative dates against the current date and use ISO 8601.
|
|
19893
|
+
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.`;
|
|
19894
|
+
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".`;
|
|
19895
|
+
function clampScope(level, ceiling) {
|
|
19896
|
+
return ceiling && SCOPE_ORDER[level] < SCOPE_ORDER[ceiling] ? ceiling : level;
|
|
19897
|
+
}
|
|
19898
|
+
function requireScopeContext(context) {
|
|
19899
|
+
const organizationId = context.requestContext?.get("organizationId");
|
|
19900
|
+
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.");
|
|
19901
|
+
const resourceId = resolveKnowledgeResourceId(context.requestContext, context.resourceId);
|
|
19902
|
+
if (!resourceId) throw new Error("Subconscious requires resourceId to derive scoped knowledge.");
|
|
19903
|
+
if (!context.threadId) throw new Error("Subconscious requires threadId to derive scoped knowledge.");
|
|
19904
|
+
return [
|
|
19905
|
+
`org:${organizationId}`,
|
|
19906
|
+
`resource:${resourceId}`,
|
|
19907
|
+
`thread:${context.threadId}`
|
|
19908
|
+
];
|
|
19909
|
+
}
|
|
19910
|
+
async function getKnowledgeStore$1(context) {
|
|
19911
|
+
if (!context.memory) throw new Error("Subconscious capture requires an active Memory instance.");
|
|
19912
|
+
const store = await context.memory.storage.getStore("knowledge");
|
|
19913
|
+
if (!store) throw new Error("Subconscious requires a knowledge storage domain. Configure a storage adapter that provides stores.knowledge.");
|
|
19914
|
+
return store;
|
|
19915
|
+
}
|
|
19916
|
+
function parseWhen(value) {
|
|
19917
|
+
if (!value) return void 0;
|
|
19918
|
+
const when = new Date(value);
|
|
19919
|
+
if (Number.isNaN(when.getTime())) throw new Error(`Invalid Subconscious record time: ${value}`);
|
|
19920
|
+
return when;
|
|
19921
|
+
}
|
|
19922
|
+
var SubconsciousCaptureExtractor = class extends Extractor {
|
|
19923
|
+
constructor(options) {
|
|
19924
|
+
const capturePinning = options.pins !== false && options.pins !== void 0 && options.pins.capturePinning;
|
|
19925
|
+
const pinNotes = /* @__PURE__ */ new WeakMap();
|
|
19926
|
+
const defaultImplementation = async (context) => {
|
|
19927
|
+
const scopeContext = requireScopeContext(context);
|
|
19928
|
+
const store = await getKnowledgeStore$1(context);
|
|
19929
|
+
const droppedPins = [];
|
|
19930
|
+
for (const extractedNode of context.current.nodes) {
|
|
19931
|
+
const nodeScope = (0, _mastra_core_storage.expandKnowledgeScope)(scopeContext, clampScope(extractedNode.scope ?? options.defaultScope, options.maxScope));
|
|
19932
|
+
const node = await store.createNode({
|
|
19933
|
+
name: extractedNode.name,
|
|
19934
|
+
kind: extractedNode.kind,
|
|
19935
|
+
scope: nodeScope
|
|
19936
|
+
});
|
|
19937
|
+
for (const extractedKnowledge of extractedNode.records) {
|
|
19938
|
+
if (capturePinning && extractedKnowledge.pin === true && options.pins) {
|
|
19939
|
+
try {
|
|
19940
|
+
await writePinnedKnowledge(store, {
|
|
19941
|
+
scope: scopeContext,
|
|
19942
|
+
sourceThreadId: context.threadId,
|
|
19943
|
+
defaultScope: options.defaultScope,
|
|
19944
|
+
maxScope: options.maxScope,
|
|
19945
|
+
maxPins: options.pins.maxPins,
|
|
19946
|
+
maxCharacters: options.pins.maxCharacters
|
|
19947
|
+
}, extractedKnowledge.text, extractedKnowledge.scope, extractedKnowledge.reason ? { reason: extractedKnowledge.reason } : void 0);
|
|
19948
|
+
} catch (error) {
|
|
19949
|
+
droppedPins.push(`Capture-time pin dropped: ${error instanceof Error ? error.message : String(error)}`);
|
|
19950
|
+
}
|
|
19951
|
+
continue;
|
|
19952
|
+
}
|
|
19953
|
+
const knowledgeLevel = clampScope(extractedKnowledge.scope ?? "thread", options.maxScope);
|
|
19954
|
+
await store.appendKnowledge({
|
|
19955
|
+
node,
|
|
19956
|
+
text: extractedKnowledge.text,
|
|
19957
|
+
scope: (0, _mastra_core_storage.expandKnowledgeScope)(scopeContext, knowledgeLevel),
|
|
19958
|
+
sourceThreadId: context.threadId,
|
|
19959
|
+
when: parseWhen(extractedKnowledge.when),
|
|
19960
|
+
maxScope: options.maxScope,
|
|
19961
|
+
metadata: extractedKnowledge.reason ? { reason: extractedKnowledge.reason } : void 0,
|
|
19962
|
+
resolutionScope: scopeContext,
|
|
19963
|
+
defaultScope: nodeScope
|
|
19964
|
+
});
|
|
19965
|
+
}
|
|
19966
|
+
}
|
|
19967
|
+
if (droppedPins.length) pinNotes.set(context.current, droppedPins);
|
|
19968
|
+
};
|
|
19969
|
+
super({
|
|
19970
|
+
name: "Capture",
|
|
19971
|
+
includePreviousExtraction: false,
|
|
19972
|
+
metadataKeyPath: false,
|
|
19973
|
+
schema: options.config?.schema ?? (capturePinning ? subconsciousCapturePinningSchema : subconsciousCaptureSchema),
|
|
19974
|
+
instructions: async (context) => {
|
|
19975
|
+
const sections = [
|
|
19976
|
+
CAPTURE_INSTRUCTIONS,
|
|
19977
|
+
!options.config?.schema ? CAPTURE_REASON_INSTRUCTIONS : void 0,
|
|
19978
|
+
capturePinning && !options.config?.schema ? CAPTURE_PINNING_INSTRUCTIONS : void 0,
|
|
19979
|
+
options.config?.instructions?.trim()
|
|
19980
|
+
];
|
|
19981
|
+
if (options.learnedGuidance) {
|
|
19982
|
+
const scopeContext = requireScopeContext(context);
|
|
19983
|
+
const store = await getKnowledgeStore$1(context);
|
|
19984
|
+
const guidanceScope = (0, _mastra_core_storage.expandKnowledgeScope)(scopeContext, clampScope(options.defaultScope, options.maxScope));
|
|
19985
|
+
const guidance = await store.getNodeByName({
|
|
19986
|
+
name: CAPTURE_GUIDANCE_PAGE,
|
|
19987
|
+
scope: guidanceScope
|
|
19988
|
+
});
|
|
19989
|
+
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)}`);
|
|
19990
|
+
}
|
|
19991
|
+
return sections.filter(Boolean).join("\n\n");
|
|
19992
|
+
},
|
|
19993
|
+
onExtracted: async (context) => {
|
|
19994
|
+
const publishActivity = async (errors) => {
|
|
19995
|
+
if (!options.activityRecentUpdates) return;
|
|
19996
|
+
const scope = requireScopeContext(context);
|
|
19997
|
+
await publishSubconsciousActivity({
|
|
19998
|
+
store: await getKnowledgeStore$1(context),
|
|
19999
|
+
scope,
|
|
20000
|
+
recentUpdates: options.activityRecentUpdates,
|
|
20001
|
+
sendStateSignal: context.sendStateSignal,
|
|
20002
|
+
errors
|
|
20003
|
+
});
|
|
20004
|
+
};
|
|
20005
|
+
try {
|
|
20006
|
+
const result = options.config?.onExtracted ? await options.config.onExtracted({
|
|
20007
|
+
...context,
|
|
20008
|
+
defaultImplementation
|
|
20009
|
+
}) : await defaultImplementation(context);
|
|
20010
|
+
const droppedPinNotes = pinNotes.get(context.current);
|
|
20011
|
+
pinNotes.delete(context.current);
|
|
20012
|
+
await publishActivity(droppedPinNotes);
|
|
20013
|
+
return result ?? context.current;
|
|
20014
|
+
} catch (error) {
|
|
20015
|
+
await publishActivity([error instanceof Error ? error.message : String(error)]).catch(() => {});
|
|
20016
|
+
throw error;
|
|
20017
|
+
}
|
|
20018
|
+
}
|
|
20019
|
+
});
|
|
20020
|
+
}
|
|
20021
|
+
};
|
|
20022
|
+
const DEFAULT_LIMIT = 10;
|
|
20023
|
+
const MAX_LIMIT = 50;
|
|
20024
|
+
function resolveScope$3(context) {
|
|
20025
|
+
const organizationId = context?.requestContext?.get("organizationId");
|
|
20026
|
+
const resourceId = resolveKnowledgeResourceId(context?.requestContext, context?.agent?.resourceId);
|
|
20027
|
+
const threadId = context?.agent?.threadId;
|
|
20028
|
+
if (typeof organizationId !== "string" || !organizationId.trim()) throw new Error("Knowledge tools require requestContext.organizationId.");
|
|
20029
|
+
if (!resourceId) throw new Error("Knowledge tools require an active resourceId.");
|
|
20030
|
+
if (!threadId) throw new Error("Knowledge tools require an active threadId.");
|
|
20031
|
+
return [
|
|
20032
|
+
`org:${organizationId}`,
|
|
20033
|
+
`resource:${resourceId}`,
|
|
20034
|
+
`thread:${threadId}`
|
|
20035
|
+
];
|
|
20036
|
+
}
|
|
20037
|
+
async function getKnowledgeStore(memory) {
|
|
20038
|
+
const store = await memory.storage.getStore("knowledge");
|
|
20039
|
+
if (!store) throw new Error("Knowledge tools require a configured knowledge storage domain.");
|
|
20040
|
+
return store;
|
|
20041
|
+
}
|
|
20042
|
+
function normalizeLimit(limit) {
|
|
20043
|
+
return Math.min(Math.max(limit ?? DEFAULT_LIMIT, 1), MAX_LIMIT);
|
|
20044
|
+
}
|
|
20045
|
+
function serializeRecord(record) {
|
|
20046
|
+
return {
|
|
20047
|
+
id: record.id,
|
|
20048
|
+
text: record.text,
|
|
20049
|
+
scope: record.scope,
|
|
20050
|
+
sourceThreadId: record.sourceThreadId,
|
|
20051
|
+
capturedAt: record.capturedAt.toISOString(),
|
|
20052
|
+
when: record.when?.toISOString()
|
|
20053
|
+
};
|
|
20054
|
+
}
|
|
20055
|
+
function serializeNode(node) {
|
|
20056
|
+
return {
|
|
20057
|
+
id: node.id,
|
|
20058
|
+
type: node.type,
|
|
20059
|
+
name: node.name,
|
|
20060
|
+
kind: node.kind,
|
|
20061
|
+
content: node.content,
|
|
20062
|
+
scope: node.scope,
|
|
20063
|
+
version: node.version,
|
|
20064
|
+
updatedAt: node.updatedAt.toISOString()
|
|
20065
|
+
};
|
|
20066
|
+
}
|
|
20067
|
+
async function loadSemanticResult(store, scope, candidate) {
|
|
20068
|
+
const type = candidate.metadata?.document_type;
|
|
20069
|
+
if (type === "node") {
|
|
20070
|
+
const node = await store.getNode(candidate.id.slice(15));
|
|
20071
|
+
if (!node || node.mergedInto || !(0, _mastra_core_storage.isKnowledgeScopeVisible)(node.scope, scope)) return null;
|
|
20072
|
+
return {
|
|
20073
|
+
type: "node",
|
|
20074
|
+
id: node.id,
|
|
20075
|
+
recordId: node.id,
|
|
20076
|
+
name: node.name,
|
|
20077
|
+
text: `${node.name}\n${node.content ?? ""}`,
|
|
20078
|
+
scope: node.scope,
|
|
20079
|
+
semanticScore: candidate.score
|
|
20080
|
+
};
|
|
20081
|
+
}
|
|
20082
|
+
if (type === "record") {
|
|
20083
|
+
const record = await store.getKnowledge({ id: candidate.id.slice(17) });
|
|
20084
|
+
if (!record || !(0, _mastra_core_storage.isKnowledgeScopeVisible)(record.scope, scope)) return null;
|
|
20085
|
+
const node = await store.getNode(record.node);
|
|
20086
|
+
const parentVisible = Boolean(node && !node.mergedInto && (0, _mastra_core_storage.isKnowledgeScopeVisible)(node.scope, scope));
|
|
20087
|
+
return {
|
|
20088
|
+
type: "record",
|
|
20089
|
+
id: record.id,
|
|
20090
|
+
recordId: parentVisible ? node.id : record.id,
|
|
20091
|
+
name: parentVisible ? node.name : "(private node)",
|
|
20092
|
+
text: record.text,
|
|
20093
|
+
scope: record.scope,
|
|
20094
|
+
semanticScore: candidate.score
|
|
20095
|
+
};
|
|
20096
|
+
}
|
|
20097
|
+
return null;
|
|
20098
|
+
}
|
|
20099
|
+
function mergeHybridResults(lexical, semantic, limit) {
|
|
20100
|
+
const ranked = /* @__PURE__ */ new Map();
|
|
20101
|
+
lexical.forEach((result, index) => {
|
|
20102
|
+
ranked.set(`${result.type}:${result.id}`, {
|
|
20103
|
+
...result,
|
|
20104
|
+
score: 1 / (60 + index + 1),
|
|
20105
|
+
sources: ["lexical"]
|
|
20106
|
+
});
|
|
20107
|
+
});
|
|
20108
|
+
semantic.forEach((result, index) => {
|
|
20109
|
+
const key = `${result.type}:${result.id}`;
|
|
20110
|
+
const existing = ranked.get(key);
|
|
20111
|
+
const reciprocalRank = 1 / (60 + index + 1);
|
|
20112
|
+
ranked.set(key, {
|
|
20113
|
+
...result,
|
|
20114
|
+
score: (existing?.score ?? 0) + reciprocalRank,
|
|
20115
|
+
sources: existing ? ["lexical", "semantic"] : ["semantic"],
|
|
20116
|
+
semanticScore: result.semanticScore
|
|
20117
|
+
});
|
|
20118
|
+
});
|
|
20119
|
+
return [...ranked.values()].sort((left, right) => right.score - left.score || left.id.localeCompare(right.id)).slice(0, limit);
|
|
20120
|
+
}
|
|
20121
|
+
function createKnowledgeTools(memory, fixedScope) {
|
|
20122
|
+
return {
|
|
20123
|
+
knowledge_search: (0, _mastra_core_tools.createTool)({
|
|
20124
|
+
id: "knowledge_search",
|
|
20125
|
+
description: "Search durable scoped knowledge across nodes and knowledge records using lexical and semantic retrieval.",
|
|
20126
|
+
inputSchema: {
|
|
20127
|
+
type: "object",
|
|
20128
|
+
properties: {
|
|
20129
|
+
query: {
|
|
20130
|
+
type: "string",
|
|
20131
|
+
minLength: 1,
|
|
20132
|
+
description: "The knowledge to search for."
|
|
20133
|
+
},
|
|
20134
|
+
limit: {
|
|
20135
|
+
type: "integer",
|
|
20136
|
+
minimum: 1,
|
|
20137
|
+
maximum: MAX_LIMIT,
|
|
20138
|
+
description: "Maximum results. Defaults to 10."
|
|
20139
|
+
}
|
|
20140
|
+
},
|
|
20141
|
+
required: ["query"],
|
|
20142
|
+
additionalProperties: false
|
|
20143
|
+
},
|
|
20144
|
+
execute: async (input, context) => {
|
|
20145
|
+
const { query, limit: requestedLimit } = input;
|
|
20146
|
+
const scope = fixedScope ?? resolveScope$3(context);
|
|
20147
|
+
const limit = normalizeLimit(requestedLimit);
|
|
20148
|
+
const store = await getKnowledgeStore(memory);
|
|
20149
|
+
const semanticCandidates = await memory.getKnowledgeSemanticIndex().then((index) => index.search(query, scope, limit * 2));
|
|
20150
|
+
return {
|
|
20151
|
+
query,
|
|
20152
|
+
results: mergeHybridResults(await store.search({
|
|
20153
|
+
query,
|
|
20154
|
+
scope,
|
|
20155
|
+
limit: limit * 2
|
|
20156
|
+
}), (await Promise.all(semanticCandidates.map((candidate) => loadSemanticResult(store, scope, candidate)))).filter((result) => Boolean(result)), limit)
|
|
20157
|
+
};
|
|
20158
|
+
}
|
|
20159
|
+
}),
|
|
20160
|
+
knowledge_read: (0, _mastra_core_tools.createTool)({
|
|
20161
|
+
id: "knowledge_read",
|
|
20162
|
+
description: "Read a knowledge node, its content, and knowledge records about or linked to it by name or ID.",
|
|
20163
|
+
inputSchema: {
|
|
20164
|
+
type: "object",
|
|
20165
|
+
properties: {
|
|
20166
|
+
id: {
|
|
20167
|
+
type: "string",
|
|
20168
|
+
minLength: 1
|
|
20169
|
+
},
|
|
20170
|
+
name: {
|
|
20171
|
+
type: "string",
|
|
20172
|
+
minLength: 1
|
|
20173
|
+
},
|
|
20174
|
+
relationship: {
|
|
20175
|
+
type: "string",
|
|
20176
|
+
enum: [
|
|
20177
|
+
"about",
|
|
20178
|
+
"mentioning",
|
|
20179
|
+
"related"
|
|
20180
|
+
],
|
|
20181
|
+
description: "Knowledge relationship to query. Defaults to about."
|
|
20182
|
+
},
|
|
20183
|
+
cursor: {
|
|
20184
|
+
type: "string",
|
|
20185
|
+
minLength: 1,
|
|
20186
|
+
description: "Return knowledge records after this record ULID."
|
|
20187
|
+
},
|
|
20188
|
+
limit: {
|
|
20189
|
+
type: "integer",
|
|
20190
|
+
minimum: 1,
|
|
20191
|
+
maximum: MAX_LIMIT
|
|
20192
|
+
}
|
|
20193
|
+
},
|
|
20194
|
+
additionalProperties: false
|
|
20195
|
+
},
|
|
20196
|
+
execute: async (input, context) => {
|
|
20197
|
+
const { id, name, relationship = "about", cursor, limit: requestedLimit } = input;
|
|
20198
|
+
if (!id && !name) throw new Error("knowledge_read requires id or name.");
|
|
20199
|
+
const scope = fixedScope ?? resolveScope$3(context);
|
|
20200
|
+
const store = await getKnowledgeStore(memory);
|
|
20201
|
+
const node = id ? await store.getNode(id) : await store.resolveNode({
|
|
20202
|
+
name,
|
|
20203
|
+
scope
|
|
20204
|
+
});
|
|
20205
|
+
if (!node || node.mergedInto || !(0, _mastra_core_storage.isKnowledgeScopeVisible)(node.scope, scope)) return { found: false };
|
|
20206
|
+
const result = await (relationship === "related" ? store.listKnowledgeRelatedTo : relationship === "mentioning" ? store.listKnowledgeMentioning : store.listKnowledgeAbout).call(store, {
|
|
20207
|
+
node,
|
|
20208
|
+
scope,
|
|
20209
|
+
after: cursor,
|
|
20210
|
+
limit: normalizeLimit(requestedLimit)
|
|
20211
|
+
});
|
|
20212
|
+
return {
|
|
20213
|
+
found: true,
|
|
20214
|
+
node: serializeNode(node),
|
|
20215
|
+
records: result.records.map(serializeRecord),
|
|
20216
|
+
nextCursor: result.nextCursor
|
|
20217
|
+
};
|
|
20218
|
+
}
|
|
20219
|
+
}),
|
|
20220
|
+
knowledge_browse: (0, _mastra_core_tools.createTool)({
|
|
20221
|
+
id: "knowledge_browse",
|
|
20222
|
+
description: "Browse visible knowledge nodes by scope and name prefix, or follow a node’s mentions and backlinks.",
|
|
20223
|
+
inputSchema: {
|
|
20224
|
+
type: "object",
|
|
20225
|
+
properties: {
|
|
20226
|
+
namePrefix: { type: "string" },
|
|
20227
|
+
kind: {
|
|
20228
|
+
type: "string",
|
|
20229
|
+
description: "Optional node kind filter."
|
|
20230
|
+
},
|
|
20231
|
+
hasContent: {
|
|
20232
|
+
type: "boolean",
|
|
20233
|
+
description: "Filter nodes by whether they have long-form content."
|
|
20234
|
+
},
|
|
20235
|
+
node: {
|
|
20236
|
+
type: "string",
|
|
20237
|
+
minLength: 1,
|
|
20238
|
+
description: "When set, return knowledge related to this node."
|
|
20239
|
+
},
|
|
20240
|
+
cursor: {
|
|
20241
|
+
type: "string",
|
|
20242
|
+
minLength: 1
|
|
20243
|
+
},
|
|
20244
|
+
limit: {
|
|
20245
|
+
type: "integer",
|
|
20246
|
+
minimum: 1,
|
|
20247
|
+
maximum: MAX_LIMIT
|
|
20248
|
+
}
|
|
20249
|
+
},
|
|
20250
|
+
additionalProperties: false
|
|
20251
|
+
},
|
|
20252
|
+
execute: async (input, context) => {
|
|
20253
|
+
const { namePrefix, kind, hasContent, node: nodeReference, cursor, limit: requestedLimit } = input;
|
|
20254
|
+
const scope = fixedScope ?? resolveScope$3(context);
|
|
20255
|
+
const limit = normalizeLimit(requestedLimit);
|
|
20256
|
+
const store = await getKnowledgeStore(memory);
|
|
20257
|
+
if (nodeReference) {
|
|
20258
|
+
const node = await store.getNode(nodeReference);
|
|
20259
|
+
if (!node || node.mergedInto || !(0, _mastra_core_storage.isKnowledgeScopeVisible)(node.scope, scope)) return { found: false };
|
|
20260
|
+
const result = await store.listKnowledgeRelatedTo({
|
|
20261
|
+
node,
|
|
20262
|
+
scope,
|
|
20263
|
+
after: cursor,
|
|
20264
|
+
limit
|
|
20265
|
+
});
|
|
20266
|
+
return {
|
|
20267
|
+
found: true,
|
|
20268
|
+
node: serializeNode(node),
|
|
20269
|
+
records: result.records.map(serializeRecord),
|
|
20270
|
+
nextCursor: result.nextCursor
|
|
20271
|
+
};
|
|
20272
|
+
}
|
|
20273
|
+
const nodes = await store.listNodes({
|
|
20274
|
+
scope,
|
|
20275
|
+
namePrefix,
|
|
20276
|
+
kind,
|
|
20277
|
+
hasContent,
|
|
20278
|
+
cursor,
|
|
20279
|
+
limit: limit + 1
|
|
20280
|
+
});
|
|
20281
|
+
const hasMore = nodes.length > limit;
|
|
20282
|
+
const visibleNodes = nodes.slice(0, limit);
|
|
20283
|
+
return {
|
|
20284
|
+
nodes: visibleNodes.map(serializeNode),
|
|
20285
|
+
nextCursor: hasMore ? (0, _mastra_core_storage.createKnowledgeNodeCursor)(visibleNodes.at(-1), {
|
|
20286
|
+
namePrefix,
|
|
20287
|
+
kind,
|
|
20288
|
+
hasContent
|
|
20289
|
+
}) : void 0
|
|
20290
|
+
};
|
|
20291
|
+
}
|
|
20292
|
+
})
|
|
20293
|
+
};
|
|
20294
|
+
}
|
|
20295
|
+
function isTieredModelTarget(model) {
|
|
20296
|
+
if (typeof model === "string") return true;
|
|
20297
|
+
if (!model || typeof model !== "object") return false;
|
|
20298
|
+
return "modelId" in model || "id" in model || "providerId" in model || "provider" in model || "doGenerate" in model && "doStream" in model;
|
|
20299
|
+
}
|
|
20300
|
+
function normalizeThresholds(config) {
|
|
20301
|
+
const entries = Object.entries(config.upTo);
|
|
20302
|
+
if (entries.length === 0) throw new Error("ModelByInputTokens requires at least one threshold in \"upTo\"");
|
|
20303
|
+
for (const [limitStr, model] of entries) {
|
|
20304
|
+
const limit = Number(limitStr);
|
|
20305
|
+
if (!Number.isFinite(limit) || limit <= 0) throw new Error(`ModelByInputTokens threshold keys must be positive numbers. Got: ${limitStr}`);
|
|
20306
|
+
if (!isTieredModelTarget(model)) throw new Error(`ModelByInputTokens requires a valid model target for threshold ${limitStr}`);
|
|
20307
|
+
}
|
|
20308
|
+
return entries.map(([limitStr, model]) => ({
|
|
20309
|
+
limit: Number(limitStr),
|
|
20310
|
+
model
|
|
20311
|
+
})).sort((a, b) => a.limit - b.limit);
|
|
20312
|
+
}
|
|
20313
|
+
var ModelByInputTokens = class {
|
|
20314
|
+
thresholds;
|
|
20315
|
+
constructor(config) {
|
|
20316
|
+
this.thresholds = normalizeThresholds(config);
|
|
20317
|
+
}
|
|
20318
|
+
resolve(inputTokens) {
|
|
20319
|
+
for (const { limit, model } of this.thresholds) if (inputTokens <= limit) return model;
|
|
20320
|
+
const maxLimit = this.thresholds[this.thresholds.length - 1].limit;
|
|
20321
|
+
throw new Error(`ModelByInputTokens: input token count (${inputTokens}) exceeds the largest configured threshold (${maxLimit}). Please configure a higher threshold or use a larger model.`);
|
|
20322
|
+
}
|
|
20323
|
+
getThresholds() {
|
|
20324
|
+
return this.thresholds.map((t) => t.limit);
|
|
20325
|
+
}
|
|
20326
|
+
};
|
|
20327
|
+
/**
|
|
20328
|
+
* Normalize an observational memory model config into something an Agent can consume directly.
|
|
20329
|
+
* Returns undefined when the OM model cannot stand alone: the 'default' sentinel and
|
|
20330
|
+
* token-routed models (ModelByInputTokens) both require engine context to resolve.
|
|
20331
|
+
*/
|
|
20332
|
+
function usableObservationalMemoryModel(model) {
|
|
20333
|
+
if (!model || model === "default") return void 0;
|
|
20334
|
+
if (model instanceof ModelByInputTokens) return void 0;
|
|
20335
|
+
if (Array.isArray(model)) return model[0]?.model;
|
|
20336
|
+
if (typeof model === "function") return (async (ctx) => {
|
|
20337
|
+
const result = await model(ctx);
|
|
20338
|
+
return Array.isArray(result) ? result[0]?.model ?? result : result;
|
|
20339
|
+
});
|
|
20340
|
+
return model;
|
|
20341
|
+
}
|
|
20342
|
+
/**
|
|
20343
|
+
* Resolve the model a subconscious agent runs on. Precedence: the per-agent config model,
|
|
20344
|
+
* then the observational memory model, then the main agent's model. Returns undefined when
|
|
20345
|
+
* no source is available so callers keep their existing throw/silent-return behavior.
|
|
20346
|
+
*/
|
|
20347
|
+
async function resolveSubconsciousAgentModel(options) {
|
|
20348
|
+
const { config, omModel, mainAgent, requestContext } = options;
|
|
20349
|
+
if (config.model) {
|
|
20350
|
+
if (mainAgent) return await mainAgent.getModel({
|
|
20351
|
+
requestContext,
|
|
20352
|
+
modelConfig: config.model
|
|
20353
|
+
});
|
|
20354
|
+
return config.model;
|
|
20355
|
+
}
|
|
20356
|
+
const fromOm = usableObservationalMemoryModel(omModel);
|
|
20357
|
+
if (fromOm) return fromOm;
|
|
20358
|
+
if (mainAgent) return await mainAgent.getModel({ requestContext });
|
|
20359
|
+
}
|
|
20360
|
+
const DEFAULT_INSTRUCTIONS$2 = `Review the current observations and use the knowledge tools to find prior knowledge that is directly relevant now.
|
|
20361
|
+
|
|
20362
|
+
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.
|
|
20363
|
+
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.
|
|
20364
|
+
If nothing is relevant, respond with exactly <no-reminder /> and nothing else.
|
|
20365
|
+
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.`;
|
|
20366
|
+
/** Own-thread records younger than this are treated as still-in-context and excluded from reminder candidates. */
|
|
20367
|
+
const FRESH_OWN_RECORD_WINDOW_MS = 1800 * 1e3;
|
|
20368
|
+
function resolveScope$2(context) {
|
|
20369
|
+
const organizationId = context.requestContext?.get("organizationId");
|
|
20370
|
+
if (typeof organizationId !== "string" || !organizationId.trim()) throw new Error("Subconscious remind requires organizationId in the request context.");
|
|
20371
|
+
const resourceId = resolveKnowledgeResourceId(context.requestContext, context.resourceId);
|
|
20372
|
+
if (!resourceId) throw new Error("Subconscious remind requires a resourceId.");
|
|
20373
|
+
return (0, _mastra_core_storage.canonicalizeKnowledgeScope)([
|
|
20374
|
+
`org:${organizationId}`,
|
|
20375
|
+
`resource:${resourceId}`,
|
|
20376
|
+
`thread:${context.threadId}`
|
|
20377
|
+
]);
|
|
20378
|
+
}
|
|
20379
|
+
const REMINDER_QUERY_STOP_WORDS = /* @__PURE__ */ new Set([
|
|
20380
|
+
"about",
|
|
20381
|
+
"after",
|
|
20382
|
+
"before",
|
|
20383
|
+
"current",
|
|
20384
|
+
"from",
|
|
20385
|
+
"have",
|
|
20386
|
+
"observations",
|
|
20387
|
+
"that",
|
|
20388
|
+
"their",
|
|
20389
|
+
"there",
|
|
20390
|
+
"they",
|
|
20391
|
+
"this",
|
|
20392
|
+
"user",
|
|
20393
|
+
"what",
|
|
20394
|
+
"when",
|
|
20395
|
+
"where",
|
|
20396
|
+
"which",
|
|
20397
|
+
"with"
|
|
20398
|
+
]);
|
|
20399
|
+
async function findReminderSources(store, scope, observations) {
|
|
20400
|
+
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);
|
|
20401
|
+
const results = (await Promise.all(terms.map((query) => store.search({
|
|
20402
|
+
query,
|
|
20403
|
+
scope,
|
|
20404
|
+
limit: 5
|
|
20405
|
+
})))).flat();
|
|
20406
|
+
return [...new Map(results.map((result) => [`${result.type}:${result.id}`, result])).values()].slice(0, 10);
|
|
20407
|
+
}
|
|
20408
|
+
/**
|
|
20409
|
+
* Drop the current thread's own freshly captured KnowledgeRecords from the candidate list. They match the
|
|
20410
|
+
* current observations almost perfectly (they were just distilled from them), so without this
|
|
20411
|
+
* guard the reminder agent mostly echoes the session's own words back at it.
|
|
20412
|
+
*/
|
|
20413
|
+
async function dropFreshOwnRecords(store, sources, threadId) {
|
|
20414
|
+
const checks = await Promise.all(sources.map(async (source) => {
|
|
20415
|
+
if (source.type !== "record") return true;
|
|
20416
|
+
const record = await store.getKnowledge({ id: source.id }).catch(() => null);
|
|
20417
|
+
if (!record) return true;
|
|
20418
|
+
const isOwnThread = record.sourceThreadId === threadId || record.sourceThreadId.startsWith(`subconscious:${threadId}:`);
|
|
20419
|
+
const isFresh = Date.now() - new Date(record.capturedAt).getTime() < FRESH_OWN_RECORD_WINDOW_MS;
|
|
20420
|
+
return !(isOwnThread && isFresh);
|
|
20421
|
+
}));
|
|
20422
|
+
return sources.filter((_, index) => checks[index]);
|
|
20423
|
+
}
|
|
20424
|
+
var SubconsciousRemindExtractor = class extends Extractor {
|
|
20425
|
+
constructor(config, omModel) {
|
|
20426
|
+
super({
|
|
20427
|
+
name: "Remind",
|
|
20428
|
+
mode: "hook",
|
|
20429
|
+
metadataKeyPath: false,
|
|
20430
|
+
onExtracted: async (context) => {
|
|
20431
|
+
if (!context.rawObservations?.trim() || !context.memory || !context.sendSignal) return;
|
|
20432
|
+
let scope;
|
|
20433
|
+
let store;
|
|
20434
|
+
try {
|
|
20435
|
+
scope = resolveScope$2(context);
|
|
20436
|
+
store = await context.memory.storage.getStore("knowledge");
|
|
20437
|
+
if (!store) throw new Error("Subconscious remind requires a configured knowledge storage domain.");
|
|
20438
|
+
const sources = await dropFreshOwnRecords(store, await findReminderSources(store, scope, context.rawObservations), context.threadId);
|
|
20439
|
+
if (sources.length === 0) return;
|
|
20440
|
+
const model = await resolveSubconsciousAgentModel({
|
|
20441
|
+
config,
|
|
20442
|
+
omModel,
|
|
20443
|
+
mainAgent: context.mainAgent,
|
|
20444
|
+
requestContext: context.requestContext
|
|
20445
|
+
});
|
|
20446
|
+
if (!model) return;
|
|
20447
|
+
const agent = new _mastra_core_agent.Agent({
|
|
20448
|
+
id: `subconscious-remind-${context.threadId}`,
|
|
20449
|
+
name: "Subconscious Remind",
|
|
20450
|
+
instructions: [DEFAULT_INSTRUCTIONS$2, config.instructions?.trim()].filter(Boolean).join("\n\n"),
|
|
20451
|
+
model,
|
|
20452
|
+
tools: createKnowledgeTools(context.memory, scope)
|
|
20453
|
+
});
|
|
20454
|
+
const recentMessagesSection = context.recentMessages?.trim() ? `\n\nRecent conversation messages (already visible to the agent — never remind about anything present here):\n${context.recentMessages}` : "";
|
|
20455
|
+
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}`, {
|
|
20456
|
+
requestContext: context.requestContext,
|
|
20457
|
+
abortSignal: context.abortSignal,
|
|
20458
|
+
maxSteps: config.maxSteps
|
|
20459
|
+
})).text.trim();
|
|
20460
|
+
if (!reminder || /^<no-reminder\s*\/>$/i.test(reminder)) return;
|
|
20461
|
+
const sourceIds = [...new Set(sources.flatMap((source) => [source.id, source.recordId]))].filter((id) => reminder.includes(id)).slice(0, 5);
|
|
20462
|
+
if (sourceIds.length === 0) return;
|
|
20463
|
+
const contents = `${reminder}\n\nSources: ${sourceIds.join(", ")}`;
|
|
20464
|
+
await context.sendSignal({
|
|
20465
|
+
id: `__subconscious_remembered_${crypto.randomUUID()}`,
|
|
20466
|
+
type: "reactive",
|
|
20467
|
+
tagName: "remembered",
|
|
20468
|
+
contents,
|
|
20469
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
20470
|
+
metadata: { origin: "subconscious" },
|
|
20471
|
+
attributes: {
|
|
20472
|
+
source: "subconscious",
|
|
20473
|
+
sourceIds: sourceIds.join(","),
|
|
20474
|
+
agent: "remind",
|
|
20475
|
+
threadId: context.threadId
|
|
20476
|
+
}
|
|
20477
|
+
});
|
|
20478
|
+
} catch (error) {
|
|
20479
|
+
await context.writer?.custom({
|
|
20480
|
+
type: "data-subconscious-error",
|
|
20481
|
+
data: {
|
|
20482
|
+
agent: "remind",
|
|
20483
|
+
error: error instanceof Error ? error.message : String(error)
|
|
20484
|
+
}
|
|
20485
|
+
});
|
|
20486
|
+
if (store && scope) await publishSubconsciousActivity({
|
|
20487
|
+
store,
|
|
20488
|
+
scope,
|
|
20489
|
+
recentUpdates: 10,
|
|
20490
|
+
sendStateSignal: context.sendStateSignal,
|
|
20491
|
+
errors: [`remind: ${error instanceof Error ? error.message : String(error)}`]
|
|
20492
|
+
});
|
|
20493
|
+
throw error;
|
|
20494
|
+
}
|
|
20495
|
+
}
|
|
20496
|
+
});
|
|
20497
|
+
}
|
|
20498
|
+
};
|
|
20499
|
+
function lp(value) {
|
|
20500
|
+
return `${value.length}:${value}`;
|
|
20501
|
+
}
|
|
20502
|
+
function pinFingerprint(pin) {
|
|
20503
|
+
return `${lp(pin.id)}${lp(pin.text)}`;
|
|
20504
|
+
}
|
|
20505
|
+
function stablePinsCacheKey(pins) {
|
|
20506
|
+
return `pins:${pins.map(pinFingerprint).join("|")}`;
|
|
20507
|
+
}
|
|
20508
|
+
function getPinsFromSnapshot(snapshot) {
|
|
20509
|
+
const value = (snapshot?.metadata)?.value?.pins;
|
|
20510
|
+
return Array.isArray(value) ? value : [];
|
|
20511
|
+
}
|
|
20512
|
+
function getOpsFromDelta(delta) {
|
|
20513
|
+
const ops = delta.metadata?.delta?.ops;
|
|
20514
|
+
return Array.isArray(ops) ? ops : [];
|
|
20515
|
+
}
|
|
20516
|
+
function applyPinOps(pins, ops) {
|
|
20517
|
+
const next = pins.slice();
|
|
20518
|
+
for (const op of ops) {
|
|
20519
|
+
if (op.op === "remove") {
|
|
20520
|
+
const idx = next.findIndex((pin) => pin.id === op.id);
|
|
20521
|
+
if (idx >= 0) next.splice(idx, 1);
|
|
20522
|
+
continue;
|
|
20523
|
+
}
|
|
20524
|
+
const idx = next.findIndex((pin) => pin.id === op.pin.id);
|
|
20525
|
+
if (idx >= 0) next[idx] = op.pin;
|
|
20526
|
+
else next.push(op.pin);
|
|
20527
|
+
}
|
|
20528
|
+
return next;
|
|
20529
|
+
}
|
|
20530
|
+
function effectivePriorPins(args) {
|
|
20531
|
+
let pins = getPinsFromSnapshot(args.lastSnapshot);
|
|
20532
|
+
for (const delta of args.deltasSinceSnapshot ?? []) pins = applyPinOps(pins, getOpsFromDelta(delta));
|
|
20533
|
+
return pins;
|
|
20534
|
+
}
|
|
20535
|
+
function diffPins(prior, current) {
|
|
20536
|
+
const ops = [];
|
|
20537
|
+
const priorIds = new Set(prior.map((pin) => pin.id));
|
|
20538
|
+
const currentIds = new Set(current.map((pin) => pin.id));
|
|
20539
|
+
for (const pin of prior) if (!currentIds.has(pin.id)) ops.push({
|
|
20540
|
+
op: "remove",
|
|
20541
|
+
id: pin.id
|
|
20542
|
+
});
|
|
20543
|
+
for (const pin of current) if (!priorIds.has(pin.id)) ops.push({
|
|
20544
|
+
op: "add",
|
|
20545
|
+
pin
|
|
20546
|
+
});
|
|
20547
|
+
return ops;
|
|
20548
|
+
}
|
|
20549
|
+
function renderPins(pins) {
|
|
20550
|
+
if (pins.length === 0) return "";
|
|
20551
|
+
return `\n${pins.map((pin) => ` • {id: ${pin.id}} ${pin.text}`).join("\n")}\n`;
|
|
20552
|
+
}
|
|
20553
|
+
function renderDelta(ops) {
|
|
20554
|
+
return `\n${ops.map((op) => op.op === "remove" ? ` - removed {id: ${op.id}}` : ` + {id: ${op.pin.id}} ${op.pin.text}`).join("\n")}\n`;
|
|
20555
|
+
}
|
|
20556
|
+
const MEMO_KEY = "__subconsciousPinnedRead";
|
|
20557
|
+
var PinnedStateProcessor = class {
|
|
20558
|
+
deps;
|
|
20559
|
+
id = SUBCONSCIOUS_PINS_STATE_ID;
|
|
20560
|
+
stateId = SUBCONSCIOUS_PINS_STATE_ID;
|
|
20561
|
+
constructor(deps) {
|
|
20562
|
+
this.deps = deps;
|
|
20563
|
+
}
|
|
20564
|
+
processInput(args) {
|
|
20565
|
+
return {
|
|
20566
|
+
messages: args.messages,
|
|
20567
|
+
systemMessages: [...args.systemMessages, {
|
|
20568
|
+
role: "system",
|
|
20569
|
+
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.`
|
|
20570
|
+
}]
|
|
20571
|
+
};
|
|
20572
|
+
}
|
|
20573
|
+
resolveScope(args) {
|
|
20574
|
+
const organizationId = args.requestContext?.get?.("organizationId");
|
|
20575
|
+
if (typeof organizationId !== "string" || !organizationId.trim()) return void 0;
|
|
20576
|
+
const resourceId = resolveKnowledgeResourceId(args.requestContext, args.resourceId);
|
|
20577
|
+
if (!resourceId) return void 0;
|
|
20578
|
+
return (0, _mastra_core_storage.canonicalizeKnowledgeScope)([
|
|
20579
|
+
`org:${organizationId}`,
|
|
20580
|
+
`resource:${resourceId}`,
|
|
20581
|
+
`thread:${args.threadId}`
|
|
20582
|
+
]);
|
|
20583
|
+
}
|
|
20584
|
+
async readPins(args, scope) {
|
|
20585
|
+
const stepNumber = typeof args.stepNumber === "number" ? args.stepNumber : 0;
|
|
20586
|
+
const scopeKey = scope.join("/");
|
|
20587
|
+
const memo = args.requestContext?.get?.(MEMO_KEY);
|
|
20588
|
+
if (memo && memo.scopeKey === scopeKey && stepNumber > memo.atStep) return memo.entries;
|
|
20589
|
+
const store = await this.deps.getKnowledgeStore();
|
|
20590
|
+
if (!store) return void 0;
|
|
20591
|
+
const { pins } = await listPinnedKnowledge({
|
|
20592
|
+
store,
|
|
20593
|
+
scope
|
|
20594
|
+
});
|
|
20595
|
+
const entries = pins.map((pin) => ({
|
|
20596
|
+
id: pin.id,
|
|
20597
|
+
text: pin.text
|
|
20598
|
+
}));
|
|
20599
|
+
args.requestContext?.set?.(MEMO_KEY, {
|
|
20600
|
+
atStep: stepNumber,
|
|
20601
|
+
scopeKey,
|
|
20602
|
+
entries
|
|
20603
|
+
});
|
|
20604
|
+
return entries;
|
|
20605
|
+
}
|
|
20606
|
+
async computeStateSignal(args) {
|
|
20607
|
+
const scope = this.resolveScope(args);
|
|
20608
|
+
if (!scope) return;
|
|
20609
|
+
const currentPins = await this.readPins(args, scope);
|
|
20610
|
+
if (!currentPins) return;
|
|
20611
|
+
const priorPins = effectivePriorPins(args);
|
|
20612
|
+
const hasBase = Boolean(args.lastSnapshot) && args.contextWindow.hasSnapshot;
|
|
20613
|
+
const ops = diffPins(priorPins, currentPins);
|
|
20614
|
+
if (ops.length === 0 && hasBase) return;
|
|
20615
|
+
if (currentPins.length === 0) {
|
|
20616
|
+
if (!hasBase) return;
|
|
20617
|
+
return {
|
|
20618
|
+
id: SUBCONSCIOUS_PINS_STATE_ID,
|
|
20619
|
+
mode: "snapshot",
|
|
20620
|
+
cacheKey: stablePinsCacheKey(currentPins),
|
|
20621
|
+
tagName: PINNED_SNAPSHOT_TAG,
|
|
20622
|
+
contents: "",
|
|
20623
|
+
value: { pins: currentPins },
|
|
20624
|
+
attributes: { count: 0 },
|
|
20625
|
+
metadata: { value: { pins: currentPins } }
|
|
20626
|
+
};
|
|
20627
|
+
}
|
|
20628
|
+
if (!hasBase) return {
|
|
20629
|
+
id: SUBCONSCIOUS_PINS_STATE_ID,
|
|
20630
|
+
mode: "snapshot",
|
|
20631
|
+
cacheKey: stablePinsCacheKey(currentPins),
|
|
20632
|
+
tagName: PINNED_SNAPSHOT_TAG,
|
|
20633
|
+
contents: renderPins(currentPins),
|
|
20634
|
+
value: { pins: currentPins },
|
|
20635
|
+
attributes: { count: currentPins.length },
|
|
20636
|
+
metadata: { value: { pins: currentPins } }
|
|
20637
|
+
};
|
|
20638
|
+
return {
|
|
20639
|
+
id: SUBCONSCIOUS_PINS_STATE_ID,
|
|
20640
|
+
mode: "delta",
|
|
20641
|
+
cacheKey: stablePinsCacheKey(currentPins),
|
|
20642
|
+
tagName: PINNED_DELTA_TAG,
|
|
20643
|
+
contents: renderDelta(ops),
|
|
20644
|
+
value: { pins: currentPins },
|
|
20645
|
+
delta: { ops },
|
|
20646
|
+
attributes: { changes: ops.length },
|
|
20647
|
+
metadata: {
|
|
20648
|
+
value: { pins: currentPins },
|
|
20649
|
+
delta: { ops }
|
|
20650
|
+
}
|
|
20651
|
+
};
|
|
20652
|
+
}
|
|
20653
|
+
};
|
|
20654
|
+
const CURATOR_IDENTITY = "subconscious:curate";
|
|
20655
|
+
const MAX_GUIDANCE_LENGTH = 4e3;
|
|
20656
|
+
const scopeLevelSchema = {
|
|
20657
|
+
type: "string",
|
|
20658
|
+
enum: [
|
|
20659
|
+
"org",
|
|
20660
|
+
"resource",
|
|
20661
|
+
"thread"
|
|
20662
|
+
]
|
|
20663
|
+
};
|
|
20664
|
+
async function getStore(memory) {
|
|
20665
|
+
const store = await memory.storage.getStore("knowledge");
|
|
20666
|
+
if (!store) throw new Error("Knowledge write tools require a configured knowledge storage domain.");
|
|
20667
|
+
return store;
|
|
20668
|
+
}
|
|
20669
|
+
function resolveWriteScope(options, level) {
|
|
20670
|
+
const scope = (0, _mastra_core_storage.expandKnowledgeScope)(options.scope, level ?? options.defaultScope);
|
|
20671
|
+
(0, _mastra_core_storage.assertKnowledgeScopeWithinCeiling)(scope, options.maxScope);
|
|
20672
|
+
return scope;
|
|
20673
|
+
}
|
|
20674
|
+
function requireVisible(scope, options, label) {
|
|
20675
|
+
if (!(0, _mastra_core_storage.isKnowledgeScopeVisible)(scope, options.scope)) throw new Error(`${label} is outside the curator's visible scope.`);
|
|
20676
|
+
}
|
|
20677
|
+
function createKnowledgeWriteTools(memory, options) {
|
|
20678
|
+
return {
|
|
20679
|
+
knowledge_append: (0, _mastra_core_tools.createTool)({
|
|
20680
|
+
id: "knowledge_append",
|
|
20681
|
+
description: "Append a scoped record to an existing node. Provenance and capture time are stamped by code.",
|
|
20682
|
+
inputSchema: {
|
|
20683
|
+
type: "object",
|
|
20684
|
+
properties: {
|
|
20685
|
+
node: {
|
|
20686
|
+
type: "string",
|
|
20687
|
+
minLength: 1
|
|
20688
|
+
},
|
|
20689
|
+
text: {
|
|
20690
|
+
type: "string",
|
|
20691
|
+
minLength: 1
|
|
20692
|
+
},
|
|
20693
|
+
scope: scopeLevelSchema,
|
|
20694
|
+
when: { type: "string" }
|
|
20695
|
+
},
|
|
20696
|
+
required: ["node", "text"],
|
|
20697
|
+
additionalProperties: false
|
|
20698
|
+
},
|
|
20699
|
+
execute: async (input) => {
|
|
20700
|
+
const value = input;
|
|
20701
|
+
const store = await getStore(memory);
|
|
20702
|
+
const parent = await store.getNode(value.node);
|
|
20703
|
+
if (!parent || parent.mergedInto) throw new Error(`Knowledge node not found: ${value.node}`);
|
|
20704
|
+
requireVisible(parent.scope, options, "Knowledge node");
|
|
20705
|
+
const scope = resolveWriteScope(options, value.scope);
|
|
20706
|
+
const when = value.when ? new Date(value.when) : void 0;
|
|
20707
|
+
if (when && Number.isNaN(when.getTime())) throw new Error("KnowledgeRecord when must be a valid date.");
|
|
20708
|
+
return store.appendKnowledge({
|
|
20709
|
+
node: parent.id,
|
|
20710
|
+
text: value.text,
|
|
20711
|
+
scope,
|
|
20712
|
+
sourceThreadId: options.sourceThreadId,
|
|
20713
|
+
when,
|
|
20714
|
+
maxScope: options.maxScope,
|
|
20715
|
+
resolutionScope: options.scope,
|
|
20716
|
+
defaultScope: (0, _mastra_core_storage.expandKnowledgeScope)(options.scope, options.defaultScope)
|
|
20717
|
+
});
|
|
20718
|
+
}
|
|
20719
|
+
}),
|
|
20720
|
+
knowledge_remove: (0, _mastra_core_tools.createTool)({
|
|
20721
|
+
id: "knowledge_remove",
|
|
20722
|
+
description: "Soft-delete a visible record. Curators cannot restore or physically erase knowledge records.",
|
|
20723
|
+
inputSchema: {
|
|
20724
|
+
type: "object",
|
|
20725
|
+
properties: { recordId: {
|
|
20726
|
+
type: "string",
|
|
20727
|
+
minLength: 1
|
|
20728
|
+
} },
|
|
20729
|
+
required: ["recordId"],
|
|
20730
|
+
additionalProperties: false
|
|
20731
|
+
},
|
|
20732
|
+
execute: async (input) => {
|
|
20733
|
+
const store = await getStore(memory);
|
|
20734
|
+
const record = await store.getKnowledge({
|
|
20735
|
+
id: input.recordId,
|
|
20736
|
+
includeDeleted: true
|
|
20737
|
+
});
|
|
20738
|
+
if (!record) throw new Error(`KnowledgeRecord not found: ${input.recordId}`);
|
|
20739
|
+
requireVisible(record.scope, options, "KnowledgeRecord");
|
|
20740
|
+
return store.removeKnowledge({
|
|
20741
|
+
id: record.id,
|
|
20742
|
+
deletedBy: CURATOR_IDENTITY
|
|
20743
|
+
});
|
|
20744
|
+
}
|
|
20745
|
+
}),
|
|
20746
|
+
knowledge_update_node: (0, _mastra_core_tools.createTool)({
|
|
20747
|
+
id: "knowledge_update_node",
|
|
20748
|
+
description: "Update a visible node name or kind using optimistic concurrency.",
|
|
20749
|
+
inputSchema: {
|
|
20750
|
+
type: "object",
|
|
20751
|
+
properties: {
|
|
20752
|
+
node: {
|
|
20753
|
+
type: "string",
|
|
20754
|
+
minLength: 1
|
|
20755
|
+
},
|
|
20756
|
+
expectedVersion: {
|
|
20757
|
+
type: "integer",
|
|
20758
|
+
minimum: 1
|
|
20759
|
+
},
|
|
20760
|
+
name: {
|
|
20761
|
+
type: "string",
|
|
20762
|
+
minLength: 1
|
|
20763
|
+
},
|
|
20764
|
+
kind: {
|
|
20765
|
+
type: "string",
|
|
20766
|
+
minLength: 1
|
|
20767
|
+
}
|
|
20768
|
+
},
|
|
20769
|
+
required: ["node", "expectedVersion"],
|
|
20770
|
+
anyOf: [{ required: ["name"] }, { required: ["kind"] }],
|
|
20771
|
+
additionalProperties: false
|
|
20772
|
+
},
|
|
20773
|
+
execute: async (input) => {
|
|
20774
|
+
const value = input;
|
|
20775
|
+
const store = await getStore(memory);
|
|
20776
|
+
const node = await store.getNode(value.node);
|
|
20777
|
+
if (!node || node.mergedInto) throw new Error(`Knowledge node not found: ${value.node}`);
|
|
20778
|
+
requireVisible(node.scope, options, "Knowledge node");
|
|
20779
|
+
return store.updateNode({
|
|
20780
|
+
id: node.id,
|
|
20781
|
+
version: value.expectedVersion,
|
|
20782
|
+
name: value.name,
|
|
20783
|
+
kind: value.kind
|
|
20784
|
+
});
|
|
20785
|
+
}
|
|
20786
|
+
}),
|
|
20787
|
+
knowledge_merge_nodes: (0, _mastra_core_tools.createTool)({
|
|
20788
|
+
id: "knowledge_merge_nodes",
|
|
20789
|
+
description: "Merge a visible duplicate node into another visible node using source-version CAS.",
|
|
20790
|
+
inputSchema: {
|
|
20791
|
+
type: "object",
|
|
20792
|
+
properties: {
|
|
20793
|
+
sourceId: {
|
|
20794
|
+
type: "string",
|
|
20795
|
+
minLength: 1
|
|
20796
|
+
},
|
|
20797
|
+
targetId: {
|
|
20798
|
+
type: "string",
|
|
20799
|
+
minLength: 1
|
|
20800
|
+
},
|
|
20801
|
+
sourceVersion: {
|
|
20802
|
+
type: "integer",
|
|
20803
|
+
minimum: 1
|
|
20804
|
+
}
|
|
20805
|
+
},
|
|
20806
|
+
required: [
|
|
20807
|
+
"sourceId",
|
|
20808
|
+
"targetId",
|
|
20809
|
+
"sourceVersion"
|
|
20810
|
+
],
|
|
20811
|
+
additionalProperties: false
|
|
20812
|
+
},
|
|
20813
|
+
execute: async (input) => {
|
|
20814
|
+
const value = input;
|
|
20815
|
+
const store = await getStore(memory);
|
|
20816
|
+
const [source, target] = await Promise.all([store.getNode(value.sourceId), store.getNode(value.targetId)]);
|
|
20817
|
+
if (!source || !target) throw new Error("Knowledge merge requires two existing nodes.");
|
|
20818
|
+
requireVisible(source.scope, options, "Knowledge merge source");
|
|
20819
|
+
requireVisible(target.scope, options, "Knowledge merge target");
|
|
20820
|
+
return store.mergeNodes(value);
|
|
20821
|
+
}
|
|
20822
|
+
}),
|
|
20823
|
+
knowledge_rescope: (0, _mastra_core_tools.createTool)({
|
|
20824
|
+
id: "knowledge_rescope",
|
|
20825
|
+
description: "Change a record visibility scope without exceeding its stamped ceiling.",
|
|
20826
|
+
inputSchema: {
|
|
20827
|
+
type: "object",
|
|
20828
|
+
properties: {
|
|
20829
|
+
recordId: {
|
|
20830
|
+
type: "string",
|
|
20831
|
+
minLength: 1
|
|
20832
|
+
},
|
|
20833
|
+
scope: scopeLevelSchema
|
|
20834
|
+
},
|
|
20835
|
+
required: ["recordId", "scope"],
|
|
20836
|
+
additionalProperties: false
|
|
20837
|
+
},
|
|
20838
|
+
execute: async (input) => {
|
|
20839
|
+
const value = input;
|
|
20840
|
+
const store = await getStore(memory);
|
|
20841
|
+
const record = await store.getKnowledge({ id: value.recordId });
|
|
20842
|
+
if (!record) throw new Error(`KnowledgeRecord not found: ${value.recordId}`);
|
|
20843
|
+
requireVisible(record.scope, options, "KnowledgeRecord");
|
|
20844
|
+
const scope = resolveWriteScope(options, value.scope);
|
|
20845
|
+
(0, _mastra_core_storage.assertKnowledgeScopeWithinCeiling)(scope, record.maxScope);
|
|
20846
|
+
return store.rescopeKnowledge({
|
|
20847
|
+
id: record.id,
|
|
20848
|
+
scope
|
|
20849
|
+
});
|
|
20850
|
+
}
|
|
20851
|
+
}),
|
|
20852
|
+
knowledge_write_node_content: (0, _mastra_core_tools.createTool)({
|
|
20853
|
+
id: "knowledge_write_node_content",
|
|
20854
|
+
description: "Create or replace long-form content on a scoped knowledge node. Existing nodes require expectedVersion.",
|
|
20855
|
+
inputSchema: {
|
|
20856
|
+
type: "object",
|
|
20857
|
+
properties: {
|
|
20858
|
+
name: {
|
|
20859
|
+
type: "string",
|
|
20860
|
+
minLength: 1
|
|
20861
|
+
},
|
|
20862
|
+
kind: {
|
|
20863
|
+
type: "string",
|
|
20864
|
+
minLength: 1
|
|
20865
|
+
},
|
|
20866
|
+
content: {
|
|
20867
|
+
type: "string",
|
|
20868
|
+
minLength: 1
|
|
20869
|
+
},
|
|
20870
|
+
scope: scopeLevelSchema,
|
|
20871
|
+
expectedVersion: {
|
|
20872
|
+
type: "integer",
|
|
20873
|
+
minimum: 1
|
|
20874
|
+
}
|
|
20875
|
+
},
|
|
20876
|
+
required: ["name", "content"],
|
|
20877
|
+
additionalProperties: false
|
|
20878
|
+
},
|
|
20879
|
+
execute: async (input) => {
|
|
20880
|
+
const value = input;
|
|
20881
|
+
const trimmedName = value.name.trim();
|
|
20882
|
+
const reservedName = trimmedName.toLowerCase();
|
|
20883
|
+
const name = reservedName === "capture-guidance" ? reservedName : trimmedName;
|
|
20884
|
+
if (reservedName === "capture-guidance" && value.content.length > MAX_GUIDANCE_LENGTH) throw new Error(`capture-guidance is limited to ${MAX_GUIDANCE_LENGTH} characters.`);
|
|
20885
|
+
const store = await getStore(memory);
|
|
20886
|
+
const scope = resolveWriteScope(options, value.scope);
|
|
20887
|
+
const resolvedNode = await store.resolveNode({
|
|
20888
|
+
name,
|
|
20889
|
+
scope
|
|
20890
|
+
});
|
|
20891
|
+
const existing = resolvedNode && (0, _mastra_core_storage.knowledgeScopeKey)(resolvedNode.scope) === (0, _mastra_core_storage.knowledgeScopeKey)(scope) ? resolvedNode : null;
|
|
20892
|
+
if (!existing) {
|
|
20893
|
+
if (value.expectedVersion !== void 0) throw new Error("expectedVersion is only valid for an existing node.");
|
|
20894
|
+
return store.createNode({
|
|
20895
|
+
name,
|
|
20896
|
+
kind: value.kind ?? "document",
|
|
20897
|
+
content: value.content,
|
|
20898
|
+
scope,
|
|
20899
|
+
resolutionScope: options.scope
|
|
20900
|
+
});
|
|
20901
|
+
}
|
|
20902
|
+
if (value.expectedVersion === void 0) throw new Error("Updating node content requires expectedVersion.");
|
|
20903
|
+
return store.updateNode({
|
|
20904
|
+
id: existing.id,
|
|
20905
|
+
version: value.expectedVersion,
|
|
20906
|
+
kind: value.kind,
|
|
20907
|
+
content: value.content,
|
|
20908
|
+
resolutionScope: options.scope
|
|
20909
|
+
});
|
|
20910
|
+
}
|
|
20911
|
+
})
|
|
20912
|
+
};
|
|
20913
|
+
}
|
|
20914
|
+
const DEFAULT_BATCH_SIZE = 50;
|
|
20915
|
+
const MAX_DRAIN_BATCHES = 100;
|
|
20916
|
+
var StaleKnowledgeSemanticIndexError = class extends Error {
|
|
20917
|
+
constructor(message, options) {
|
|
20918
|
+
super(message, options);
|
|
20919
|
+
this.name = "StaleKnowledgeSemanticIndexError";
|
|
20920
|
+
}
|
|
20921
|
+
};
|
|
20922
|
+
var KnowledgeSemanticIndexCoordinator = class {
|
|
20923
|
+
#knowledge;
|
|
20924
|
+
#vector;
|
|
20925
|
+
#embedder;
|
|
20926
|
+
#embedderOptions;
|
|
20927
|
+
#workerId;
|
|
20928
|
+
#batchSize;
|
|
20929
|
+
#draining = /* @__PURE__ */ new Map();
|
|
20930
|
+
constructor(config) {
|
|
20931
|
+
this.#knowledge = config.knowledge;
|
|
20932
|
+
this.#vector = config.vector;
|
|
20933
|
+
this.#embedder = config.embedder;
|
|
20934
|
+
this.#embedderOptions = config.embedderOptions;
|
|
20935
|
+
this.#workerId = config.workerId ?? `knowledge-index-${crypto.randomUUID()}`;
|
|
20936
|
+
this.#batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
20937
|
+
}
|
|
20938
|
+
async drain(scope) {
|
|
20939
|
+
const key = scope?.join("") ?? "*";
|
|
20940
|
+
const active = this.#draining.get(key);
|
|
20941
|
+
if (active) return active;
|
|
20942
|
+
const draining = this.#drain(scope).finally(() => {
|
|
20943
|
+
this.#draining.delete(key);
|
|
20944
|
+
});
|
|
20945
|
+
this.#draining.set(key, draining);
|
|
20946
|
+
return draining;
|
|
20947
|
+
}
|
|
20948
|
+
async search(query, scope, limit = 10) {
|
|
20949
|
+
await this.drain(scope);
|
|
20950
|
+
const embedding = (await this.#embedder.doEmbed({
|
|
20951
|
+
values: [query],
|
|
20952
|
+
...this.#embedderOptions ?? {}
|
|
20953
|
+
})).embeddings[0];
|
|
20954
|
+
if (!embedding?.length) throw new Error("Embedder returned no vector for knowledge search query.");
|
|
20955
|
+
const indexName = this.#indexName(embedding.length);
|
|
20956
|
+
if (!(await this.#knowledgeIndexes()).includes(indexName)) throw new StaleKnowledgeSemanticIndexError(`Knowledge semantic index ${indexName} is unavailable. Capture or index knowledge before searching.`);
|
|
20957
|
+
const visibleScopeKeys = scope.map((_, index) => scope.slice(0, index + 1).join(""));
|
|
20958
|
+
const batches = await Promise.all(visibleScopeKeys.map((scopeKey) => this.#vector.query({
|
|
20959
|
+
indexName,
|
|
20960
|
+
queryVector: embedding,
|
|
20961
|
+
topK: limit,
|
|
20962
|
+
filter: { scope_key: scopeKey }
|
|
20963
|
+
})));
|
|
20964
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
20965
|
+
for (const candidate of batches.flat()) {
|
|
20966
|
+
const candidateScope = candidate.metadata?.scope;
|
|
20967
|
+
if (!Array.isArray(candidateScope)) continue;
|
|
20968
|
+
let visible = false;
|
|
20969
|
+
try {
|
|
20970
|
+
visible = (0, _mastra_core_storage.isKnowledgeScopeVisible)((0, _mastra_core_storage.canonicalizeKnowledgeScope)(candidateScope.map(String)), scope);
|
|
20971
|
+
} catch {
|
|
20972
|
+
continue;
|
|
20973
|
+
}
|
|
20974
|
+
if (!visible) continue;
|
|
20975
|
+
const existing = deduped.get(candidate.id);
|
|
20976
|
+
if (!existing || candidate.score > existing.score) deduped.set(candidate.id, candidate);
|
|
20977
|
+
}
|
|
20978
|
+
return [...deduped.values()].sort((left, right) => right.score - left.score || left.id.localeCompare(right.id)).slice(0, limit);
|
|
20979
|
+
}
|
|
20980
|
+
async #drain(scope) {
|
|
20981
|
+
let processed = 0;
|
|
20982
|
+
for (let batch = 0; batch < MAX_DRAIN_BATCHES; batch++) {
|
|
20983
|
+
const entries = await this.#knowledge.claimSemanticOutbox({
|
|
20984
|
+
workerId: this.#workerId,
|
|
20985
|
+
limit: this.#batchSize,
|
|
20986
|
+
scope
|
|
20987
|
+
});
|
|
20988
|
+
if (entries.length === 0) {
|
|
20989
|
+
const [pending, processing] = await Promise.all([this.#knowledge.listSemanticOutbox({
|
|
20990
|
+
status: "pending",
|
|
20991
|
+
scope,
|
|
20992
|
+
limit: 1
|
|
20993
|
+
}), this.#knowledge.listSemanticOutbox({
|
|
20994
|
+
status: "processing",
|
|
20995
|
+
scope,
|
|
20996
|
+
limit: 1
|
|
20997
|
+
})]);
|
|
20998
|
+
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.");
|
|
20999
|
+
return processed;
|
|
21000
|
+
}
|
|
21001
|
+
for (let index = 0; index < entries.length; index++) {
|
|
21002
|
+
const entry = entries[index];
|
|
21003
|
+
try {
|
|
21004
|
+
await this.#apply(entry);
|
|
21005
|
+
await this.#knowledge.completeSemanticOutbox({
|
|
21006
|
+
ids: [entry.id],
|
|
21007
|
+
workerId: this.#workerId
|
|
21008
|
+
});
|
|
21009
|
+
processed++;
|
|
21010
|
+
} catch (error) {
|
|
21011
|
+
await this.#knowledge.releaseSemanticOutbox({
|
|
21012
|
+
ids: entries.slice(index).map((pendingEntry) => pendingEntry.id),
|
|
21013
|
+
workerId: this.#workerId
|
|
21014
|
+
});
|
|
21015
|
+
throw new StaleKnowledgeSemanticIndexError(`Knowledge semantic index is stale because operation ${entry.id} could not be applied.`, { cause: error });
|
|
21016
|
+
}
|
|
21017
|
+
}
|
|
21018
|
+
}
|
|
21019
|
+
throw new StaleKnowledgeSemanticIndexError(`Knowledge semantic index remained stale after ${MAX_DRAIN_BATCHES} processing batches.`);
|
|
21020
|
+
}
|
|
21021
|
+
async #apply(entry) {
|
|
21022
|
+
if (entry.operation === "delete") {
|
|
21023
|
+
await this.#deleteDocument(entry.documentId);
|
|
21024
|
+
return;
|
|
21025
|
+
}
|
|
21026
|
+
const document = await this.#loadDocument(entry);
|
|
21027
|
+
if (!document) {
|
|
21028
|
+
await this.#deleteDocument(entry.documentId);
|
|
21029
|
+
return;
|
|
21030
|
+
}
|
|
21031
|
+
const embedding = (await this.#embedder.doEmbed({
|
|
21032
|
+
values: [document.text],
|
|
21033
|
+
...this.#embedderOptions ?? {}
|
|
21034
|
+
})).embeddings[0];
|
|
21035
|
+
if (!embedding?.length) throw new Error(`Embedder returned no vector for ${entry.documentId}`);
|
|
21036
|
+
const indexName = this.#indexName(embedding.length);
|
|
21037
|
+
const indexes = await this.#knowledgeIndexes();
|
|
21038
|
+
if (!indexes.includes(indexName)) await this.#vector.createIndex({
|
|
21039
|
+
indexName,
|
|
21040
|
+
dimension: embedding.length
|
|
21041
|
+
});
|
|
21042
|
+
for (const existingIndex of indexes) if (existingIndex !== indexName) await this.#vector.deleteVectors({
|
|
21043
|
+
indexName: existingIndex,
|
|
21044
|
+
ids: [entry.documentId]
|
|
21045
|
+
});
|
|
21046
|
+
await this.#vector.upsert({
|
|
21047
|
+
indexName,
|
|
21048
|
+
ids: [entry.documentId],
|
|
21049
|
+
vectors: [embedding],
|
|
21050
|
+
metadata: [this.#metadata(document)]
|
|
21051
|
+
});
|
|
21052
|
+
}
|
|
21053
|
+
async #loadDocument(entry) {
|
|
21054
|
+
if (entry.documentType === "node") {
|
|
21055
|
+
const node = await this.#knowledge.getNode(entry.documentId.slice(15));
|
|
21056
|
+
if (!node || node.mergedInto) return null;
|
|
21057
|
+
return {
|
|
21058
|
+
text: `${node.name}\n${node.content ?? ""}`,
|
|
21059
|
+
name: node.name,
|
|
21060
|
+
scope: node.scope,
|
|
21061
|
+
recordId: node.id,
|
|
21062
|
+
type: "node"
|
|
21063
|
+
};
|
|
21064
|
+
}
|
|
21065
|
+
const record = await this.#knowledge.getKnowledge({
|
|
21066
|
+
id: entry.documentId.slice(17),
|
|
21067
|
+
includeDeleted: true
|
|
21068
|
+
});
|
|
21069
|
+
if (!record || record.deletedAt) return null;
|
|
21070
|
+
const node = await this.#knowledge.getNode(record.node);
|
|
21071
|
+
if (!node) return null;
|
|
21072
|
+
return {
|
|
21073
|
+
text: `${node.name}\n${record.text}`,
|
|
21074
|
+
name: node.name,
|
|
21075
|
+
scope: record.scope,
|
|
21076
|
+
recordId: node.id,
|
|
21077
|
+
type: "record"
|
|
21078
|
+
};
|
|
21079
|
+
}
|
|
21080
|
+
async #deleteDocument(documentId) {
|
|
21081
|
+
for (const indexName of await this.#knowledgeIndexes()) await this.#vector.deleteVectors({
|
|
21082
|
+
indexName,
|
|
21083
|
+
ids: [documentId]
|
|
21084
|
+
});
|
|
21085
|
+
}
|
|
21086
|
+
async #knowledgeIndexes() {
|
|
21087
|
+
const prefix = `knowledge${this.#vector.indexSeparator ?? "_"}documents`;
|
|
21088
|
+
return (await this.#vector.listIndexes()).filter((index) => index === prefix || index.startsWith(`${prefix}${this.#vector.indexSeparator ?? "_"}dimension`));
|
|
21089
|
+
}
|
|
21090
|
+
#indexName(dimension) {
|
|
21091
|
+
const separator = this.#vector.indexSeparator ?? "_";
|
|
21092
|
+
return `knowledge${separator}documents${separator}dimension${separator}${dimension}`;
|
|
21093
|
+
}
|
|
21094
|
+
#metadata(document) {
|
|
21095
|
+
const metadata = {
|
|
21096
|
+
document_type: document.type,
|
|
21097
|
+
record_id: document.recordId,
|
|
21098
|
+
name: document.name,
|
|
21099
|
+
scope: [...document.scope],
|
|
21100
|
+
scope_key: document.scope.join(""),
|
|
21101
|
+
text: document.text
|
|
21102
|
+
};
|
|
21103
|
+
for (const entry of document.scope) {
|
|
21104
|
+
const separator = entry.indexOf(":");
|
|
21105
|
+
metadata[`scope_${entry.slice(0, separator)}`] = entry.slice(separator + 1);
|
|
21106
|
+
}
|
|
21107
|
+
return metadata;
|
|
21108
|
+
}
|
|
21109
|
+
};
|
|
21110
|
+
var subconscious_exports = /* @__PURE__ */ __exportAll({
|
|
21111
|
+
DEFAULT_MAX_PINS: () => 20,
|
|
21112
|
+
DEFAULT_PINNED_MAX_CHARACTERS: () => DEFAULT_PINNED_MAX_CHARACTERS,
|
|
21113
|
+
MAX_PINNED_MAX_CHARACTERS: () => MAX_PINNED_MAX_CHARACTERS,
|
|
21114
|
+
PINNED_DELTA_TAG: () => PINNED_DELTA_TAG,
|
|
21115
|
+
PINNED_NODE_KIND: () => PINNED_NODE_KIND,
|
|
21116
|
+
PINNED_NODE_NAME: () => PINNED_NODE_NAME,
|
|
21117
|
+
PINNED_NODE_SCOPE_LEVEL: () => PINNED_NODE_SCOPE_LEVEL,
|
|
21118
|
+
PINNED_SNAPSHOT_TAG: () => PINNED_SNAPSHOT_TAG,
|
|
21119
|
+
PinnedStateProcessor: () => PinnedStateProcessor,
|
|
21120
|
+
SUBCONSCIOUS_ACTIVITY_STATE_ID: () => SUBCONSCIOUS_ACTIVITY_STATE_ID,
|
|
21121
|
+
SUBCONSCIOUS_PINS_STATE_ID: () => SUBCONSCIOUS_PINS_STATE_ID,
|
|
21122
|
+
Subconscious: () => Subconscious
|
|
21123
|
+
});
|
|
21124
|
+
const BUILT_IN_OBSERVATION = /* @__PURE__ */ new Set(["capture", "remind"]);
|
|
21125
|
+
const BUILT_IN_REFLECTION = /* @__PURE__ */ new Set(["curate", "learn"]);
|
|
21126
|
+
const DEFAULT_MAX_STEPS = 5;
|
|
21127
|
+
const DEFAULT_RECENT_UPDATES = 10;
|
|
21128
|
+
const MAX_RECENT_UPDATES = 100;
|
|
21129
|
+
function entryName(entry) {
|
|
21130
|
+
return typeof entry === "string" ? entry : entry.name.trim();
|
|
21131
|
+
}
|
|
21132
|
+
function assertUniqueNames(entries, phase) {
|
|
21133
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21134
|
+
for (const entry of entries) {
|
|
21135
|
+
const name = entryName(entry);
|
|
21136
|
+
if (!name) throw new Error(`Subconscious ${phase} agent name is required.`);
|
|
21137
|
+
if (seen.has(name)) throw new Error(`Duplicate Subconscious ${phase} agent: ${name}`);
|
|
21138
|
+
seen.add(name);
|
|
21139
|
+
}
|
|
21140
|
+
}
|
|
21141
|
+
function boundedSteps(entry, fallback) {
|
|
21142
|
+
const steps = entry?.maxSteps ?? fallback;
|
|
21143
|
+
if (!Number.isInteger(steps) || steps < 1 || steps > 25) throw new Error("Subconscious maxSteps must be an integer between 1 and 25.");
|
|
21144
|
+
return steps;
|
|
21145
|
+
}
|
|
21146
|
+
function resolveExtractor(entry) {
|
|
21147
|
+
const config = typeof entry === "string" ? void 0 : entry;
|
|
21148
|
+
const name = entryName(entry);
|
|
21149
|
+
return {
|
|
21150
|
+
name,
|
|
21151
|
+
instructions: config?.instructions,
|
|
21152
|
+
builtIn: name === "capture"
|
|
21153
|
+
};
|
|
21154
|
+
}
|
|
21155
|
+
function resolveAgent(entry, builtIns, globalModel, globalMaxSteps) {
|
|
21156
|
+
const config = typeof entry === "string" ? void 0 : entry;
|
|
21157
|
+
const name = entryName(entry);
|
|
21158
|
+
return {
|
|
21159
|
+
name,
|
|
21160
|
+
instructions: config?.instructions,
|
|
21161
|
+
model: config?.model ?? globalModel,
|
|
21162
|
+
agent: config?.agent,
|
|
21163
|
+
maxSteps: boundedSteps(config, globalMaxSteps),
|
|
21164
|
+
builtIn: builtIns.has(name)
|
|
21165
|
+
};
|
|
21166
|
+
}
|
|
21167
|
+
/**
|
|
21168
|
+
* Configures experimental autonomous knowledge extraction and reflection.
|
|
21169
|
+
*
|
|
21170
|
+
* @experimental This API may change without notice.
|
|
21171
|
+
*/
|
|
21172
|
+
var Subconscious = class {
|
|
21173
|
+
config;
|
|
21174
|
+
resolved;
|
|
21175
|
+
constructor(config = {}) {
|
|
21176
|
+
const observation = config.observation ?? ["capture", "remind"];
|
|
21177
|
+
const reflection = config.reflection ?? ["curate", "learn"];
|
|
21178
|
+
assertUniqueNames(observation, "observation");
|
|
21179
|
+
assertUniqueNames(reflection, "reflection");
|
|
21180
|
+
const maxSteps = boundedSteps(config, DEFAULT_MAX_STEPS);
|
|
21181
|
+
for (const entry of observation) this.#validateObservationEntry(entry);
|
|
21182
|
+
for (const entry of reflection) this.#validateReflectionEntry(entry);
|
|
21183
|
+
const recentUpdates = config.activity === false ? false : config.activity?.recentUpdates ?? DEFAULT_RECENT_UPDATES;
|
|
21184
|
+
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}.`);
|
|
21185
|
+
const pins = config.pins === void 0 || config.pins === false ? false : {
|
|
21186
|
+
maxPins: (config.pins === true ? void 0 : config.pins.maxPins) ?? 20,
|
|
21187
|
+
maxCharacters: (config.pins === true ? void 0 : config.pins.maxCharacters) ?? 2e3,
|
|
21188
|
+
capturePinning: (config.pins === true ? void 0 : config.pins.capturePinning) ?? false
|
|
21189
|
+
};
|
|
21190
|
+
if (pins !== false) {
|
|
21191
|
+
if (!Number.isInteger(pins.maxPins) || pins.maxPins < 1) throw new Error("Subconscious pins.maxPins must be a positive integer.");
|
|
21192
|
+
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}.`);
|
|
21193
|
+
}
|
|
21194
|
+
if (config.curationCadence !== void 0 && (!Number.isInteger(config.curationCadence) || config.curationCadence < 1)) throw new Error("Subconscious curationCadence must be a positive integer.");
|
|
21195
|
+
this.config = Object.freeze({
|
|
21196
|
+
...config,
|
|
21197
|
+
observation: [...observation],
|
|
21198
|
+
reflection: [...reflection]
|
|
21199
|
+
});
|
|
21200
|
+
this.resolved = Object.freeze({
|
|
21201
|
+
observation: observation.map((entry) => entryName(entry) === "remind" ? resolveAgent(entry, BUILT_IN_OBSERVATION, config.model, maxSteps) : resolveExtractor(entry)),
|
|
21202
|
+
reflection: reflection.map((entry) => resolveAgent(entry, BUILT_IN_REFLECTION, config.model, maxSteps)),
|
|
21203
|
+
defaultScope: config.defaultScope ?? "resource",
|
|
21204
|
+
maxScope: config.maxScope,
|
|
21205
|
+
learnedGuidance: config.learnedGuidance !== false,
|
|
21206
|
+
tools: config.tools !== false,
|
|
21207
|
+
activity: recentUpdates === false ? false : { recentUpdates },
|
|
21208
|
+
pins,
|
|
21209
|
+
curationCadence: config.curationCadence
|
|
21210
|
+
});
|
|
21211
|
+
}
|
|
21212
|
+
createObservationExtractors(omModel) {
|
|
21213
|
+
const extractors = [];
|
|
21214
|
+
for (const entry of this.config.observation ?? []) {
|
|
21215
|
+
const name = entryName(entry);
|
|
21216
|
+
if (name === "capture") extractors.push(new SubconsciousCaptureExtractor({
|
|
21217
|
+
config: typeof entry === "string" ? void 0 : entry,
|
|
21218
|
+
defaultScope: this.resolved.defaultScope,
|
|
21219
|
+
maxScope: this.resolved.maxScope,
|
|
21220
|
+
learnedGuidance: this.resolved.learnedGuidance,
|
|
21221
|
+
activityRecentUpdates: this.resolved.activity === false ? void 0 : this.resolved.activity.recentUpdates,
|
|
21222
|
+
pins: this.resolved.pins
|
|
21223
|
+
}));
|
|
21224
|
+
else if (name === "remind") {
|
|
21225
|
+
const resolved = this.resolved.observation.find((agent) => agent.name === name);
|
|
21226
|
+
if (resolved) extractors.push(new SubconsciousRemindExtractor(resolved, omModel));
|
|
21227
|
+
} else if (!BUILT_IN_OBSERVATION.has(name)) {
|
|
21228
|
+
const custom = entry;
|
|
21229
|
+
extractors.push(new Extractor({
|
|
21230
|
+
name: custom.name,
|
|
21231
|
+
instructions: custom.instructions?.trim() || `Extract ${custom.name} from the current observations.`,
|
|
21232
|
+
schema: custom.schema,
|
|
21233
|
+
metadataKeyPath: false,
|
|
21234
|
+
includePreviousExtraction: false,
|
|
21235
|
+
onExtracted: custom.onExtracted
|
|
21236
|
+
}));
|
|
21237
|
+
}
|
|
21238
|
+
}
|
|
21239
|
+
return extractors;
|
|
21240
|
+
}
|
|
21241
|
+
#validateObservationEntry(entry) {
|
|
21242
|
+
const name = entryName(entry);
|
|
21243
|
+
if (typeof entry === "string") {
|
|
21244
|
+
if (!BUILT_IN_OBSERVATION.has(name)) throw new Error(`Unknown Subconscious observation agent: ${name}`);
|
|
21245
|
+
return;
|
|
21246
|
+
}
|
|
21247
|
+
if (BUILT_IN_OBSERVATION.has(name)) {
|
|
21248
|
+
if (name === "capture") {
|
|
21249
|
+
if ("model" in entry || "maxSteps" in entry) throw new Error("Subconscious capture shares the Observer model and does not accept model or maxSteps.");
|
|
21250
|
+
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.");
|
|
21251
|
+
}
|
|
21252
|
+
return;
|
|
21253
|
+
}
|
|
21254
|
+
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.`);
|
|
21255
|
+
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.`);
|
|
21256
|
+
}
|
|
21257
|
+
#validateReflectionEntry(entry) {
|
|
21258
|
+
const name = entryName(entry);
|
|
21259
|
+
if (typeof entry === "string") {
|
|
21260
|
+
if (!BUILT_IN_REFLECTION.has(name)) throw new Error(`Unknown Subconscious reflection agent: ${name}`);
|
|
21261
|
+
return;
|
|
21262
|
+
}
|
|
21263
|
+
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.`);
|
|
21264
|
+
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.`);
|
|
21265
|
+
}
|
|
21266
|
+
};
|
|
21267
|
+
const CURATION_AGENT = "curate";
|
|
21268
|
+
const DEFAULT_INSTRUCTIONS$1 = `Maintain durable scoped knowledge from the committed observation worklist.
|
|
21269
|
+
|
|
21270
|
+
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.
|
|
21271
|
+
|
|
21272
|
+
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.`;
|
|
21273
|
+
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.`;
|
|
21274
|
+
function resolveScope$1(context) {
|
|
21275
|
+
const organizationId = context.requestContext?.get("organizationId");
|
|
21276
|
+
if (typeof organizationId !== "string" || !organizationId.trim()) throw new Error("Subconscious curate requires organizationId in the request context.");
|
|
21277
|
+
return (0, _mastra_core_storage.canonicalizeKnowledgeScope)([
|
|
21278
|
+
`org:${organizationId}`,
|
|
21279
|
+
`resource:${resolveKnowledgeResourceId(context.requestContext, context.resourceId)}`,
|
|
21280
|
+
`thread:${context.parentThreadId}`
|
|
21281
|
+
]);
|
|
21282
|
+
}
|
|
21283
|
+
async function readWorklist$1(store, sourceThreadId, scope, after) {
|
|
21284
|
+
const records = [];
|
|
21285
|
+
let cursor = after;
|
|
21286
|
+
do {
|
|
21287
|
+
const page = await store.knowledgeBySource({
|
|
21288
|
+
sourceThreadId,
|
|
21289
|
+
scope,
|
|
21290
|
+
after: cursor,
|
|
21291
|
+
limit: 100,
|
|
21292
|
+
includeDeleted: true
|
|
21293
|
+
});
|
|
21294
|
+
records.push(...page.records);
|
|
21295
|
+
cursor = page.nextCursor;
|
|
21296
|
+
} while (cursor && records.length < 500);
|
|
21297
|
+
return {
|
|
21298
|
+
records,
|
|
21299
|
+
hasMore: Boolean(cursor)
|
|
21300
|
+
};
|
|
21301
|
+
}
|
|
21302
|
+
function createCuratorHandler(memory, subconscious, curatorMemory = memory, options) {
|
|
21303
|
+
const config = subconscious.reflection.find((agent) => agent.name === CURATION_AGENT);
|
|
21304
|
+
if (!config) return async () => "no-op";
|
|
21305
|
+
return async (context) => {
|
|
21306
|
+
let store;
|
|
21307
|
+
let scope;
|
|
21308
|
+
try {
|
|
21309
|
+
scope = resolveScope$1(context);
|
|
21310
|
+
store = await memory.storage.getStore("knowledge");
|
|
21311
|
+
if (!store) throw new Error("Subconscious curate requires a configured knowledge storage domain.");
|
|
21312
|
+
const cursor = await store.getCurationCursor({
|
|
21313
|
+
sourceThreadId: context.parentThreadId,
|
|
21314
|
+
agent: CURATION_AGENT
|
|
21315
|
+
});
|
|
21316
|
+
const worklist = await readWorklist$1(store, context.parentThreadId, scope, cursor?.lastKnowledgeId);
|
|
21317
|
+
if (!worklist.records.length && !context.observations.trim()) return "no-op";
|
|
21318
|
+
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)}`, {
|
|
21319
|
+
requestContext: context.requestContext,
|
|
21320
|
+
abortSignal: context.abortSignal,
|
|
21321
|
+
maxSteps: config.maxSteps,
|
|
21322
|
+
memory: {
|
|
21323
|
+
thread: `subconscious:${context.parentThreadId}:curate`,
|
|
21324
|
+
resource: context.resourceId
|
|
21325
|
+
}
|
|
21326
|
+
});
|
|
21327
|
+
if (worklist.records.length) {
|
|
21328
|
+
const acknowledgedId = [...result.text.matchAll(/<curation-complete\s+through=["']([^"']+)["']\s*\/>/gi)].at(-1)?.[1];
|
|
21329
|
+
if (!acknowledgedId || !worklist.records.some((record) => record.id === acknowledgedId)) throw new Error("Curator did not acknowledge a valid processed KnowledgeRecord cursor.");
|
|
21330
|
+
await store.advanceCurationCursor({
|
|
21331
|
+
sourceThreadId: context.parentThreadId,
|
|
21332
|
+
agent: CURATION_AGENT,
|
|
21333
|
+
lastKnowledgeId: acknowledgedId
|
|
21334
|
+
});
|
|
21335
|
+
}
|
|
21336
|
+
return "ran";
|
|
21337
|
+
} catch (error) {
|
|
21338
|
+
const message = `curate: ${error instanceof Error ? error.message : String(error)}`;
|
|
21339
|
+
await context.writer?.custom({
|
|
21340
|
+
type: "data-subconscious-error",
|
|
21341
|
+
data: {
|
|
21342
|
+
agent: "curate",
|
|
21343
|
+
error: message
|
|
21344
|
+
}
|
|
21345
|
+
});
|
|
21346
|
+
if (store && scope) await publishSubconsciousActivity({
|
|
21347
|
+
store,
|
|
21348
|
+
scope,
|
|
21349
|
+
recentUpdates: subconscious.activity === false ? 10 : subconscious.activity.recentUpdates,
|
|
21350
|
+
sendStateSignal: context.sendStateSignal,
|
|
21351
|
+
errors: [message]
|
|
21352
|
+
});
|
|
21353
|
+
else await publishSubconsciousError({
|
|
21354
|
+
error: message,
|
|
21355
|
+
sendStateSignal: context.sendStateSignal
|
|
21356
|
+
});
|
|
21357
|
+
throw error;
|
|
21358
|
+
}
|
|
21359
|
+
};
|
|
21360
|
+
}
|
|
21361
|
+
async function createCuratorAgent(memory, curatorMemory, context, scope, config, subconscious, omModel) {
|
|
21362
|
+
const model = await resolveSubconsciousAgentModel({
|
|
21363
|
+
config,
|
|
21364
|
+
omModel,
|
|
21365
|
+
mainAgent: context.mainAgent,
|
|
21366
|
+
requestContext: context.requestContext
|
|
21367
|
+
});
|
|
21368
|
+
if (!model) throw new Error("Subconscious curate requires the main agent to resolve its model.");
|
|
21369
|
+
return new _mastra_core_agent.Agent({
|
|
21370
|
+
id: `subconscious-curate-${context.parentThreadId}`,
|
|
21371
|
+
name: "Subconscious Curate",
|
|
21372
|
+
instructions: [
|
|
21373
|
+
DEFAULT_INSTRUCTIONS$1,
|
|
21374
|
+
subconscious.pins ? PINNED_INSTRUCTIONS : void 0,
|
|
21375
|
+
config.instructions?.trim()
|
|
21376
|
+
].filter(Boolean).join("\n\n"),
|
|
21377
|
+
model,
|
|
21378
|
+
memory: curatorMemory,
|
|
21379
|
+
tools: {
|
|
21380
|
+
...createKnowledgeTools(memory),
|
|
21381
|
+
...createKnowledgeWriteTools(memory, {
|
|
21382
|
+
scope,
|
|
21383
|
+
sourceThreadId: context.parentThreadId,
|
|
21384
|
+
defaultScope: subconscious.defaultScope,
|
|
21385
|
+
maxScope: subconscious.maxScope
|
|
21386
|
+
}),
|
|
21387
|
+
...subconscious.pins ? createPinnedTools(memory, {
|
|
21388
|
+
scope,
|
|
21389
|
+
sourceThreadId: context.parentThreadId,
|
|
21390
|
+
defaultScope: subconscious.defaultScope,
|
|
21391
|
+
maxScope: subconscious.maxScope,
|
|
21392
|
+
maxPins: subconscious.pins.maxPins,
|
|
21393
|
+
maxCharacters: subconscious.pins.maxCharacters
|
|
21394
|
+
}) : {}
|
|
21395
|
+
}
|
|
21396
|
+
});
|
|
21397
|
+
}
|
|
21398
|
+
const LEARN_AGENT = "learn";
|
|
21399
|
+
const ULID_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
21400
|
+
const DEFAULT_INSTRUCTIONS = `Learn reusable skills from the full pre-reflection observations and pending knowledge records.
|
|
21401
|
+
|
|
21402
|
+
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.
|
|
21403
|
+
|
|
21404
|
+
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.
|
|
21405
|
+
|
|
21406
|
+
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.`;
|
|
21407
|
+
function resolveScope(context) {
|
|
21408
|
+
const organizationId = context.requestContext?.get("organizationId");
|
|
21409
|
+
if (typeof organizationId !== "string" || !organizationId.trim()) throw new Error("Subconscious learn requires organizationId in the request context.");
|
|
21410
|
+
return (0, _mastra_core_storage.canonicalizeKnowledgeScope)([
|
|
21411
|
+
`org:${organizationId}`,
|
|
21412
|
+
`resource:${resolveKnowledgeResourceId(context.requestContext, context.resourceId)}`,
|
|
21413
|
+
`thread:${context.parentThreadId}`
|
|
21414
|
+
]);
|
|
21415
|
+
}
|
|
21416
|
+
async function readWorklist(store, sourceThreadId, scope, after) {
|
|
21417
|
+
const records = [];
|
|
21418
|
+
let cursor = after;
|
|
21419
|
+
do {
|
|
21420
|
+
const page = await store.knowledgeBySource({
|
|
21421
|
+
sourceThreadId,
|
|
21422
|
+
scope,
|
|
21423
|
+
after: cursor,
|
|
21424
|
+
limit: 100
|
|
21425
|
+
});
|
|
21426
|
+
records.push(...page.records);
|
|
21427
|
+
cursor = page.nextCursor;
|
|
21428
|
+
} while (cursor && records.length < 500);
|
|
21429
|
+
return {
|
|
21430
|
+
records,
|
|
21431
|
+
hasMore: Boolean(cursor)
|
|
21432
|
+
};
|
|
21433
|
+
}
|
|
21434
|
+
function evidenceRecordId(sourceRecordId, skillName) {
|
|
21435
|
+
const hash = (0, crypto$1.createHash)("sha256").update(`${skillName.trim().toLocaleLowerCase()}\0${sourceRecordId}`).digest();
|
|
21436
|
+
let suffix = "";
|
|
21437
|
+
for (let index = 0; index < 16; index++) suffix += ULID_ALPHABET[hash[index] & 31];
|
|
21438
|
+
return `${sourceRecordId.slice(0, 10)}${suffix}`;
|
|
21439
|
+
}
|
|
21440
|
+
function createLearnerRecordSkillTool(input) {
|
|
21441
|
+
return (0, _mastra_core_tools.createTool)({
|
|
21442
|
+
id: "knowledge_record_skill",
|
|
21443
|
+
description: "Create or update one reusable skill using at least two distinct pending knowledge records. Evidence writes are idempotent across retries.",
|
|
21444
|
+
inputSchema: {
|
|
21445
|
+
type: "object",
|
|
21446
|
+
properties: {
|
|
21447
|
+
name: {
|
|
21448
|
+
type: "string",
|
|
21449
|
+
minLength: 1
|
|
21450
|
+
},
|
|
21451
|
+
procedure: {
|
|
21452
|
+
type: "string",
|
|
21453
|
+
minLength: 1
|
|
21454
|
+
},
|
|
21455
|
+
sourceRecordIds: {
|
|
21456
|
+
type: "array",
|
|
21457
|
+
items: {
|
|
21458
|
+
type: "string",
|
|
21459
|
+
minLength: 1
|
|
21460
|
+
},
|
|
21461
|
+
minItems: 2,
|
|
21462
|
+
uniqueItems: true
|
|
21463
|
+
}
|
|
21464
|
+
},
|
|
21465
|
+
required: [
|
|
21466
|
+
"name",
|
|
21467
|
+
"procedure",
|
|
21468
|
+
"sourceRecordIds"
|
|
21469
|
+
],
|
|
21470
|
+
additionalProperties: false
|
|
21471
|
+
},
|
|
21472
|
+
execute: async (raw) => {
|
|
21473
|
+
const value = raw;
|
|
21474
|
+
const sourceIds = [...new Set(value.sourceRecordIds)];
|
|
21475
|
+
const pending = new Map(input.pendingRecords.map((record) => [record.id, record]));
|
|
21476
|
+
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.");
|
|
21477
|
+
const normalizedName = value.name.trim();
|
|
21478
|
+
if (input.state.recordedName && input.state.recordedName.toLocaleLowerCase() !== normalizedName.toLocaleLowerCase()) throw new Error("The learner may record at most one skill per reflection.");
|
|
21479
|
+
input.state.recordedName = normalizedName;
|
|
21480
|
+
const nodeScope = (0, _mastra_core_storage.expandKnowledgeScope)(input.scope, input.defaultScope);
|
|
21481
|
+
let node = await input.store.resolveNode({
|
|
21482
|
+
name: normalizedName,
|
|
21483
|
+
scope: input.scope
|
|
21484
|
+
});
|
|
21485
|
+
if (node && node.kind !== "skill") throw new Error(`Knowledge node is not a skill: ${normalizedName}`);
|
|
21486
|
+
node ??= await input.store.createNode({
|
|
21487
|
+
name: normalizedName,
|
|
21488
|
+
kind: "skill",
|
|
21489
|
+
scope: nodeScope
|
|
21490
|
+
});
|
|
21491
|
+
const evidence = [];
|
|
21492
|
+
for (const sourceId of sourceIds) {
|
|
21493
|
+
const id = evidenceRecordId(sourceId, normalizedName);
|
|
21494
|
+
const existing = await input.store.getKnowledge({ id });
|
|
21495
|
+
if (existing) {
|
|
21496
|
+
evidence.push(existing);
|
|
21497
|
+
continue;
|
|
21498
|
+
}
|
|
21499
|
+
const source = pending.get(sourceId);
|
|
21500
|
+
try {
|
|
21501
|
+
evidence.push(await input.store.appendKnowledge({
|
|
21502
|
+
id,
|
|
21503
|
+
node: node.id,
|
|
21504
|
+
text: `Procedure: ${value.procedure.trim()} Evidence source: ${source.id}.`,
|
|
21505
|
+
scope: source.scope,
|
|
21506
|
+
sourceThreadId: `subconscious:${input.parentThreadId}:learn`,
|
|
21507
|
+
maxScope: source.maxScope ?? input.maxScope,
|
|
21508
|
+
resolutionScope: input.scope,
|
|
21509
|
+
defaultScope: nodeScope
|
|
21510
|
+
}));
|
|
21511
|
+
} catch (error) {
|
|
21512
|
+
const raced = await input.store.getKnowledge({ id });
|
|
21513
|
+
if (!raced) throw error;
|
|
21514
|
+
evidence.push(raced);
|
|
21515
|
+
}
|
|
21516
|
+
}
|
|
21517
|
+
return {
|
|
21518
|
+
node,
|
|
21519
|
+
evidence
|
|
21520
|
+
};
|
|
21521
|
+
}
|
|
21522
|
+
});
|
|
21523
|
+
}
|
|
21524
|
+
function composeReflectionAgentHandlers(handlers) {
|
|
21525
|
+
return async (context) => {
|
|
21526
|
+
for (const handler of handlers) try {
|
|
21527
|
+
await handler(context);
|
|
21528
|
+
} catch (error) {
|
|
21529
|
+
if (context.abortSignal?.aborted) throw error;
|
|
21530
|
+
}
|
|
21531
|
+
};
|
|
21532
|
+
}
|
|
21533
|
+
function createLearnerHandler(memory, subconscious, learnerMemory = memory, options) {
|
|
21534
|
+
const config = subconscious.reflection.find((agent) => agent.name === LEARN_AGENT);
|
|
21535
|
+
if (!config) return async () => {};
|
|
21536
|
+
return async (context) => {
|
|
21537
|
+
let store;
|
|
21538
|
+
let scope;
|
|
21539
|
+
try {
|
|
21540
|
+
scope = resolveScope(context);
|
|
21541
|
+
store = await memory.storage.getStore("knowledge");
|
|
21542
|
+
if (!store) throw new Error("Subconscious learn requires a configured knowledge storage domain.");
|
|
21543
|
+
const cursor = await store.getCurationCursor({
|
|
21544
|
+
sourceThreadId: context.parentThreadId,
|
|
21545
|
+
agent: LEARN_AGENT
|
|
21546
|
+
});
|
|
21547
|
+
const worklist = await readWorklist(store, context.parentThreadId, scope, cursor?.lastKnowledgeId);
|
|
21548
|
+
if (!worklist.records.length) return;
|
|
21549
|
+
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)}`, {
|
|
21550
|
+
requestContext: context.requestContext,
|
|
21551
|
+
abortSignal: context.abortSignal,
|
|
21552
|
+
maxSteps: config.maxSteps,
|
|
21553
|
+
memory: {
|
|
21554
|
+
thread: `subconscious:${context.parentThreadId}:learn`,
|
|
21555
|
+
resource: context.resourceId
|
|
21556
|
+
}
|
|
21557
|
+
})).text.match(/<learning-complete\s+through=["']([^"']+)["']\s*\/>/i)?.[1];
|
|
21558
|
+
if (!acknowledgedId || !worklist.records.some((record) => record.id === acknowledgedId)) throw new Error("Learner did not acknowledge a valid reviewed record cursor.");
|
|
21559
|
+
await store.advanceCurationCursor({
|
|
21560
|
+
sourceThreadId: context.parentThreadId,
|
|
21561
|
+
agent: LEARN_AGENT,
|
|
21562
|
+
lastKnowledgeId: acknowledgedId
|
|
21563
|
+
});
|
|
21564
|
+
} catch (error) {
|
|
21565
|
+
const message = `learn: ${error instanceof Error ? error.message : String(error)}`;
|
|
21566
|
+
await context.writer?.custom({
|
|
21567
|
+
type: "data-subconscious-error",
|
|
21568
|
+
data: {
|
|
21569
|
+
agent: "learn",
|
|
21570
|
+
error: message
|
|
21571
|
+
}
|
|
21572
|
+
});
|
|
21573
|
+
if (store && scope) await publishSubconsciousActivity({
|
|
21574
|
+
store,
|
|
21575
|
+
scope,
|
|
21576
|
+
recentUpdates: subconscious.activity === false ? 10 : subconscious.activity.recentUpdates,
|
|
21577
|
+
sendStateSignal: context.sendStateSignal,
|
|
21578
|
+
errors: [message]
|
|
21579
|
+
});
|
|
21580
|
+
else await publishSubconsciousError({
|
|
21581
|
+
error: message,
|
|
21582
|
+
sendStateSignal: context.sendStateSignal
|
|
21583
|
+
});
|
|
21584
|
+
throw error;
|
|
21585
|
+
}
|
|
21586
|
+
};
|
|
21587
|
+
}
|
|
21588
|
+
async function createLearnerAgent(memory, learnerMemory, context, scope, pendingRecords, config, subconscious, omModel) {
|
|
21589
|
+
const model = await resolveSubconsciousAgentModel({
|
|
21590
|
+
config,
|
|
21591
|
+
omModel,
|
|
21592
|
+
mainAgent: context.mainAgent,
|
|
21593
|
+
requestContext: context.requestContext
|
|
21594
|
+
});
|
|
21595
|
+
if (!model) throw new Error("Subconscious learn requires the main agent to resolve its model.");
|
|
21596
|
+
const store = await memory.storage.getStore("knowledge");
|
|
21597
|
+
if (!store) throw new Error("Subconscious learn requires a configured knowledge storage domain.");
|
|
21598
|
+
const state = {};
|
|
21599
|
+
return new _mastra_core_agent.Agent({
|
|
21600
|
+
id: `subconscious-learn-${context.parentThreadId}`,
|
|
21601
|
+
name: "Subconscious Learn",
|
|
21602
|
+
instructions: [DEFAULT_INSTRUCTIONS, config.instructions?.trim()].filter(Boolean).join("\n\n"),
|
|
21603
|
+
model,
|
|
21604
|
+
memory: learnerMemory,
|
|
21605
|
+
tools: {
|
|
21606
|
+
...createKnowledgeTools(memory, scope),
|
|
21607
|
+
...createKnowledgeWriteTools(memory, {
|
|
21608
|
+
scope,
|
|
21609
|
+
sourceThreadId: context.parentThreadId,
|
|
21610
|
+
defaultScope: subconscious.defaultScope,
|
|
21611
|
+
maxScope: subconscious.maxScope
|
|
21612
|
+
}),
|
|
21613
|
+
knowledge_record_skill: createLearnerRecordSkillTool({
|
|
21614
|
+
store,
|
|
21615
|
+
scope,
|
|
21616
|
+
pendingRecords,
|
|
21617
|
+
parentThreadId: context.parentThreadId,
|
|
21618
|
+
defaultScope: subconscious.defaultScope,
|
|
21619
|
+
maxScope: subconscious.maxScope,
|
|
21620
|
+
state
|
|
21621
|
+
})
|
|
21622
|
+
}
|
|
21623
|
+
});
|
|
21624
|
+
}
|
|
19243
21625
|
var constants_exports = /* @__PURE__ */ __exportAll({
|
|
19244
21626
|
OBSERVATIONAL_MEMORY_DEFAULTS: () => OBSERVATIONAL_MEMORY_DEFAULTS,
|
|
19245
21627
|
OBSERVATION_CONTEXT_INSTRUCTIONS: () => OBSERVATION_CONTEXT_INSTRUCTIONS,
|
|
@@ -19394,232 +21776,6 @@ Observation groups with range IDs and your recall tool allows you to think back
|
|
|
19394
21776
|
### Additional recall guidance
|
|
19395
21777
|
${custom}`;
|
|
19396
21778
|
}
|
|
19397
|
-
const BUILT_IN_SLUGS = /* @__PURE__ */ new Set([
|
|
19398
|
-
"current-task",
|
|
19399
|
-
"suggested-response",
|
|
19400
|
-
"thread-title"
|
|
19401
|
-
]);
|
|
19402
|
-
const EXTRACTED_VALUES_TAG = "extracted-values";
|
|
19403
|
-
const RESERVED_XML_TAGS = /* @__PURE__ */ new Set([
|
|
19404
|
-
"observations",
|
|
19405
|
-
"observation",
|
|
19406
|
-
EXTRACTED_VALUES_TAG,
|
|
19407
|
-
"thread",
|
|
19408
|
-
"message",
|
|
19409
|
-
"messages",
|
|
19410
|
-
"conversation",
|
|
19411
|
-
"history",
|
|
19412
|
-
"system",
|
|
19413
|
-
"user",
|
|
19414
|
-
"assistant",
|
|
19415
|
-
"tool",
|
|
19416
|
-
...BUILT_IN_SLUGS
|
|
19417
|
-
]);
|
|
19418
|
-
const BUILT_IN_EXTRACTOR_SLUGS = [...BUILT_IN_SLUGS];
|
|
19419
|
-
function isBuiltInExtractorSlug(slug) {
|
|
19420
|
-
return BUILT_IN_SLUGS.has(slug);
|
|
19421
|
-
}
|
|
19422
|
-
function slugifyExtractorName(name) {
|
|
19423
|
-
let normalized = "";
|
|
19424
|
-
let previousWasSeparator = false;
|
|
19425
|
-
for (const char of name.trim().toLowerCase()) {
|
|
19426
|
-
const code = char.charCodeAt(0);
|
|
19427
|
-
if (code >= 97 && code <= 122 || code >= 48 && code <= 57) {
|
|
19428
|
-
normalized += char;
|
|
19429
|
-
previousWasSeparator = false;
|
|
19430
|
-
continue;
|
|
19431
|
-
}
|
|
19432
|
-
if (char === "'" || char === "\"" || char === "`") continue;
|
|
19433
|
-
if (!previousWasSeparator && normalized.length > 0) {
|
|
19434
|
-
normalized += "-";
|
|
19435
|
-
previousWasSeparator = true;
|
|
19436
|
-
}
|
|
19437
|
-
}
|
|
19438
|
-
return normalized.endsWith("-") ? normalized.slice(0, -1) : normalized;
|
|
19439
|
-
}
|
|
19440
|
-
function assertValidSlug(slug, name) {
|
|
19441
|
-
if (!slug) throw new Error(`Extractor name "${name}" must produce a non-empty slug.`);
|
|
19442
|
-
const first = slug.charCodeAt(0);
|
|
19443
|
-
const last = slug.charCodeAt(slug.length - 1);
|
|
19444
|
-
const startsWithLetter = first >= 97 && first <= 122;
|
|
19445
|
-
const endsWithLetterOrNumber = last >= 97 && last <= 122 || last >= 48 && last <= 57;
|
|
19446
|
-
const hasOnlySlugCharacters = [...slug].every((char) => {
|
|
19447
|
-
const code = char.charCodeAt(0);
|
|
19448
|
-
return code >= 97 && code <= 122 || code >= 48 && code <= 57 || char === "-";
|
|
19449
|
-
});
|
|
19450
|
-
if (!startsWithLetter || !endsWithLetterOrNumber || !hasOnlySlugCharacters) throw new Error(`Extractor name "${name}" produced invalid slug "${slug}".`);
|
|
19451
|
-
}
|
|
19452
|
-
var Extractor = class Extractor {
|
|
19453
|
-
name;
|
|
19454
|
-
slug;
|
|
19455
|
-
instructions;
|
|
19456
|
-
schema;
|
|
19457
|
-
mode;
|
|
19458
|
-
includePreviousExtraction;
|
|
19459
|
-
metadataKeyPath;
|
|
19460
|
-
onExtracted;
|
|
19461
|
-
retryStructuredExtractionOnEmptyObject;
|
|
19462
|
-
/** @internal */
|
|
19463
|
-
internal;
|
|
19464
|
-
instructionsConfig;
|
|
19465
|
-
schemaConfig;
|
|
19466
|
-
constructor(config, internal = false) {
|
|
19467
|
-
const name = config.name.trim();
|
|
19468
|
-
const instructions = typeof config.instructions === "string" ? config.instructions.trim() : void 0;
|
|
19469
|
-
const slug = slugifyExtractorName(name);
|
|
19470
|
-
if (!name) throw new Error("Extractor name is required.");
|
|
19471
|
-
if (instructions !== void 0 && !instructions) throw new Error(`Extractor "${name}" must include instructions.`);
|
|
19472
|
-
assertValidSlug(slug, name);
|
|
19473
|
-
if (!internal && RESERVED_XML_TAGS.has(slug)) throw new Error(`Extractor slug "${slug}" is reserved by Observational Memory.`);
|
|
19474
|
-
this.name = name;
|
|
19475
|
-
this.slug = slug;
|
|
19476
|
-
this.instructionsConfig = config.instructions;
|
|
19477
|
-
this.schemaConfig = config.schema;
|
|
19478
|
-
this.instructions = instructions ?? "";
|
|
19479
|
-
this.schema = typeof config.schema === "function" ? zod.z.string() : config.schema ?? zod.z.string();
|
|
19480
|
-
this.mode = internal || !config.schema ? "inline" : "structured";
|
|
19481
|
-
this.includePreviousExtraction = config.includePreviousExtraction ?? true;
|
|
19482
|
-
this.metadataKeyPath = config.metadataKeyPath ?? `extracted.${slug}`;
|
|
19483
|
-
this.onExtracted = config.onExtracted;
|
|
19484
|
-
this.retryStructuredExtractionOnEmptyObject = config.retryStructuredExtractionOnEmptyObject ?? false;
|
|
19485
|
-
this.internal = internal;
|
|
19486
|
-
}
|
|
19487
|
-
async resolve(context) {
|
|
19488
|
-
const instructions = typeof this.instructionsConfig === "function" ? (await this.instructionsConfig(context)).trim() : this.instructionsConfig.trim();
|
|
19489
|
-
if (!instructions) throw new Error(`Extractor "${this.name}" must include instructions.`);
|
|
19490
|
-
const schema = typeof this.schemaConfig === "function" ? await this.schemaConfig(context) : this.schemaConfig;
|
|
19491
|
-
return new Extractor({
|
|
19492
|
-
name: this.name,
|
|
19493
|
-
instructions,
|
|
19494
|
-
...schema ? { schema } : {},
|
|
19495
|
-
includePreviousExtraction: this.includePreviousExtraction,
|
|
19496
|
-
metadataKeyPath: this.metadataKeyPath,
|
|
19497
|
-
onExtracted: this.onExtracted,
|
|
19498
|
-
retryStructuredExtractionOnEmptyObject: this.retryStructuredExtractionOnEmptyObject
|
|
19499
|
-
}, this.internal);
|
|
19500
|
-
}
|
|
19501
|
-
};
|
|
19502
|
-
async function resolveExtractors(extractors, context) {
|
|
19503
|
-
return Promise.all(extractors.map((extractor) => extractor.resolve(context)));
|
|
19504
|
-
}
|
|
19505
|
-
function validateExtractorList(extractors) {
|
|
19506
|
-
const seen = /* @__PURE__ */ new Map();
|
|
19507
|
-
for (const extractor of extractors) {
|
|
19508
|
-
assertValidSlug(extractor.slug, extractor.name);
|
|
19509
|
-
if (!extractor.internal && RESERVED_XML_TAGS.has(extractor.slug)) throw new Error(`Extractor slug "${extractor.slug}" is reserved by Observational Memory.`);
|
|
19510
|
-
const previous = seen.get(extractor.slug);
|
|
19511
|
-
if (previous) throw new Error(`Duplicate extractor slug "${extractor.slug}" from "${previous}" and "${extractor.name}".`);
|
|
19512
|
-
seen.set(extractor.slug, extractor.name);
|
|
19513
|
-
}
|
|
19514
|
-
return [...extractors];
|
|
19515
|
-
}
|
|
19516
|
-
function isJsonLike(value) {
|
|
19517
|
-
return /^(?:[\[{"-]|\d|true\b|false\b|null\b)/.test(value.trim());
|
|
19518
|
-
}
|
|
19519
|
-
function candidateValues(raw) {
|
|
19520
|
-
const trimmed = raw.trim();
|
|
19521
|
-
const candidates = [];
|
|
19522
|
-
const add = (value) => {
|
|
19523
|
-
if (!candidates.some((candidate) => Object.is(candidate, value))) candidates.push(value);
|
|
19524
|
-
};
|
|
19525
|
-
if (isJsonLike(trimmed)) try {
|
|
19526
|
-
add(JSON.parse(trimmed));
|
|
19527
|
-
} catch {}
|
|
19528
|
-
add(trimmed);
|
|
19529
|
-
if (!isJsonLike(trimmed)) try {
|
|
19530
|
-
add(JSON.parse(trimmed));
|
|
19531
|
-
} catch {}
|
|
19532
|
-
return candidates;
|
|
19533
|
-
}
|
|
19534
|
-
function parseExtractorValue(extractor, raw) {
|
|
19535
|
-
const failures = [];
|
|
19536
|
-
for (const candidate of candidateValues(raw)) {
|
|
19537
|
-
const parsed = extractor.schema.safeParse(candidate);
|
|
19538
|
-
if (parsed.success) return parsed.data;
|
|
19539
|
-
failures.push(parsed.error.message);
|
|
19540
|
-
}
|
|
19541
|
-
throw new Error(`Extractor "${extractor.slug}" output did not match its schema: ${failures[0] ?? "invalid value"}`);
|
|
19542
|
-
}
|
|
19543
|
-
function parseExtractedValuesObject(raw) {
|
|
19544
|
-
const trimmed = raw.trim();
|
|
19545
|
-
if (!trimmed || trimmed.startsWith("Write only the extracted values JSON object here.")) return;
|
|
19546
|
-
const parsed = JSON.parse(trimmed);
|
|
19547
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`${EXTRACTED_VALUES_TAG} must contain a JSON object.`);
|
|
19548
|
-
return parsed;
|
|
19549
|
-
}
|
|
19550
|
-
function parseExtractedValues(output, extractors) {
|
|
19551
|
-
const values = {};
|
|
19552
|
-
const failures = [];
|
|
19553
|
-
const inlineExtractors = extractors.filter((extractor) => extractor.mode === "inline");
|
|
19554
|
-
const regex = new RegExp(`<${EXTRACTED_VALUES_TAG}>([\\s\\S]*?)<\\/${EXTRACTED_VALUES_TAG}>`, "gi");
|
|
19555
|
-
const matches = [...output.matchAll(regex)];
|
|
19556
|
-
for (const match of matches) try {
|
|
19557
|
-
const extractedValues = parseExtractedValuesObject(match[1] ?? "");
|
|
19558
|
-
if (!extractedValues) continue;
|
|
19559
|
-
for (const extractor of inlineExtractors) {
|
|
19560
|
-
if (!Object.prototype.hasOwnProperty.call(extractedValues, extractor.slug)) continue;
|
|
19561
|
-
const rawValue = extractedValues[extractor.slug];
|
|
19562
|
-
if (rawValue === void 0 || rawValue === null || rawValue === "") continue;
|
|
19563
|
-
const parsed = extractor.schema.safeParse(rawValue);
|
|
19564
|
-
if (parsed.success) values[extractor.slug] = parsed.data;
|
|
19565
|
-
else failures.push({
|
|
19566
|
-
slug: extractor.slug,
|
|
19567
|
-
error: parsed.error.message
|
|
19568
|
-
});
|
|
19569
|
-
}
|
|
19570
|
-
} catch (error) {
|
|
19571
|
-
failures.push({
|
|
19572
|
-
slug: EXTRACTED_VALUES_TAG,
|
|
19573
|
-
error: error instanceof Error ? error.message : String(error)
|
|
19574
|
-
});
|
|
19575
|
-
}
|
|
19576
|
-
for (const extractor of inlineExtractors) {
|
|
19577
|
-
if (Object.prototype.hasOwnProperty.call(values, extractor.slug)) continue;
|
|
19578
|
-
const tagRegex = new RegExp(`<${extractor.slug}>([\\s\\S]*?)<\\/${extractor.slug}>`, "gi");
|
|
19579
|
-
const rawValue = [...output.matchAll(tagRegex)].at(-1)?.[1]?.trim();
|
|
19580
|
-
if (!rawValue) continue;
|
|
19581
|
-
try {
|
|
19582
|
-
values[extractor.slug] = parseExtractorValue(extractor, rawValue);
|
|
19583
|
-
} catch (error) {
|
|
19584
|
-
failures.push({
|
|
19585
|
-
slug: extractor.slug,
|
|
19586
|
-
error: error instanceof Error ? error.message : String(error)
|
|
19587
|
-
});
|
|
19588
|
-
}
|
|
19589
|
-
}
|
|
19590
|
-
return {
|
|
19591
|
-
values,
|
|
19592
|
-
failures
|
|
19593
|
-
};
|
|
19594
|
-
}
|
|
19595
|
-
function stripExtractorSections(output, extractors) {
|
|
19596
|
-
const inlineExtractors = extractors.filter((extractor) => extractor.mode === "inline");
|
|
19597
|
-
let stripped = output.replace(new RegExp(`[ \\t]*<${EXTRACTED_VALUES_TAG}>[\\s\\S]*?<\\/${EXTRACTED_VALUES_TAG}>\\s*`, "gi"), "");
|
|
19598
|
-
for (const extractor of inlineExtractors) stripped = stripped.replace(new RegExp(`[ \\t]*<${extractor.slug}>[\\s\\S]*?<\\/${extractor.slug}>\\s*`, "gi"), "");
|
|
19599
|
-
return stripped;
|
|
19600
|
-
}
|
|
19601
|
-
function buildExtractorOutputSections(extractors) {
|
|
19602
|
-
const inlineExtractors = extractors.filter((extractor) => extractor.mode === "inline");
|
|
19603
|
-
if (inlineExtractors.length === 0) return "";
|
|
19604
|
-
return `Additional optional XML sections:\nIf the observations include information relevant to any of these tags, output that tag after <observations> and include the relevant information.\n${inlineExtractors.map((extractor) => `<${extractor.slug}>
|
|
19605
|
-
${extractor.instructions}
|
|
19606
|
-
Include this section when the observations contain relevant information for <${extractor.slug}>. Write only that information inside the tag.
|
|
19607
|
-
</${extractor.slug}>`).join("\n\n")}`;
|
|
19608
|
-
}
|
|
19609
|
-
function renderPriorValue(value) {
|
|
19610
|
-
return typeof value === "string" ? value : JSON.stringify(value);
|
|
19611
|
-
}
|
|
19612
|
-
function buildExtractorPriorLines(extractors, priorExtractedValues) {
|
|
19613
|
-
if (!priorExtractedValues) return [];
|
|
19614
|
-
const lines = [];
|
|
19615
|
-
for (const extractor of extractors) {
|
|
19616
|
-
if (!extractor.includePreviousExtraction) continue;
|
|
19617
|
-
const value = priorExtractedValues[extractor.slug];
|
|
19618
|
-
if (value === void 0 || value === null || value === "") continue;
|
|
19619
|
-
lines.push(`<${extractor.slug}>\n${renderPriorValue(value)}\n</${extractor.slug}>`);
|
|
19620
|
-
}
|
|
19621
|
-
return lines;
|
|
19622
|
-
}
|
|
19623
21779
|
const BUILT_IN_METADATA_FIELDS = {
|
|
19624
21780
|
"current-task": {
|
|
19625
21781
|
metadataField: "currentTask",
|
|
@@ -19741,9 +21897,11 @@ async function applyExtractorHooks(opts) {
|
|
|
19741
21897
|
const values = normalizeExtractedValues(opts.values) ?? {};
|
|
19742
21898
|
const failures = [...opts.failures ?? []];
|
|
19743
21899
|
for (const extractor of opts.extractors) {
|
|
19744
|
-
|
|
19745
|
-
const
|
|
19746
|
-
if (!
|
|
21900
|
+
const isHook = extractor.mode === "hook";
|
|
21901
|
+
const hasValue = Object.prototype.hasOwnProperty.call(values, extractor.slug);
|
|
21902
|
+
if (!isHook && !hasValue) continue;
|
|
21903
|
+
const current = isHook ? opts.rawObservations : values[extractor.slug];
|
|
21904
|
+
if (!isHook && !isPresentExtractedValue(current)) {
|
|
19747
21905
|
delete values[extractor.slug];
|
|
19748
21906
|
continue;
|
|
19749
21907
|
}
|
|
@@ -19754,14 +21912,19 @@ async function applyExtractorHooks(opts) {
|
|
|
19754
21912
|
extractor,
|
|
19755
21913
|
threadId: opts.threadId,
|
|
19756
21914
|
resourceId: opts.resourceId,
|
|
19757
|
-
previous: opts.previousValues?.[extractor.slug],
|
|
21915
|
+
previous: isHook ? void 0 : opts.previousValues?.[extractor.slug],
|
|
19758
21916
|
current,
|
|
21917
|
+
rawObservations: opts.rawObservations,
|
|
21918
|
+
recentMessages: opts.recentMessages,
|
|
19759
21919
|
mainAgent: opts.mainAgent,
|
|
19760
21920
|
memory: opts.memory,
|
|
19761
21921
|
sendSignal: opts.sendSignal,
|
|
21922
|
+
sendStateSignal: opts.sendStateSignal,
|
|
21923
|
+
writer: opts.writer,
|
|
21924
|
+
abortSignal: opts.abortSignal,
|
|
19762
21925
|
requestContext: opts.requestContext
|
|
19763
21926
|
});
|
|
19764
|
-
if (hookValue === void 0) continue;
|
|
21927
|
+
if (isHook || hookValue === void 0) continue;
|
|
19765
21928
|
const parsed = extractor.schema.safeParse(hookValue);
|
|
19766
21929
|
if (parsed.success) values[extractor.slug] = parsed.data;
|
|
19767
21930
|
else {
|
|
@@ -19772,7 +21935,8 @@ async function applyExtractorHooks(opts) {
|
|
|
19772
21935
|
});
|
|
19773
21936
|
}
|
|
19774
21937
|
} catch (error) {
|
|
19775
|
-
|
|
21938
|
+
if (opts.abortSignal?.aborted) throw error;
|
|
21939
|
+
if (!isHook) delete values[extractor.slug];
|
|
19776
21940
|
failures.push({
|
|
19777
21941
|
slug: extractor.slug,
|
|
19778
21942
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -20821,11 +22985,11 @@ function formatObserverAttachmentPlaceholder(part, counter) {
|
|
|
20821
22985
|
const label = resolveObserverAttachmentLabel(part);
|
|
20822
22986
|
return label ? `[${attachmentType} #${attachmentId}: ${label}]` : `[${attachmentType} #${attachmentId}]`;
|
|
20823
22987
|
}
|
|
20824
|
-
function isRecord$
|
|
22988
|
+
function isRecord$3(value) {
|
|
20825
22989
|
return !!value && typeof value === "object";
|
|
20826
22990
|
}
|
|
20827
22991
|
function mapToolResultBlockToAttachment(block) {
|
|
20828
|
-
if (!isRecord$
|
|
22992
|
+
if (!isRecord$3(block) || typeof block.type !== "string") return;
|
|
20829
22993
|
const mediaType = typeof block.mediaType === "string" ? block.mediaType : void 0;
|
|
20830
22994
|
const filename = typeof block.filename === "string" ? block.filename : void 0;
|
|
20831
22995
|
switch (block.type) {
|
|
@@ -20886,7 +23050,7 @@ function mapToolResultBlockToAttachment(block) {
|
|
|
20886
23050
|
}
|
|
20887
23051
|
}
|
|
20888
23052
|
function extractToolResultAttachments(result, counter, attachmentFilter) {
|
|
20889
|
-
if (!isRecord$
|
|
23053
|
+
if (!isRecord$3(result) || result.type !== "content" || !Array.isArray(result.value)) return {
|
|
20890
23054
|
resultWithoutAttachments: result,
|
|
20891
23055
|
attachments: []
|
|
20892
23056
|
};
|
|
@@ -20900,7 +23064,7 @@ function extractToolResultAttachments(result, counter, attachmentFilter) {
|
|
|
20900
23064
|
if (shouldIncludeObserverAttachment(attachment, attachmentFilter)) attachments.push(toObserverInputAttachmentPart(attachment));
|
|
20901
23065
|
const placeholder = formatObserverAttachmentPlaceholder(attachment, counter);
|
|
20902
23066
|
return {
|
|
20903
|
-
type: isRecord$
|
|
23067
|
+
type: isRecord$3(block) ? block.type : void 0,
|
|
20904
23068
|
placeholder
|
|
20905
23069
|
};
|
|
20906
23070
|
});
|
|
@@ -21441,17 +23605,17 @@ const TRANSIENT_MESSAGE_SUBSTRINGS = [
|
|
|
21441
23605
|
"connection reset",
|
|
21442
23606
|
"connection closed"
|
|
21443
23607
|
];
|
|
21444
|
-
function isRecord$
|
|
23608
|
+
function isRecord$2(value) {
|
|
21445
23609
|
return typeof value === "object" && value !== null;
|
|
21446
23610
|
}
|
|
21447
23611
|
function isAbortError$5(error) {
|
|
21448
|
-
if (!isRecord$
|
|
23612
|
+
if (!isRecord$2(error)) return false;
|
|
21449
23613
|
if (error.name === "AbortError") return true;
|
|
21450
23614
|
if (typeof error.code === "string" && error.code === "ABORT_ERR") return true;
|
|
21451
23615
|
return false;
|
|
21452
23616
|
}
|
|
21453
23617
|
function hasTransientMessage(value) {
|
|
21454
|
-
if (!isRecord$
|
|
23618
|
+
if (!isRecord$2(value)) return false;
|
|
21455
23619
|
const message = typeof value.message === "string" ? value.message.toLowerCase() : "";
|
|
21456
23620
|
if (message && TRANSIENT_MESSAGE_SUBSTRINGS.some((sub) => message.includes(sub))) return true;
|
|
21457
23621
|
if (typeof value.code === "string" && value.code.toUpperCase().startsWith("UND_ERR_")) return true;
|
|
@@ -21462,13 +23626,13 @@ function isRetryableHttpStatus(status) {
|
|
|
21462
23626
|
return status >= 500 && status <= 599;
|
|
21463
23627
|
}
|
|
21464
23628
|
function hasRetryableHttpStatus(value) {
|
|
21465
|
-
if (!isRecord$
|
|
23629
|
+
if (!isRecord$2(value)) return false;
|
|
21466
23630
|
if (typeof value.statusCode === "number" && isRetryableHttpStatus(value.statusCode)) return true;
|
|
21467
23631
|
if (typeof value.code === "number" && isRetryableHttpStatus(value.code)) return true;
|
|
21468
23632
|
return false;
|
|
21469
23633
|
}
|
|
21470
23634
|
function hasIsRetryableFlag(value) {
|
|
21471
|
-
if (!isRecord$
|
|
23635
|
+
if (!isRecord$2(value)) return false;
|
|
21472
23636
|
return value.isRetryable === true;
|
|
21473
23637
|
}
|
|
21474
23638
|
/**
|
|
@@ -21486,14 +23650,14 @@ function isTransientLLMError(error) {
|
|
|
21486
23650
|
if (isAbortError$5(error)) return false;
|
|
21487
23651
|
const visited = /* @__PURE__ */ new WeakSet();
|
|
21488
23652
|
function visit(candidate) {
|
|
21489
|
-
if (isRecord$
|
|
23653
|
+
if (isRecord$2(candidate)) {
|
|
21490
23654
|
if (visited.has(candidate)) return false;
|
|
21491
23655
|
visited.add(candidate);
|
|
21492
23656
|
}
|
|
21493
23657
|
if (hasTransientMessage(candidate)) return true;
|
|
21494
23658
|
if (hasRetryableHttpStatus(candidate)) return true;
|
|
21495
23659
|
if (hasIsRetryableFlag(candidate)) return true;
|
|
21496
|
-
if (isRecord$
|
|
23660
|
+
if (isRecord$2(candidate)) {
|
|
21497
23661
|
if (visit(candidate.cause)) return true;
|
|
21498
23662
|
if (visit(candidate.error)) return true;
|
|
21499
23663
|
}
|
|
@@ -24713,38 +26877,6 @@ function createWorkingMemoryTool(config, options = {}) {
|
|
|
24713
26877
|
tool
|
|
24714
26878
|
};
|
|
24715
26879
|
}
|
|
24716
|
-
function isTieredModelTarget(model) {
|
|
24717
|
-
if (typeof model === "string") return true;
|
|
24718
|
-
if (!model || typeof model !== "object") return false;
|
|
24719
|
-
return "modelId" in model || "id" in model || "providerId" in model || "provider" in model || "doGenerate" in model && "doStream" in model;
|
|
24720
|
-
}
|
|
24721
|
-
function normalizeThresholds(config) {
|
|
24722
|
-
const entries = Object.entries(config.upTo);
|
|
24723
|
-
if (entries.length === 0) throw new Error("ModelByInputTokens requires at least one threshold in \"upTo\"");
|
|
24724
|
-
for (const [limitStr, model] of entries) {
|
|
24725
|
-
const limit = Number(limitStr);
|
|
24726
|
-
if (!Number.isFinite(limit) || limit <= 0) throw new Error(`ModelByInputTokens threshold keys must be positive numbers. Got: ${limitStr}`);
|
|
24727
|
-
if (!isTieredModelTarget(model)) throw new Error(`ModelByInputTokens requires a valid model target for threshold ${limitStr}`);
|
|
24728
|
-
}
|
|
24729
|
-
return entries.map(([limitStr, model]) => ({
|
|
24730
|
-
limit: Number(limitStr),
|
|
24731
|
-
model
|
|
24732
|
-
})).sort((a, b) => a.limit - b.limit);
|
|
24733
|
-
}
|
|
24734
|
-
var ModelByInputTokens = class {
|
|
24735
|
-
thresholds;
|
|
24736
|
-
constructor(config) {
|
|
24737
|
-
this.thresholds = normalizeThresholds(config);
|
|
24738
|
-
}
|
|
24739
|
-
resolve(inputTokens) {
|
|
24740
|
-
for (const { limit, model } of this.thresholds) if (inputTokens <= limit) return model;
|
|
24741
|
-
const maxLimit = this.thresholds[this.thresholds.length - 1].limit;
|
|
24742
|
-
throw new Error(`ModelByInputTokens: input token count (${inputTokens}) exceeds the largest configured threshold (${maxLimit}). Please configure a higher threshold or use a larger model.`);
|
|
24743
|
-
}
|
|
24744
|
-
getThresholds() {
|
|
24745
|
-
return this.thresholds.map((t) => t.limit);
|
|
24746
|
-
}
|
|
24747
|
-
};
|
|
24748
26880
|
const MINUTE = 6e4;
|
|
24749
26881
|
const HOUR = 60 * MINUTE;
|
|
24750
26882
|
const SHORT_TTL = 5 * MINUTE;
|
|
@@ -25537,6 +27669,39 @@ function reconcileObservationGroupsFromReflection(content, sourceObservations) {
|
|
|
25537
27669
|
if (derivedGroups.length > 0) return derivedGroups.map((group) => wrapInObservationGroup(group.content, group.range, group.id, void 0, group.kind)).join("\n\n");
|
|
25538
27670
|
return wrapInObservationGroup(normalizedContent, combineObservationGroupRanges(sourceGroups), generateAnchorId(), void 0, "reflection");
|
|
25539
27671
|
}
|
|
27672
|
+
function isRecord$1(value) {
|
|
27673
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
27674
|
+
}
|
|
27675
|
+
function isSubconsciousSignalMessage(message) {
|
|
27676
|
+
if (message.role !== "signal" || !isRecord$1(message.content)) return false;
|
|
27677
|
+
const contentMetadata = message.content.metadata;
|
|
27678
|
+
if (!isRecord$1(contentMetadata) || !isRecord$1(contentMetadata.signal)) return false;
|
|
27679
|
+
const signal = contentMetadata.signal;
|
|
27680
|
+
const metadata = signal.metadata;
|
|
27681
|
+
const attributes = signal.attributes;
|
|
27682
|
+
return signal.origin === "subconscious" || isRecord$1(metadata) && metadata.origin === "subconscious" || isRecord$1(attributes) && attributes.source === "subconscious";
|
|
27683
|
+
}
|
|
27684
|
+
function isSubconsciousSignalPart(part) {
|
|
27685
|
+
if (!part || typeof part !== "object") return false;
|
|
27686
|
+
const candidate = part;
|
|
27687
|
+
return candidate.type === "data-signal" && (candidate.data?.metadata?.origin === "subconscious" || candidate.data?.attributes?.source === "subconscious");
|
|
27688
|
+
}
|
|
27689
|
+
function stripSubconsciousSignals(messages) {
|
|
27690
|
+
return messages.flatMap((message) => {
|
|
27691
|
+
if (isSubconsciousSignalMessage(message)) return [];
|
|
27692
|
+
if (typeof message.content === "string") return [message];
|
|
27693
|
+
const parts = message.content.parts.filter((part) => !isSubconsciousSignalPart(part));
|
|
27694
|
+
if (parts.length === message.content.parts.length) return [message];
|
|
27695
|
+
if (parts.length === 0) return [];
|
|
27696
|
+
return [{
|
|
27697
|
+
...message,
|
|
27698
|
+
content: {
|
|
27699
|
+
...message.content,
|
|
27700
|
+
parts
|
|
27701
|
+
}
|
|
27702
|
+
}];
|
|
27703
|
+
});
|
|
27704
|
+
}
|
|
25540
27705
|
/**
|
|
25541
27706
|
* Get the maximum value from a threshold (simple number or range).
|
|
25542
27707
|
*/
|
|
@@ -25690,8 +27855,9 @@ var ObservationStrategy = class ObservationStrategy {
|
|
|
25690
27855
|
if (fresh?.lastObservedAt && record.lastObservedAt && fresh.lastObservedAt > record.lastObservedAt) return { observed: false };
|
|
25691
27856
|
}
|
|
25692
27857
|
const { messages, existingObservations } = await this.prepare();
|
|
27858
|
+
const observationMessages = stripSubconsciousSignals(messages);
|
|
25693
27859
|
await this.emitStartMarkers(cycleId);
|
|
25694
|
-
const output = await this.observe(existingObservations,
|
|
27860
|
+
const output = await this.observe(existingObservations, observationMessages);
|
|
25695
27861
|
const processed = await this.process(output, existingObservations);
|
|
25696
27862
|
await this.persist(processed);
|
|
25697
27863
|
await this.emitEndMarkers(cycleId, processed);
|
|
@@ -25706,6 +27872,7 @@ var ObservationStrategy = class ObservationStrategy {
|
|
|
25706
27872
|
abortSignal,
|
|
25707
27873
|
mainAgent: this.opts.agent,
|
|
25708
27874
|
sendSignal: this.opts.sendSignal,
|
|
27875
|
+
sendStateSignal: this.opts.sendStateSignal,
|
|
25709
27876
|
reflectionHooks,
|
|
25710
27877
|
requestContext,
|
|
25711
27878
|
observabilityContext: this.opts.observabilityContext
|
|
@@ -25991,11 +28158,16 @@ var SyncObservationStrategy = class extends ObservationStrategy {
|
|
|
25991
28158
|
values: result.extractedValues,
|
|
25992
28159
|
failures: result.extractionFailures,
|
|
25993
28160
|
previousValues: this.priorExtractedValues,
|
|
28161
|
+
rawObservations: result.observations,
|
|
28162
|
+
recentMessages: formatMessagesForObserver(this.opts.messages, { maxPartLength: 500 }),
|
|
25994
28163
|
threadId: this.opts.threadId,
|
|
25995
28164
|
resourceId: this.opts.resourceId,
|
|
25996
28165
|
mainAgent: this.opts.agent,
|
|
25997
28166
|
memory: this.deps.memory,
|
|
25998
28167
|
sendSignal: this.opts.sendSignal,
|
|
28168
|
+
sendStateSignal: this.opts.sendStateSignal,
|
|
28169
|
+
writer: this.opts.writer,
|
|
28170
|
+
abortSignal: this.opts.abortSignal,
|
|
25999
28171
|
requestContext: this.opts.requestContext
|
|
26000
28172
|
});
|
|
26001
28173
|
const output = {
|
|
@@ -26169,11 +28341,16 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
|
|
|
26169
28341
|
values: result.extractedValues,
|
|
26170
28342
|
failures: result.extractionFailures,
|
|
26171
28343
|
previousValues: this.priorExtractedValues,
|
|
28344
|
+
rawObservations: result.observations,
|
|
28345
|
+
recentMessages: formatMessagesForObserver(messages, { maxPartLength: 500 }),
|
|
26172
28346
|
threadId: this.opts.threadId,
|
|
26173
28347
|
resourceId: this.opts.resourceId,
|
|
26174
28348
|
mainAgent: this.opts.agent,
|
|
26175
28349
|
memory: this.deps.memory,
|
|
26176
28350
|
sendSignal: this.opts.sendSignal,
|
|
28351
|
+
sendStateSignal: this.opts.sendStateSignal,
|
|
28352
|
+
writer: this.opts.writer,
|
|
28353
|
+
abortSignal: this.opts.abortSignal,
|
|
26177
28354
|
requestContext: this.opts.requestContext
|
|
26178
28355
|
});
|
|
26179
28356
|
return {
|
|
@@ -26506,11 +28683,25 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
|
|
|
26506
28683
|
values: result.extractedValues,
|
|
26507
28684
|
failures: result.extractionFailures,
|
|
26508
28685
|
previousValues,
|
|
28686
|
+
rawObservations: result.observations,
|
|
28687
|
+
recentMessages: formatMessagesForObserver(threadMessages, { maxPartLength: 500 }),
|
|
26509
28688
|
threadId,
|
|
26510
28689
|
resourceId: this.resourceId,
|
|
26511
28690
|
mainAgent: this.opts.agent,
|
|
26512
28691
|
memory: this.deps.memory,
|
|
26513
|
-
sendSignal: this.opts.
|
|
28692
|
+
sendSignal: this.opts.agent ? async (signal) => {
|
|
28693
|
+
const delivery = this.opts.agent.sendSignal(signal, {
|
|
28694
|
+
resourceId: this.resourceId,
|
|
28695
|
+
threadId,
|
|
28696
|
+
ifActive: { behavior: "deliver" },
|
|
28697
|
+
ifIdle: { behavior: "persist" }
|
|
28698
|
+
});
|
|
28699
|
+
await delivery.accepted;
|
|
28700
|
+
return delivery.signal;
|
|
28701
|
+
} : void 0,
|
|
28702
|
+
sendStateSignal: this.opts.sendStateSignal,
|
|
28703
|
+
writer: this.opts.writer,
|
|
28704
|
+
abortSignal: this.opts.abortSignal,
|
|
26514
28705
|
requestContext: this.opts.requestContext
|
|
26515
28706
|
});
|
|
26516
28707
|
this.observationResults.push({
|
|
@@ -26830,6 +29021,9 @@ var ObservationStep = class {
|
|
|
26830
29021
|
pendingTokens: statusSnapshot.pendingTokens,
|
|
26831
29022
|
record: statusSnapshot.record,
|
|
26832
29023
|
writer: this.turn.writer,
|
|
29024
|
+
agent: this.turn.agent,
|
|
29025
|
+
sendSignal: this.turn.sendSignal,
|
|
29026
|
+
sendStateSignal: this.turn.sendStateSignal,
|
|
26833
29027
|
requestContext: this.turn.requestContext,
|
|
26834
29028
|
observabilityContext: this.turn.observabilityContext
|
|
26835
29029
|
}).catch((err) => {
|
|
@@ -27006,6 +29200,9 @@ var ObservationStep = class {
|
|
|
27006
29200
|
messages: observableMessages,
|
|
27007
29201
|
messageList,
|
|
27008
29202
|
trigger: "turn-sync",
|
|
29203
|
+
agent: this.turn.agent,
|
|
29204
|
+
sendSignal: this.turn.sendSignal,
|
|
29205
|
+
sendStateSignal: this.turn.sendStateSignal,
|
|
27009
29206
|
requestContext: this.turn.requestContext,
|
|
27010
29207
|
writer: this.turn.writer,
|
|
27011
29208
|
observabilityContext: this.turn.observabilityContext
|
|
@@ -27083,6 +29280,8 @@ var ObservationTurn = class {
|
|
|
27083
29280
|
agent;
|
|
27084
29281
|
/** Optional signal sender for processor-originated notifications. */
|
|
27085
29282
|
sendSignal;
|
|
29283
|
+
/** Optional state signal sender for processor-originated snapshots. */
|
|
29284
|
+
sendStateSignal;
|
|
27086
29285
|
/** Current actor model for this step. Updated by the processor before prepare(). */
|
|
27087
29286
|
actorModelContext;
|
|
27088
29287
|
/** The active assistant response message ID for this step. Updated by the processor before prepare(). */
|
|
@@ -27096,6 +29295,7 @@ var ObservationTurn = class {
|
|
|
27096
29295
|
this.messageList = opts.messageList;
|
|
27097
29296
|
this.agent = opts.agent;
|
|
27098
29297
|
this.sendSignal = opts.sendSignal;
|
|
29298
|
+
this.sendStateSignal = opts.sendStateSignal;
|
|
27099
29299
|
this.requestContext = opts.requestContext;
|
|
27100
29300
|
this.observabilityContext = opts.observabilityContext;
|
|
27101
29301
|
this.hooks = opts.hooks ?? {};
|
|
@@ -27208,6 +29408,7 @@ var ObservationTurn = class {
|
|
|
27208
29408
|
writer: this.writer,
|
|
27209
29409
|
agent: this.agent,
|
|
27210
29410
|
sendSignal: this.sendSignal,
|
|
29411
|
+
sendStateSignal: this.sendStateSignal,
|
|
27211
29412
|
requestContext: this.requestContext,
|
|
27212
29413
|
currentModel: this.actorModelContext,
|
|
27213
29414
|
observabilityContext: this.observabilityContext,
|
|
@@ -27612,6 +29813,7 @@ var ReflectorRunner = class {
|
|
|
27612
29813
|
persistMarkerToMessage;
|
|
27613
29814
|
getCompressionStartLevel;
|
|
27614
29815
|
memory;
|
|
29816
|
+
onReflectionCommitted;
|
|
27615
29817
|
mastra;
|
|
27616
29818
|
constructor(opts) {
|
|
27617
29819
|
this.reflectionConfig = opts.reflectionConfig;
|
|
@@ -27627,6 +29829,7 @@ var ReflectorRunner = class {
|
|
|
27627
29829
|
this.getCompressionStartLevel = opts.getCompressionStartLevel;
|
|
27628
29830
|
this.mastra = opts.mastra;
|
|
27629
29831
|
this.memory = opts.memory;
|
|
29832
|
+
this.onReflectionCommitted = opts.onReflectionCommitted;
|
|
27630
29833
|
}
|
|
27631
29834
|
__registerMastra(mastra) {
|
|
27632
29835
|
this.mastra = mastra;
|
|
@@ -27830,6 +30033,8 @@ var ReflectorRunner = class {
|
|
|
27830
30033
|
mainAgent,
|
|
27831
30034
|
memory: this.memory,
|
|
27832
30035
|
sendSignal,
|
|
30036
|
+
writer: streamContext?.writer,
|
|
30037
|
+
abortSignal,
|
|
27833
30038
|
requestContext
|
|
27834
30039
|
});
|
|
27835
30040
|
const extractedValues = hookedValues.values;
|
|
@@ -27985,7 +30190,7 @@ var ReflectorRunner = class {
|
|
|
27985
30190
|
* "activated", "no buffer present", and "suppressed by overshoot guard"
|
|
27986
30191
|
* without re-deriving that state.
|
|
27987
30192
|
*/
|
|
27988
|
-
async tryActivateBufferedReflection(record, lockKey, writer, messageList, activationMetadata) {
|
|
30193
|
+
async tryActivateBufferedReflection(record, lockKey, writer, messageList, activationMetadata, committedContext) {
|
|
27989
30194
|
const bufferKey = this.buffering.getReflectionBufferKey(lockKey);
|
|
27990
30195
|
const asyncOp = BufferingCoordinator.asyncBufferingOps.get(bufferKey);
|
|
27991
30196
|
if (asyncOp) if (activationMetadata?.triggeredBy === "ttl" || activationMetadata?.triggeredBy === "provider_change") omDebug(`[OM:reflect] tryActivateBufferedReflection: async op in progress, not blocking for ${activationMetadata.triggeredBy} trigger`);
|
|
@@ -28004,7 +30209,8 @@ var ReflectorRunner = class {
|
|
|
28004
30209
|
}
|
|
28005
30210
|
const beforeTokens = freshRecord.observationTokenCount ?? 0;
|
|
28006
30211
|
const reflectedLineCount = freshRecord.reflectedObservationLineCount ?? 0;
|
|
28007
|
-
const
|
|
30212
|
+
const allLines = (freshRecord.activeObservations ?? "").split("\n");
|
|
30213
|
+
const unreflectedLines = allLines.slice(reflectedLineCount);
|
|
28008
30214
|
const unreflectedContent = unreflectedLines.join("\n").trim();
|
|
28009
30215
|
const combinedObservations = unreflectedContent ? `${freshRecord.bufferedReflection}\n\n${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
28010
30216
|
const combinedTokenCount = this.tokenCounter.countObservations(combinedObservations);
|
|
@@ -28035,6 +30241,11 @@ var ReflectorRunner = class {
|
|
|
28035
30241
|
currentRecord: freshRecord,
|
|
28036
30242
|
tokenCount: combinedTokenCount
|
|
28037
30243
|
});
|
|
30244
|
+
if (committedContext) await this.notifyReflectionCommitted({
|
|
30245
|
+
...committedContext,
|
|
30246
|
+
observations: allLines.slice(0, reflectedLineCount).join("\n").trim(),
|
|
30247
|
+
writer
|
|
30248
|
+
});
|
|
28038
30249
|
BufferingCoordinator.lastBufferedBoundary.delete(bufferKey);
|
|
28039
30250
|
const afterRecord = await this.storage.getObservationalMemory(record.threadId, record.resourceId);
|
|
28040
30251
|
const afterTokens = afterRecord?.observationTokenCount ?? 0;
|
|
@@ -28070,13 +30281,21 @@ var ReflectorRunner = class {
|
|
|
28070
30281
|
BufferingCoordinator.reflectionBufferCycleIds.delete(bufferKey);
|
|
28071
30282
|
return { status: "activated" };
|
|
28072
30283
|
}
|
|
30284
|
+
async notifyReflectionCommitted(context) {
|
|
30285
|
+
if (!this.onReflectionCommitted || !context.parentThreadId || !context.resourceId) return;
|
|
30286
|
+
try {
|
|
30287
|
+
await this.onReflectionCommitted(context);
|
|
30288
|
+
} catch (error) {
|
|
30289
|
+
omDebug(`[OM:reflect] post-commit reflection agent failed: ${error}`);
|
|
30290
|
+
}
|
|
30291
|
+
}
|
|
28073
30292
|
/**
|
|
28074
30293
|
* Check if reflection needed and trigger if so.
|
|
28075
30294
|
* Supports both synchronous reflection and async buffered reflection.
|
|
28076
30295
|
* @internal Used by observation strategies. Do not call directly.
|
|
28077
30296
|
*/
|
|
28078
30297
|
async maybeReflect(opts) {
|
|
28079
|
-
const { record, observationTokens, writer, abortSignal, mainAgent, sendSignal, messageList, currentModel, reflectionHooks, requestContext, observabilityContext, lastActivityAt, threadId: requestedThreadId } = opts;
|
|
30298
|
+
const { record, observationTokens, writer, abortSignal, mainAgent, sendSignal, sendStateSignal, messageList, currentModel, reflectionHooks, requestContext, observabilityContext, lastActivityAt, threadId: requestedThreadId } = opts;
|
|
28080
30299
|
const lockKey = this.buffering.getLockKey(record.threadId, record.resourceId);
|
|
28081
30300
|
const reflectThreshold = getMaxThreshold(this.getEffectiveReflectionTokens(record));
|
|
28082
30301
|
const priorExtractedValues = await getThreadExtractedValues(this.storage, this.reflectionConfig.extractors, requestedThreadId ?? record.threadId);
|
|
@@ -28121,7 +30340,15 @@ var ReflectorRunner = class {
|
|
|
28121
30340
|
await this.storage.setReflectingFlag(record.id, false);
|
|
28122
30341
|
}
|
|
28123
30342
|
if (this.buffering.isAsyncReflectionEnabled()) {
|
|
28124
|
-
const activationResult = await this.tryActivateBufferedReflection(record, lockKey, writer, messageList, activationMetadata
|
|
30343
|
+
const activationResult = await this.tryActivateBufferedReflection(record, lockKey, writer, messageList, activationMetadata, {
|
|
30344
|
+
parentThreadId: requestedThreadId ?? record.threadId ?? "",
|
|
30345
|
+
resourceId: record.resourceId ?? "",
|
|
30346
|
+
requestContext,
|
|
30347
|
+
mainAgent,
|
|
30348
|
+
sendStateSignal,
|
|
30349
|
+
abortSignal,
|
|
30350
|
+
observabilityContext
|
|
30351
|
+
});
|
|
28125
30352
|
if (activationResult.status === "activated") return;
|
|
28126
30353
|
if (activationResult.status === "suppressed") {
|
|
28127
30354
|
omDebug(`[OM:reflect] skipping sync fallback / re-buffer after suppressed early ${activationMetadata.triggeredBy} activation (reason=${activationResult.reason})`);
|
|
@@ -28192,6 +30419,17 @@ var ReflectorRunner = class {
|
|
|
28192
30419
|
reflection: reflectResult.observations,
|
|
28193
30420
|
tokenCount: reflectionTokenCount
|
|
28194
30421
|
});
|
|
30422
|
+
await this.notifyReflectionCommitted({
|
|
30423
|
+
parentThreadId: requestedThreadId ?? record.threadId ?? "",
|
|
30424
|
+
resourceId: record.resourceId ?? "",
|
|
30425
|
+
observations: record.activeObservations ?? "",
|
|
30426
|
+
requestContext,
|
|
30427
|
+
mainAgent,
|
|
30428
|
+
sendStateSignal,
|
|
30429
|
+
writer,
|
|
30430
|
+
abortSignal,
|
|
30431
|
+
observabilityContext
|
|
30432
|
+
});
|
|
28195
30433
|
if (writer && streamContext) {
|
|
28196
30434
|
const endMarker = createObservationEndMarker({
|
|
28197
30435
|
cycleId: streamContext.cycleId,
|
|
@@ -28717,6 +30955,7 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
28717
30955
|
hasher = e();
|
|
28718
30956
|
mastra;
|
|
28719
30957
|
memory;
|
|
30958
|
+
curationCadence;
|
|
28720
30959
|
/**
|
|
28721
30960
|
* Track message IDs observed during this instance's lifetime.
|
|
28722
30961
|
* Prevents re-observing messages when per-thread lastObservedAt cursors
|
|
@@ -28814,6 +31053,7 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
28814
31053
|
this.hooks = config.hooks;
|
|
28815
31054
|
this.mastra = config.mastra;
|
|
28816
31055
|
this.memory = config.memory;
|
|
31056
|
+
this.curationCadence = config.curationCadence;
|
|
28817
31057
|
const resolveModel = (model, defaultModel) => model === "default" ? defaultModel : model;
|
|
28818
31058
|
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;
|
|
28819
31059
|
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;
|
|
@@ -28909,7 +31149,8 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
28909
31149
|
getCompressionStartLevel: (rc) => this.getCompressionStartLevel(rc),
|
|
28910
31150
|
resolveModel: (inputTokens) => this.resolveReflectionModel(inputTokens),
|
|
28911
31151
|
mastra: config.mastra,
|
|
28912
|
-
memory: this.memory
|
|
31152
|
+
memory: this.memory,
|
|
31153
|
+
onReflectionCommitted: config.onReflectionCommitted
|
|
28913
31154
|
});
|
|
28914
31155
|
this.validateBufferConfig();
|
|
28915
31156
|
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}`);
|
|
@@ -30383,6 +32624,7 @@ ${formattedMessages}
|
|
|
30383
32624
|
writer,
|
|
30384
32625
|
agent: opts.agent,
|
|
30385
32626
|
sendSignal: opts.sendSignal,
|
|
32627
|
+
sendStateSignal: opts.sendStateSignal,
|
|
30386
32628
|
requestContext,
|
|
30387
32629
|
currentModel: opts.currentModel,
|
|
30388
32630
|
observabilityContext
|
|
@@ -30727,6 +32969,8 @@ ${formattedMessages}
|
|
|
30727
32969
|
messageList: opts.messageList,
|
|
30728
32970
|
reflectionHooks,
|
|
30729
32971
|
agent: opts.agent,
|
|
32972
|
+
sendSignal: opts.sendSignal,
|
|
32973
|
+
sendStateSignal: opts.sendStateSignal,
|
|
30730
32974
|
requestContext,
|
|
30731
32975
|
writer: opts.writer,
|
|
30732
32976
|
observabilityContext: opts.observabilityContext
|
|
@@ -30747,6 +32991,9 @@ ${formattedMessages}
|
|
|
30747
32991
|
});
|
|
30748
32992
|
const record = await this.getOrCreateRecord(threadId, resourceId);
|
|
30749
32993
|
const reflected = record.generationCount > generationBefore && generationBefore >= 0;
|
|
32994
|
+
if (observed) this.maybeTriggerCadenceCuration(threadId, resourceId, record, requestContext).catch((error) => {
|
|
32995
|
+
omDebug(`[OM:observe] cadence curation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
32996
|
+
});
|
|
30750
32997
|
return {
|
|
30751
32998
|
observed,
|
|
30752
32999
|
reflected,
|
|
@@ -30754,6 +33001,31 @@ ${formattedMessages}
|
|
|
30754
33001
|
};
|
|
30755
33002
|
}
|
|
30756
33003
|
/**
|
|
33004
|
+
* Count committed observation runs on the sync observe path and run the curator every
|
|
33005
|
+
* `curationCadence` runs. The counter is persisted at `record.config.subconscious.observationRuns`
|
|
33006
|
+
* (the OM record's config jsonb, deep-merged; threshold resolution only reads `_overrides`, so the
|
|
33007
|
+
* sibling key is inert). Concurrent commits can miscount — the curator run is advisory, and the
|
|
33008
|
+
* curation cursor is the real serializer. The async-buffer lane bypasses observe(); deployments
|
|
33009
|
+
* that gate on this cadence (factory's resource scope) disable async buffering.
|
|
33010
|
+
*/
|
|
33011
|
+
async maybeTriggerCadenceCuration(threadId, resourceId, record, requestContext) {
|
|
33012
|
+
const cadence = this.curationCadence;
|
|
33013
|
+
const memory = this.memory;
|
|
33014
|
+
if (!cadence || cadence < 1 || !memory) return;
|
|
33015
|
+
const runs = ((record.config ?? {}).subconscious?.observationRuns ?? 0) + 1;
|
|
33016
|
+
const fire = runs >= cadence;
|
|
33017
|
+
await this.storage.updateObservationalMemoryConfig({
|
|
33018
|
+
id: record.id,
|
|
33019
|
+
config: { subconscious: { observationRuns: fire ? 0 : runs } }
|
|
33020
|
+
});
|
|
33021
|
+
if (!fire) return;
|
|
33022
|
+
omDebug(`[OM:observe] cadence curation outcome=${(await memory.runCuration({
|
|
33023
|
+
threadId,
|
|
33024
|
+
resourceId: resourceId ?? threadId,
|
|
33025
|
+
requestContext
|
|
33026
|
+
})).outcome} thread=${threadId}`);
|
|
33027
|
+
}
|
|
33028
|
+
/**
|
|
30757
33029
|
* Manually trigger reflection with optional guidance prompt.
|
|
30758
33030
|
*
|
|
30759
33031
|
* @example
|
|
@@ -30771,6 +33043,18 @@ ${formattedMessages}
|
|
|
30771
33043
|
record,
|
|
30772
33044
|
usage: void 0
|
|
30773
33045
|
};
|
|
33046
|
+
if (record.isReflecting) {
|
|
33047
|
+
if (isOpActiveInProcess(record.id, "reflecting")) {
|
|
33048
|
+
omDebug(`[OM:reflect] isReflecting=true and active in this process, skipping`);
|
|
33049
|
+
return {
|
|
33050
|
+
reflected: false,
|
|
33051
|
+
record,
|
|
33052
|
+
usage: void 0
|
|
33053
|
+
};
|
|
33054
|
+
}
|
|
33055
|
+
omDebug(`[OM:reflect] isReflecting=true but NOT active in this process — stale flag from dead process, clearing`);
|
|
33056
|
+
await this.storage.setReflectingFlag(record.id, false);
|
|
33057
|
+
}
|
|
30774
33058
|
await this.storage.setReflectingFlag(record.id, true);
|
|
30775
33059
|
registerOp(record.id, "reflecting");
|
|
30776
33060
|
const hooks = this.composeHooks(void 0, {
|
|
@@ -30956,6 +33240,8 @@ ${formattedMessages}
|
|
|
30956
33240
|
resourceId: opts.resourceId,
|
|
30957
33241
|
messageList: opts.messageList,
|
|
30958
33242
|
agent: opts.agent,
|
|
33243
|
+
sendSignal: opts.sendSignal,
|
|
33244
|
+
sendStateSignal: opts.sendStateSignal,
|
|
30959
33245
|
observabilityContext: opts.observabilityContext,
|
|
30960
33246
|
hooks: opts.hooks
|
|
30961
33247
|
});
|
|
@@ -31178,6 +33464,7 @@ var ObservationalMemoryProcessor = class {
|
|
|
31178
33464
|
});
|
|
31179
33465
|
this.turn.writer = writer;
|
|
31180
33466
|
this.turn.sendSignal = args.sendSignal;
|
|
33467
|
+
this.turn.sendStateSignal = args.sendStateSignal;
|
|
31181
33468
|
this.turn.agent = args.agent;
|
|
31182
33469
|
this.turn.requestContext = requestContext;
|
|
31183
33470
|
await this.turn.start(this.memory);
|
|
@@ -31318,6 +33605,7 @@ function getObservationsAsOf(activeObservations, asOf) {
|
|
|
31318
33605
|
}
|
|
31319
33606
|
var observational_memory_exports = /* @__PURE__ */ __exportAll({
|
|
31320
33607
|
Extractor: () => Extractor,
|
|
33608
|
+
KnowledgeSemanticIndexCoordinator: () => KnowledgeSemanticIndexCoordinator,
|
|
31321
33609
|
ModelByInputTokens: () => ModelByInputTokens,
|
|
31322
33610
|
OBSERVATIONAL_MEMORY_DEFAULTS: () => OBSERVATIONAL_MEMORY_DEFAULTS,
|
|
31323
33611
|
OBSERVATION_CONTEXT_INSTRUCTIONS: () => OBSERVATION_CONTEXT_INSTRUCTIONS,
|
|
@@ -31326,10 +33614,16 @@ var observational_memory_exports = /* @__PURE__ */ __exportAll({
|
|
|
31326
33614
|
OBSERVER_SYSTEM_PROMPT: () => OBSERVER_SYSTEM_PROMPT,
|
|
31327
33615
|
ObservationalMemory: () => ObservationalMemory,
|
|
31328
33616
|
ObservationalMemoryProcessor: () => ObservationalMemoryProcessor,
|
|
33617
|
+
SUBCONSCIOUS_ACTIVITY_STATE_ID: () => SUBCONSCIOUS_ACTIVITY_STATE_ID,
|
|
33618
|
+
StaleKnowledgeSemanticIndexError: () => StaleKnowledgeSemanticIndexError,
|
|
33619
|
+
Subconscious: () => Subconscious,
|
|
33620
|
+
SubconsciousCaptureExtractor: () => SubconsciousCaptureExtractor,
|
|
33621
|
+
SubconsciousRemindExtractor: () => SubconsciousRemindExtractor,
|
|
31329
33622
|
TokenCounter: () => TokenCounter,
|
|
31330
33623
|
WorkingMemoryExtractor: () => WorkingMemoryExtractor,
|
|
31331
33624
|
buildObserverPrompt: () => buildObserverPrompt,
|
|
31332
33625
|
buildObserverSystemPrompt: () => buildObserverSystemPrompt,
|
|
33626
|
+
buildSubconsciousActivitySnapshot: () => buildSubconsciousActivitySnapshot,
|
|
31333
33627
|
combineObservationGroupRanges: () => combineObservationGroupRanges,
|
|
31334
33628
|
deriveObservationGroupProvenance: () => deriveObservationGroupProvenance,
|
|
31335
33629
|
extractCurrentTask: () => extractCurrentTask,
|
|
@@ -31341,10 +33635,13 @@ var observational_memory_exports = /* @__PURE__ */ __exportAll({
|
|
|
31341
33635
|
parseAnchorId: () => parseAnchorId,
|
|
31342
33636
|
parseObservationGroups: () => parseObservationGroups,
|
|
31343
33637
|
parseObserverOutput: () => parseObserverOutput,
|
|
33638
|
+
publishSubconsciousActivity: () => publishSubconsciousActivity,
|
|
31344
33639
|
reconcileObservationGroupsFromReflection: () => reconcileObservationGroupsFromReflection,
|
|
31345
33640
|
renderObservationGroupsForReflection: () => renderObservationGroupsForReflection,
|
|
33641
|
+
renderSubconsciousActivity: () => renderSubconsciousActivity,
|
|
31346
33642
|
stripEphemeralAnchorIds: () => stripEphemeralAnchorIds,
|
|
31347
33643
|
stripObservationGroups: () => stripObservationGroups,
|
|
33644
|
+
subconsciousCaptureSchema: () => subconsciousCaptureSchema,
|
|
31348
33645
|
summarizeConversation: () => summarizeConversation,
|
|
31349
33646
|
wrapInObservationGroup: () => wrapInObservationGroup
|
|
31350
33647
|
});
|
|
@@ -31568,10 +33865,11 @@ const DEFAULT_EMBEDDING_CACHE_MAX_SIZE = 1e3;
|
|
|
31568
33865
|
* Concrete implementation of MastraMemory that adds support for thread configuration
|
|
31569
33866
|
* and message injection.
|
|
31570
33867
|
*/
|
|
31571
|
-
var Memory = class extends _mastra_core_memory.MastraMemory {
|
|
33868
|
+
var Memory = class Memory extends _mastra_core_memory.MastraMemory {
|
|
31572
33869
|
_omEngine;
|
|
31573
33870
|
_omEngineInstance;
|
|
31574
33871
|
_mastraInstance;
|
|
33872
|
+
_knowledgeSemanticIndex;
|
|
31575
33873
|
/**
|
|
31576
33874
|
* Every vector cleanup that deleteThread or deleteMessages started in the background.
|
|
31577
33875
|
* Callers do not wait for the cleanup, so this handle is the only join point.
|
|
@@ -31619,7 +33917,60 @@ var Memory = class extends _mastra_core_memory.MastraMemory {
|
|
|
31619
33917
|
else this._omEngine?.then((engine) => engine?.__registerMastra(mastra));
|
|
31620
33918
|
}
|
|
31621
33919
|
getMergedThreadConfig(config) {
|
|
31622
|
-
|
|
33920
|
+
const merged = super.getMergedThreadConfig(config);
|
|
33921
|
+
return this.applyManagedWorkingMemoryDefaults(this.applySubconsciousDefaults(merged));
|
|
33922
|
+
}
|
|
33923
|
+
applySubconsciousDefaults(config) {
|
|
33924
|
+
const omConfig = normalizeObservationalMemoryConfig(config.observationalMemory);
|
|
33925
|
+
if (!omConfig?.experimental_subconscious) return config;
|
|
33926
|
+
if (!(omConfig.experimental_subconscious instanceof Subconscious)) throw new Error("observationalMemory.experimental_subconscious must be a Subconscious instance.");
|
|
33927
|
+
const observation = omConfig.observation ?? {};
|
|
33928
|
+
const extract = observation.extract ?? [];
|
|
33929
|
+
const existingSlugs = new Set(extract.map((extractor) => extractor.slug));
|
|
33930
|
+
const subconsciousExtractors = omConfig.experimental_subconscious.createObservationExtractors(observation.model ?? omConfig.model).filter((extractor) => !existingSlugs.has(extractor.slug));
|
|
33931
|
+
return {
|
|
33932
|
+
...config,
|
|
33933
|
+
observationalMemory: {
|
|
33934
|
+
...omConfig,
|
|
33935
|
+
observation: {
|
|
33936
|
+
...observation,
|
|
33937
|
+
extract: [...extract, ...subconsciousExtractors]
|
|
33938
|
+
}
|
|
33939
|
+
}
|
|
33940
|
+
};
|
|
33941
|
+
}
|
|
33942
|
+
/** Threads with a curation currently in flight in this process; guards same-process double-fire only. */
|
|
33943
|
+
_curationsInFlight = /* @__PURE__ */ new Set();
|
|
33944
|
+
/**
|
|
33945
|
+
* Run the subconscious curator directly over the pending fact worklist, without a reflection.
|
|
33946
|
+
* Cross-process serialization is the curation cursor's job; a lost race wastes one advisory run.
|
|
33947
|
+
*
|
|
33948
|
+
* Outcomes: `ran` (curator executed), `no-op` (empty worklist and no prompt, or no curate agent
|
|
33949
|
+
* configured), `skipped` (a curation for this thread is already in flight in this process), and
|
|
33950
|
+
* `no-model` (no per-agent, observational memory, or main-agent model could be resolved).
|
|
33951
|
+
*/
|
|
33952
|
+
async runCuration(options) {
|
|
33953
|
+
const omConfig = normalizeObservationalMemoryConfig(this.threadConfig.observationalMemory);
|
|
33954
|
+
const subconscious = omConfig?.experimental_subconscious;
|
|
33955
|
+
if (!omConfig || !(subconscious instanceof Subconscious)) return { outcome: "no-op" };
|
|
33956
|
+
if (this._curationsInFlight.has(options.threadId)) return { outcome: "skipped" };
|
|
33957
|
+
this._curationsInFlight.add(options.threadId);
|
|
33958
|
+
try {
|
|
33959
|
+
return { outcome: await createCuratorHandler(this, subconscious.resolved, new Memory({
|
|
33960
|
+
storage: this.storage,
|
|
33961
|
+
options: { observationalMemory: false }
|
|
33962
|
+
}), { omModel: omConfig.observation?.model ?? omConfig.model })({
|
|
33963
|
+
parentThreadId: options.threadId,
|
|
33964
|
+
resourceId: options.resourceId,
|
|
33965
|
+
observations: options.prompt ?? "",
|
|
33966
|
+
requestContext: options.requestContext
|
|
33967
|
+
}) === "ran" ? "ran" : "no-op" };
|
|
33968
|
+
} catch (error) {
|
|
33969
|
+
if (error instanceof Error && error.message.includes("requires the main agent to resolve its model")) return { outcome: "no-model" };
|
|
33970
|
+
throw error;
|
|
33971
|
+
} finally {
|
|
33972
|
+
this._curationsInFlight.delete(options.threadId);
|
|
33973
|
+
}
|
|
31623
33974
|
}
|
|
31624
33975
|
applyManagedWorkingMemoryDefaults(config) {
|
|
31625
33976
|
const omConfig = normalizeObservationalMemoryConfig(config.observationalMemory);
|
|
@@ -31663,6 +34014,28 @@ var Memory = class extends _mastra_core_memory.MastraMemory {
|
|
|
31663
34014
|
if (!this.vector) throw new Error("`retrieval: { vector: true }` requires a vector store. Pass a `vector` option to your Memory instance.");
|
|
31664
34015
|
if (!this.embedder) throw new Error("`retrieval: { vector: true }` requires an embedder. Pass an `embedder` option to your Memory instance.");
|
|
31665
34016
|
}
|
|
34017
|
+
if (omConfig?.experimental_subconscious) {
|
|
34018
|
+
if (!this.vector) throw new Error("Subconscious semantic knowledge requires a vector store. Pass a `vector` option to Memory.");
|
|
34019
|
+
if (!this.embedder) throw new Error("Subconscious semantic knowledge requires an embedder. Pass an `embedder` option to Memory.");
|
|
34020
|
+
}
|
|
34021
|
+
}
|
|
34022
|
+
async getKnowledgeStore() {
|
|
34023
|
+
const store = await this.storage.getStore("knowledge");
|
|
34024
|
+
if (!store) throw new Error(`Knowledge storage domain is not available on ${this.storage.constructor.name}`);
|
|
34025
|
+
return store;
|
|
34026
|
+
}
|
|
34027
|
+
async getKnowledgeSemanticIndex() {
|
|
34028
|
+
if (!this.vector || !this.embedder) throw new Error("Subconscious semantic knowledge requires both a vector store and an embedder.");
|
|
34029
|
+
this._knowledgeSemanticIndex ??= this.getKnowledgeStore().then((knowledge) => new KnowledgeSemanticIndexCoordinator({
|
|
34030
|
+
knowledge,
|
|
34031
|
+
vector: this.vector,
|
|
34032
|
+
embedder: this.embedder,
|
|
34033
|
+
embedderOptions: this.embedderOptions
|
|
34034
|
+
}));
|
|
34035
|
+
return this._knowledgeSemanticIndex;
|
|
34036
|
+
}
|
|
34037
|
+
async drainKnowledgeSemanticIndex(scope) {
|
|
34038
|
+
return (await this.getKnowledgeSemanticIndex()).drain(scope);
|
|
31666
34039
|
}
|
|
31667
34040
|
/**
|
|
31668
34041
|
* Gets the memory storage domain, throwing if not available.
|
|
@@ -32431,6 +34804,7 @@ ${workingMemory}`;
|
|
|
32431
34804
|
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.");
|
|
32432
34805
|
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.");
|
|
32433
34806
|
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.");
|
|
34807
|
+
if (omConfig.experimental_subconscious) await this.getKnowledgeStore();
|
|
32434
34808
|
const { ObservationalMemory: OMClass } = await Promise.resolve().then(() => observational_memory_exports);
|
|
32435
34809
|
const onIndexObservations = this.hasRetrievalSearch(omConfig.retrieval) ? async (observation) => {
|
|
32436
34810
|
await this.indexObservation(observation);
|
|
@@ -32444,9 +34818,21 @@ ${workingMemory}`;
|
|
|
32444
34818
|
activateOnProviderChange: omConfig.activateOnProviderChange,
|
|
32445
34819
|
shareTokenBudget: omConfig.shareTokenBudget,
|
|
32446
34820
|
model: omConfig.model,
|
|
34821
|
+
curationCadence: omConfig.experimental_subconscious instanceof Subconscious ? omConfig.experimental_subconscious.resolved.curationCadence : void 0,
|
|
32447
34822
|
mastra: this._mastraInstance,
|
|
32448
34823
|
onIndexObservations,
|
|
32449
34824
|
hooks: omConfig.hooks,
|
|
34825
|
+
onReflectionCommitted: omConfig.experimental_subconscious instanceof Subconscious ? (() => {
|
|
34826
|
+
const resolved = omConfig.experimental_subconscious.resolved;
|
|
34827
|
+
const omModel = omConfig.observation?.model ?? omConfig.model;
|
|
34828
|
+
return composeReflectionAgentHandlers([createCuratorHandler(this, resolved, new Memory({
|
|
34829
|
+
storage: this.storage,
|
|
34830
|
+
options: { observationalMemory: false }
|
|
34831
|
+
}), { omModel }), createLearnerHandler(this, resolved, new Memory({
|
|
34832
|
+
storage: this.storage,
|
|
34833
|
+
options: { observationalMemory: false }
|
|
34834
|
+
}), { omModel })]);
|
|
34835
|
+
})() : void 0,
|
|
32450
34836
|
observation: omConfig.observation ? {
|
|
32451
34837
|
model: omConfig.observation.model,
|
|
32452
34838
|
messageTokens: omConfig.observation.messageTokens,
|
|
@@ -32836,6 +35222,7 @@ Notes:
|
|
|
32836
35222
|
retrievalScope: typeof omConfig.retrieval === "object" ? omConfig.retrieval.scope ?? "resource" : "resource",
|
|
32837
35223
|
searchEnabled: this.hasRetrievalSearch(omConfig.retrieval)
|
|
32838
35224
|
});
|
|
35225
|
+
if (omConfig?.experimental_subconscious instanceof Subconscious && omConfig.experimental_subconscious.resolved.tools) Object.assign(tools, createKnowledgeTools(this));
|
|
32839
35226
|
return tools;
|
|
32840
35227
|
}
|
|
32841
35228
|
/**
|
|
@@ -33316,6 +35703,8 @@ Notes:
|
|
|
33316
35703
|
if (om) processors.push(om);
|
|
33317
35704
|
const wm = await this.createWorkingMemoryStateProcessor(configuredProcessors, context);
|
|
33318
35705
|
if (wm) processors.push(wm);
|
|
35706
|
+
const pins = await this.createPinnedStateProcessor(configuredProcessors, context);
|
|
35707
|
+
if (pins) processors.push(pins);
|
|
33319
35708
|
return processors;
|
|
33320
35709
|
}
|
|
33321
35710
|
/**
|
|
@@ -33363,6 +35752,21 @@ Notes:
|
|
|
33363
35752
|
if (configuredProcessors.some((p) => !("workflow" in p) && p.id === WORKING_MEMORY_STATE_PROCESSOR_ID)) return null;
|
|
33364
35753
|
return new WorkingMemoryStateProcessor(this, runtimeMemory?.memoryConfig);
|
|
33365
35754
|
}
|
|
35755
|
+
/**
|
|
35756
|
+
* Creates a PinnedStateProcessor when Subconscious pins are enabled on the
|
|
35757
|
+
* merged thread config. The gate is the validated `resolved.pins` on the
|
|
35758
|
+
* Subconscious instance, never the raw user object. Returns null when pins
|
|
35759
|
+
* are off or the processor is already present in the user's configured
|
|
35760
|
+
* processors.
|
|
35761
|
+
*/
|
|
35762
|
+
async createPinnedStateProcessor(configuredProcessors = [], context) {
|
|
35763
|
+
const runtimeMemory = context?.get("MastraMemory");
|
|
35764
|
+
const subconscious = normalizeObservationalMemoryConfig(this.getMergedThreadConfig(runtimeMemory?.memoryConfig).observationalMemory)?.experimental_subconscious;
|
|
35765
|
+
if (!(subconscious instanceof Subconscious) || subconscious.resolved.pins === false) return null;
|
|
35766
|
+
const { PinnedStateProcessor, SUBCONSCIOUS_PINS_STATE_ID } = await Promise.resolve().then(() => subconscious_exports);
|
|
35767
|
+
if (configuredProcessors.some((p) => !("workflow" in p) && p.id === SUBCONSCIOUS_PINS_STATE_ID)) return null;
|
|
35768
|
+
return new PinnedStateProcessor({ getKnowledgeStore: () => this.storage.getStore("knowledge") });
|
|
35769
|
+
}
|
|
33366
35770
|
};
|
|
33367
35771
|
//#endregion
|
|
33368
35772
|
//#region ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@/ignore/7.0.5/d6411cd18dd20fbb425503c2ba0ac12fc6b5c7b2b2481cbdc797c212eb8be050/node_modules/ignore/index.js
|
|
@@ -43749,4 +46153,4 @@ const agentBuilderWorkflows = {
|
|
|
43749
46153
|
//#endregion
|
|
43750
46154
|
exports.agentBuilderWorkflows = agentBuilderWorkflows;
|
|
43751
46155
|
|
|
43752
|
-
//# sourceMappingURL=dist-
|
|
46156
|
+
//# sourceMappingURL=dist-Jy_bgZ7X.cjs.map
|