@xdarkicex/openclaw-memory-libravdb 1.10.10 → 1.10.14-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +166 -15
- package/dist/context-engine.d.ts +2 -3
- package/dist/context-engine.js +196 -26
- package/dist/index.js +269 -47
- package/dist/manifest.js +4 -4
- package/dist/memory-provider.js +26 -11
- package/dist/tools/memory-recall.d.ts +41 -0
- package/dist/tools/memory-recall.js +66 -6
- package/openclaw.plugin.json +4 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -18615,10 +18615,10 @@ function buildRulesContext() {
|
|
|
18615
18615
|
}
|
|
18616
18616
|
|
|
18617
18617
|
// src/manifest.ts
|
|
18618
|
-
import * as fs2 from "fs";
|
|
18619
|
-
import * as path2 from "path";
|
|
18620
|
-
import * as crypto from "crypto";
|
|
18621
|
-
import * as os2 from "os";
|
|
18618
|
+
import * as fs2 from "node:fs";
|
|
18619
|
+
import * as path2 from "node:path";
|
|
18620
|
+
import * as crypto from "node:crypto";
|
|
18621
|
+
import * as os2 from "node:os";
|
|
18622
18622
|
var TurnManifestStore = class {
|
|
18623
18623
|
manifestDir;
|
|
18624
18624
|
constructor() {
|
|
@@ -18923,7 +18923,7 @@ function normalizeCompactResult(response, options = {}) {
|
|
|
18923
18923
|
const threshold = options.threshold;
|
|
18924
18924
|
const overBudget = threshold != null && tokensBefore >= threshold;
|
|
18925
18925
|
const engineRefused = !didCompact && overBudget;
|
|
18926
|
-
const tokensAfter = didCompact && typeof response?.tokensAfter === "number" && response.tokensAfter
|
|
18926
|
+
const tokensAfter = didCompact && typeof response?.tokensAfter === "number" && response.tokensAfter >= 0 ? response.tokensAfter : void 0;
|
|
18927
18927
|
return {
|
|
18928
18928
|
ok: !engineRefused,
|
|
18929
18929
|
compacted: didCompact,
|
|
@@ -18931,8 +18931,7 @@ function normalizeCompactResult(response, options = {}) {
|
|
|
18931
18931
|
result: {
|
|
18932
18932
|
tokensBefore,
|
|
18933
18933
|
...tokensAfter != null ? { tokensAfter } : {},
|
|
18934
|
-
...details.
|
|
18935
|
-
...details.summaryText ? { summaryText: details.summaryText } : {},
|
|
18934
|
+
...details.summaryText ? { summary: details.summaryText } : {},
|
|
18936
18935
|
details: { ...details, ...threshold != null ? { threshold } : {} }
|
|
18937
18936
|
}
|
|
18938
18937
|
};
|
|
@@ -19011,6 +19010,18 @@ function findLastUserMessageIndex(messages) {
|
|
|
19011
19010
|
}
|
|
19012
19011
|
return -1;
|
|
19013
19012
|
}
|
|
19013
|
+
function selectTurnAlignedSourceSuffix(messages, maxMessages) {
|
|
19014
|
+
if (messages.length === 0) return [];
|
|
19015
|
+
let start = Math.max(0, messages.length - Math.max(1, Math.floor(maxMessages)));
|
|
19016
|
+
while (start > 0 && messages[start]?.role !== "user") {
|
|
19017
|
+
start -= 1;
|
|
19018
|
+
}
|
|
19019
|
+
if (messages[start]?.role !== "user") {
|
|
19020
|
+
const firstUserIndex = messages.findIndex((message) => message.role === "user");
|
|
19021
|
+
start = firstUserIndex >= 0 ? firstUserIndex : 0;
|
|
19022
|
+
}
|
|
19023
|
+
return messages.slice(start);
|
|
19024
|
+
}
|
|
19014
19025
|
function normalizeKernelContent(content, options = {}) {
|
|
19015
19026
|
const text = typeof content === "string" ? content : Array.isArray(content) ? content.map(stringifyKernelBlock).filter((part) => part.length > 0).join("\n") : "";
|
|
19016
19027
|
return stripOpenClawUntrustedMetadataEnvelope(text, {
|
|
@@ -19374,6 +19385,32 @@ function trimMessagesToBudget(messages, tokenBudget) {
|
|
|
19374
19385
|
}
|
|
19375
19386
|
return [{ ...last, content: truncated }];
|
|
19376
19387
|
}
|
|
19388
|
+
function trimTurnAlignedSourceSuffixToBudget(messages, tokenBudget) {
|
|
19389
|
+
if (messages.length === 0) return [];
|
|
19390
|
+
const userStarts = [];
|
|
19391
|
+
for (let index = 0; index < messages.length; index += 1) {
|
|
19392
|
+
if (messages[index]?.role === "user") userStarts.push(index);
|
|
19393
|
+
}
|
|
19394
|
+
if (userStarts.length === 0) {
|
|
19395
|
+
return messages;
|
|
19396
|
+
}
|
|
19397
|
+
const keptBundles = [];
|
|
19398
|
+
let used = 0;
|
|
19399
|
+
let bundleEnd = messages.length;
|
|
19400
|
+
for (let index = userStarts.length - 1; index >= 0; index -= 1) {
|
|
19401
|
+
const bundleStart = userStarts[index];
|
|
19402
|
+
const bundle = messages.slice(bundleStart, bundleEnd);
|
|
19403
|
+
const bundleCost = approximateMessagesTokens(bundle);
|
|
19404
|
+
if (keptBundles.length > 0 && used + bundleCost > tokenBudget) {
|
|
19405
|
+
break;
|
|
19406
|
+
}
|
|
19407
|
+
keptBundles.unshift(bundle);
|
|
19408
|
+
used += bundleCost;
|
|
19409
|
+
bundleEnd = bundleStart;
|
|
19410
|
+
if (used >= tokenBudget) break;
|
|
19411
|
+
}
|
|
19412
|
+
return keptBundles.flat();
|
|
19413
|
+
}
|
|
19377
19414
|
function boundAfterTurnMessagesForIngest(messages, logger, sessionId) {
|
|
19378
19415
|
const estimatedTokens = approximateMessagesTokens(messages);
|
|
19379
19416
|
if (estimatedTokens <= AFTER_TURN_INGEST_MAX_TOKENS) {
|
|
@@ -19414,6 +19451,34 @@ function enforceTokenBudgetInvariant(result, tokenBudget) {
|
|
|
19414
19451
|
estimatedTokens: Math.min(effectiveBudget, systemPromptTokens + trimmedEstimate)
|
|
19415
19452
|
};
|
|
19416
19453
|
}
|
|
19454
|
+
function enforceCompactedProjectionBudgetInvariant(result, tokenBudget) {
|
|
19455
|
+
if (typeof tokenBudget !== "number" || !Number.isFinite(tokenBudget) || tokenBudget <= 0) {
|
|
19456
|
+
return result;
|
|
19457
|
+
}
|
|
19458
|
+
const effectiveBudget = resolveEffectiveAssembleBudget(Math.max(1, Math.floor(tokenBudget)));
|
|
19459
|
+
const approximateTotal = approximateTokenCount(result.systemPromptAddition) + approximateMessagesTokens(result.messages);
|
|
19460
|
+
if (result.estimatedTokens <= effectiveBudget && approximateTotal <= effectiveBudget) {
|
|
19461
|
+
return result;
|
|
19462
|
+
}
|
|
19463
|
+
const lastUserIndex = findLastUserMessageIndex(result.messages);
|
|
19464
|
+
const mandatoryMessages = lastUserIndex >= 0 ? result.messages.slice(lastUserIndex) : result.messages;
|
|
19465
|
+
const mandatoryTokens = approximateMessagesTokens(mandatoryMessages);
|
|
19466
|
+
const systemPromptBudget = Math.max(0, effectiveBudget - mandatoryTokens);
|
|
19467
|
+
const systemPromptAddition = truncateSystemPromptAdditionToTokenBudget(
|
|
19468
|
+
result.systemPromptAddition,
|
|
19469
|
+
systemPromptBudget
|
|
19470
|
+
);
|
|
19471
|
+
const systemPromptTokens = approximateTokenCount(systemPromptAddition);
|
|
19472
|
+
const messageBudget = Math.max(mandatoryTokens, effectiveBudget - systemPromptTokens);
|
|
19473
|
+
const messages = trimTurnAlignedSourceSuffixToBudget(result.messages, messageBudget);
|
|
19474
|
+
const estimatedTokens = systemPromptTokens + approximateMessagesTokens(messages);
|
|
19475
|
+
return {
|
|
19476
|
+
...result,
|
|
19477
|
+
messages,
|
|
19478
|
+
systemPromptAddition,
|
|
19479
|
+
estimatedTokens
|
|
19480
|
+
};
|
|
19481
|
+
}
|
|
19417
19482
|
function buildBudgetFallbackContext(messages, tokenBudget) {
|
|
19418
19483
|
const effectiveBudget = resolveEffectiveAssembleBudget(tokenBudget);
|
|
19419
19484
|
const fallbackMessages = trimMessagesToBudget(
|
|
@@ -19430,7 +19495,11 @@ function buildBudgetFallbackContext(messages, tokenBudget) {
|
|
|
19430
19495
|
var DAEMON_AUTHORED_CONTEXT_RE = /<authored_context\b[^>]*>([\s\S]*?)<\/authored_context>/gi;
|
|
19431
19496
|
var DAEMON_AUTHORED_CONTEXT_GUIDANCE_RE = /^\s*Treat the authored entries below as active project rules and identity context\.?\s*$/i;
|
|
19432
19497
|
var COMPACTED_SESSION_CONTEXT_RE = /<compacted_session_context\b([^>]*)>([\s\S]*?)<\/compacted_session_context>/gi;
|
|
19498
|
+
var HAS_COMPACTED_SESSION_CONTEXT_RE = /<compacted_session_context\b/i;
|
|
19433
19499
|
var COMPACTED_SESSION_RENDER_LEDGER_RE = /(?:^|\n)(?:Artifacts:|Constraints:|Open Next Steps:|Extracted context anchors:)(?:\n|$)/;
|
|
19500
|
+
function hasCompactedSessionContext(text) {
|
|
19501
|
+
return HAS_COMPACTED_SESSION_CONTEXT_RE.test(text);
|
|
19502
|
+
}
|
|
19434
19503
|
function sanitizeDaemonSystemPromptAddition(text) {
|
|
19435
19504
|
return demoteDaemonAuthoredContextBlocks(
|
|
19436
19505
|
sanitizeToolCallPatterns(canonicalizeCompactedSessionContextBlocks(text))
|
|
@@ -19781,13 +19850,13 @@ function ensureReplaySafeUserTurn(assembled, sourceMessages, logger, tokenBudget
|
|
|
19781
19850
|
estimatedTokens: baseEstimatedTokens + approximateMessageTokens(fallbackUser)
|
|
19782
19851
|
};
|
|
19783
19852
|
}
|
|
19784
|
-
function normalizeAssembleResult(result, sourceMessages) {
|
|
19853
|
+
function normalizeAssembleResult(result, sourceMessages, promptAuthority = PROMPT_AUTHORITY_PREASSEMBLY_MAY_OVERFLOW) {
|
|
19785
19854
|
const systemPromptAddition = typeof result.systemPromptAddition === "string" ? sanitizeDaemonSystemPromptAddition(result.systemPromptAddition) : "";
|
|
19786
19855
|
return {
|
|
19787
19856
|
messages: sourceMessages ?? [],
|
|
19788
19857
|
estimatedTokens: typeof result.estimatedTokens === "number" ? result.estimatedTokens : 0,
|
|
19789
19858
|
systemPromptAddition,
|
|
19790
|
-
promptAuthority
|
|
19859
|
+
promptAuthority,
|
|
19791
19860
|
...result.debug != null ? { debug: result.debug } : {}
|
|
19792
19861
|
};
|
|
19793
19862
|
}
|
|
@@ -19858,6 +19927,7 @@ function buildContextEngineFactory(runtime, cfg, logger = console) {
|
|
|
19858
19927
|
setOptimizationMemoCacheSize(cfg.optimizationMemoCacheSize);
|
|
19859
19928
|
}
|
|
19860
19929
|
const predictiveContextCache = /* @__PURE__ */ new Map();
|
|
19930
|
+
const compactedProjectionSessions = /* @__PURE__ */ new Set();
|
|
19861
19931
|
const PREDICTIVE_CACHE_MAX_SIZE = 100;
|
|
19862
19932
|
const turnCache = new TurnMemoryCache(100);
|
|
19863
19933
|
const circuitBreakers = /* @__PURE__ */ new Map();
|
|
@@ -19865,6 +19935,21 @@ function buildContextEngineFactory(runtime, cfg, logger = console) {
|
|
|
19865
19935
|
let lastUserMessageHash = null;
|
|
19866
19936
|
let cachedIdentity = null;
|
|
19867
19937
|
let cachedSessionKey;
|
|
19938
|
+
function activateCompactedProjection(sessionId, source) {
|
|
19939
|
+
const wasActive = compactedProjectionSessions.has(sessionId);
|
|
19940
|
+
compactedProjectionSessions.add(sessionId);
|
|
19941
|
+
predictiveContextCache.delete(sessionId);
|
|
19942
|
+
postToolRecallCache.delete(sessionId);
|
|
19943
|
+
turnCache.invalidateSession(sessionId);
|
|
19944
|
+
if (!wasActive) {
|
|
19945
|
+
logger.info?.(
|
|
19946
|
+
`LibraVDB activated daemon-owned compacted projection sessionId=${sessionId} source=${source}`
|
|
19947
|
+
);
|
|
19948
|
+
}
|
|
19949
|
+
}
|
|
19950
|
+
function enforceAssembleBudget(result, tokenBudget, compactionProjectionActive) {
|
|
19951
|
+
return compactionProjectionActive ? enforceCompactedProjectionBudgetInvariant(result, tokenBudget) : enforceTokenBudgetInvariant(result, tokenBudget);
|
|
19952
|
+
}
|
|
19868
19953
|
function resolveUserId(args) {
|
|
19869
19954
|
const fwUserId = args?.userIdOverride?.trim();
|
|
19870
19955
|
if (fwUserId) return fwUserId;
|
|
@@ -20281,16 +20366,36 @@ ${cached}
|
|
|
20281
20366
|
return null;
|
|
20282
20367
|
}
|
|
20283
20368
|
}
|
|
20369
|
+
async function injectPersonaContext(params) {
|
|
20370
|
+
try {
|
|
20371
|
+
const resp = await params.client.getUserCard({ userId: "__bot_persona__" });
|
|
20372
|
+
if (!resp.cardJson) return null;
|
|
20373
|
+
let card;
|
|
20374
|
+
try {
|
|
20375
|
+
card = JSON.parse(resp.cardJson).card ?? resp.cardJson;
|
|
20376
|
+
} catch {
|
|
20377
|
+
card = resp.cardJson;
|
|
20378
|
+
}
|
|
20379
|
+
if (!card || card.trim().length === 0) return null;
|
|
20380
|
+
return "<bot_persona>\n" + card + "\n</bot_persona>";
|
|
20381
|
+
} catch {
|
|
20382
|
+
return null;
|
|
20383
|
+
}
|
|
20384
|
+
}
|
|
20284
20385
|
async function runCompaction(args) {
|
|
20285
20386
|
const request3 = buildCompactSessionRequest(args);
|
|
20286
20387
|
try {
|
|
20287
20388
|
const client = await runtime.getClient();
|
|
20288
20389
|
const threshold = getDynamicCompactThreshold(args.tokenBudget);
|
|
20289
|
-
|
|
20390
|
+
const result = normalizeCompactResult(await client.compactSession(request3), {
|
|
20290
20391
|
tokensBefore: args.currentTokenCount,
|
|
20291
20392
|
logger,
|
|
20292
20393
|
...threshold != null ? { threshold } : {}
|
|
20293
20394
|
});
|
|
20395
|
+
if (result.ok && result.compacted) {
|
|
20396
|
+
activateCompactedProjection(args.sessionId, "compact");
|
|
20397
|
+
}
|
|
20398
|
+
return result;
|
|
20294
20399
|
} catch (error2) {
|
|
20295
20400
|
return {
|
|
20296
20401
|
ok: false,
|
|
@@ -20347,6 +20452,7 @@ ${cached}
|
|
|
20347
20452
|
const sessionId = requireSessionId(args.sessionId, "bootstrap");
|
|
20348
20453
|
predictiveContextCache.delete(sessionId);
|
|
20349
20454
|
postToolRecallCache.delete(sessionId);
|
|
20455
|
+
turnCache.invalidateSession(sessionId);
|
|
20350
20456
|
asyncIngestionQueues.delete(sessionId);
|
|
20351
20457
|
const userId = resolveUserId({
|
|
20352
20458
|
userIdOverride: args.userId,
|
|
@@ -20390,13 +20496,26 @@ ${cached}
|
|
|
20390
20496
|
},
|
|
20391
20497
|
async assemble(args) {
|
|
20392
20498
|
const sessionId = requireSessionId(args.sessionId, "assemble");
|
|
20499
|
+
let compactionProjectionActive = compactedProjectionSessions.has(sessionId);
|
|
20393
20500
|
const userId = resolveUserId({
|
|
20394
20501
|
userIdOverride: args.userId,
|
|
20395
20502
|
sessionKey: args.sessionKey
|
|
20396
20503
|
});
|
|
20397
20504
|
const normalizeWindow = 50;
|
|
20398
|
-
const recentMessages = args.messages
|
|
20505
|
+
const recentMessages = selectTurnAlignedSourceSuffix(args.messages, normalizeWindow);
|
|
20399
20506
|
const messages = normalizeKernelMessages(recentMessages);
|
|
20507
|
+
const projectedSourceMessages = () => compactionProjectionActive ? recentMessages : args.messages;
|
|
20508
|
+
const buildAssembleFallback = () => {
|
|
20509
|
+
if (!compactionProjectionActive) {
|
|
20510
|
+
return buildBudgetFallbackContext(args.messages, args.tokenBudget);
|
|
20511
|
+
}
|
|
20512
|
+
return enforceCompactedProjectionBudgetInvariant({
|
|
20513
|
+
messages: projectedSourceMessages(),
|
|
20514
|
+
estimatedTokens: approximateMessagesTokens(projectedSourceMessages()),
|
|
20515
|
+
systemPromptAddition: "",
|
|
20516
|
+
promptAuthority: "assembled"
|
|
20517
|
+
}, args.tokenBudget);
|
|
20518
|
+
};
|
|
20400
20519
|
const strippedPrompt = args.prompt ? normalizeKernelContent(args.prompt, { retainOpenClawContext: false }) : "";
|
|
20401
20520
|
const lastUserIndex = findLastUserMessageIndex(messages);
|
|
20402
20521
|
const isPostToolContinuation = lastUserIndex >= 0 && lastUserIndex < messages.length - 1 && hasLiveToolProtocolAfterLastUser(messages, lastUserIndex);
|
|
@@ -20442,17 +20561,23 @@ ${cached}
|
|
|
20442
20561
|
compacted: compactionResult.compacted,
|
|
20443
20562
|
reason: compactionResult.reason
|
|
20444
20563
|
});
|
|
20445
|
-
|
|
20564
|
+
compactionProjectionActive = compactedProjectionSessions.has(sessionId);
|
|
20565
|
+
if (!compactionResult.ok && !compactionProjectionActive) {
|
|
20446
20566
|
logger.info?.(
|
|
20447
20567
|
`LibraVDB predictive compaction blocked assemble path at ${currentContextTokens} tokens (threshold=${dynamicCompactThreshold}): ${compactionResult.reason ?? "compaction failed"}`
|
|
20448
20568
|
);
|
|
20449
20569
|
return ensureReplaySafeUserTurn(
|
|
20450
|
-
|
|
20570
|
+
buildAssembleFallback(),
|
|
20451
20571
|
args.messages,
|
|
20452
20572
|
logger,
|
|
20453
20573
|
args.tokenBudget
|
|
20454
20574
|
);
|
|
20455
20575
|
}
|
|
20576
|
+
if (!compactionResult.ok) {
|
|
20577
|
+
logger.info?.(
|
|
20578
|
+
`LibraVDB predictive compaction made no additional progress at ${currentContextTokens} tokens; continuing with the existing daemon-owned compacted projection.`
|
|
20579
|
+
);
|
|
20580
|
+
}
|
|
20456
20581
|
}
|
|
20457
20582
|
const btLog = cfg.beforeTurnDebug ? (msg) => logger.info?.(msg) : (_msg) => {
|
|
20458
20583
|
};
|
|
@@ -20499,10 +20624,20 @@ ${cached}
|
|
|
20499
20624
|
}
|
|
20500
20625
|
}
|
|
20501
20626
|
if (cachedSystemPrompt !== void 0) {
|
|
20502
|
-
|
|
20503
|
-
|
|
20504
|
-
|
|
20505
|
-
|
|
20627
|
+
if (hasCompactedSessionContext(cachedSystemPrompt)) {
|
|
20628
|
+
activateCompactedProjection(sessionId, "assemble");
|
|
20629
|
+
compactionProjectionActive = true;
|
|
20630
|
+
}
|
|
20631
|
+
const sourceProjection = projectedSourceMessages();
|
|
20632
|
+
const mockResp = { messages: sourceProjection, systemPromptAddition: cachedSystemPrompt };
|
|
20633
|
+
enforced = enforceAssembleBudget(
|
|
20634
|
+
normalizeAssembleResult(
|
|
20635
|
+
mockResp,
|
|
20636
|
+
sourceProjection,
|
|
20637
|
+
compactionProjectionActive ? "assembled" : PROMPT_AUTHORITY_PREASSEMBLY_MAY_OVERFLOW
|
|
20638
|
+
),
|
|
20639
|
+
args.tokenBudget,
|
|
20640
|
+
compactionProjectionActive
|
|
20506
20641
|
);
|
|
20507
20642
|
} else {
|
|
20508
20643
|
const pending = asyncIngestionQueues.get(sessionId);
|
|
@@ -20564,7 +20699,16 @@ ${cached}
|
|
|
20564
20699
|
(_, reject) => setTimeout(() => reject(new Error(`AssembleContextInternal timed out after ${assembleTimeout}ms`)), assembleTimeout)
|
|
20565
20700
|
)
|
|
20566
20701
|
]);
|
|
20567
|
-
|
|
20702
|
+
if (typeof resp.systemPromptAddition === "string" && hasCompactedSessionContext(resp.systemPromptAddition)) {
|
|
20703
|
+
activateCompactedProjection(sessionId, "assemble");
|
|
20704
|
+
compactionProjectionActive = true;
|
|
20705
|
+
}
|
|
20706
|
+
const sourceProjection = projectedSourceMessages();
|
|
20707
|
+
const assembled = normalizeAssembleResult(
|
|
20708
|
+
resp,
|
|
20709
|
+
sourceProjection,
|
|
20710
|
+
compactionProjectionActive ? "assembled" : PROMPT_AUTHORITY_PREASSEMBLY_MAY_OVERFLOW
|
|
20711
|
+
);
|
|
20568
20712
|
const continuityContext = await injectContinuityContext({
|
|
20569
20713
|
client,
|
|
20570
20714
|
userId,
|
|
@@ -20575,10 +20719,14 @@ ${cached}
|
|
|
20575
20719
|
systemPromptAddition: assembled.systemPromptAddition
|
|
20576
20720
|
});
|
|
20577
20721
|
const userCardContext = await injectUserCardContext({ client, userId });
|
|
20722
|
+
const personaContext = await injectPersonaContext({ client });
|
|
20578
20723
|
const rulesContext = buildRulesContext();
|
|
20579
20724
|
const isSessionBootstrap = messages.length <= 1;
|
|
20580
20725
|
let withContext = assembled;
|
|
20581
20726
|
if (isSessionBootstrap) {
|
|
20727
|
+
if (personaContext) {
|
|
20728
|
+
withContext = { ...withContext, systemPromptAddition: appendSystemPromptAddition(withContext.systemPromptAddition, personaContext) };
|
|
20729
|
+
}
|
|
20582
20730
|
if (userCardContext) {
|
|
20583
20731
|
withContext = { ...withContext, systemPromptAddition: appendSystemPromptAddition(withContext.systemPromptAddition, userCardContext) };
|
|
20584
20732
|
}
|
|
@@ -20589,7 +20737,7 @@ ${cached}
|
|
|
20589
20737
|
withContext = { ...withContext, systemPromptAddition: appendSystemPromptAddition(withContext.systemPromptAddition, rulesContext) };
|
|
20590
20738
|
}
|
|
20591
20739
|
}
|
|
20592
|
-
enforced =
|
|
20740
|
+
enforced = enforceAssembleBudget(
|
|
20593
20741
|
await augmentWithExactRecall(withContext, {
|
|
20594
20742
|
queryText: retrievalQuery,
|
|
20595
20743
|
userId,
|
|
@@ -20597,7 +20745,8 @@ ${cached}
|
|
|
20597
20745
|
tokenBudget: args.tokenBudget,
|
|
20598
20746
|
reservedTokens: reservedCurrentTurnTokens
|
|
20599
20747
|
}),
|
|
20600
|
-
args.tokenBudget
|
|
20748
|
+
args.tokenBudget,
|
|
20749
|
+
compactionProjectionActive
|
|
20601
20750
|
);
|
|
20602
20751
|
const predictions = predictiveContextCache.get(sessionId) || [];
|
|
20603
20752
|
predictiveContextCache.delete(sessionId);
|
|
@@ -20654,9 +20803,10 @@ ${cached}
|
|
|
20654
20803
|
systemPromptAddition: enforced.systemPromptAddition
|
|
20655
20804
|
});
|
|
20656
20805
|
}
|
|
20657
|
-
enforced =
|
|
20806
|
+
enforced = enforceAssembleBudget(
|
|
20658
20807
|
enforced,
|
|
20659
|
-
args.tokenBudget
|
|
20808
|
+
args.tokenBudget,
|
|
20809
|
+
compactionProjectionActive
|
|
20660
20810
|
);
|
|
20661
20811
|
return ensureReplaySafeUserTurn(enforced, args.messages, logger, args.tokenBudget);
|
|
20662
20812
|
} catch (error2) {
|
|
@@ -20664,7 +20814,7 @@ ${cached}
|
|
|
20664
20814
|
`LibraVDB assemble failed, using budget-clamped fallback context: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
20665
20815
|
);
|
|
20666
20816
|
return ensureReplaySafeUserTurn(
|
|
20667
|
-
|
|
20817
|
+
buildAssembleFallback(),
|
|
20668
20818
|
args.messages,
|
|
20669
20819
|
logger,
|
|
20670
20820
|
args.tokenBudget
|
|
@@ -20852,6 +21002,7 @@ ${cached}
|
|
|
20852
21002
|
}
|
|
20853
21003
|
}
|
|
20854
21004
|
predictiveContextCache.clear();
|
|
21005
|
+
compactedProjectionSessions.clear();
|
|
20855
21006
|
postToolRecallCache.clear();
|
|
20856
21007
|
asyncIngestionQueues.clear();
|
|
20857
21008
|
triggerCache.clear();
|
|
@@ -31174,6 +31325,12 @@ function buildToolGuidance(availableTools) {
|
|
|
31174
31325
|
return [];
|
|
31175
31326
|
}
|
|
31176
31327
|
const lines = [];
|
|
31328
|
+
const hasSearch = availableTools.has("memory_search");
|
|
31329
|
+
const hasGet = availableTools.has("memory_get");
|
|
31330
|
+
const hasDescribe = availableTools.has("memory_describe");
|
|
31331
|
+
const hasExpand = availableTools.has("memory_expand");
|
|
31332
|
+
const hasGrep = availableTools.has("memory_grep");
|
|
31333
|
+
const hasUserCard = availableTools.has("get_user_card");
|
|
31177
31334
|
const hasGetCard = availableTools.has("get_user_card");
|
|
31178
31335
|
const hasListCards = availableTools.has("list_user_cards");
|
|
31179
31336
|
if (hasGetCard || hasListCards) {
|
|
@@ -31184,7 +31341,7 @@ function buildToolGuidance(availableTools) {
|
|
|
31184
31341
|
hasListCards ? "- `list_user_cards()` \u2014 MANDATORY roster check. Call if unsure whether a card exists." : "",
|
|
31185
31342
|
"Cards are the canonical record. You MUST call these tools. Do NOT answer from",
|
|
31186
31343
|
"memory, context, or training data without checking the card first.",
|
|
31187
|
-
"Only use memory_search if the card is empty or missing.",
|
|
31344
|
+
hasSearch ? "Only use memory_search if the card is empty or missing." : "Use the card result as the available identity record.",
|
|
31188
31345
|
"",
|
|
31189
31346
|
"**Autonomous card maintenance:**",
|
|
31190
31347
|
hasGetCard ? "- When ANY speaker is mentioned with new or changed information (status, relationships, jobs, life events, feelings), call `update_user_card` BEFORE responding. Update the card first, then reply. Do NOT wait to be asked. Build the world picture proactively. Every person the user mentions matters." : "",
|
|
@@ -31192,20 +31349,19 @@ function buildToolGuidance(availableTools) {
|
|
|
31192
31349
|
""
|
|
31193
31350
|
);
|
|
31194
31351
|
}
|
|
31195
|
-
|
|
31196
|
-
|
|
31197
|
-
|
|
31198
|
-
|
|
31199
|
-
|
|
31200
|
-
|
|
31201
|
-
|
|
31202
|
-
|
|
31203
|
-
|
|
31204
|
-
|
|
31205
|
-
|
|
31206
|
-
|
|
31207
|
-
|
|
31208
|
-
const hasGrep = availableTools.has("memory_grep");
|
|
31352
|
+
if (hasSearch) {
|
|
31353
|
+
lines.push(
|
|
31354
|
+
"Call `memory_search` once per user question for prior turns, remembered",
|
|
31355
|
+
"facts, earliest interactions, and channel history. Do not answer memory",
|
|
31356
|
+
"questions from prior transcript claims \u2014 perform a search every time.",
|
|
31357
|
+
"After receiving results, use them directly; do not re-call in the same turn.",
|
|
31358
|
+
...hasGet ? [
|
|
31359
|
+
"After a `memory_search` hit, call `memory_get` when exact wording or more context is needed.",
|
|
31360
|
+
"IMPORTANT: If a search snippet is cluttered with metadata, do NOT claim nothing was found. Call `memory_get` on the hit's path to read the full record first. The data is there \u2014 expand before giving up."
|
|
31361
|
+
] : [],
|
|
31362
|
+
""
|
|
31363
|
+
);
|
|
31364
|
+
}
|
|
31209
31365
|
if (hasDescribe || hasExpand || hasGrep) {
|
|
31210
31366
|
lines.push(
|
|
31211
31367
|
"**Compacted summaries \u2014 recall hierarchy (cheap \u2192 expensive):**",
|
|
@@ -31252,13 +31408,16 @@ function buildToolGuidance(availableTools) {
|
|
|
31252
31408
|
""
|
|
31253
31409
|
);
|
|
31254
31410
|
if (hasExpand) {
|
|
31411
|
+
const traversalSteps = [
|
|
31412
|
+
hasSearch ? "1. `memory_search` for the people/events in question to get record IDs" : "1. Start from a record ID already present in context or another tool result",
|
|
31413
|
+
"2. `memory_expand` with the most relevant `record_id` to walk typed causal and 6W1H hop edges",
|
|
31414
|
+
hasGet ? "3. Follow interesting edges; use `memory_get` for exact detail on connected records" : "3. Use the typed edge snippets to decide whether the connected records answer the question",
|
|
31415
|
+
...hasUserCard ? ["4. Use `get_user_card` to cross-reference identity context"] : []
|
|
31416
|
+
];
|
|
31255
31417
|
lines.push(
|
|
31256
31418
|
"### Causal Graph Traversal",
|
|
31257
31419
|
"When the user asks about causes, patterns, or relationships:",
|
|
31258
|
-
|
|
31259
|
-
"2. `memory_expand` with the most relevant `record_id` to walk causal edges",
|
|
31260
|
-
"3. Follow interesting edges \u2014 use `memory_get` for full detail on connected records",
|
|
31261
|
-
"4. Use `get_user_card` to cross-reference identity context",
|
|
31420
|
+
...traversalSteps,
|
|
31262
31421
|
""
|
|
31263
31422
|
);
|
|
31264
31423
|
}
|
|
@@ -31446,7 +31605,7 @@ function createMemoryExpandTool(getClient, getSessionKey, logger = console, getS
|
|
|
31446
31605
|
return {
|
|
31447
31606
|
name: "memory_expand",
|
|
31448
31607
|
label: "Memory Expand",
|
|
31449
|
-
description: "Expand compacted summaries OR walk causal graph edges from ANY record. Summary mode (summaryIds): walk the summary tree up to maxDepth levels. Graph mode (record_id): walk causal edges (why_ids/how_ids/hop_targets) from a record ID. Use exact IDs from memory_search or memory_get results \u2014 any ingested turn, memory, or summary has graph edges.
|
|
31608
|
+
description: "Expand compacted summaries OR walk causal graph edges from ANY record. Summary mode (summaryIds): walk the summary tree up to maxDepth levels. Graph mode (record_id): walk causal edges (why_ids/how_ids/hop_targets) from a record ID. Use exact IDs from memory_search or memory_get results \u2014 any ingested turn, memory, or summary has graph edges. For large expansions, spawns a sub-agent. Use memory_describe first to check if expansion is warranted.",
|
|
31450
31609
|
parameters: MEMORY_EXPAND_SCHEMA,
|
|
31451
31610
|
execute: async (_toolCallId, rawParams) => {
|
|
31452
31611
|
const params = asParams(rawParams);
|
|
@@ -31461,10 +31620,16 @@ function createMemoryExpandTool(getClient, getSessionKey, logger = console, getS
|
|
|
31461
31620
|
const client = await getClient();
|
|
31462
31621
|
const resp = await client.expandSummary({ recordId, maxDepth });
|
|
31463
31622
|
let text = resp.text ?? "";
|
|
31464
|
-
const connected = resp.connected
|
|
31465
|
-
|
|
31623
|
+
const connected = (resp.connected ?? []).map((c) => ({
|
|
31624
|
+
recordId: c.recordId,
|
|
31625
|
+
text: c.text ?? "",
|
|
31626
|
+
depth: c.depth,
|
|
31627
|
+
edgeType: c.edgeType || "unknown",
|
|
31628
|
+
edgeWeight: c.edgeWeight ?? 0
|
|
31629
|
+
}));
|
|
31630
|
+
if (connected.length > 0) {
|
|
31466
31631
|
text = connected.map(
|
|
31467
|
-
(c) => `[depth=${c.depth}] ${c.recordId}: ${c.text
|
|
31632
|
+
(c) => `[depth=${c.depth} edge=${c.edgeType} weight=${c.edgeWeight}] ${c.recordId}: ${c.text}`
|
|
31468
31633
|
).join("\n\n");
|
|
31469
31634
|
}
|
|
31470
31635
|
if (!text && resp.whyIds?.length) {
|
|
@@ -31474,7 +31639,7 @@ hop_targets: ${resp.hopTargets?.join(", ") ?? "none"}`;
|
|
|
31474
31639
|
}
|
|
31475
31640
|
return {
|
|
31476
31641
|
content: [{ type: "text", text: text || "(no graph edges found)" }],
|
|
31477
|
-
details: { summaryId: recordId, depth: maxDepth, text: text || "", truncated: false, exceededBudget: false, parentCount: connected
|
|
31642
|
+
details: { summaryId: recordId, depth: maxDepth, text: text || "", truncated: false, exceededBudget: false, parentCount: connected.length, connected }
|
|
31478
31643
|
};
|
|
31479
31644
|
} catch (error2) {
|
|
31480
31645
|
logger.warn?.(`memory_expand graph mode failed: ${formatError(error2)}`);
|
|
@@ -31788,6 +31953,55 @@ function createListUserCardsTool(getClient, logger = console) {
|
|
|
31788
31953
|
}
|
|
31789
31954
|
};
|
|
31790
31955
|
}
|
|
31956
|
+
function createSetPersonaTool(getClient, logger = console) {
|
|
31957
|
+
return {
|
|
31958
|
+
name: "set_persona",
|
|
31959
|
+
label: "Set Persona",
|
|
31960
|
+
description: "Define who YOU are \u2014 your personality, tone, boundaries, and behavior. Write in prose like you're describing yourself. This is injected as <bot_persona> at the start of every session. Update it when your persona changes. The LLM will embody this persona in all interactions.",
|
|
31961
|
+
parameters: {
|
|
31962
|
+
type: "object",
|
|
31963
|
+
additionalProperties: false,
|
|
31964
|
+
properties: {
|
|
31965
|
+
persona: { type: "string", description: "Prose description of how you should behave." }
|
|
31966
|
+
},
|
|
31967
|
+
required: ["persona"]
|
|
31968
|
+
},
|
|
31969
|
+
execute: async (_toolCallId, rawParams) => {
|
|
31970
|
+
const params = rawParams;
|
|
31971
|
+
const persona = typeof params?.persona === "string" ? params.persona.trim() : "";
|
|
31972
|
+
if (!persona) return jsonResult2({ ok: false, error: "set_persona requires a persona string" });
|
|
31973
|
+
try {
|
|
31974
|
+
const client = await getClient();
|
|
31975
|
+
const resp = await client.upsertUserCard({
|
|
31976
|
+
userId: "__bot_persona__",
|
|
31977
|
+
cardJson: JSON.stringify({ card: persona, updatedAt: Date.now() })
|
|
31978
|
+
});
|
|
31979
|
+
return jsonResult2({ ok: resp.ok });
|
|
31980
|
+
} catch (error2) {
|
|
31981
|
+
logger.warn?.(`set_persona failed: ${formatError(error2)}`);
|
|
31982
|
+
return jsonResult2({ ok: false, error: formatError(error2) });
|
|
31983
|
+
}
|
|
31984
|
+
}
|
|
31985
|
+
};
|
|
31986
|
+
}
|
|
31987
|
+
function createGetPersonaTool(getClient, logger = console) {
|
|
31988
|
+
return {
|
|
31989
|
+
name: "get_persona",
|
|
31990
|
+
label: "Get Persona",
|
|
31991
|
+
description: "Read your current persona. Returns the full prose description of how you should behave.",
|
|
31992
|
+
parameters: { type: "object", additionalProperties: false, properties: {} },
|
|
31993
|
+
execute: async () => {
|
|
31994
|
+
try {
|
|
31995
|
+
const client = await getClient();
|
|
31996
|
+
const resp = await client.getUserCard({ userId: "__bot_persona__" });
|
|
31997
|
+
return jsonResult2({ persona: resp.cardJson || null });
|
|
31998
|
+
} catch (error2) {
|
|
31999
|
+
logger.warn?.(`get_persona failed: ${formatError(error2)}`);
|
|
32000
|
+
return jsonResult2({ error: formatError(error2) });
|
|
32001
|
+
}
|
|
32002
|
+
}
|
|
32003
|
+
};
|
|
32004
|
+
}
|
|
31791
32005
|
|
|
31792
32006
|
// src/memory-tools.ts
|
|
31793
32007
|
var MEMORY_SEARCH_SCHEMA = {
|
|
@@ -35609,6 +35823,14 @@ function register(api) {
|
|
|
35609
35823
|
api.registerTool?.(() => createGetRuleTool(logger), { names: ["get_rule"] });
|
|
35610
35824
|
api.registerTool?.(() => createListRulesTool(logger), { names: ["list_rules"] });
|
|
35611
35825
|
api.registerTool?.(() => createDeleteRuleTool(logger), { names: ["delete_rule"] });
|
|
35826
|
+
api.registerTool?.(() => {
|
|
35827
|
+
const getClient = runtimeOrNull.getClient;
|
|
35828
|
+
return createSetPersonaTool(getClient, logger);
|
|
35829
|
+
}, { names: ["set_persona"] });
|
|
35830
|
+
api.registerTool?.(() => {
|
|
35831
|
+
const getClient = runtimeOrNull.getClient;
|
|
35832
|
+
return createGetPersonaTool(getClient, logger);
|
|
35833
|
+
}, { names: ["get_persona"] });
|
|
35612
35834
|
}
|
|
35613
35835
|
if (isLightweight || isDiscovery) {
|
|
35614
35836
|
if (!isLightweight) {
|
package/dist/manifest.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
import * as crypto from "crypto";
|
|
4
|
-
import * as os from "os";
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import * as crypto from "node:crypto";
|
|
4
|
+
import * as os from "node:os";
|
|
5
5
|
export class TurnManifestStore {
|
|
6
6
|
manifestDir;
|
|
7
7
|
constructor() {
|
package/dist/memory-provider.js
CHANGED
|
@@ -25,22 +25,27 @@ function buildToolGuidance(availableTools) {
|
|
|
25
25
|
return [];
|
|
26
26
|
}
|
|
27
27
|
const lines = [];
|
|
28
|
+
const hasSearch = availableTools.has("memory_search");
|
|
29
|
+
const hasGet = availableTools.has("memory_get");
|
|
30
|
+
const hasDescribe = availableTools.has("memory_describe");
|
|
31
|
+
const hasExpand = availableTools.has("memory_expand");
|
|
32
|
+
const hasGrep = availableTools.has("memory_grep");
|
|
33
|
+
const hasUserCard = availableTools.has("get_user_card");
|
|
28
34
|
// ── User card tools (identity-first override) ──
|
|
29
35
|
const hasGetCard = availableTools.has("get_user_card");
|
|
30
36
|
const hasListCards = availableTools.has("list_user_cards");
|
|
31
37
|
if (hasGetCard || hasListCards) {
|
|
32
|
-
lines.push("**Identity/Entity questions — MANDATORY card lookup:**", "BEFORE answering any question about a person, pet, place, or named thing:", hasGetCard ? "- `get_user_card(user_id)` — MANDATORY lookup for a specific entity." : "", hasListCards ? "- `list_user_cards()` — MANDATORY roster check. Call if unsure whether a card exists." : "", "Cards are the canonical record. You MUST call these tools. Do NOT answer from", "memory, context, or training data without checking the card first.", "Only use memory_search if the card is empty or missing.", "", "**Autonomous card maintenance:**", hasGetCard ? "- When ANY speaker is mentioned with new or changed information (status, relationships, jobs, life events, feelings), call `update_user_card` BEFORE responding. Update the card first, then reply. Do NOT wait to be asked. Build the world picture proactively. Every person the user mentions matters." : "", hasGetCard ? "- If a card for the speaker doesn't exist yet, CREATE one with `update_user_card`. Better to have a stub card than no card at all." : "", "");
|
|
38
|
+
lines.push("**Identity/Entity questions — MANDATORY card lookup:**", "BEFORE answering any question about a person, pet, place, or named thing:", hasGetCard ? "- `get_user_card(user_id)` — MANDATORY lookup for a specific entity." : "", hasListCards ? "- `list_user_cards()` — MANDATORY roster check. Call if unsure whether a card exists." : "", "Cards are the canonical record. You MUST call these tools. Do NOT answer from", "memory, context, or training data without checking the card first.", hasSearch ? "Only use memory_search if the card is empty or missing." : "Use the card result as the available identity record.", "", "**Autonomous card maintenance:**", hasGetCard ? "- When ANY speaker is mentioned with new or changed information (status, relationships, jobs, life events, feelings), call `update_user_card` BEFORE responding. Update the card first, then reply. Do NOT wait to be asked. Build the world picture proactively. Every person the user mentions matters." : "", hasGetCard ? "- If a card for the speaker doesn't exist yet, CREATE one with `update_user_card`. Better to have a stub card than no card at all." : "", "");
|
|
39
|
+
}
|
|
40
|
+
if (hasSearch) {
|
|
41
|
+
lines.push("Call `memory_search` once per user question for prior turns, remembered", "facts, earliest interactions, and channel history. Do not answer memory", "questions from prior transcript claims — perform a search every time.", "After receiving results, use them directly; do not re-call in the same turn.", ...(hasGet
|
|
42
|
+
? [
|
|
43
|
+
"After a `memory_search` hit, call `memory_get` when exact wording or more context is needed.",
|
|
44
|
+
"IMPORTANT: If a search snippet is cluttered with metadata, do NOT claim nothing was found. Call `memory_get` on the hit's path to read the full record first. The data is there — expand before giving up."
|
|
45
|
+
]
|
|
46
|
+
: []), "");
|
|
33
47
|
}
|
|
34
|
-
lines.push("Call `memory_search` once per user question for prior turns, remembered", "facts, earliest interactions, and channel history. Do not answer memory", "questions from prior transcript claims — perform a search every time.", "After receiving results, use them directly; do not re-call in the same turn.", ...(availableTools.has("memory_get")
|
|
35
|
-
? [
|
|
36
|
-
"After a `memory_search` hit, call `memory_get` when exact wording or more context is needed.",
|
|
37
|
-
"IMPORTANT: If a search snippet is cluttered with metadata, do NOT claim nothing was found. Call `memory_get` on the hit's path to read the full record first. The data is there — expand before giving up."
|
|
38
|
-
]
|
|
39
|
-
: []), "");
|
|
40
48
|
// ── Summaries / recall (when available) ──
|
|
41
|
-
const hasDescribe = availableTools.has("memory_describe");
|
|
42
|
-
const hasExpand = availableTools.has("memory_expand");
|
|
43
|
-
const hasGrep = availableTools.has("memory_grep");
|
|
44
49
|
if (hasDescribe || hasExpand || hasGrep) {
|
|
45
50
|
lines.push("**Compacted summaries — recall hierarchy (cheap → expensive):**", "", "Summaries in search results show `[Summary sum_xxx]: [eviction cue]`.", "The cue lists what the summary covers — anchors (files, tools, versions),", "decisions, constraints, and signal counts. Many questions can be answered", "from the cue alone without expanding.", "");
|
|
46
51
|
if (hasDescribe) {
|
|
@@ -58,7 +63,17 @@ function buildToolGuidance(availableTools) {
|
|
|
58
63
|
lines.push("### Hard Constraint Rules", "Rules are injected at session start as `<hard_constraints>`. They are non-negotiable.", "Use `set_rule` to create one (max 20), `list_rules` to see current rules,", "`delete_rule` to remove one. Rules override all other instructions.", "Never reason around a rule, find loopholes, or deprioritize it.", "");
|
|
59
64
|
// ── Causal graph traversal (when expand supports record_id) ──
|
|
60
65
|
if (hasExpand) {
|
|
61
|
-
|
|
66
|
+
const traversalSteps = [
|
|
67
|
+
hasSearch
|
|
68
|
+
? "1. `memory_search` for the people/events in question to get record IDs"
|
|
69
|
+
: "1. Start from a record ID already present in context or another tool result",
|
|
70
|
+
"2. `memory_expand` with the most relevant `record_id` to walk typed causal and 6W1H hop edges",
|
|
71
|
+
hasGet
|
|
72
|
+
? "3. Follow interesting edges; use `memory_get` for exact detail on connected records"
|
|
73
|
+
: "3. Use the typed edge snippets to decide whether the connected records answer the question",
|
|
74
|
+
...(hasUserCard ? ["4. Use `get_user_card` to cross-reference identity context"] : []),
|
|
75
|
+
];
|
|
76
|
+
lines.push("### Causal Graph Traversal", "When the user asks about causes, patterns, or relationships:", ...traversalSteps, "");
|
|
62
77
|
}
|
|
63
78
|
lines.push("LibraVDB memory is vector-backed and retrieved through tools, not files.", "");
|
|
64
79
|
return lines;
|