@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 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 = 1;
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
- this.appendMessage(sessionId, msg.turnIndex, msg.role, msg.content, msg.metadata);
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 asNonEmptyString(value) {
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 asNonEmptyString(value);
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 = asNonEmptyString(value);
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 = asNonEmptyString(value);
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 = asNonEmptyString(value);
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 = asNonEmptyString(value);
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 = asNonEmptyString(params.memoryId) ?? asNonEmptyString(params.category) ?? asNonEmptyString(params.linkTargetId);
43342
- const content = asNonEmptyString(params.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 (!asNonEmptyString(sessionKey)) validationErrors.push("sessionKey is required");
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: asNonEmptyString(sessionKey),
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 = asNonEmptyString(content);
44397
- const memoryIdValue = asNonEmptyString(memoryId);
44398
- const linkTargetIdValue = asNonEmptyString(linkTargetId);
44399
- const linkTypeValue = asNonEmptyString(linkType);
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: asNonEmptyString(sessionKey),
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: asNonEmptyString(p.owner),
45319
- assignee: asNonEmptyString(p.assignee),
45320
- projectId: asNonEmptyString(p.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: asNonEmptyString(p.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 = asNonEmptyString(p.id);
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: asNonEmptyString(p.owner),
45338
- assignee: asNonEmptyString(p.assignee),
45339
- projectId: asNonEmptyString(p.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 = asNonEmptyString(p.id);
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 = asNonEmptyString(p.id);
45369
- const rawStatus = asNonEmptyString(p.status);
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 = asNonEmptyString(p.id);
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: asNonEmptyString(p.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 = asNonEmptyString(p.id);
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 = asNonEmptyString(p.id);
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 = asNonEmptyString(p.id);
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 = asNonEmptyString(p.taskId);
45472
- const projectId = asNonEmptyString(p.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 = asNonEmptyString(p.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: asNonEmptyString(p.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 = asNonEmptyString(params.format) ?? "ascii";
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 = asNonEmptyString(params.format) ?? "ascii";
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
- if ("cwd" in args && args.cwd !== void 0 && args.cwd !== null && typeof args.cwd !== "string") {
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: typeof args.sessionKey === "string" ? args.sessionKey : "",
58705
- messages: Array.isArray(args.messages) ? args.messages : [],
58706
- namespace: typeof args.namespace === "string" ? args.namespace : void 0,
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: args.skipExtraction === true,
58709
- cwd: typeof args.cwd === "string" ? args.cwd : void 0,
58710
- projectTag: typeof args.projectTag === "string" ? args.projectTag : void 0
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 isRecord3(value) {
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 (isRecord3(value)) return value;
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 isRecord3(parsed) ? parsed : void 0;
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 (isRecord3(block) && block.type === "text" && typeof block.text === "string") {
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 (isRecord3(value)) {
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 (!isRecord3(call)) continue;
69313
+ if (!isRecord4(call)) continue;
67891
69314
  const toolCallId = optionalString2(call.id) ?? optionalString2(call.toolCallId);
67892
69315
  if (!toolCallId) continue;
67893
- const fn = isRecord3(call.function) ? call.function : void 0;
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 (isRecord3(parsedPayload)) {
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 (!hasEnabledLiveConnector2(orchestrator.config.connectors)) return;
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 { role: rawRole, content };
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(