@remnic/plugin-openclaw 1.0.12 → 1.0.14
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/dist/index.js +1515 -77
- package/openclaw.plugin.json +27 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -601,6 +601,9 @@ var MEMORY_OS_PRESETS = {
|
|
|
601
601
|
contextCompressionActionsEnabled: true,
|
|
602
602
|
compressionGuidelineLearningEnabled: true,
|
|
603
603
|
compressionGuidelineSemanticRefinementEnabled: true,
|
|
604
|
+
explicitCueRecallEnabled: true,
|
|
605
|
+
explicitCueRecallMaxChars: 3200,
|
|
606
|
+
lcmEnabled: true,
|
|
604
607
|
maxProactiveQuestionsPerExtraction: 4,
|
|
605
608
|
maxCompressionTokensPerHour: 3e3,
|
|
606
609
|
behaviorLoopAutoTuneEnabled: true
|
|
@@ -1897,6 +1900,9 @@ function parseConfig(raw) {
|
|
|
1897
1900
|
temporalMemoryTreeEnabled: cfg.temporalMemoryTreeEnabled === true,
|
|
1898
1901
|
tmtHourlyMinMemories: typeof cfg.tmtHourlyMinMemories === "number" ? cfg.tmtHourlyMinMemories : 3,
|
|
1899
1902
|
tmtSummaryMaxTokens: typeof cfg.tmtSummaryMaxTokens === "number" ? cfg.tmtSummaryMaxTokens : 300,
|
|
1903
|
+
explicitCueRecallEnabled: coerceBool(cfg.explicitCueRecallEnabled) === true,
|
|
1904
|
+
explicitCueRecallMaxChars: coerceNumber(cfg.explicitCueRecallMaxChars) !== void 0 ? Math.max(0, Math.floor(coerceNumber(cfg.explicitCueRecallMaxChars))) : 2400,
|
|
1905
|
+
explicitCueRecallMaxReferences: coerceNumber(cfg.explicitCueRecallMaxReferences) !== void 0 ? Math.max(0, Math.floor(coerceNumber(cfg.explicitCueRecallMaxReferences))) : 24,
|
|
1900
1906
|
// Lossless Context Management (LCM)
|
|
1901
1907
|
lcmEnabled: cfg.lcmEnabled === true,
|
|
1902
1908
|
lcmLeafBatchSize: typeof cfg.lcmLeafBatchSize === "number" ? Math.max(2, Math.floor(cfg.lcmLeafBatchSize)) : 8,
|
|
@@ -1906,6 +1912,8 @@ function parseConfig(raw) {
|
|
|
1906
1912
|
lcmRecallBudgetShare: typeof cfg.lcmRecallBudgetShare === "number" ? Math.max(0, Math.min(1, cfg.lcmRecallBudgetShare)) : 0.15,
|
|
1907
1913
|
lcmDeterministicMaxTokens: typeof cfg.lcmDeterministicMaxTokens === "number" ? Math.max(64, Math.floor(cfg.lcmDeterministicMaxTokens)) : 512,
|
|
1908
1914
|
lcmArchiveRetentionDays: typeof cfg.lcmArchiveRetentionDays === "number" ? Math.max(1, Math.floor(cfg.lcmArchiveRetentionDays)) : 90,
|
|
1915
|
+
messagePartsEnabled: coerceBooleanLike(cfg.messagePartsEnabled) === true,
|
|
1916
|
+
messagePartsRecallMaxResults: typeof cfg.messagePartsRecallMaxResults === "number" ? Math.max(0, Math.floor(cfg.messagePartsRecallMaxResults)) : 6,
|
|
1909
1917
|
// v9.1 Parallel Specialized Retrieval
|
|
1910
1918
|
parallelRetrievalEnabled: cfg.parallelRetrievalEnabled === true,
|
|
1911
1919
|
parallelAgentWeights: (() => {
|
|
@@ -2252,6 +2260,12 @@ function buildDefaultRecallPipeline(cfg) {
|
|
|
2252
2260
|
enabled: cfg.sharedContextEnabled === true,
|
|
2253
2261
|
maxChars: typeof cfg.sharedContextMaxInjectChars === "number" ? Math.max(0, Math.floor(cfg.sharedContextMaxInjectChars)) : 4e3
|
|
2254
2262
|
},
|
|
2263
|
+
{
|
|
2264
|
+
id: "explicit-cue",
|
|
2265
|
+
enabled: coerceBool(cfg.explicitCueRecallEnabled) === true,
|
|
2266
|
+
maxChars: coerceNumber(cfg.explicitCueRecallMaxChars) !== void 0 ? Math.max(0, Math.floor(coerceNumber(cfg.explicitCueRecallMaxChars))) : 2400,
|
|
2267
|
+
maxResults: coerceNumber(cfg.explicitCueRecallMaxReferences) !== void 0 ? Math.max(0, Math.floor(coerceNumber(cfg.explicitCueRecallMaxReferences))) : 24
|
|
2268
|
+
},
|
|
2255
2269
|
{
|
|
2256
2270
|
id: "profile",
|
|
2257
2271
|
enabled: true,
|
|
@@ -19349,7 +19363,9 @@ var RECALL_XRAY_SERVED_BY_VALUES = [
|
|
|
19349
19363
|
"graph",
|
|
19350
19364
|
"recent-scan",
|
|
19351
19365
|
"procedural",
|
|
19352
|
-
"review-context"
|
|
19366
|
+
"review-context",
|
|
19367
|
+
"lcm-file-parts",
|
|
19368
|
+
"lcm-tool-parts"
|
|
19353
19369
|
];
|
|
19354
19370
|
function isRecallXrayServedBy(value) {
|
|
19355
19371
|
return typeof value === "string" && RECALL_XRAY_SERVED_BY_VALUES.includes(value);
|
|
@@ -23083,6 +23099,630 @@ async function readRecentEntityTranscriptEntries(transcriptEntriesPromise, recen
|
|
|
23083
23099
|
}
|
|
23084
23100
|
var entityRecentTranscriptLookbackHours = RECENT_TRANSCRIPT_LOOKBACK_HOURS;
|
|
23085
23101
|
|
|
23102
|
+
// ../remnic-core/src/evidence-pack.ts
|
|
23103
|
+
var DEFAULT_MAX_ITEM_CHARS = 1200;
|
|
23104
|
+
function buildEvidencePack(items, options) {
|
|
23105
|
+
const budget = normalizePositiveInteger(options.maxChars);
|
|
23106
|
+
if (budget <= 0 || items.length === 0) {
|
|
23107
|
+
return "";
|
|
23108
|
+
}
|
|
23109
|
+
const maxItemChars = normalizePositiveInteger(
|
|
23110
|
+
options.maxItemChars ?? DEFAULT_MAX_ITEM_CHARS
|
|
23111
|
+
);
|
|
23112
|
+
if (maxItemChars <= 0) {
|
|
23113
|
+
return "";
|
|
23114
|
+
}
|
|
23115
|
+
const title = options.title ?? "Evidence";
|
|
23116
|
+
const lines = [`## ${title}`];
|
|
23117
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
23118
|
+
const seenContent = /* @__PURE__ */ new Set();
|
|
23119
|
+
let used = lines[0].length;
|
|
23120
|
+
for (const item of items) {
|
|
23121
|
+
const content = item.content.trim();
|
|
23122
|
+
if (!content) continue;
|
|
23123
|
+
const id = item.id ?? evidenceItemFallbackId(item);
|
|
23124
|
+
if (id && seenIds.has(id)) continue;
|
|
23125
|
+
const contentKey = normalizeEvidenceContent(content);
|
|
23126
|
+
if (seenContent.has(contentKey)) continue;
|
|
23127
|
+
const label = formatEvidenceLabel(item);
|
|
23128
|
+
const clipped = clipText(content, maxItemChars);
|
|
23129
|
+
const block = `${label}: ${clipped}`;
|
|
23130
|
+
const separatorLength = lines.length > 0 ? 2 : 0;
|
|
23131
|
+
const remaining = budget - used - separatorLength;
|
|
23132
|
+
if (remaining <= 0) break;
|
|
23133
|
+
const finalBlock = block.length > remaining ? clipText(block, remaining) : block;
|
|
23134
|
+
if (!finalBlock.trim()) break;
|
|
23135
|
+
lines.push(finalBlock);
|
|
23136
|
+
used += separatorLength + finalBlock.length;
|
|
23137
|
+
if (id) seenIds.add(id);
|
|
23138
|
+
seenContent.add(contentKey);
|
|
23139
|
+
}
|
|
23140
|
+
return lines.length === 1 ? "" : lines.join("\n\n");
|
|
23141
|
+
}
|
|
23142
|
+
function normalizePositiveInteger(value) {
|
|
23143
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
23144
|
+
return 0;
|
|
23145
|
+
}
|
|
23146
|
+
return Math.floor(value);
|
|
23147
|
+
}
|
|
23148
|
+
function evidenceItemFallbackId(item) {
|
|
23149
|
+
if (item.sessionId && typeof item.turnIndex === "number") {
|
|
23150
|
+
return `${item.sessionId}:${item.turnIndex}`;
|
|
23151
|
+
}
|
|
23152
|
+
return void 0;
|
|
23153
|
+
}
|
|
23154
|
+
function normalizeEvidenceContent(content) {
|
|
23155
|
+
return content.toLowerCase().replace(/\s+/g, " ").trim();
|
|
23156
|
+
}
|
|
23157
|
+
function formatEvidenceLabel(item) {
|
|
23158
|
+
const parts = [];
|
|
23159
|
+
if (item.sessionId) parts.push(item.sessionId);
|
|
23160
|
+
if (typeof item.turnIndex === "number") parts.push(`turn ${item.turnIndex}`);
|
|
23161
|
+
if (item.role) parts.push(item.role);
|
|
23162
|
+
if (typeof item.score === "number" && Number.isFinite(item.score)) {
|
|
23163
|
+
parts.push(`score ${item.score.toFixed(3)}`);
|
|
23164
|
+
}
|
|
23165
|
+
return parts.length > 0 ? `[${parts.join(", ")}]` : "[evidence]";
|
|
23166
|
+
}
|
|
23167
|
+
function clipText(text, maxChars) {
|
|
23168
|
+
if (text.length <= maxChars) {
|
|
23169
|
+
return text;
|
|
23170
|
+
}
|
|
23171
|
+
if (maxChars <= 1) {
|
|
23172
|
+
return text.slice(0, maxChars);
|
|
23173
|
+
}
|
|
23174
|
+
if (maxChars <= 3) {
|
|
23175
|
+
return text.slice(0, maxChars);
|
|
23176
|
+
}
|
|
23177
|
+
return `${text.slice(0, maxChars - 3).trimEnd()}...`;
|
|
23178
|
+
}
|
|
23179
|
+
|
|
23180
|
+
// ../remnic-core/src/explicit-cue-recall.ts
|
|
23181
|
+
var DEFAULT_MAX_CHARS = 2400;
|
|
23182
|
+
var DEFAULT_MAX_ITEM_CHARS2 = 1200;
|
|
23183
|
+
var DEFAULT_MAX_REFERENCES = 24;
|
|
23184
|
+
var REFERENCE_SCAN_TOKEN_FACTOR = 3;
|
|
23185
|
+
var TURN_REFERENCE_WINDOW_RADIUS = 0;
|
|
23186
|
+
var LEXICAL_CUE_WINDOW_RADIUS = 1;
|
|
23187
|
+
var LEXICAL_CUE_SEARCH_LIMIT = 3;
|
|
23188
|
+
var LEXICAL_CUE_MAX_TOKENS = 400;
|
|
23189
|
+
var LATEST_STATE_CUES = /* @__PURE__ */ new Set([
|
|
23190
|
+
"as of",
|
|
23191
|
+
"currently",
|
|
23192
|
+
"latest",
|
|
23193
|
+
"most recent",
|
|
23194
|
+
"newest",
|
|
23195
|
+
"now",
|
|
23196
|
+
"updated",
|
|
23197
|
+
"changed",
|
|
23198
|
+
"change"
|
|
23199
|
+
]);
|
|
23200
|
+
var RELATIVE_TEMPORAL_CUES = [
|
|
23201
|
+
"as of",
|
|
23202
|
+
"most recent",
|
|
23203
|
+
"last time",
|
|
23204
|
+
"last week",
|
|
23205
|
+
"last month",
|
|
23206
|
+
"last year",
|
|
23207
|
+
"last session",
|
|
23208
|
+
"last conversation",
|
|
23209
|
+
"next time",
|
|
23210
|
+
"next week",
|
|
23211
|
+
"next month",
|
|
23212
|
+
"next year",
|
|
23213
|
+
"next session",
|
|
23214
|
+
"next conversation",
|
|
23215
|
+
"previous time",
|
|
23216
|
+
"previous week",
|
|
23217
|
+
"previous month",
|
|
23218
|
+
"previous year",
|
|
23219
|
+
"previous session",
|
|
23220
|
+
"previous conversation",
|
|
23221
|
+
"prior time",
|
|
23222
|
+
"prior week",
|
|
23223
|
+
"prior month",
|
|
23224
|
+
"prior year",
|
|
23225
|
+
"prior session",
|
|
23226
|
+
"prior conversation",
|
|
23227
|
+
"today",
|
|
23228
|
+
"yesterday",
|
|
23229
|
+
"tomorrow",
|
|
23230
|
+
"tonight",
|
|
23231
|
+
"earlier",
|
|
23232
|
+
"later",
|
|
23233
|
+
"recently",
|
|
23234
|
+
"previously",
|
|
23235
|
+
"currently",
|
|
23236
|
+
"now",
|
|
23237
|
+
"latest",
|
|
23238
|
+
"newest",
|
|
23239
|
+
"oldest",
|
|
23240
|
+
"earliest",
|
|
23241
|
+
"before",
|
|
23242
|
+
"after",
|
|
23243
|
+
"since",
|
|
23244
|
+
"updated",
|
|
23245
|
+
"changed",
|
|
23246
|
+
"change"
|
|
23247
|
+
];
|
|
23248
|
+
var SPEAKER_NAME_STOPWORDS = /* @__PURE__ */ new Set([
|
|
23249
|
+
"A",
|
|
23250
|
+
"According",
|
|
23251
|
+
"An",
|
|
23252
|
+
"And",
|
|
23253
|
+
"Are",
|
|
23254
|
+
"As",
|
|
23255
|
+
"At",
|
|
23256
|
+
"Before",
|
|
23257
|
+
"Can",
|
|
23258
|
+
"Compare",
|
|
23259
|
+
"Could",
|
|
23260
|
+
"Did",
|
|
23261
|
+
"Do",
|
|
23262
|
+
"Does",
|
|
23263
|
+
"For",
|
|
23264
|
+
"From",
|
|
23265
|
+
"Had",
|
|
23266
|
+
"Has",
|
|
23267
|
+
"Have",
|
|
23268
|
+
"How",
|
|
23269
|
+
"In",
|
|
23270
|
+
"Is",
|
|
23271
|
+
"It",
|
|
23272
|
+
"Of",
|
|
23273
|
+
"On",
|
|
23274
|
+
"Or",
|
|
23275
|
+
"Please",
|
|
23276
|
+
"Review",
|
|
23277
|
+
"Step",
|
|
23278
|
+
"Tell",
|
|
23279
|
+
"The",
|
|
23280
|
+
"To",
|
|
23281
|
+
"Turn",
|
|
23282
|
+
"Use",
|
|
23283
|
+
"Was",
|
|
23284
|
+
"Were",
|
|
23285
|
+
"What",
|
|
23286
|
+
"When",
|
|
23287
|
+
"Where",
|
|
23288
|
+
"Which",
|
|
23289
|
+
"Who",
|
|
23290
|
+
"Why",
|
|
23291
|
+
"Will",
|
|
23292
|
+
"Would"
|
|
23293
|
+
]);
|
|
23294
|
+
var QUESTION_SLOT_STOPWORDS = /* @__PURE__ */ new Set([
|
|
23295
|
+
"answer",
|
|
23296
|
+
"choice",
|
|
23297
|
+
"did",
|
|
23298
|
+
"does",
|
|
23299
|
+
"do",
|
|
23300
|
+
"is",
|
|
23301
|
+
"should",
|
|
23302
|
+
"single",
|
|
23303
|
+
"the",
|
|
23304
|
+
"user",
|
|
23305
|
+
"was",
|
|
23306
|
+
"were"
|
|
23307
|
+
]);
|
|
23308
|
+
async function buildExplicitCueRecallSection(options) {
|
|
23309
|
+
const engine = options.engine;
|
|
23310
|
+
const query = options.query.trim();
|
|
23311
|
+
const maxChars = normalizePositiveInteger2(options.maxChars, DEFAULT_MAX_CHARS);
|
|
23312
|
+
if (!engine || query.length === 0 || maxChars <= 0) {
|
|
23313
|
+
return "";
|
|
23314
|
+
}
|
|
23315
|
+
const maxReferences = normalizePositiveInteger2(
|
|
23316
|
+
options.maxReferences,
|
|
23317
|
+
DEFAULT_MAX_REFERENCES
|
|
23318
|
+
);
|
|
23319
|
+
if (maxReferences <= 0) {
|
|
23320
|
+
return "";
|
|
23321
|
+
}
|
|
23322
|
+
const evidenceItems = [];
|
|
23323
|
+
const seenTurns = /* @__PURE__ */ new Set();
|
|
23324
|
+
await collectTurnReferenceEvidence({
|
|
23325
|
+
engine,
|
|
23326
|
+
sessionId: options.sessionId,
|
|
23327
|
+
query,
|
|
23328
|
+
maxReferences,
|
|
23329
|
+
evidenceItems,
|
|
23330
|
+
seenTurns
|
|
23331
|
+
});
|
|
23332
|
+
await collectLexicalCueEvidence({
|
|
23333
|
+
engine,
|
|
23334
|
+
sessionId: options.sessionId,
|
|
23335
|
+
query,
|
|
23336
|
+
maxReferences,
|
|
23337
|
+
evidenceItems,
|
|
23338
|
+
seenTurns
|
|
23339
|
+
});
|
|
23340
|
+
return buildEvidencePack(evidenceItems, {
|
|
23341
|
+
title: "Explicit Cue Evidence",
|
|
23342
|
+
maxChars,
|
|
23343
|
+
maxItemChars: normalizePositiveInteger2(
|
|
23344
|
+
options.maxItemChars,
|
|
23345
|
+
DEFAULT_MAX_ITEM_CHARS2
|
|
23346
|
+
)
|
|
23347
|
+
});
|
|
23348
|
+
}
|
|
23349
|
+
async function collectTurnReferenceEvidence(options) {
|
|
23350
|
+
if (!options.sessionId) {
|
|
23351
|
+
return;
|
|
23352
|
+
}
|
|
23353
|
+
const references = collectExplicitTurnReferences(options.query).slice(
|
|
23354
|
+
0,
|
|
23355
|
+
options.maxReferences
|
|
23356
|
+
);
|
|
23357
|
+
if (references.length === 0) {
|
|
23358
|
+
return;
|
|
23359
|
+
}
|
|
23360
|
+
const windows = /* @__PURE__ */ new Map();
|
|
23361
|
+
for (const reference of references) {
|
|
23362
|
+
for (const center of candidateTurnIndexesForReference(reference)) {
|
|
23363
|
+
if (center < 0) {
|
|
23364
|
+
continue;
|
|
23365
|
+
}
|
|
23366
|
+
const fromTurn = Math.max(0, center - TURN_REFERENCE_WINDOW_RADIUS);
|
|
23367
|
+
const toTurn = center + TURN_REFERENCE_WINDOW_RADIUS;
|
|
23368
|
+
windows.set(`${fromTurn}:${toTurn}`, { fromTurn, toTurn });
|
|
23369
|
+
}
|
|
23370
|
+
}
|
|
23371
|
+
for (const window of [...windows.values()].sort(
|
|
23372
|
+
(left, right) => left.fromTurn - right.fromTurn || left.toTurn - right.toTurn
|
|
23373
|
+
)) {
|
|
23374
|
+
const expanded = await options.engine.expandContext(
|
|
23375
|
+
options.sessionId,
|
|
23376
|
+
window.fromTurn,
|
|
23377
|
+
window.toTurn,
|
|
23378
|
+
2e3
|
|
23379
|
+
);
|
|
23380
|
+
appendExpandedEvidence(
|
|
23381
|
+
options.evidenceItems,
|
|
23382
|
+
options.seenTurns,
|
|
23383
|
+
options.sessionId,
|
|
23384
|
+
expanded
|
|
23385
|
+
);
|
|
23386
|
+
}
|
|
23387
|
+
}
|
|
23388
|
+
async function collectLexicalCueEvidence(options) {
|
|
23389
|
+
const cues = collectLexicalCues(options.query).slice(0, options.maxReferences);
|
|
23390
|
+
const preferLatest = hasLatestStateIntent(options.query);
|
|
23391
|
+
for (const cue of cues) {
|
|
23392
|
+
const results = sortLexicalCueResults(
|
|
23393
|
+
await options.engine.searchContextFull(
|
|
23394
|
+
cue,
|
|
23395
|
+
LEXICAL_CUE_SEARCH_LIMIT,
|
|
23396
|
+
options.sessionId
|
|
23397
|
+
),
|
|
23398
|
+
preferLatest
|
|
23399
|
+
);
|
|
23400
|
+
for (const result of results) {
|
|
23401
|
+
const windowRadius = preferLatest ? 0 : LEXICAL_CUE_WINDOW_RADIUS;
|
|
23402
|
+
const fromTurn = Math.max(0, result.turn_index - windowRadius);
|
|
23403
|
+
const toTurn = result.turn_index + windowRadius;
|
|
23404
|
+
const expanded = await options.engine.expandContext(
|
|
23405
|
+
result.session_id,
|
|
23406
|
+
fromTurn,
|
|
23407
|
+
toTurn,
|
|
23408
|
+
LEXICAL_CUE_MAX_TOKENS
|
|
23409
|
+
);
|
|
23410
|
+
if (expanded.length === 0) {
|
|
23411
|
+
appendEvidenceItem(options.evidenceItems, options.seenTurns, {
|
|
23412
|
+
id: `${result.session_id}:${result.turn_index}`,
|
|
23413
|
+
sessionId: result.session_id,
|
|
23414
|
+
turnIndex: result.turn_index,
|
|
23415
|
+
role: result.role,
|
|
23416
|
+
content: result.content,
|
|
23417
|
+
...typeof result.score === "number" ? { score: result.score } : {}
|
|
23418
|
+
});
|
|
23419
|
+
continue;
|
|
23420
|
+
}
|
|
23421
|
+
appendExpandedEvidence(
|
|
23422
|
+
options.evidenceItems,
|
|
23423
|
+
options.seenTurns,
|
|
23424
|
+
result.session_id,
|
|
23425
|
+
expanded
|
|
23426
|
+
);
|
|
23427
|
+
}
|
|
23428
|
+
}
|
|
23429
|
+
}
|
|
23430
|
+
function appendExpandedEvidence(evidenceItems, seenTurns, sessionId, expanded) {
|
|
23431
|
+
for (const message of expanded) {
|
|
23432
|
+
appendEvidenceItem(evidenceItems, seenTurns, {
|
|
23433
|
+
id: `${sessionId}:${message.turn_index}`,
|
|
23434
|
+
sessionId,
|
|
23435
|
+
turnIndex: message.turn_index,
|
|
23436
|
+
role: message.role,
|
|
23437
|
+
content: message.content
|
|
23438
|
+
});
|
|
23439
|
+
}
|
|
23440
|
+
}
|
|
23441
|
+
function appendEvidenceItem(evidenceItems, seenTurns, item) {
|
|
23442
|
+
if (seenTurns.has(item.id)) {
|
|
23443
|
+
return;
|
|
23444
|
+
}
|
|
23445
|
+
seenTurns.add(item.id);
|
|
23446
|
+
evidenceItems.push(item);
|
|
23447
|
+
}
|
|
23448
|
+
function collectExplicitTurnReferences(query) {
|
|
23449
|
+
const references = /* @__PURE__ */ new Map();
|
|
23450
|
+
const addReference = (value, label) => {
|
|
23451
|
+
const existing = references.get(String(value));
|
|
23452
|
+
references.set(String(value), {
|
|
23453
|
+
number: value,
|
|
23454
|
+
includeDirectTurn: (existing?.includeDirectTurn ?? false) || label === "turn"
|
|
23455
|
+
});
|
|
23456
|
+
};
|
|
23457
|
+
const tokens = tokenizeReferenceQuery(query);
|
|
23458
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
23459
|
+
const label = normalizeReferenceLabel(tokens[index]);
|
|
23460
|
+
if (!label) {
|
|
23461
|
+
continue;
|
|
23462
|
+
}
|
|
23463
|
+
const parsed = parseReferenceNumbers(tokens, index + 1);
|
|
23464
|
+
for (const number of parsed.numbers) {
|
|
23465
|
+
addReference(number, label);
|
|
23466
|
+
}
|
|
23467
|
+
index = Math.max(index, parsed.nextIndex - 1);
|
|
23468
|
+
}
|
|
23469
|
+
return [...references.values()].sort((left, right) => left.number - right.number);
|
|
23470
|
+
}
|
|
23471
|
+
function collectLexicalCues(query) {
|
|
23472
|
+
const cues = /* @__PURE__ */ new Set();
|
|
23473
|
+
for (const match of query.matchAll(/\b[A-Za-z][A-Za-z0-9]{0,12}\d+:\d+\b/g)) {
|
|
23474
|
+
cues.add(match[0]);
|
|
23475
|
+
}
|
|
23476
|
+
for (const match of query.matchAll(/\b\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}(?::\d{2})?Z?)?\b/g)) {
|
|
23477
|
+
cues.add(match[0]);
|
|
23478
|
+
}
|
|
23479
|
+
for (const cue of collectTemporalLexicalCues(query)) {
|
|
23480
|
+
cues.add(cue);
|
|
23481
|
+
}
|
|
23482
|
+
for (const cue of collectQuestionSlotCues(query)) {
|
|
23483
|
+
cues.add(cue);
|
|
23484
|
+
}
|
|
23485
|
+
for (const match of query.matchAll(/\b(?:session|source|chat|plan|task|event|file|tool)[_-][A-Za-z0-9][A-Za-z0-9_.:-]{0,80}\b/gi)) {
|
|
23486
|
+
cues.add(match[0]);
|
|
23487
|
+
}
|
|
23488
|
+
for (const match of query.matchAll(/\b[A-Z][a-z]{1,30}(?:\s+[A-Z][a-z]{1,30}){0,2}\b/g)) {
|
|
23489
|
+
const value = normalizeSpeakerNameCue(match[0]);
|
|
23490
|
+
if (value) {
|
|
23491
|
+
cues.add(value);
|
|
23492
|
+
}
|
|
23493
|
+
}
|
|
23494
|
+
for (const match of query.matchAll(/\[([A-Za-z0-9][A-Za-z0-9_.:/ -]{1,80})\]/g)) {
|
|
23495
|
+
const value = match[1]?.trim();
|
|
23496
|
+
if (value) {
|
|
23497
|
+
cues.add(value);
|
|
23498
|
+
}
|
|
23499
|
+
}
|
|
23500
|
+
return [...cues].sort((left, right) => left.localeCompare(right));
|
|
23501
|
+
}
|
|
23502
|
+
function collectQuestionSlotCues(query) {
|
|
23503
|
+
const cues = /* @__PURE__ */ new Set();
|
|
23504
|
+
for (const match of query.matchAll(
|
|
23505
|
+
/\b(?:what|which)\s+([a-z][a-z0-9_-]{2,30})\s+(?:does|do|did|is|are|was|were|should|would|could|can|will)\b/gi
|
|
23506
|
+
)) {
|
|
23507
|
+
const value = match[1]?.toLowerCase();
|
|
23508
|
+
if (value && !QUESTION_SLOT_STOPWORDS.has(value)) {
|
|
23509
|
+
cues.add(value);
|
|
23510
|
+
}
|
|
23511
|
+
}
|
|
23512
|
+
return [...cues].sort((left, right) => left.localeCompare(right));
|
|
23513
|
+
}
|
|
23514
|
+
function collectTemporalLexicalCues(query) {
|
|
23515
|
+
const cues = /* @__PURE__ */ new Set();
|
|
23516
|
+
const normalizedQuery = query.toLowerCase().replace(/\s+/g, " ");
|
|
23517
|
+
for (const cue of RELATIVE_TEMPORAL_CUES) {
|
|
23518
|
+
let searchFrom = 0;
|
|
23519
|
+
while (searchFrom < normalizedQuery.length) {
|
|
23520
|
+
const index = normalizedQuery.indexOf(cue, searchFrom);
|
|
23521
|
+
if (index < 0) {
|
|
23522
|
+
break;
|
|
23523
|
+
}
|
|
23524
|
+
const afterIndex = index + cue.length;
|
|
23525
|
+
if (isTemporalCueBoundary(normalizedQuery[index - 1]) && isTemporalCueBoundary(normalizedQuery[afterIndex])) {
|
|
23526
|
+
cues.add(cue);
|
|
23527
|
+
break;
|
|
23528
|
+
}
|
|
23529
|
+
searchFrom = afterIndex;
|
|
23530
|
+
}
|
|
23531
|
+
}
|
|
23532
|
+
return [...cues].sort((left, right) => left.localeCompare(right));
|
|
23533
|
+
}
|
|
23534
|
+
function hasLatestStateIntent(query) {
|
|
23535
|
+
return collectTemporalLexicalCues(query).some(
|
|
23536
|
+
(cue) => LATEST_STATE_CUES.has(cue)
|
|
23537
|
+
);
|
|
23538
|
+
}
|
|
23539
|
+
function sortLexicalCueResults(results, preferLatest) {
|
|
23540
|
+
return [...results].sort((left, right) => {
|
|
23541
|
+
if (preferLatest) {
|
|
23542
|
+
const sessionOrder2 = left.session_id.localeCompare(right.session_id);
|
|
23543
|
+
if (sessionOrder2 !== 0) {
|
|
23544
|
+
return sessionOrder2;
|
|
23545
|
+
}
|
|
23546
|
+
const turnOrder = right.turn_index - left.turn_index;
|
|
23547
|
+
if (turnOrder !== 0) {
|
|
23548
|
+
return turnOrder;
|
|
23549
|
+
}
|
|
23550
|
+
return (right.score ?? 0) - (left.score ?? 0);
|
|
23551
|
+
}
|
|
23552
|
+
const scoreDelta = (right.score ?? 0) - (left.score ?? 0);
|
|
23553
|
+
if (scoreDelta !== 0) {
|
|
23554
|
+
return scoreDelta;
|
|
23555
|
+
}
|
|
23556
|
+
const sessionOrder = left.session_id.localeCompare(right.session_id);
|
|
23557
|
+
if (sessionOrder !== 0) {
|
|
23558
|
+
return sessionOrder;
|
|
23559
|
+
}
|
|
23560
|
+
return left.turn_index - right.turn_index;
|
|
23561
|
+
});
|
|
23562
|
+
}
|
|
23563
|
+
function normalizeSpeakerNameCue(value) {
|
|
23564
|
+
const words = value.trim().split(/\s+/).filter(Boolean);
|
|
23565
|
+
while (words.length > 0 && SPEAKER_NAME_STOPWORDS.has(words[0])) {
|
|
23566
|
+
words.shift();
|
|
23567
|
+
}
|
|
23568
|
+
while (words.length > 0 && SPEAKER_NAME_STOPWORDS.has(words[words.length - 1])) {
|
|
23569
|
+
words.pop();
|
|
23570
|
+
}
|
|
23571
|
+
return words.length > 0 ? words.join(" ") : void 0;
|
|
23572
|
+
}
|
|
23573
|
+
function isTemporalCueBoundary(char) {
|
|
23574
|
+
if (!char) {
|
|
23575
|
+
return true;
|
|
23576
|
+
}
|
|
23577
|
+
return !isAsciiLetterOrDigit(char);
|
|
23578
|
+
}
|
|
23579
|
+
function tokenizeReferenceQuery(query) {
|
|
23580
|
+
const tokens = [];
|
|
23581
|
+
let current = "";
|
|
23582
|
+
const flushCurrent = () => {
|
|
23583
|
+
if (current) {
|
|
23584
|
+
tokens.push(current);
|
|
23585
|
+
current = "";
|
|
23586
|
+
}
|
|
23587
|
+
};
|
|
23588
|
+
for (const char of query) {
|
|
23589
|
+
if (isAsciiLetterOrDigit(char)) {
|
|
23590
|
+
current += char;
|
|
23591
|
+
continue;
|
|
23592
|
+
}
|
|
23593
|
+
flushCurrent();
|
|
23594
|
+
if (char === "#" || char === ",") {
|
|
23595
|
+
tokens.push(char);
|
|
23596
|
+
} else if (isReferenceDash(char)) {
|
|
23597
|
+
tokens.push("-");
|
|
23598
|
+
}
|
|
23599
|
+
}
|
|
23600
|
+
flushCurrent();
|
|
23601
|
+
return tokens;
|
|
23602
|
+
}
|
|
23603
|
+
function parseReferenceNumbers(tokens, startIndex) {
|
|
23604
|
+
const numbers = [];
|
|
23605
|
+
let lastNumber;
|
|
23606
|
+
let pendingRangeStart;
|
|
23607
|
+
let index = startIndex;
|
|
23608
|
+
const scanEnd = Math.min(
|
|
23609
|
+
tokens.length,
|
|
23610
|
+
startIndex + DEFAULT_MAX_REFERENCES * REFERENCE_SCAN_TOKEN_FACTOR
|
|
23611
|
+
);
|
|
23612
|
+
for (; index < scanEnd; index += 1) {
|
|
23613
|
+
const token = tokens[index];
|
|
23614
|
+
const normalized = token.toLowerCase();
|
|
23615
|
+
const value = parseNonNegativeIntegerToken(token);
|
|
23616
|
+
if (value !== void 0) {
|
|
23617
|
+
if (pendingRangeStart !== void 0) {
|
|
23618
|
+
numbers.push(...expandReferenceRange(pendingRangeStart, value));
|
|
23619
|
+
pendingRangeStart = void 0;
|
|
23620
|
+
} else {
|
|
23621
|
+
numbers.push(value);
|
|
23622
|
+
}
|
|
23623
|
+
lastNumber = value;
|
|
23624
|
+
continue;
|
|
23625
|
+
}
|
|
23626
|
+
if (normalized === "#" || normalized === "number" || normalized === ",") {
|
|
23627
|
+
continue;
|
|
23628
|
+
}
|
|
23629
|
+
if (normalized === "-" || normalized === "to" || normalized === "through" || normalized === "thru") {
|
|
23630
|
+
if (lastNumber !== void 0) {
|
|
23631
|
+
if (numbers[numbers.length - 1] === lastNumber) {
|
|
23632
|
+
numbers.pop();
|
|
23633
|
+
}
|
|
23634
|
+
pendingRangeStart = lastNumber;
|
|
23635
|
+
}
|
|
23636
|
+
continue;
|
|
23637
|
+
}
|
|
23638
|
+
if (normalized === "and" && numbers.length > 0) {
|
|
23639
|
+
continue;
|
|
23640
|
+
}
|
|
23641
|
+
if (normalizeReferenceLabel(token)) {
|
|
23642
|
+
break;
|
|
23643
|
+
}
|
|
23644
|
+
break;
|
|
23645
|
+
}
|
|
23646
|
+
if (pendingRangeStart !== void 0) {
|
|
23647
|
+
numbers.push(pendingRangeStart);
|
|
23648
|
+
}
|
|
23649
|
+
return {
|
|
23650
|
+
numbers: [...new Set(numbers)],
|
|
23651
|
+
nextIndex: index
|
|
23652
|
+
};
|
|
23653
|
+
}
|
|
23654
|
+
function expandReferenceRange(start, end) {
|
|
23655
|
+
const low = Math.min(start, end);
|
|
23656
|
+
const high = Math.max(start, end);
|
|
23657
|
+
if (high - low + 1 > DEFAULT_MAX_REFERENCES) {
|
|
23658
|
+
return [start, end];
|
|
23659
|
+
}
|
|
23660
|
+
const values = [];
|
|
23661
|
+
for (let value = low; value <= high; value += 1) {
|
|
23662
|
+
values.push(value);
|
|
23663
|
+
}
|
|
23664
|
+
return values;
|
|
23665
|
+
}
|
|
23666
|
+
function normalizeReferenceLabel(token) {
|
|
23667
|
+
const normalized = token?.toLowerCase();
|
|
23668
|
+
switch (normalized) {
|
|
23669
|
+
case "step":
|
|
23670
|
+
case "steps":
|
|
23671
|
+
return "step";
|
|
23672
|
+
case "turn":
|
|
23673
|
+
case "turns":
|
|
23674
|
+
return "turn";
|
|
23675
|
+
case "action":
|
|
23676
|
+
case "actions":
|
|
23677
|
+
return "action";
|
|
23678
|
+
case "observation":
|
|
23679
|
+
case "observations":
|
|
23680
|
+
return "observation";
|
|
23681
|
+
default:
|
|
23682
|
+
return void 0;
|
|
23683
|
+
}
|
|
23684
|
+
}
|
|
23685
|
+
function candidateTurnIndexesForReference(reference) {
|
|
23686
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
23687
|
+
if (reference.includeDirectTurn) {
|
|
23688
|
+
for (let offset = -1; offset <= 1; offset += 1) {
|
|
23689
|
+
candidates.add(reference.number + offset);
|
|
23690
|
+
}
|
|
23691
|
+
}
|
|
23692
|
+
const pairedBase = reference.number * 2;
|
|
23693
|
+
for (let offset = -2; offset <= 3; offset += 1) {
|
|
23694
|
+
candidates.add(pairedBase + offset);
|
|
23695
|
+
}
|
|
23696
|
+
return [...candidates].sort((left, right) => left - right);
|
|
23697
|
+
}
|
|
23698
|
+
function parseNonNegativeIntegerToken(token) {
|
|
23699
|
+
if (token.length === 0) {
|
|
23700
|
+
return void 0;
|
|
23701
|
+
}
|
|
23702
|
+
let value = 0;
|
|
23703
|
+
for (const char of token) {
|
|
23704
|
+
const code = char.charCodeAt(0);
|
|
23705
|
+
if (code < 48 || code > 57) {
|
|
23706
|
+
return void 0;
|
|
23707
|
+
}
|
|
23708
|
+
value = value * 10 + (code - 48);
|
|
23709
|
+
}
|
|
23710
|
+
return value;
|
|
23711
|
+
}
|
|
23712
|
+
function normalizePositiveInteger2(value, fallback) {
|
|
23713
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
23714
|
+
return fallback;
|
|
23715
|
+
}
|
|
23716
|
+
return Math.max(0, Math.floor(value));
|
|
23717
|
+
}
|
|
23718
|
+
function isAsciiLetterOrDigit(char) {
|
|
23719
|
+
const code = char.charCodeAt(0);
|
|
23720
|
+
return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122;
|
|
23721
|
+
}
|
|
23722
|
+
function isReferenceDash(char) {
|
|
23723
|
+
return char === "-" || char === "\u2010" || char === "\u2011" || char === "\u2012" || char === "\u2013" || char === "\u2014" || char === "\u2015";
|
|
23724
|
+
}
|
|
23725
|
+
|
|
23086
23726
|
// ../remnic-core/src/recall-query-policy.ts
|
|
23087
23727
|
var DEFAULT_STOPWORDS = /* @__PURE__ */ new Set([
|
|
23088
23728
|
"the",
|
|
@@ -26180,7 +26820,7 @@ function validateReplayTurn(turn, index) {
|
|
|
26180
26820
|
// ../remnic-core/src/lcm/schema.ts
|
|
26181
26821
|
import path37 from "path";
|
|
26182
26822
|
import { mkdir as mkdir26 } from "fs/promises";
|
|
26183
|
-
var LCM_SCHEMA_VERSION =
|
|
26823
|
+
var LCM_SCHEMA_VERSION = 2;
|
|
26184
26824
|
function openLcmDatabase(memoryDir) {
|
|
26185
26825
|
const dbPath = path37.join(memoryDir, "state", "lcm.sqlite");
|
|
26186
26826
|
const db = openBetterSqlite3(dbPath);
|
|
@@ -26226,6 +26866,23 @@ function createTables(db) {
|
|
|
26226
26866
|
CREATE INDEX IF NOT EXISTS idx_lcm_messages_session
|
|
26227
26867
|
ON lcm_messages(session_id, turn_index);
|
|
26228
26868
|
|
|
26869
|
+
CREATE TABLE IF NOT EXISTS lcm_message_parts (
|
|
26870
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
26871
|
+
message_id INTEGER NOT NULL REFERENCES lcm_messages(id) ON DELETE CASCADE,
|
|
26872
|
+
ordinal INTEGER NOT NULL,
|
|
26873
|
+
kind TEXT NOT NULL,
|
|
26874
|
+
payload TEXT NOT NULL,
|
|
26875
|
+
tool_name TEXT,
|
|
26876
|
+
file_path TEXT,
|
|
26877
|
+
created_at TEXT NOT NULL
|
|
26878
|
+
);
|
|
26879
|
+
CREATE INDEX IF NOT EXISTS idx_lcm_message_parts_msg
|
|
26880
|
+
ON lcm_message_parts(message_id, ordinal);
|
|
26881
|
+
CREATE INDEX IF NOT EXISTS idx_lcm_message_parts_tool
|
|
26882
|
+
ON lcm_message_parts(tool_name);
|
|
26883
|
+
CREATE INDEX IF NOT EXISTS idx_lcm_message_parts_file
|
|
26884
|
+
ON lcm_message_parts(file_path);
|
|
26885
|
+
|
|
26229
26886
|
CREATE TABLE IF NOT EXISTS lcm_summary_nodes (
|
|
26230
26887
|
id TEXT PRIMARY KEY,
|
|
26231
26888
|
session_id TEXT NOT NULL,
|
|
@@ -26278,6 +26935,423 @@ function createTables(db) {
|
|
|
26278
26935
|
);
|
|
26279
26936
|
}
|
|
26280
26937
|
|
|
26938
|
+
// ../remnic-core/src/message-parts/index.ts
|
|
26939
|
+
var LCM_MESSAGE_PART_KINDS = [
|
|
26940
|
+
"text",
|
|
26941
|
+
"tool_call",
|
|
26942
|
+
"tool_result",
|
|
26943
|
+
"patch",
|
|
26944
|
+
"file_read",
|
|
26945
|
+
"file_write",
|
|
26946
|
+
"step_start",
|
|
26947
|
+
"step_finish",
|
|
26948
|
+
"snapshot",
|
|
26949
|
+
"retry"
|
|
26950
|
+
];
|
|
26951
|
+
var SECRET_KEY_RE = /(api[_-]?key|authorization|bearer|credential|password|secret|token)/i;
|
|
26952
|
+
var MAX_PAYLOAD_STRING = 8e3;
|
|
26953
|
+
var MAX_FILE_SCAN_CHARS = 2e4;
|
|
26954
|
+
function isLcmMessagePartKind(value) {
|
|
26955
|
+
return typeof value === "string" && LCM_MESSAGE_PART_KINDS.includes(value);
|
|
26956
|
+
}
|
|
26957
|
+
function parseMessageParts(input, options = {}) {
|
|
26958
|
+
const explicit = normalizeExplicitParts(input);
|
|
26959
|
+
if (explicit.length > 0) return explicit;
|
|
26960
|
+
const format = options.sourceFormat ?? inferSourceFormat(input);
|
|
26961
|
+
switch (format) {
|
|
26962
|
+
case "openai":
|
|
26963
|
+
return withRenderedFallback(parseOpenAiMessageParts(input, options), options);
|
|
26964
|
+
case "anthropic":
|
|
26965
|
+
return withRenderedFallback(parseAnthropicMessageParts(input, options), options);
|
|
26966
|
+
case "openclaw":
|
|
26967
|
+
return withRenderedFallback(parseOpenClawMessageParts(input, options), options);
|
|
26968
|
+
case "lossless-claw":
|
|
26969
|
+
case "remnic":
|
|
26970
|
+
return withRenderedFallback(normalizeExplicitParts(input), options);
|
|
26971
|
+
default:
|
|
26972
|
+
return renderedFallbackParts(options);
|
|
26973
|
+
}
|
|
26974
|
+
}
|
|
26975
|
+
function normalizeExplicitParts(input) {
|
|
26976
|
+
const rawParts = pickArray(input, "parts") ?? pickArray(input, "message_parts");
|
|
26977
|
+
if (!rawParts) return [];
|
|
26978
|
+
const parts = [];
|
|
26979
|
+
rawParts.forEach((raw, index) => {
|
|
26980
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
|
|
26981
|
+
const obj = raw;
|
|
26982
|
+
const kind = normalizeKind(obj.kind ?? obj.type);
|
|
26983
|
+
if (!kind) return;
|
|
26984
|
+
const payload = obj.payload && typeof obj.payload === "object" && !Array.isArray(obj.payload) ? obj.payload : { value: sanitizePayload(obj) };
|
|
26985
|
+
const toolName = asNonEmptyString(obj.toolName ?? obj.tool_name ?? obj.name);
|
|
26986
|
+
const filePath = asNonEmptyString(obj.filePath ?? obj.file_path ?? obj.path);
|
|
26987
|
+
const ordinal = typeof obj.ordinal === "number" && Number.isInteger(obj.ordinal) ? Math.max(0, obj.ordinal) : index;
|
|
26988
|
+
parts.push({
|
|
26989
|
+
ordinal,
|
|
26990
|
+
kind,
|
|
26991
|
+
payload: sanitizePayload(payload),
|
|
26992
|
+
toolName,
|
|
26993
|
+
filePath,
|
|
26994
|
+
createdAt: asNonEmptyString(obj.createdAt ?? obj.created_at)
|
|
26995
|
+
});
|
|
26996
|
+
});
|
|
26997
|
+
return parts;
|
|
26998
|
+
}
|
|
26999
|
+
function parseOpenAiMessageParts(input, _options = {}) {
|
|
27000
|
+
const items = gatherOpenAiItems(input);
|
|
27001
|
+
const parts = [];
|
|
27002
|
+
for (const item of items) {
|
|
27003
|
+
const type = asNonEmptyString(item.type) ?? asNonEmptyString(item.kind);
|
|
27004
|
+
if (!type) continue;
|
|
27005
|
+
if (isOpenAiContentBlock(item)) {
|
|
27006
|
+
const text = asNonEmptyString(item.text ?? item.content);
|
|
27007
|
+
if (text) parts.push(makePart("text", { type, text }, { filePath: firstFilePath(text) }));
|
|
27008
|
+
continue;
|
|
27009
|
+
}
|
|
27010
|
+
if (type === "message") {
|
|
27011
|
+
for (const block of gatherContentBlocks(item.content)) {
|
|
27012
|
+
const text = asNonEmptyString(block.text ?? block.content);
|
|
27013
|
+
if (text) parts.push(makePart("text", { type, text }, { filePath: firstFilePath(text) }));
|
|
27014
|
+
}
|
|
27015
|
+
continue;
|
|
27016
|
+
}
|
|
27017
|
+
if (type === "function_call") {
|
|
27018
|
+
const toolName = asNonEmptyString(item.name ?? item.tool_name);
|
|
27019
|
+
const payload = {
|
|
27020
|
+
id: item.id ?? item.call_id,
|
|
27021
|
+
name: toolName,
|
|
27022
|
+
arguments: parseMaybeJson(item.arguments)
|
|
27023
|
+
};
|
|
27024
|
+
parts.push(classifyToolPart(toolName, payload));
|
|
27025
|
+
continue;
|
|
27026
|
+
}
|
|
27027
|
+
if (type === "function_call_output") {
|
|
27028
|
+
const output = asNonEmptyString(item.output) ?? JSON.stringify(sanitizePayload(item.output ?? item));
|
|
27029
|
+
parts.push(makePart("tool_result", { id: item.id ?? item.call_id, output }, {
|
|
27030
|
+
filePath: firstFilePath(output)
|
|
27031
|
+
}));
|
|
27032
|
+
continue;
|
|
27033
|
+
}
|
|
27034
|
+
if (type === "reasoning") {
|
|
27035
|
+
parts.push(makePart("step_start", { type, summary: sanitizePayload(item.summary ?? item) }));
|
|
27036
|
+
continue;
|
|
27037
|
+
}
|
|
27038
|
+
if (type === "retry") {
|
|
27039
|
+
parts.push(makePart("retry", { type, item: sanitizePayload(item) }));
|
|
27040
|
+
}
|
|
27041
|
+
}
|
|
27042
|
+
return withOrdinals(parts);
|
|
27043
|
+
}
|
|
27044
|
+
function parseAnthropicMessageParts(input, _options = {}) {
|
|
27045
|
+
const blocks = gatherContentBlocks(
|
|
27046
|
+
Array.isArray(input) ? input : input && typeof input === "object" ? input.content : input
|
|
27047
|
+
);
|
|
27048
|
+
const parts = [];
|
|
27049
|
+
for (const block of blocks) {
|
|
27050
|
+
const type = asNonEmptyString(block.type);
|
|
27051
|
+
if (type === "text") {
|
|
27052
|
+
const text = asNonEmptyString(block.text);
|
|
27053
|
+
if (text) parts.push(makePart("text", { type, text }, { filePath: firstFilePath(text) }));
|
|
27054
|
+
continue;
|
|
27055
|
+
}
|
|
27056
|
+
if (type === "tool_use") {
|
|
27057
|
+
const toolName = asNonEmptyString(block.name);
|
|
27058
|
+
parts.push(classifyToolPart(toolName, {
|
|
27059
|
+
id: block.id,
|
|
27060
|
+
name: toolName,
|
|
27061
|
+
input: sanitizePayload(block.input)
|
|
27062
|
+
}));
|
|
27063
|
+
continue;
|
|
27064
|
+
}
|
|
27065
|
+
if (type === "tool_result") {
|
|
27066
|
+
const content = block.content;
|
|
27067
|
+
const rendered = renderUnknownContent(content);
|
|
27068
|
+
parts.push(makePart("tool_result", { id: block.tool_use_id, content: sanitizePayload(content) }, {
|
|
27069
|
+
filePath: firstFilePath(rendered)
|
|
27070
|
+
}));
|
|
27071
|
+
continue;
|
|
27072
|
+
}
|
|
27073
|
+
if (type === "thinking") {
|
|
27074
|
+
parts.push(makePart("step_start", {
|
|
27075
|
+
type,
|
|
27076
|
+
thinking: truncateString(asNonEmptyString(block.thinking) ?? ""),
|
|
27077
|
+
signature: asNonEmptyString(block.signature)
|
|
27078
|
+
}));
|
|
27079
|
+
continue;
|
|
27080
|
+
}
|
|
27081
|
+
if (type === "redacted_thinking") {
|
|
27082
|
+
parts.push(makePart("step_finish", { type }));
|
|
27083
|
+
}
|
|
27084
|
+
}
|
|
27085
|
+
return withOrdinals(parts);
|
|
27086
|
+
}
|
|
27087
|
+
function parseOpenClawMessageParts(input, options = {}) {
|
|
27088
|
+
const explicit = normalizeExplicitParts(input);
|
|
27089
|
+
if (explicit.length > 0) return explicit;
|
|
27090
|
+
if (!input || typeof input !== "object") return [];
|
|
27091
|
+
const obj = input;
|
|
27092
|
+
const content = obj.content;
|
|
27093
|
+
if (Array.isArray(content)) {
|
|
27094
|
+
const hasOpenAiBlocks = content.some(isOpenAiContentBlock);
|
|
27095
|
+
if (hasOpenAiBlocks) return parseOpenAiMessageParts(content, options);
|
|
27096
|
+
const hasAnthropicBlocks = content.some(
|
|
27097
|
+
(block) => block && typeof block === "object" && typeof block.type === "string"
|
|
27098
|
+
);
|
|
27099
|
+
if (hasAnthropicBlocks) return parseAnthropicMessageParts({ content }, options);
|
|
27100
|
+
}
|
|
27101
|
+
const toolName = asNonEmptyString(obj.toolName ?? obj.tool_name ?? obj.name);
|
|
27102
|
+
if (toolName) {
|
|
27103
|
+
return withOrdinals([
|
|
27104
|
+
classifyToolPart(toolName, {
|
|
27105
|
+
name: toolName,
|
|
27106
|
+
input: sanitizePayload(obj.input ?? obj.arguments ?? obj.params),
|
|
27107
|
+
output: sanitizePayload(obj.output ?? obj.result)
|
|
27108
|
+
})
|
|
27109
|
+
]);
|
|
27110
|
+
}
|
|
27111
|
+
const rendered = options.renderedContent ?? asNonEmptyString(obj.content);
|
|
27112
|
+
return rendered ? withOrdinals(partsFromRenderedText(rendered)) : [];
|
|
27113
|
+
}
|
|
27114
|
+
function partsFromRenderedText(text) {
|
|
27115
|
+
if (text.includes("*** Begin Patch")) {
|
|
27116
|
+
const paths2 = extractFilePaths(text);
|
|
27117
|
+
const patchPaths = extractPatchPaths(text);
|
|
27118
|
+
return withOrdinals((patchPaths.length > 0 ? patchPaths : paths2).map(
|
|
27119
|
+
(filePath) => makePart("patch", { text: truncateString(text) }, { filePath })
|
|
27120
|
+
));
|
|
27121
|
+
}
|
|
27122
|
+
const paths = extractFilePaths(text);
|
|
27123
|
+
if (paths.length === 0) return [];
|
|
27124
|
+
return withOrdinals(paths.map(
|
|
27125
|
+
(filePath) => makePart("file_read", { text: truncateString(text) }, { filePath })
|
|
27126
|
+
));
|
|
27127
|
+
}
|
|
27128
|
+
function inferSourceFormat(input) {
|
|
27129
|
+
if (input && typeof input === "object") {
|
|
27130
|
+
const obj = input;
|
|
27131
|
+
const explicit = asNonEmptyString(obj.sourceFormat ?? obj.source_format);
|
|
27132
|
+
if (explicit === "openai" || explicit === "anthropic" || explicit === "openclaw" || explicit === "lossless-claw" || explicit === "remnic") {
|
|
27133
|
+
return explicit;
|
|
27134
|
+
}
|
|
27135
|
+
if (Array.isArray(obj.output)) return "openai";
|
|
27136
|
+
if (isOpenAiResponseItem(obj)) return "openai";
|
|
27137
|
+
if (Array.isArray(obj.content) && obj.content.some(isOpenAiContentBlock)) return "openai";
|
|
27138
|
+
if (Array.isArray(obj.content)) return "anthropic";
|
|
27139
|
+
}
|
|
27140
|
+
if (Array.isArray(input)) {
|
|
27141
|
+
return input.some(
|
|
27142
|
+
(item) => isRecord3(item) && (isOpenAiResponseItem(item) || isOpenAiContentBlock(item))
|
|
27143
|
+
) ? "openai" : "anthropic";
|
|
27144
|
+
}
|
|
27145
|
+
return void 0;
|
|
27146
|
+
}
|
|
27147
|
+
function isOpenAiResponseItem(obj) {
|
|
27148
|
+
const type = asNonEmptyString(obj.type ?? obj.kind);
|
|
27149
|
+
return type === "message" || type === "function_call" || type === "function_call_output" || type === "reasoning" || type === "retry";
|
|
27150
|
+
}
|
|
27151
|
+
function isOpenAiContentBlock(value) {
|
|
27152
|
+
if (!isRecord3(value)) return false;
|
|
27153
|
+
const type = asNonEmptyString(value.type);
|
|
27154
|
+
return type === "input_text" || type === "output_text" || type === "input_image" || type === "input_file" || type === "refusal";
|
|
27155
|
+
}
|
|
27156
|
+
function gatherOpenAiItems(input) {
|
|
27157
|
+
if (Array.isArray(input)) return input.filter(isRecord3);
|
|
27158
|
+
if (!isRecord3(input)) return [];
|
|
27159
|
+
if (Array.isArray(input.output)) return input.output.filter(isRecord3);
|
|
27160
|
+
if (Array.isArray(input.items)) return input.items.filter(isRecord3);
|
|
27161
|
+
return [input];
|
|
27162
|
+
}
|
|
27163
|
+
function gatherContentBlocks(input) {
|
|
27164
|
+
if (Array.isArray(input)) return input.filter(isRecord3);
|
|
27165
|
+
if (typeof input === "string") return [{ type: "text", text: input }];
|
|
27166
|
+
if (isRecord3(input)) return [input];
|
|
27167
|
+
return [];
|
|
27168
|
+
}
|
|
27169
|
+
function classifyToolPart(toolName, payload) {
|
|
27170
|
+
const normalized = (toolName ?? "").toLowerCase();
|
|
27171
|
+
const rendered = renderUnknownContent(payload);
|
|
27172
|
+
const filePath = firstFilePathFromObject(payload) ?? firstFilePath(rendered) ?? null;
|
|
27173
|
+
if (normalized.includes("apply_patch") || rendered.includes("*** Begin Patch")) {
|
|
27174
|
+
return makePart("patch", payload, { toolName, filePath: filePath ?? extractPatchPaths(rendered)[0] ?? null });
|
|
27175
|
+
}
|
|
27176
|
+
if (/(write|edit|multiedit|create|save)/i.test(normalized)) {
|
|
27177
|
+
return makePart("file_write", payload, { toolName, filePath });
|
|
27178
|
+
}
|
|
27179
|
+
if (/(read|grep|glob|search|list|ls)/i.test(normalized)) {
|
|
27180
|
+
return makePart("file_read", payload, { toolName, filePath });
|
|
27181
|
+
}
|
|
27182
|
+
return makePart("tool_call", payload, { toolName, filePath });
|
|
27183
|
+
}
|
|
27184
|
+
function makePart(kind, payload, options = {}) {
|
|
27185
|
+
return {
|
|
27186
|
+
kind,
|
|
27187
|
+
payload: sanitizePayload(payload),
|
|
27188
|
+
toolName: options.toolName ?? null,
|
|
27189
|
+
filePath: options.filePath ?? null
|
|
27190
|
+
};
|
|
27191
|
+
}
|
|
27192
|
+
function withOrdinals(parts) {
|
|
27193
|
+
return parts.map((part, ordinal) => ({ ...part, ordinal: part.ordinal ?? ordinal }));
|
|
27194
|
+
}
|
|
27195
|
+
function withRenderedFallback(parts, options) {
|
|
27196
|
+
return parts.length > 0 ? parts : renderedFallbackParts(options);
|
|
27197
|
+
}
|
|
27198
|
+
function renderedFallbackParts(options) {
|
|
27199
|
+
const rendered = asNonEmptyString(options.renderedContent);
|
|
27200
|
+
return rendered ? partsFromRenderedText(rendered) : [];
|
|
27201
|
+
}
|
|
27202
|
+
function normalizeKind(value) {
|
|
27203
|
+
if (isLcmMessagePartKind(value)) return value;
|
|
27204
|
+
if (value === "tool_use" || value === "function_call") return "tool_call";
|
|
27205
|
+
if (value === "function_call_output") return "tool_result";
|
|
27206
|
+
if (value === "thinking" || value === "reasoning") return "step_start";
|
|
27207
|
+
return null;
|
|
27208
|
+
}
|
|
27209
|
+
function pickArray(input, key) {
|
|
27210
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return null;
|
|
27211
|
+
const value = input[key];
|
|
27212
|
+
return Array.isArray(value) ? value : null;
|
|
27213
|
+
}
|
|
27214
|
+
function asNonEmptyString(value) {
|
|
27215
|
+
if (typeof value !== "string") return null;
|
|
27216
|
+
const trimmed = value.trim();
|
|
27217
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
27218
|
+
}
|
|
27219
|
+
function isRecord3(value) {
|
|
27220
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
27221
|
+
}
|
|
27222
|
+
function parseMaybeJson(value) {
|
|
27223
|
+
if (typeof value !== "string") return sanitizePayload(value);
|
|
27224
|
+
try {
|
|
27225
|
+
return sanitizePayload(JSON.parse(value));
|
|
27226
|
+
} catch {
|
|
27227
|
+
return truncateString(value);
|
|
27228
|
+
}
|
|
27229
|
+
}
|
|
27230
|
+
function sanitizePayload(value, depth = 0) {
|
|
27231
|
+
if (value === null || value === void 0) return value;
|
|
27232
|
+
if (typeof value === "string") return truncateString(value);
|
|
27233
|
+
if (typeof value === "number" || typeof value === "boolean") return value;
|
|
27234
|
+
if (Array.isArray(value)) {
|
|
27235
|
+
if (depth >= 4) return "[truncated]";
|
|
27236
|
+
return value.slice(0, 100).map((item) => sanitizePayload(item, depth + 1));
|
|
27237
|
+
}
|
|
27238
|
+
if (typeof value === "object") {
|
|
27239
|
+
if (depth >= 4) return "[truncated]";
|
|
27240
|
+
const out = {};
|
|
27241
|
+
for (const [key, child] of Object.entries(value)) {
|
|
27242
|
+
out[key] = SECRET_KEY_RE.test(key) ? "[redacted]" : sanitizePayload(child, depth + 1);
|
|
27243
|
+
}
|
|
27244
|
+
return out;
|
|
27245
|
+
}
|
|
27246
|
+
return String(value);
|
|
27247
|
+
}
|
|
27248
|
+
function truncateString(value) {
|
|
27249
|
+
return value.length > MAX_PAYLOAD_STRING ? `${value.slice(0, MAX_PAYLOAD_STRING)}...[truncated]` : value;
|
|
27250
|
+
}
|
|
27251
|
+
function renderUnknownContent(value) {
|
|
27252
|
+
if (typeof value === "string") return value;
|
|
27253
|
+
try {
|
|
27254
|
+
return JSON.stringify(value ?? "");
|
|
27255
|
+
} catch {
|
|
27256
|
+
return String(value ?? "");
|
|
27257
|
+
}
|
|
27258
|
+
}
|
|
27259
|
+
function firstFilePathFromObject(value) {
|
|
27260
|
+
if (!isRecord3(value)) return null;
|
|
27261
|
+
const keys = ["file_path", "filePath", "path", "filename", "cwd"];
|
|
27262
|
+
for (const key of keys) {
|
|
27263
|
+
const candidate = asNonEmptyString(value[key]);
|
|
27264
|
+
if (candidate) return candidate;
|
|
27265
|
+
}
|
|
27266
|
+
for (const child of Object.values(value)) {
|
|
27267
|
+
if (typeof child === "string") {
|
|
27268
|
+
const fromText = extractPatchPaths(child)[0] ?? firstFilePath(child);
|
|
27269
|
+
if (fromText) return fromText;
|
|
27270
|
+
}
|
|
27271
|
+
if (isRecord3(child)) {
|
|
27272
|
+
const nested = firstFilePathFromObject(child);
|
|
27273
|
+
if (nested) return nested;
|
|
27274
|
+
}
|
|
27275
|
+
}
|
|
27276
|
+
return null;
|
|
27277
|
+
}
|
|
27278
|
+
function firstFilePath(text) {
|
|
27279
|
+
return extractFilePaths(text)[0] ?? null;
|
|
27280
|
+
}
|
|
27281
|
+
function extractFilePaths(text) {
|
|
27282
|
+
const out = /* @__PURE__ */ new Set();
|
|
27283
|
+
let token = "";
|
|
27284
|
+
const scanLength = Math.min(text.length, MAX_FILE_SCAN_CHARS);
|
|
27285
|
+
for (let index = 0; index <= scanLength; index += 1) {
|
|
27286
|
+
const char = index < scanLength ? text[index] : " ";
|
|
27287
|
+
if (isFilePathTokenSeparator(char)) {
|
|
27288
|
+
addFilePathCandidate(out, token);
|
|
27289
|
+
token = "";
|
|
27290
|
+
continue;
|
|
27291
|
+
}
|
|
27292
|
+
token += char;
|
|
27293
|
+
if (token.length > 512) {
|
|
27294
|
+
addFilePathCandidate(out, token);
|
|
27295
|
+
token = "";
|
|
27296
|
+
}
|
|
27297
|
+
}
|
|
27298
|
+
return [...out].slice(0, 20);
|
|
27299
|
+
}
|
|
27300
|
+
function isFilePathTokenSeparator(char) {
|
|
27301
|
+
return char === " " || char === "\n" || char === "\r" || char === " " || char === '"' || char === "'" || char === "`" || char === "(" || char === ")" || char === "[" || char === "]" || char === "{" || char === "}" || char === "<" || char === ">" || char === ",";
|
|
27302
|
+
}
|
|
27303
|
+
function addFilePathCandidate(out, raw) {
|
|
27304
|
+
const candidate = trimFilePathPunctuation(raw);
|
|
27305
|
+
if (candidate.length === 0 || candidate.includes("://")) return;
|
|
27306
|
+
if (isLikelyFilePath(candidate)) out.add(candidate);
|
|
27307
|
+
}
|
|
27308
|
+
function trimFilePathPunctuation(raw) {
|
|
27309
|
+
let start = 0;
|
|
27310
|
+
let end = raw.length;
|
|
27311
|
+
while (start < end && isLeadingFilePathPunctuation(raw[start])) start += 1;
|
|
27312
|
+
while (end > start && isTrailingFilePathPunctuation(raw[end - 1])) end -= 1;
|
|
27313
|
+
return raw.slice(start, end);
|
|
27314
|
+
}
|
|
27315
|
+
function isLeadingFilePathPunctuation(char) {
|
|
27316
|
+
return char === ":" || char === ";" || char === "!" || char === "?" || char === "|" || char === "*" || char === "=";
|
|
27317
|
+
}
|
|
27318
|
+
function isTrailingFilePathPunctuation(char) {
|
|
27319
|
+
return char === "." || char === ":" || char === ";" || char === "!" || char === "?" || char === "|" || char === "*" || char === "=";
|
|
27320
|
+
}
|
|
27321
|
+
function isLikelyFilePath(value) {
|
|
27322
|
+
if (value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value.startsWith("~/")) {
|
|
27323
|
+
return hasValidFileExtension(value);
|
|
27324
|
+
}
|
|
27325
|
+
if (value.includes("/")) return hasValidFileExtension(value);
|
|
27326
|
+
return hasValidFileExtension(value);
|
|
27327
|
+
}
|
|
27328
|
+
function hasValidFileExtension(value) {
|
|
27329
|
+
const lastSlash = value.lastIndexOf("/");
|
|
27330
|
+
const basename3 = value.slice(lastSlash + 1);
|
|
27331
|
+
const dot = basename3.lastIndexOf(".");
|
|
27332
|
+
if (dot <= 0 || dot === basename3.length - 1) return false;
|
|
27333
|
+
const ext = basename3.slice(dot + 1);
|
|
27334
|
+
if (ext.length < 1 || ext.length > 12) return false;
|
|
27335
|
+
for (const char of ext) {
|
|
27336
|
+
if (!isFileExtensionChar(char)) return false;
|
|
27337
|
+
}
|
|
27338
|
+
return true;
|
|
27339
|
+
}
|
|
27340
|
+
function isFileExtensionChar(char) {
|
|
27341
|
+
const code = char.charCodeAt(0);
|
|
27342
|
+
return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || char === "_" || char === "+" || char === "-";
|
|
27343
|
+
}
|
|
27344
|
+
function extractPatchPaths(text) {
|
|
27345
|
+
const out = /* @__PURE__ */ new Set();
|
|
27346
|
+
for (const line of text.split(/\r?\n/)) {
|
|
27347
|
+
const match = line.match(/^\*\*\* (?:Add|Update|Delete) File: (.+)$/);
|
|
27348
|
+
if (match?.[1]) out.add(match[1].trim());
|
|
27349
|
+
const move = line.match(/^\*\*\* Move to: (.+)$/);
|
|
27350
|
+
if (move?.[1]) out.add(move[1].trim());
|
|
27351
|
+
}
|
|
27352
|
+
return [...out].slice(0, 20);
|
|
27353
|
+
}
|
|
27354
|
+
|
|
26281
27355
|
// ../remnic-core/src/lcm/archive.ts
|
|
26282
27356
|
function estimateTokens3(text) {
|
|
26283
27357
|
return Math.ceil(text.length / 4);
|
|
@@ -26288,7 +27362,7 @@ var LcmArchive = class {
|
|
|
26288
27362
|
}
|
|
26289
27363
|
db;
|
|
26290
27364
|
/** Append a message to the archive. Returns the row id. */
|
|
26291
|
-
appendMessage(sessionId, turnIndex, role, content, metadata) {
|
|
27365
|
+
appendMessage(sessionId, turnIndex, role, content, metadata, parts) {
|
|
26292
27366
|
const tokenCount = estimateTokens3(content);
|
|
26293
27367
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
26294
27368
|
const metaJson = metadata ? JSON.stringify(metadata) : null;
|
|
@@ -26299,18 +27373,58 @@ var LcmArchive = class {
|
|
|
26299
27373
|
const result = stmt.run(sessionId, turnIndex, role, content, tokenCount, now, metaJson);
|
|
26300
27374
|
const rowId = Number(result.lastInsertRowid);
|
|
26301
27375
|
this.db.prepare("INSERT INTO lcm_messages_fts (rowid, content) VALUES (?, ?)").run(rowId, content);
|
|
27376
|
+
if (parts && parts.length > 0) {
|
|
27377
|
+
this.insertMessageParts(rowId, parts, now);
|
|
27378
|
+
}
|
|
26302
27379
|
return rowId;
|
|
26303
27380
|
}
|
|
26304
27381
|
/** Append multiple messages in a single transaction. */
|
|
26305
|
-
appendMessages(sessionId, messages) {
|
|
27382
|
+
appendMessages(sessionId, messages, options = {}) {
|
|
26306
27383
|
if (messages.length === 0) return;
|
|
27384
|
+
const captureMessageParts = options.messagePartsEnabled !== false;
|
|
26307
27385
|
const txn = this.db.transaction(() => {
|
|
26308
27386
|
for (const msg of messages) {
|
|
26309
|
-
|
|
27387
|
+
const explicitParts = msg.parts && msg.parts.length > 0 ? msg.parts : void 0;
|
|
27388
|
+
const rawContent = msg.rawContent ?? msg.content;
|
|
27389
|
+
const parts = captureMessageParts ? explicitParts ?? parseMessageParts(rawContent, {
|
|
27390
|
+
sourceFormat: msg.sourceFormat,
|
|
27391
|
+
renderedContent: msg.content
|
|
27392
|
+
}) : void 0;
|
|
27393
|
+
this.appendMessage(
|
|
27394
|
+
sessionId,
|
|
27395
|
+
msg.turnIndex,
|
|
27396
|
+
msg.role,
|
|
27397
|
+
msg.content,
|
|
27398
|
+
msg.metadata,
|
|
27399
|
+
parts
|
|
27400
|
+
);
|
|
26310
27401
|
}
|
|
26311
27402
|
});
|
|
26312
27403
|
txn();
|
|
26313
27404
|
}
|
|
27405
|
+
insertMessageParts(messageId, parts, fallbackCreatedAt) {
|
|
27406
|
+
if (parts.length === 0) return;
|
|
27407
|
+
const stmt = this.db.prepare(`
|
|
27408
|
+
INSERT INTO lcm_message_parts (message_id, ordinal, kind, payload, tool_name, file_path, created_at)
|
|
27409
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
27410
|
+
`);
|
|
27411
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
27412
|
+
const part = parts[index];
|
|
27413
|
+
const rawPart = part;
|
|
27414
|
+
const toolName = part.toolName ?? asNullableString(rawPart.tool_name);
|
|
27415
|
+
const filePath = part.filePath ?? asNullableString(rawPart.file_path);
|
|
27416
|
+
const createdAt = part.createdAt ?? asNullableString(rawPart.created_at);
|
|
27417
|
+
stmt.run(
|
|
27418
|
+
messageId,
|
|
27419
|
+
part.ordinal ?? index,
|
|
27420
|
+
part.kind,
|
|
27421
|
+
JSON.stringify(part.payload ?? {}),
|
|
27422
|
+
toolName ?? null,
|
|
27423
|
+
filePath ?? null,
|
|
27424
|
+
createdAt ?? fallbackCreatedAt
|
|
27425
|
+
);
|
|
27426
|
+
}
|
|
27427
|
+
}
|
|
26314
27428
|
/** Get the highest turn_index for a session, or -1 if none. */
|
|
26315
27429
|
getMaxTurnIndex(sessionId) {
|
|
26316
27430
|
const row = this.db.prepare("SELECT MAX(turn_index) as max_turn FROM lcm_messages WHERE session_id = ?").get(sessionId);
|
|
@@ -26432,6 +27546,55 @@ var LcmArchive = class {
|
|
|
26432
27546
|
return [];
|
|
26433
27547
|
}
|
|
26434
27548
|
}
|
|
27549
|
+
searchStructuredParts(query, limit, sessionId) {
|
|
27550
|
+
const cappedLimit = Math.max(0, Math.min(20, Math.floor(limit)));
|
|
27551
|
+
if (cappedLimit === 0) return [];
|
|
27552
|
+
const fileTerms = extractStructuredFileTerms(query);
|
|
27553
|
+
const toolTerms = extractStructuredToolTerms(query);
|
|
27554
|
+
if (fileTerms.length === 0 && toolTerms.length === 0) return [];
|
|
27555
|
+
const matchWhere = [];
|
|
27556
|
+
const whereParams = [];
|
|
27557
|
+
for (const term of fileTerms) {
|
|
27558
|
+
matchWhere.push("(p.file_path = ? OR p.file_path LIKE ? ESCAPE '\\')");
|
|
27559
|
+
whereParams.push(term, `%${escapeLike(term)}%`);
|
|
27560
|
+
}
|
|
27561
|
+
for (const term of toolTerms) {
|
|
27562
|
+
matchWhere.push("p.tool_name LIKE ? ESCAPE '\\'");
|
|
27563
|
+
whereParams.push(`%${escapeLike(term)}%`);
|
|
27564
|
+
}
|
|
27565
|
+
const where = [`(${matchWhere.join(" OR ")})`];
|
|
27566
|
+
if (sessionId) {
|
|
27567
|
+
where.push("m.session_id = ?");
|
|
27568
|
+
whereParams.push(sessionId);
|
|
27569
|
+
}
|
|
27570
|
+
const exactFileScoreParams = [...fileTerms];
|
|
27571
|
+
const sqlParams = [...exactFileScoreParams, ...whereParams, cappedLimit];
|
|
27572
|
+
const rows = this.db.prepare(`
|
|
27573
|
+
SELECT
|
|
27574
|
+
p.id AS part_id,
|
|
27575
|
+
p.message_id AS message_id,
|
|
27576
|
+
m.turn_index AS turn_index,
|
|
27577
|
+
m.role AS role,
|
|
27578
|
+
m.content AS content,
|
|
27579
|
+
m.session_id AS session_id,
|
|
27580
|
+
p.kind AS kind,
|
|
27581
|
+
p.tool_name AS tool_name,
|
|
27582
|
+
p.file_path AS file_path,
|
|
27583
|
+
p.payload AS payload,
|
|
27584
|
+
CASE
|
|
27585
|
+
WHEN p.file_path IN (${fileTerms.map(() => "?").join(",") || "NULL"}) THEN 3
|
|
27586
|
+
WHEN p.file_path IS NOT NULL THEN 2
|
|
27587
|
+
WHEN p.tool_name IS NOT NULL THEN 1
|
|
27588
|
+
ELSE 0
|
|
27589
|
+
END AS score
|
|
27590
|
+
FROM lcm_message_parts p
|
|
27591
|
+
JOIN lcm_messages m ON m.id = p.message_id
|
|
27592
|
+
WHERE ${where.join(" AND ")}
|
|
27593
|
+
ORDER BY score DESC, m.turn_index DESC, p.ordinal ASC
|
|
27594
|
+
LIMIT ?
|
|
27595
|
+
`).all(...sqlParams);
|
|
27596
|
+
return rows;
|
|
27597
|
+
}
|
|
26435
27598
|
/** Get total message count for a session. */
|
|
26436
27599
|
getMessageCount(sessionId) {
|
|
26437
27600
|
const row = this.db.prepare("SELECT COUNT(*) as cnt FROM lcm_messages WHERE session_id = ?").get(sessionId);
|
|
@@ -26583,6 +27746,72 @@ var STOPWORDS = /* @__PURE__ */ new Set([
|
|
|
26583
27746
|
"we",
|
|
26584
27747
|
"they"
|
|
26585
27748
|
]);
|
|
27749
|
+
function extractStructuredFileTerms(query) {
|
|
27750
|
+
const terms = /* @__PURE__ */ new Set();
|
|
27751
|
+
for (const raw of splitQueryTerms(query)) {
|
|
27752
|
+
const cleaned = trimStructuredQueryTerm(raw);
|
|
27753
|
+
if (cleaned.includes("/") || hasStructuredFileExtension(cleaned)) {
|
|
27754
|
+
terms.add(cleaned);
|
|
27755
|
+
const basename3 = cleaned.split("/").pop();
|
|
27756
|
+
if (basename3 && basename3 !== cleaned) terms.add(basename3);
|
|
27757
|
+
}
|
|
27758
|
+
}
|
|
27759
|
+
return [...terms].filter((term) => term.length > 1).slice(0, 12);
|
|
27760
|
+
}
|
|
27761
|
+
function splitQueryTerms(query) {
|
|
27762
|
+
const terms = [];
|
|
27763
|
+
let term = "";
|
|
27764
|
+
for (const char of query.slice(0, 2e4)) {
|
|
27765
|
+
if (char === " " || char === "\n" || char === "\r" || char === " ") {
|
|
27766
|
+
if (term.length > 0) terms.push(term);
|
|
27767
|
+
term = "";
|
|
27768
|
+
continue;
|
|
27769
|
+
}
|
|
27770
|
+
term += char;
|
|
27771
|
+
if (term.length > 512) {
|
|
27772
|
+
terms.push(term);
|
|
27773
|
+
term = "";
|
|
27774
|
+
}
|
|
27775
|
+
}
|
|
27776
|
+
if (term.length > 0) terms.push(term);
|
|
27777
|
+
return terms;
|
|
27778
|
+
}
|
|
27779
|
+
function trimStructuredQueryTerm(raw) {
|
|
27780
|
+
const leading = /* @__PURE__ */ new Set(["`", "'", '"', "(", "[", "{"]);
|
|
27781
|
+
const trailing = /* @__PURE__ */ new Set(["`", "'", '"', ",", ".", "?", "!", ":", ";", ")", "]", "}"]);
|
|
27782
|
+
let start = 0;
|
|
27783
|
+
let end = raw.length;
|
|
27784
|
+
while (start < end && leading.has(raw[start])) start += 1;
|
|
27785
|
+
while (end > start && trailing.has(raw[end - 1])) end -= 1;
|
|
27786
|
+
return raw.slice(start, end);
|
|
27787
|
+
}
|
|
27788
|
+
function hasStructuredFileExtension(value) {
|
|
27789
|
+
const slash = value.lastIndexOf("/");
|
|
27790
|
+
const basename3 = value.slice(slash + 1);
|
|
27791
|
+
const dot = basename3.lastIndexOf(".");
|
|
27792
|
+
if (dot <= 0 || dot === basename3.length - 1) return false;
|
|
27793
|
+
const ext = basename3.slice(dot + 1);
|
|
27794
|
+
if (ext.length < 1 || ext.length > 12) return false;
|
|
27795
|
+
for (const char of ext) {
|
|
27796
|
+
const code = char.charCodeAt(0);
|
|
27797
|
+
const valid = code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || char === "_" || char === "+" || char === "-";
|
|
27798
|
+
if (!valid) return false;
|
|
27799
|
+
}
|
|
27800
|
+
return true;
|
|
27801
|
+
}
|
|
27802
|
+
function extractStructuredToolTerms(query) {
|
|
27803
|
+
const lower = query.toLowerCase();
|
|
27804
|
+
if (!/\b(tool|command|invocation|called|used|ran|read|write|patch|edit|grep|search)\b/.test(lower)) {
|
|
27805
|
+
return [];
|
|
27806
|
+
}
|
|
27807
|
+
return query.replace(/[^\w.-]/g, " ").split(/\s+/).filter((term) => term.length > 2 && !STOPWORDS.has(term.toLowerCase())).slice(0, 8);
|
|
27808
|
+
}
|
|
27809
|
+
function escapeLike(value) {
|
|
27810
|
+
return value.replace(/[\\%_]/g, (char) => `\\${char}`);
|
|
27811
|
+
}
|
|
27812
|
+
function asNullableString(value) {
|
|
27813
|
+
return typeof value === "string" && value.trim().length > 0 ? value : null;
|
|
27814
|
+
}
|
|
26586
27815
|
function sanitizeFtsQuery(raw) {
|
|
26587
27816
|
const words = raw.replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 1 && !STOPWORDS.has(w.toLowerCase()));
|
|
26588
27817
|
if (words.length === 0) {
|
|
@@ -27052,7 +28281,9 @@ function extractLcmConfig(cfg) {
|
|
|
27052
28281
|
maxDepth: cfg.lcmMaxDepth ?? 5,
|
|
27053
28282
|
deterministicMaxTokens: cfg.lcmDeterministicMaxTokens ?? 512,
|
|
27054
28283
|
archiveRetentionDays: cfg.lcmArchiveRetentionDays ?? 90,
|
|
27055
|
-
recallBudgetShare: cfg.lcmRecallBudgetShare ?? 0.15
|
|
28284
|
+
recallBudgetShare: cfg.lcmRecallBudgetShare ?? 0.15,
|
|
28285
|
+
messagePartsEnabled: cfg.messagePartsEnabled === true,
|
|
28286
|
+
messagePartsRecallMaxResults: typeof cfg.messagePartsRecallMaxResults === "number" ? Math.max(0, Math.floor(cfg.messagePartsRecallMaxResults)) : 6
|
|
27056
28287
|
};
|
|
27057
28288
|
}
|
|
27058
28289
|
var LcmEngine = class {
|
|
@@ -27184,9 +28415,14 @@ var LcmEngine = class {
|
|
|
27184
28415
|
const newMessages = messages.map((m, i) => ({
|
|
27185
28416
|
turnIndex: currentMax + 1 + i,
|
|
27186
28417
|
role: m.role,
|
|
27187
|
-
content: m.content
|
|
28418
|
+
content: m.content,
|
|
28419
|
+
parts: this.config.messagePartsEnabled ? m.parts : void 0,
|
|
28420
|
+
rawContent: this.config.messagePartsEnabled ? m.rawContent : void 0,
|
|
28421
|
+
sourceFormat: this.config.messagePartsEnabled ? m.sourceFormat : void 0
|
|
27188
28422
|
}));
|
|
27189
|
-
this.archive.appendMessages(sessionId, newMessages
|
|
28423
|
+
this.archive.appendMessages(sessionId, newMessages, {
|
|
28424
|
+
messagePartsEnabled: this.config.messagePartsEnabled
|
|
28425
|
+
});
|
|
27190
28426
|
try {
|
|
27191
28427
|
await this.summarizer.summarizeIncremental(sessionId);
|
|
27192
28428
|
} catch (err) {
|
|
@@ -27264,6 +28500,28 @@ var LcmEngine = class {
|
|
|
27264
28500
|
budgetChars: effectiveBudget
|
|
27265
28501
|
});
|
|
27266
28502
|
}
|
|
28503
|
+
async searchStructuredParts(sessionId, query, limit = this.config.messagePartsRecallMaxResults) {
|
|
28504
|
+
if (!this.config.enabled || !this.config.messagePartsEnabled) return [];
|
|
28505
|
+
await this.ensureInitialized();
|
|
28506
|
+
if (!this.archive) return [];
|
|
28507
|
+
return this.archive.searchStructuredParts(query, limit, sessionId);
|
|
28508
|
+
}
|
|
28509
|
+
formatStructuredRecall(matches, budgetChars) {
|
|
28510
|
+
if (matches.length === 0 || budgetChars <= 0) return "";
|
|
28511
|
+
const lines = [];
|
|
28512
|
+
let used = "## Structured Session Matches\n\n".length;
|
|
28513
|
+
for (const match of matches) {
|
|
28514
|
+
const label = match.file_path ? `${match.kind} ${match.file_path}` : match.tool_name ? `${match.kind} ${match.tool_name}` : match.kind;
|
|
28515
|
+
const excerpt = match.content.replace(/\s+/g, " ").slice(0, 220);
|
|
28516
|
+
const line = `- turn ${match.turn_index} (${match.role}): ${label} \u2014 ${excerpt}`;
|
|
28517
|
+
if (used + line.length + 1 > budgetChars) break;
|
|
28518
|
+
lines.push(line);
|
|
28519
|
+
used += line.length + 1;
|
|
28520
|
+
}
|
|
28521
|
+
return lines.length > 0 ? `## Structured Session Matches
|
|
28522
|
+
|
|
28523
|
+
${lines.join("\n")}` : "";
|
|
28524
|
+
}
|
|
27267
28525
|
/** Flush pending summaries before compaction (called from before_compaction hook). */
|
|
27268
28526
|
async preCompactionFlush(sessionId) {
|
|
27269
28527
|
if (!this.config.enabled) return;
|
|
@@ -35504,6 +36762,7 @@ ${r.snippet.trim()}
|
|
|
35504
36762
|
let recalledMemoryIds = [];
|
|
35505
36763
|
let recalledMemoryPaths = [];
|
|
35506
36764
|
let xrayRecalledResults = [];
|
|
36765
|
+
const lcmStructuredXrayResults = [];
|
|
35507
36766
|
const xrayBranchPoolSize = {
|
|
35508
36767
|
hot_qmd: 0,
|
|
35509
36768
|
hot_embedding: 0,
|
|
@@ -37307,6 +38566,27 @@ ${formatted}`;
|
|
|
37307
38566
|
this.profiler.startSpan("assembly", profileTraceId);
|
|
37308
38567
|
if (sharedCtx)
|
|
37309
38568
|
this.appendRecallSection(sectionBuckets, "shared-context", sharedCtx);
|
|
38569
|
+
const explicitCueMaxChars = this.getRecallSectionMaxChars("explicit-cue") ?? this.config.explicitCueRecallMaxChars;
|
|
38570
|
+
if (this.config.explicitCueRecallEnabled && this.isRecallSectionEnabled("explicit-cue") && explicitCueMaxChars !== 0 && this.lcmEngine?.enabled && recallMode !== "no_recall") {
|
|
38571
|
+
try {
|
|
38572
|
+
const explicitCueSection = await buildExplicitCueRecallSection({
|
|
38573
|
+
engine: this.lcmEngine,
|
|
38574
|
+
sessionId: sessionKey,
|
|
38575
|
+
query: retrievalQuery,
|
|
38576
|
+
maxChars: explicitCueMaxChars,
|
|
38577
|
+
maxReferences: this.getRecallSectionNumber("explicit-cue", "maxResults") ?? this.config.explicitCueRecallMaxReferences
|
|
38578
|
+
});
|
|
38579
|
+
if (explicitCueSection) {
|
|
38580
|
+
this.appendRecallSection(
|
|
38581
|
+
sectionBuckets,
|
|
38582
|
+
"explicit-cue",
|
|
38583
|
+
explicitCueSection
|
|
38584
|
+
);
|
|
38585
|
+
}
|
|
38586
|
+
} catch (err) {
|
|
38587
|
+
log.debug(`Explicit cue recall assembly error: ${err}`);
|
|
38588
|
+
}
|
|
38589
|
+
}
|
|
37310
38590
|
if (profile)
|
|
37311
38591
|
this.appendRecallSection(
|
|
37312
38592
|
sectionBuckets,
|
|
@@ -37427,6 +38707,32 @@ ${tmtNode.summary}`
|
|
|
37427
38707
|
}
|
|
37428
38708
|
if (this.lcmEngine?.enabled && recallMode !== "minimal" && recallMode !== "no_recall") {
|
|
37429
38709
|
try {
|
|
38710
|
+
const structuredMatches = await this.lcmEngine.searchStructuredParts(
|
|
38711
|
+
sessionKey ?? "default",
|
|
38712
|
+
retrievalQuery
|
|
38713
|
+
);
|
|
38714
|
+
const structuredSection = this.lcmEngine.formatStructuredRecall(
|
|
38715
|
+
structuredMatches,
|
|
38716
|
+
Math.ceil(this.config.recallBudgetChars * 0.08)
|
|
38717
|
+
);
|
|
38718
|
+
if (structuredSection) {
|
|
38719
|
+
const structuredAppended = this.appendRecallSection(
|
|
38720
|
+
sectionBuckets,
|
|
38721
|
+
"lcm-message-parts",
|
|
38722
|
+
structuredSection
|
|
38723
|
+
);
|
|
38724
|
+
if (structuredAppended) {
|
|
38725
|
+
for (const match of structuredMatches) {
|
|
38726
|
+
lcmStructuredXrayResults.push({
|
|
38727
|
+
memoryId: `lcm-message-part-${match.part_id}`,
|
|
38728
|
+
path: `lcm://${match.session_id}/turn/${match.turn_index}/part/${match.part_id}`,
|
|
38729
|
+
servedBy: match.file_path ? "lcm-file-parts" : "lcm-tool-parts",
|
|
38730
|
+
scoreDecomposition: { final: match.score },
|
|
38731
|
+
admittedBy: ["lcm-message-parts"]
|
|
38732
|
+
});
|
|
38733
|
+
}
|
|
38734
|
+
}
|
|
38735
|
+
}
|
|
37430
38736
|
const lcmSection = await this.lcmEngine.assembleRecall(
|
|
37431
38737
|
sessionKey ?? "default",
|
|
37432
38738
|
this.config.recallBudgetChars
|
|
@@ -38327,10 +39633,17 @@ _Context: ${topQuestion.context}_`
|
|
|
38327
39633
|
admitted: recalledMemoryIds.length
|
|
38328
39634
|
}
|
|
38329
39635
|
];
|
|
39636
|
+
if (lcmStructuredXrayResults.length > 0) {
|
|
39637
|
+
filters.push({
|
|
39638
|
+
name: "lcm-message-parts",
|
|
39639
|
+
considered: lcmStructuredXrayResults.length,
|
|
39640
|
+
admitted: lcmStructuredXrayResults.length
|
|
39641
|
+
});
|
|
39642
|
+
}
|
|
38330
39643
|
this.lastXraySnapshot = buildXraySnapshot({
|
|
38331
39644
|
query: retrievalQuery,
|
|
38332
39645
|
tierExplain: null,
|
|
38333
|
-
results,
|
|
39646
|
+
results: [...results, ...lcmStructuredXrayResults],
|
|
38334
39647
|
filters,
|
|
38335
39648
|
budget: {
|
|
38336
39649
|
chars: this.getRecallBudgetChars(options.budgetCharsOverride),
|
|
@@ -38497,13 +39810,28 @@ _Context: ${topQuestion.context}_`
|
|
|
38497
39810
|
role: turn.role,
|
|
38498
39811
|
content: turn.content,
|
|
38499
39812
|
timestamp: turn.timestamp,
|
|
38500
|
-
sessionKey: key
|
|
39813
|
+
sessionKey: key,
|
|
39814
|
+
parts: turn.parts,
|
|
39815
|
+
rawContent: turn.rawContent,
|
|
39816
|
+
sourceFormat: turn.sourceFormat
|
|
38501
39817
|
});
|
|
38502
39818
|
bySession.set(key, list);
|
|
38503
39819
|
}
|
|
38504
39820
|
const replayTasks = [];
|
|
38505
39821
|
for (const [key, sessionTurns] of bySession.entries()) {
|
|
38506
39822
|
if (sessionTurns.length === 0) continue;
|
|
39823
|
+
if (options.archiveLcm !== false && this.lcmEngine?.enabled) {
|
|
39824
|
+
await this.lcmEngine.observeMessages(
|
|
39825
|
+
key,
|
|
39826
|
+
sessionTurns.map((turn) => ({
|
|
39827
|
+
role: turn.role,
|
|
39828
|
+
content: turn.content,
|
|
39829
|
+
parts: turn.parts,
|
|
39830
|
+
rawContent: turn.rawContent,
|
|
39831
|
+
sourceFormat: turn.sourceFormat
|
|
39832
|
+
}))
|
|
39833
|
+
);
|
|
39834
|
+
}
|
|
38507
39835
|
replayTasks.push(
|
|
38508
39836
|
new Promise((resolve2, reject) => {
|
|
38509
39837
|
void this.queueBufferedExtraction(sessionTurns, "trigger_mode", {
|
|
@@ -38592,10 +39920,25 @@ _Context: ${topQuestion.context}_`
|
|
|
38592
39920
|
role: turn.role,
|
|
38593
39921
|
content: turn.content,
|
|
38594
39922
|
timestamp: turn.timestamp,
|
|
38595
|
-
sessionKey
|
|
39923
|
+
sessionKey,
|
|
39924
|
+
parts: turn.parts,
|
|
39925
|
+
rawContent: turn.rawContent,
|
|
39926
|
+
sourceFormat: turn.sourceFormat
|
|
38596
39927
|
});
|
|
38597
39928
|
}
|
|
38598
39929
|
if (sessionTurns.length === 0) return;
|
|
39930
|
+
if (this.lcmEngine?.enabled) {
|
|
39931
|
+
await this.lcmEngine.observeMessages(
|
|
39932
|
+
sessionKey,
|
|
39933
|
+
sessionTurns.map((turn) => ({
|
|
39934
|
+
role: turn.role,
|
|
39935
|
+
content: turn.content,
|
|
39936
|
+
parts: turn.parts,
|
|
39937
|
+
rawContent: turn.rawContent,
|
|
39938
|
+
sourceFormat: turn.sourceFormat
|
|
39939
|
+
}))
|
|
39940
|
+
);
|
|
39941
|
+
}
|
|
38599
39942
|
await new Promise((resolve2, reject) => {
|
|
38600
39943
|
void this.queueBufferedExtraction(sessionTurns, "trigger_mode", {
|
|
38601
39944
|
skipDedupeCheck: true,
|
|
@@ -43124,13 +44467,13 @@ function toolJsonResult(value, options) {
|
|
|
43124
44467
|
function workLayerTextResult(text, options) {
|
|
43125
44468
|
return toolResult2(wrapWorkLayerContext(text, { linkToMemory: options?.linkToMemory === true }));
|
|
43126
44469
|
}
|
|
43127
|
-
function
|
|
44470
|
+
function asNonEmptyString2(value) {
|
|
43128
44471
|
if (typeof value !== "string") return void 0;
|
|
43129
44472
|
const trimmed = value.trim();
|
|
43130
44473
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
43131
44474
|
}
|
|
43132
44475
|
function normalizeToolNamespace(value) {
|
|
43133
|
-
return
|
|
44476
|
+
return asNonEmptyString2(value);
|
|
43134
44477
|
}
|
|
43135
44478
|
function clampUnitInterval(value, fallback) {
|
|
43136
44479
|
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
@@ -43179,17 +44522,17 @@ var WORK_TASK_STATUSES = /* @__PURE__ */ new Set(["todo", "in_progress", "blocke
|
|
|
43179
44522
|
var WORK_TASK_PRIORITIES = /* @__PURE__ */ new Set(["low", "medium", "high"]);
|
|
43180
44523
|
var WORK_PROJECT_STATUSES = /* @__PURE__ */ new Set(["active", "on_hold", "completed", "archived"]);
|
|
43181
44524
|
function asTaskStatus(value) {
|
|
43182
|
-
const normalized =
|
|
44525
|
+
const normalized = asNonEmptyString2(value);
|
|
43183
44526
|
if (!normalized || !WORK_TASK_STATUSES.has(normalized)) return void 0;
|
|
43184
44527
|
return normalized;
|
|
43185
44528
|
}
|
|
43186
44529
|
function asTaskPriority(value) {
|
|
43187
|
-
const normalized =
|
|
44530
|
+
const normalized = asNonEmptyString2(value);
|
|
43188
44531
|
if (!normalized || !WORK_TASK_PRIORITIES.has(normalized)) return void 0;
|
|
43189
44532
|
return normalized;
|
|
43190
44533
|
}
|
|
43191
44534
|
function asProjectStatus(value) {
|
|
43192
|
-
const normalized =
|
|
44535
|
+
const normalized = asNonEmptyString2(value);
|
|
43193
44536
|
if (!normalized || !WORK_PROJECT_STATUSES.has(normalized)) return void 0;
|
|
43194
44537
|
return normalized;
|
|
43195
44538
|
}
|
|
@@ -43329,7 +44672,7 @@ function registerTools(api, orchestrator) {
|
|
|
43329
44672
|
return createHash10("sha256").update(input).digest("hex").slice(0, 16);
|
|
43330
44673
|
}
|
|
43331
44674
|
function normalizeMemoryCategory(value, fallback) {
|
|
43332
|
-
const normalized =
|
|
44675
|
+
const normalized = asNonEmptyString2(value);
|
|
43333
44676
|
if (!normalized) return fallback;
|
|
43334
44677
|
if (!VALID_MEMORY_CATEGORIES.has(normalized)) return void 0;
|
|
43335
44678
|
return normalized;
|
|
@@ -43338,8 +44681,8 @@ function registerTools(api, orchestrator) {
|
|
|
43338
44681
|
return typeof value === "string" && actionTypeSet.has(value);
|
|
43339
44682
|
}
|
|
43340
44683
|
function buildActionInputSummary(action, params) {
|
|
43341
|
-
const primary =
|
|
43342
|
-
const content =
|
|
44684
|
+
const primary = asNonEmptyString2(params.memoryId) ?? asNonEmptyString2(params.category) ?? asNonEmptyString2(params.linkTargetId);
|
|
44685
|
+
const content = asNonEmptyString2(params.content);
|
|
43343
44686
|
let summary = primary ? `${action} => ${primary}` : action;
|
|
43344
44687
|
if (content) {
|
|
43345
44688
|
summary += ` | ${content.slice(0, 120)}`;
|
|
@@ -44224,14 +45567,14 @@ NOTE: You did not provide sessionKey; under concurrency this may not match your
|
|
|
44224
45567
|
return toolResult2(`Recorded context checkpoint telemetry in namespace=${ns}.`);
|
|
44225
45568
|
}
|
|
44226
45569
|
const validationErrors = [];
|
|
44227
|
-
if (!
|
|
45570
|
+
if (!asNonEmptyString2(sessionKey)) validationErrors.push("sessionKey is required");
|
|
44228
45571
|
if (!Array.isArray(turns) || turns.length === 0) validationErrors.push("turns must be a non-empty array");
|
|
44229
45572
|
const baseEvent = {
|
|
44230
45573
|
action: "summarize_node",
|
|
44231
45574
|
namespace: ns,
|
|
44232
45575
|
actor: "tool.context_checkpoint",
|
|
44233
45576
|
subsystem: "tools.context_checkpoint",
|
|
44234
|
-
sourceSessionKey:
|
|
45577
|
+
sourceSessionKey: asNonEmptyString2(sessionKey),
|
|
44235
45578
|
inputSummary: summary,
|
|
44236
45579
|
checkpointTurnCount: Array.isArray(turns) ? turns.length : void 0,
|
|
44237
45580
|
dryRun: dryRun === true,
|
|
@@ -44393,10 +45736,10 @@ NOTE: You did not provide sessionKey; under concurrency this may not match your
|
|
|
44393
45736
|
return toolResult2(`Validation failed: invalid action ${String(action)}.`);
|
|
44394
45737
|
}
|
|
44395
45738
|
const validationErrors = [];
|
|
44396
|
-
const contentValue =
|
|
44397
|
-
const memoryIdValue =
|
|
44398
|
-
const linkTargetIdValue =
|
|
44399
|
-
const linkTypeValue =
|
|
45739
|
+
const contentValue = asNonEmptyString2(content);
|
|
45740
|
+
const memoryIdValue = asNonEmptyString2(memoryId);
|
|
45741
|
+
const linkTargetIdValue = asNonEmptyString2(linkTargetId);
|
|
45742
|
+
const linkTypeValue = asNonEmptyString2(linkType);
|
|
44400
45743
|
const structuredActionRequest = execute === true || Object.prototype.hasOwnProperty.call(params, "content") || Object.prototype.hasOwnProperty.call(params, "category") || Object.prototype.hasOwnProperty.call(params, "linkTargetId") || Object.prototype.hasOwnProperty.call(params, "linkType") || Object.prototype.hasOwnProperty.call(params, "linkStrength") || Object.prototype.hasOwnProperty.call(params, "artifactType");
|
|
44401
45744
|
const baseEvent = {
|
|
44402
45745
|
action,
|
|
@@ -44405,7 +45748,7 @@ NOTE: You did not provide sessionKey; under concurrency this may not match your
|
|
|
44405
45748
|
subsystem: "tools.memory_action_apply",
|
|
44406
45749
|
reason,
|
|
44407
45750
|
memoryId: memoryIdValue,
|
|
44408
|
-
sourceSessionKey:
|
|
45751
|
+
sourceSessionKey: asNonEmptyString2(sessionKey),
|
|
44409
45752
|
inputSummary: buildActionInputSummary(action, params),
|
|
44410
45753
|
promptHash: promptHashForTelemetry(sourcePrompt)
|
|
44411
45754
|
};
|
|
@@ -45315,16 +46658,16 @@ Best for:
|
|
|
45315
46658
|
description: typeof p.description === "string" ? p.description : void 0,
|
|
45316
46659
|
status: asTaskStatus(p.status),
|
|
45317
46660
|
priority: asTaskPriority(p.priority),
|
|
45318
|
-
owner:
|
|
45319
|
-
assignee:
|
|
45320
|
-
projectId:
|
|
46661
|
+
owner: asNonEmptyString2(p.owner),
|
|
46662
|
+
assignee: asNonEmptyString2(p.assignee),
|
|
46663
|
+
projectId: asNonEmptyString2(p.projectId),
|
|
45321
46664
|
tags: Array.isArray(p.tags) ? p.tags.filter((x) => typeof x === "string") : void 0,
|
|
45322
|
-
dueAt:
|
|
46665
|
+
dueAt: asNonEmptyString2(p.dueAt)
|
|
45323
46666
|
});
|
|
45324
46667
|
return toolJsonResult({ action, task: created });
|
|
45325
46668
|
}
|
|
45326
46669
|
if (action === "get") {
|
|
45327
|
-
const taskId =
|
|
46670
|
+
const taskId = asNonEmptyString2(p.id);
|
|
45328
46671
|
if (!taskId) {
|
|
45329
46672
|
return workLayerTextResult("work_task.get requires `id`.");
|
|
45330
46673
|
}
|
|
@@ -45334,14 +46677,14 @@ Best for:
|
|
|
45334
46677
|
if (action === "list") {
|
|
45335
46678
|
const tasks = await storage.listTasks({
|
|
45336
46679
|
status: asTaskStatus(p.status),
|
|
45337
|
-
owner:
|
|
45338
|
-
assignee:
|
|
45339
|
-
projectId:
|
|
46680
|
+
owner: asNonEmptyString2(p.owner),
|
|
46681
|
+
assignee: asNonEmptyString2(p.assignee),
|
|
46682
|
+
projectId: asNonEmptyString2(p.projectId)
|
|
45340
46683
|
});
|
|
45341
46684
|
return toolJsonResult({ action, count: tasks.length, tasks });
|
|
45342
46685
|
}
|
|
45343
46686
|
if (action === "update") {
|
|
45344
|
-
const taskId =
|
|
46687
|
+
const taskId = asNonEmptyString2(p.id);
|
|
45345
46688
|
if (!taskId) {
|
|
45346
46689
|
return workLayerTextResult("work_task.update requires `id`.");
|
|
45347
46690
|
}
|
|
@@ -45365,8 +46708,8 @@ Best for:
|
|
|
45365
46708
|
return toolJsonResult({ action, task: updated });
|
|
45366
46709
|
}
|
|
45367
46710
|
if (action === "transition") {
|
|
45368
|
-
const taskId =
|
|
45369
|
-
const rawStatus =
|
|
46711
|
+
const taskId = asNonEmptyString2(p.id);
|
|
46712
|
+
const rawStatus = asNonEmptyString2(p.status);
|
|
45370
46713
|
if (!taskId || !rawStatus) {
|
|
45371
46714
|
return workLayerTextResult("work_task.transition requires `id` and `status`.");
|
|
45372
46715
|
}
|
|
@@ -45378,7 +46721,7 @@ Best for:
|
|
|
45378
46721
|
return toolJsonResult({ action, task });
|
|
45379
46722
|
}
|
|
45380
46723
|
if (action === "delete") {
|
|
45381
|
-
const taskId =
|
|
46724
|
+
const taskId = asNonEmptyString2(p.id);
|
|
45382
46725
|
if (!taskId) {
|
|
45383
46726
|
return workLayerTextResult("work_task.delete requires `id`.");
|
|
45384
46727
|
}
|
|
@@ -45426,13 +46769,13 @@ Best for:
|
|
|
45426
46769
|
name: p.name,
|
|
45427
46770
|
description: typeof p.description === "string" ? p.description : void 0,
|
|
45428
46771
|
status: asProjectStatus(p.status),
|
|
45429
|
-
owner:
|
|
46772
|
+
owner: asNonEmptyString2(p.owner),
|
|
45430
46773
|
tags: Array.isArray(p.tags) ? p.tags.filter((x) => typeof x === "string") : void 0
|
|
45431
46774
|
});
|
|
45432
46775
|
return toolJsonResult({ action, project });
|
|
45433
46776
|
}
|
|
45434
46777
|
if (action === "get") {
|
|
45435
|
-
const projectId =
|
|
46778
|
+
const projectId = asNonEmptyString2(p.id);
|
|
45436
46779
|
if (!projectId) {
|
|
45437
46780
|
return workLayerTextResult("work_project.get requires `id`.");
|
|
45438
46781
|
}
|
|
@@ -45444,7 +46787,7 @@ Best for:
|
|
|
45444
46787
|
return toolJsonResult({ action, count: projects.length, projects });
|
|
45445
46788
|
}
|
|
45446
46789
|
if (action === "update") {
|
|
45447
|
-
const projectId =
|
|
46790
|
+
const projectId = asNonEmptyString2(p.id);
|
|
45448
46791
|
if (!projectId) {
|
|
45449
46792
|
return workLayerTextResult("work_project.update requires `id`.");
|
|
45450
46793
|
}
|
|
@@ -45460,7 +46803,7 @@ Best for:
|
|
|
45460
46803
|
return toolJsonResult({ action, project });
|
|
45461
46804
|
}
|
|
45462
46805
|
if (action === "delete") {
|
|
45463
|
-
const projectId =
|
|
46806
|
+
const projectId = asNonEmptyString2(p.id);
|
|
45464
46807
|
if (!projectId) {
|
|
45465
46808
|
return workLayerTextResult("work_project.delete requires `id`.");
|
|
45466
46809
|
}
|
|
@@ -45468,8 +46811,8 @@ Best for:
|
|
|
45468
46811
|
return toolJsonResult({ action, deleted });
|
|
45469
46812
|
}
|
|
45470
46813
|
if (action === "link_task") {
|
|
45471
|
-
const taskId =
|
|
45472
|
-
const projectId =
|
|
46814
|
+
const taskId = asNonEmptyString2(p.taskId);
|
|
46815
|
+
const projectId = asNonEmptyString2(p.projectId);
|
|
45473
46816
|
if (!taskId || !projectId) {
|
|
45474
46817
|
return workLayerTextResult("work_project.link_task requires `taskId` and `projectId`.");
|
|
45475
46818
|
}
|
|
@@ -45505,7 +46848,7 @@ Best for:
|
|
|
45505
46848
|
async execute(_toolCallId, params) {
|
|
45506
46849
|
const p = params;
|
|
45507
46850
|
const action = String(p.action ?? "");
|
|
45508
|
-
const projectId =
|
|
46851
|
+
const projectId = asNonEmptyString2(p.projectId);
|
|
45509
46852
|
const linkToMemory = p.linkToMemory === true;
|
|
45510
46853
|
try {
|
|
45511
46854
|
await new WorkStorage(orchestrator.config.memoryDir).ensureDirectories();
|
|
@@ -45525,7 +46868,7 @@ Best for:
|
|
|
45525
46868
|
const result = await importWorkBoardSnapshot({
|
|
45526
46869
|
memoryDir: orchestrator.config.memoryDir,
|
|
45527
46870
|
snapshot,
|
|
45528
|
-
projectId:
|
|
46871
|
+
projectId: asNonEmptyString2(p.projectId)
|
|
45529
46872
|
});
|
|
45530
46873
|
return toolJsonResult({ action, result }, { linkToMemory });
|
|
45531
46874
|
}
|
|
@@ -45791,7 +47134,7 @@ Returns: Performance trace data with timing breakdown`,
|
|
|
45791
47134
|
"Profiling is disabled. Set profilingEnabled: true in your plugin config to enable."
|
|
45792
47135
|
);
|
|
45793
47136
|
}
|
|
45794
|
-
const format =
|
|
47137
|
+
const format = asNonEmptyString2(params.format) ?? "ascii";
|
|
45795
47138
|
const limit = Math.min(typeof params.limit === "number" ? params.limit : 5, 20);
|
|
45796
47139
|
const traces = profiler.getRecentTraces(limit);
|
|
45797
47140
|
const stats = profiler.getStats();
|
|
@@ -45862,7 +47205,7 @@ Returns: Performance trace data with timing breakdown`,
|
|
|
45862
47205
|
"Profiling is disabled. Set profilingEnabled: true in your plugin config to enable."
|
|
45863
47206
|
);
|
|
45864
47207
|
}
|
|
45865
|
-
const format =
|
|
47208
|
+
const format = asNonEmptyString2(params.format) ?? "ascii";
|
|
45866
47209
|
const limit = Math.min(typeof params.limit === "number" ? params.limit : 5, 20);
|
|
45867
47210
|
const traces = profiler.getRecentTraces(limit);
|
|
45868
47211
|
const stats = profiler.getStats();
|
|
@@ -55320,10 +56663,15 @@ var EngramAccessService = class {
|
|
|
55320
56663
|
sessionKey: lcmSessionKey,
|
|
55321
56664
|
role: m.role,
|
|
55322
56665
|
content: m.content,
|
|
56666
|
+
parts: m.parts,
|
|
56667
|
+
rawContent: m.rawContent,
|
|
56668
|
+
sourceFormat: m.sourceFormat,
|
|
55323
56669
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
55324
56670
|
}));
|
|
55325
56671
|
try {
|
|
55326
|
-
const extractionPromise = this.orchestrator.ingestReplayBatch(turns
|
|
56672
|
+
const extractionPromise = this.orchestrator.ingestReplayBatch(turns, {
|
|
56673
|
+
archiveLcm: false
|
|
56674
|
+
});
|
|
55327
56675
|
extractionPromise.catch((err) => {
|
|
55328
56676
|
log.error(`access-observe background extraction failed: ${err}`);
|
|
55329
56677
|
});
|
|
@@ -56705,7 +58053,33 @@ var setCodingContextRequestSchema = external_exports.object({
|
|
|
56705
58053
|
});
|
|
56706
58054
|
var messageSchema = external_exports.object({
|
|
56707
58055
|
role: external_exports.enum(["user", "assistant"]),
|
|
56708
|
-
content: external_exports.string().min(1, "message content must be non-empty")
|
|
58056
|
+
content: external_exports.string().min(1, "message content must be non-empty"),
|
|
58057
|
+
sourceFormat: external_exports.enum(["openai", "anthropic", "openclaw", "lossless-claw", "remnic"]).nullable().optional(),
|
|
58058
|
+
rawContent: external_exports.unknown().nullable().optional(),
|
|
58059
|
+
parts: external_exports.array(
|
|
58060
|
+
external_exports.object({
|
|
58061
|
+
ordinal: external_exports.number().int().min(0).nullable().optional(),
|
|
58062
|
+
kind: external_exports.enum([
|
|
58063
|
+
"text",
|
|
58064
|
+
"tool_call",
|
|
58065
|
+
"tool_result",
|
|
58066
|
+
"patch",
|
|
58067
|
+
"file_read",
|
|
58068
|
+
"file_write",
|
|
58069
|
+
"step_start",
|
|
58070
|
+
"step_finish",
|
|
58071
|
+
"snapshot",
|
|
58072
|
+
"retry"
|
|
58073
|
+
]),
|
|
58074
|
+
payload: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
58075
|
+
toolName: external_exports.string().nullable().optional(),
|
|
58076
|
+
tool_name: external_exports.string().nullable().optional(),
|
|
58077
|
+
filePath: external_exports.string().nullable().optional(),
|
|
58078
|
+
file_path: external_exports.string().nullable().optional(),
|
|
58079
|
+
createdAt: external_exports.string().nullable().optional(),
|
|
58080
|
+
created_at: external_exports.string().nullable().optional()
|
|
58081
|
+
})
|
|
58082
|
+
).nullable().optional()
|
|
56709
58083
|
});
|
|
56710
58084
|
var observeRequestSchema = external_exports.object({
|
|
56711
58085
|
sessionKey: external_exports.string().trim().min(1, "sessionKey is required").max(512),
|
|
@@ -57351,9 +58725,51 @@ var EngramMcpServer = class {
|
|
|
57351
58725
|
type: "object",
|
|
57352
58726
|
properties: {
|
|
57353
58727
|
role: { type: "string", enum: ["user", "assistant"] },
|
|
57354
|
-
content: { type: "string" }
|
|
58728
|
+
content: { type: "string" },
|
|
58729
|
+
sourceFormat: {
|
|
58730
|
+
type: "string",
|
|
58731
|
+
enum: ["openai", "anthropic", "openclaw", "lossless-claw", "remnic"]
|
|
58732
|
+
},
|
|
58733
|
+
rawContent: {
|
|
58734
|
+
description: "Optional native provider content blocks for structured message-part capture."
|
|
58735
|
+
},
|
|
58736
|
+
parts: {
|
|
58737
|
+
type: "array",
|
|
58738
|
+
description: "Optional normalized Remnic LCM message parts.",
|
|
58739
|
+
items: {
|
|
58740
|
+
type: "object",
|
|
58741
|
+
properties: {
|
|
58742
|
+
ordinal: { type: ["number", "null"], minimum: 0 },
|
|
58743
|
+
kind: {
|
|
58744
|
+
type: "string",
|
|
58745
|
+
enum: [
|
|
58746
|
+
"text",
|
|
58747
|
+
"tool_call",
|
|
58748
|
+
"tool_result",
|
|
58749
|
+
"patch",
|
|
58750
|
+
"file_read",
|
|
58751
|
+
"file_write",
|
|
58752
|
+
"step_start",
|
|
58753
|
+
"step_finish",
|
|
58754
|
+
"snapshot",
|
|
58755
|
+
"retry"
|
|
58756
|
+
]
|
|
58757
|
+
},
|
|
58758
|
+
payload: { type: "object", additionalProperties: true },
|
|
58759
|
+
toolName: { type: ["string", "null"] },
|
|
58760
|
+
tool_name: { type: ["string", "null"] },
|
|
58761
|
+
filePath: { type: ["string", "null"] },
|
|
58762
|
+
file_path: { type: ["string", "null"] },
|
|
58763
|
+
createdAt: { type: ["string", "null"] },
|
|
58764
|
+
created_at: { type: ["string", "null"] }
|
|
58765
|
+
},
|
|
58766
|
+
required: ["kind", "payload"],
|
|
58767
|
+
additionalProperties: false
|
|
58768
|
+
}
|
|
58769
|
+
}
|
|
57355
58770
|
},
|
|
57356
|
-
required: ["role", "content"]
|
|
58771
|
+
required: ["role", "content"],
|
|
58772
|
+
additionalProperties: false
|
|
57357
58773
|
},
|
|
57358
58774
|
description: "Conversation messages to observe"
|
|
57359
58775
|
},
|
|
@@ -58694,20 +60110,21 @@ ${body}`;
|
|
|
58694
60110
|
effectivePrincipal
|
|
58695
60111
|
);
|
|
58696
60112
|
case "engram.observe": {
|
|
58697
|
-
|
|
58698
|
-
throw new EngramAccessInputError("cwd must be a string");
|
|
58699
|
-
}
|
|
58700
|
-
if ("projectTag" in args && args.projectTag !== void 0 && args.projectTag !== null && typeof args.projectTag !== "string") {
|
|
58701
|
-
throw new EngramAccessInputError("projectTag must be a string");
|
|
58702
|
-
}
|
|
60113
|
+
const body = parseMcpRequest("observe", args);
|
|
58703
60114
|
return this.service.observe({
|
|
58704
|
-
sessionKey:
|
|
58705
|
-
messages:
|
|
58706
|
-
|
|
60115
|
+
sessionKey: body.sessionKey,
|
|
60116
|
+
messages: body.messages.map((message) => ({
|
|
60117
|
+
role: message.role,
|
|
60118
|
+
content: message.content,
|
|
60119
|
+
parts: message.parts ?? void 0,
|
|
60120
|
+
rawContent: message.rawContent ?? void 0,
|
|
60121
|
+
sourceFormat: message.sourceFormat ?? void 0
|
|
60122
|
+
})),
|
|
60123
|
+
namespace: body.namespace,
|
|
58707
60124
|
authenticatedPrincipal: effectivePrincipal,
|
|
58708
|
-
skipExtraction:
|
|
58709
|
-
cwd:
|
|
58710
|
-
projectTag:
|
|
60125
|
+
skipExtraction: body.skipExtraction === true,
|
|
60126
|
+
cwd: body.cwd,
|
|
60127
|
+
projectTag: body.projectTag
|
|
58711
60128
|
});
|
|
58712
60129
|
}
|
|
58713
60130
|
case "engram.lcm_search":
|
|
@@ -59886,7 +61303,13 @@ var EngramAccessHttpServer = class {
|
|
|
59886
61303
|
this.ensureWriteRateLimitAvailable();
|
|
59887
61304
|
const response = await this.service.observe({
|
|
59888
61305
|
sessionKey: body.sessionKey,
|
|
59889
|
-
messages: body.messages
|
|
61306
|
+
messages: body.messages.map((message) => ({
|
|
61307
|
+
role: message.role,
|
|
61308
|
+
content: message.content,
|
|
61309
|
+
sourceFormat: message.sourceFormat ?? void 0,
|
|
61310
|
+
rawContent: message.rawContent ?? void 0,
|
|
61311
|
+
parts: message.parts ?? void 0
|
|
61312
|
+
})),
|
|
59890
61313
|
namespace: this.resolveNamespace(req, body.namespace),
|
|
59891
61314
|
authenticatedPrincipal: this.resolveRequestPrincipal(req),
|
|
59892
61315
|
skipExtraction: body.skipExtraction === true,
|
|
@@ -67824,7 +69247,7 @@ import crypto2 from "crypto";
|
|
|
67824
69247
|
function hashSha256(value) {
|
|
67825
69248
|
return crypto2.createHash("sha256").update(value).digest("hex");
|
|
67826
69249
|
}
|
|
67827
|
-
function
|
|
69250
|
+
function isRecord4(value) {
|
|
67828
69251
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
67829
69252
|
}
|
|
67830
69253
|
function optionalString2(value) {
|
|
@@ -67840,11 +69263,11 @@ function normalizedToolName(toolName) {
|
|
|
67840
69263
|
return toolNameTokens(toolName).join("_");
|
|
67841
69264
|
}
|
|
67842
69265
|
function parseToolArguments(value) {
|
|
67843
|
-
if (
|
|
69266
|
+
if (isRecord4(value)) return value;
|
|
67844
69267
|
if (typeof value !== "string") return void 0;
|
|
67845
69268
|
try {
|
|
67846
69269
|
const parsed = JSON.parse(value);
|
|
67847
|
-
return
|
|
69270
|
+
return isRecord4(parsed) ? parsed : void 0;
|
|
67848
69271
|
} catch {
|
|
67849
69272
|
return void 0;
|
|
67850
69273
|
}
|
|
@@ -67854,13 +69277,13 @@ function extractTextContent(value) {
|
|
|
67854
69277
|
if (Array.isArray(value)) {
|
|
67855
69278
|
return value.map((block) => {
|
|
67856
69279
|
if (typeof block === "string") return block.trim();
|
|
67857
|
-
if (
|
|
69280
|
+
if (isRecord4(block) && block.type === "text" && typeof block.text === "string") {
|
|
67858
69281
|
return block.text.trim();
|
|
67859
69282
|
}
|
|
67860
69283
|
return "";
|
|
67861
69284
|
}).filter((item) => item.length > 0).join("\n");
|
|
67862
69285
|
}
|
|
67863
|
-
if (
|
|
69286
|
+
if (isRecord4(value)) {
|
|
67864
69287
|
return JSON.stringify(value);
|
|
67865
69288
|
}
|
|
67866
69289
|
return "";
|
|
@@ -67887,10 +69310,10 @@ function getToolCallContexts(messages) {
|
|
|
67887
69310
|
const toolCalls = message.tool_calls ?? message.toolCalls;
|
|
67888
69311
|
if (!Array.isArray(toolCalls)) continue;
|
|
67889
69312
|
for (const call of toolCalls) {
|
|
67890
|
-
if (!
|
|
69313
|
+
if (!isRecord4(call)) continue;
|
|
67891
69314
|
const toolCallId = optionalString2(call.id) ?? optionalString2(call.toolCallId);
|
|
67892
69315
|
if (!toolCallId) continue;
|
|
67893
|
-
const fn =
|
|
69316
|
+
const fn = isRecord4(call.function) ? call.function : void 0;
|
|
67894
69317
|
const toolName = optionalString2(fn?.name) ?? optionalString2(call.name);
|
|
67895
69318
|
const args = parseToolArguments(fn?.arguments) ?? parseToolArguments(call.arguments) ?? parseToolArguments(call.args) ?? parseToolArguments(call.input);
|
|
67896
69319
|
contexts.set(toolCallId, { toolCallId, toolName, args });
|
|
@@ -67933,7 +69356,7 @@ function fileContentHash(args) {
|
|
|
67933
69356
|
}
|
|
67934
69357
|
function inferOutcome(message, parsedPayload) {
|
|
67935
69358
|
if (message.isError === true) return "failure";
|
|
67936
|
-
if (
|
|
69359
|
+
if (isRecord4(parsedPayload)) {
|
|
67937
69360
|
if (parsedPayload.partial === true || parsedPayload.status === "partial") return "partial";
|
|
67938
69361
|
if (parsedPayload.success === false || parsedPayload.ok === false) return "failure";
|
|
67939
69362
|
if (parsedPayload.success === true || parsedPayload.ok === true) return "success";
|
|
@@ -70148,8 +71571,7 @@ function buildTurnFingerprint(input) {
|
|
|
70148
71571
|
// ../../src/index.ts
|
|
70149
71572
|
import {
|
|
70150
71573
|
resolvePrincipal as resolvePrincipal2,
|
|
70151
|
-
resolveAgentAccessAuthToken as resolveAgentAccessAuthToken2
|
|
70152
|
-
hasEnabledLiveConnector as hasEnabledLiveConnector2
|
|
71574
|
+
resolveAgentAccessAuthToken as resolveAgentAccessAuthToken2
|
|
70153
71575
|
} from "@remnic/core";
|
|
70154
71576
|
|
|
70155
71577
|
// ../remnic-core/src/surfaces/dreams.ts
|
|
@@ -70728,6 +72150,17 @@ function liveConnectorCronExprForConfig(connectors) {
|
|
|
70728
72150
|
return hasEnabledConnector ? ENABLED_LIVE_CONNECTOR_CRON_EXPR : DEFAULT_LIVE_CONNECTOR_CRON_EXPR;
|
|
70729
72151
|
}
|
|
70730
72152
|
|
|
72153
|
+
// ../../src/openclaw-live-connector-config.ts
|
|
72154
|
+
function hasEnabledLiveConnectorConfig(config) {
|
|
72155
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) return false;
|
|
72156
|
+
return Object.values(config).some((connector) => {
|
|
72157
|
+
if (!connector || typeof connector !== "object" || Array.isArray(connector)) {
|
|
72158
|
+
return false;
|
|
72159
|
+
}
|
|
72160
|
+
return connector.enabled === true;
|
|
72161
|
+
});
|
|
72162
|
+
}
|
|
72163
|
+
|
|
70731
72164
|
// ../../src/index.ts
|
|
70732
72165
|
var ENGRAM_MIGRATION_PROMISE = "__openclawEngramMigrationPromise";
|
|
70733
72166
|
var CLI_REGISTERED_GUARD = "__openclawEngramCliRegistered";
|
|
@@ -70839,7 +72272,7 @@ function readPluginHooksPolicy(apiConfig, pluginId) {
|
|
|
70839
72272
|
return loadPluginEntryFromFile(pluginId)?.["hooks"];
|
|
70840
72273
|
}
|
|
70841
72274
|
async function maybeRegisterLiveConnectorCron(orchestrator) {
|
|
70842
|
-
if (!
|
|
72275
|
+
if (!hasEnabledLiveConnectorConfig(orchestrator.config.connectors)) return;
|
|
70843
72276
|
const jobsPath = path99.join(resolveHomeDir(), ".openclaw", "cron", "jobs.json");
|
|
70844
72277
|
try {
|
|
70845
72278
|
if (!existsSync12(jobsPath)) {
|
|
@@ -72525,7 +73958,12 @@ Keep the reflection grounded in the evidence below.
|
|
|
72525
73958
|
const lcmMessages = lastTurn.map((msg) => {
|
|
72526
73959
|
const rawRole = typeof msg.role === "string" ? msg.role : "";
|
|
72527
73960
|
const content = extractTextContent2(msg);
|
|
72528
|
-
return {
|
|
73961
|
+
return {
|
|
73962
|
+
role: rawRole,
|
|
73963
|
+
content,
|
|
73964
|
+
rawContent: msg,
|
|
73965
|
+
sourceFormat: "openclaw"
|
|
73966
|
+
};
|
|
72529
73967
|
}).filter((m) => m.content.length > 0);
|
|
72530
73968
|
if (lcmMessages.length > 0) {
|
|
72531
73969
|
orchestrator.lcmEngine.enqueueObserveMessages(
|