@remnic/plugin-openclaw 1.0.11 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/dist/capsule-cli-GBM3WPAM.js +33 -0
  2. package/dist/capsule-export-IXVERCQG.js +17 -0
  3. package/dist/{capsule-import-CFX7BY5W.js → capsule-import-IA6VIOPQ.js} +3 -3
  4. package/dist/{capsule-merge-7RVOHJK3.js → capsule-merge-IWOQ34KL.js} +2 -2
  5. package/dist/{causal-consolidation-JD6KJJH6.js → causal-consolidation-YI53C2AO.js} +3 -3
  6. package/dist/{chunk-K7EUBNDD.js → chunk-4LYQ4ONL.js} +1 -1
  7. package/dist/{chunk-N7EOZY6F.js → chunk-6F6EKSVP.js} +54 -1
  8. package/dist/{chunk-TNH24SF6.js → chunk-7UZNLMW5.js} +301 -125
  9. package/dist/{chunk-TVKKIS53.js → chunk-CDAZGIGT.js} +1 -1
  10. package/dist/{chunk-XMSDA5WA.js → chunk-EXDYWXMB.js} +1 -0
  11. package/dist/{capsule-export-CVA3CKUQ.js → chunk-FGTYFLL5.js} +21 -12
  12. package/dist/{chunk-ETJZRIAM.js → chunk-L6I4MQKO.js} +1 -1
  13. package/dist/{capsule-cli-TFKLAG3S.js → chunk-LLUROTZJ.js} +11 -12
  14. package/dist/{chunk-L4PRBB2A.js → chunk-OAE7AQ6R.js} +63 -91
  15. package/dist/{chunk-3I7RHWYT.js → chunk-RKR6PTPA.js} +105 -11
  16. package/dist/{chunk-P3DIW2SD.js → chunk-TILAJIJR.js} +1 -1
  17. package/dist/{engine-VMTFKFGO.js → engine-BIYI3P4J.js} +6 -4
  18. package/dist/index.js +1864 -381
  19. package/dist/{memory-governance-DWGFV4FX.js → memory-governance-SJ5DGRB3.js} +3 -3
  20. package/dist/{secure-store-FWJ7LBPH.js → secure-store-A4NGCNXV.js} +8 -2
  21. package/dist/{storage-T2OGFUF4.js → storage-PTQ2H2YJ.js} +2 -2
  22. package/dist/{types-H5R5D3WF.js → types-R4DO7AKM.js} +2 -2
  23. package/openclaw.plugin.json +10 -0
  24. package/package.json +2 -2
  25. package/dist/chunk-YGXXBRV7.js +0 -10
package/dist/index.js CHANGED
@@ -1,10 +1,6 @@
1
1
  import {
2
2
  compareVersions
3
3
  } from "./chunk-GUSMRW4H.js";
4
- import {
5
- EXPORT_FORMAT,
6
- EXPORT_SCHEMA_VERSION
7
- } from "./chunk-YGXXBRV7.js";
8
4
  import {
9
5
  isSafeRouteNamespace,
10
6
  selectRouteRule,
@@ -12,10 +8,20 @@ import {
12
8
  } from "./chunk-FQRSVYY4.js";
13
9
  import {
14
10
  importCapsule
15
- } from "./chunk-ETJZRIAM.js";
11
+ } from "./chunk-L6I4MQKO.js";
16
12
  import {
13
+ EXPORT_FORMAT,
14
+ EXPORT_SCHEMA_VERSION,
15
+ exportCapsule,
16
+ isValidCapsuleSince
17
+ } from "./chunk-FGTYFLL5.js";
18
+ import {
19
+ CAPSULE_ID_PATTERN,
17
20
  ExportBundleV1Schema
18
- } from "./chunk-K7EUBNDD.js";
21
+ } from "./chunk-4LYQ4ONL.js";
22
+ import {
23
+ defaultCapsulesDir
24
+ } from "./chunk-LLUROTZJ.js";
19
25
  import {
20
26
  TierMigrationExecutor
21
27
  } from "./chunk-BU5KJVWF.js";
@@ -37,7 +43,7 @@ import {
37
43
  readMemoryGovernanceRunArtifact,
38
44
  restoreMemoryGovernanceRun,
39
45
  runMemoryGovernance
40
- } from "./chunk-TVKKIS53.js";
46
+ } from "./chunk-CDAZGIGT.js";
41
47
  import {
42
48
  clamp01,
43
49
  clampLifecycleThreshold,
@@ -84,7 +90,7 @@ import {
84
90
  parseOperatorAwareConsolidationResponse,
85
91
  renderExtensionsFooter,
86
92
  resolveExtensionsRoot
87
- } from "./chunk-P3DIW2SD.js";
93
+ } from "./chunk-TILAJIJR.js";
88
94
  import {
89
95
  BoxBuilder,
90
96
  countRecallTokenOverlap,
@@ -109,11 +115,12 @@ import {
109
115
  CompoundingEngine,
110
116
  SharedContextManager,
111
117
  defaultTierMigrationCycleBudget
112
- } from "./chunk-L4PRBB2A.js";
118
+ } from "./chunk-OAE7AQ6R.js";
113
119
  import {
120
+ ZodError,
114
121
  external_exports
115
- } from "./chunk-XMSDA5WA.js";
116
- import "./chunk-N7EOZY6F.js";
122
+ } from "./chunk-EXDYWXMB.js";
123
+ import "./chunk-6F6EKSVP.js";
117
124
  import {
118
125
  keyring_exports,
119
126
  secureStoreDir
@@ -181,11 +188,11 @@ import {
181
188
  sortMemoryLifecycleEvents,
182
189
  stripCitationForTemplate,
183
190
  toMemoryPathRel
184
- } from "./chunk-TNH24SF6.js";
191
+ } from "./chunk-7UZNLMW5.js";
185
192
  import {
186
193
  sidecarKey
187
194
  } from "./chunk-6OJAU466.js";
188
- import "./chunk-3I7RHWYT.js";
195
+ import "./chunk-RKR6PTPA.js";
189
196
  import {
190
197
  initLogger,
191
198
  log
@@ -1899,6 +1906,8 @@ function parseConfig(raw) {
1899
1906
  lcmRecallBudgetShare: typeof cfg.lcmRecallBudgetShare === "number" ? Math.max(0, Math.min(1, cfg.lcmRecallBudgetShare)) : 0.15,
1900
1907
  lcmDeterministicMaxTokens: typeof cfg.lcmDeterministicMaxTokens === "number" ? Math.max(64, Math.floor(cfg.lcmDeterministicMaxTokens)) : 512,
1901
1908
  lcmArchiveRetentionDays: typeof cfg.lcmArchiveRetentionDays === "number" ? Math.max(1, Math.floor(cfg.lcmArchiveRetentionDays)) : 90,
1909
+ messagePartsEnabled: coerceBooleanLike(cfg.messagePartsEnabled) === true,
1910
+ messagePartsRecallMaxResults: typeof cfg.messagePartsRecallMaxResults === "number" ? Math.max(0, Math.floor(cfg.messagePartsRecallMaxResults)) : 6,
1902
1911
  // v9.1 Parallel Specialized Retrieval
1903
1912
  parallelRetrievalEnabled: cfg.parallelRetrievalEnabled === true,
1904
1913
  parallelAgentWeights: (() => {
@@ -19342,7 +19351,9 @@ var RECALL_XRAY_SERVED_BY_VALUES = [
19342
19351
  "graph",
19343
19352
  "recent-scan",
19344
19353
  "procedural",
19345
- "review-context"
19354
+ "review-context",
19355
+ "lcm-file-parts",
19356
+ "lcm-tool-parts"
19346
19357
  ];
19347
19358
  function isRecallXrayServedBy(value) {
19348
19359
  return typeof value === "string" && RECALL_XRAY_SERVED_BY_VALUES.includes(value);
@@ -26173,7 +26184,7 @@ function validateReplayTurn(turn, index) {
26173
26184
  // ../remnic-core/src/lcm/schema.ts
26174
26185
  import path37 from "path";
26175
26186
  import { mkdir as mkdir26 } from "fs/promises";
26176
- var LCM_SCHEMA_VERSION = 1;
26187
+ var LCM_SCHEMA_VERSION = 2;
26177
26188
  function openLcmDatabase(memoryDir) {
26178
26189
  const dbPath = path37.join(memoryDir, "state", "lcm.sqlite");
26179
26190
  const db = openBetterSqlite3(dbPath);
@@ -26219,6 +26230,23 @@ function createTables(db) {
26219
26230
  CREATE INDEX IF NOT EXISTS idx_lcm_messages_session
26220
26231
  ON lcm_messages(session_id, turn_index);
26221
26232
 
26233
+ CREATE TABLE IF NOT EXISTS lcm_message_parts (
26234
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
26235
+ message_id INTEGER NOT NULL REFERENCES lcm_messages(id) ON DELETE CASCADE,
26236
+ ordinal INTEGER NOT NULL,
26237
+ kind TEXT NOT NULL,
26238
+ payload TEXT NOT NULL,
26239
+ tool_name TEXT,
26240
+ file_path TEXT,
26241
+ created_at TEXT NOT NULL
26242
+ );
26243
+ CREATE INDEX IF NOT EXISTS idx_lcm_message_parts_msg
26244
+ ON lcm_message_parts(message_id, ordinal);
26245
+ CREATE INDEX IF NOT EXISTS idx_lcm_message_parts_tool
26246
+ ON lcm_message_parts(tool_name);
26247
+ CREATE INDEX IF NOT EXISTS idx_lcm_message_parts_file
26248
+ ON lcm_message_parts(file_path);
26249
+
26222
26250
  CREATE TABLE IF NOT EXISTS lcm_summary_nodes (
26223
26251
  id TEXT PRIMARY KEY,
26224
26252
  session_id TEXT NOT NULL,
@@ -26271,6 +26299,423 @@ function createTables(db) {
26271
26299
  );
26272
26300
  }
26273
26301
 
26302
+ // ../remnic-core/src/message-parts/index.ts
26303
+ var LCM_MESSAGE_PART_KINDS = [
26304
+ "text",
26305
+ "tool_call",
26306
+ "tool_result",
26307
+ "patch",
26308
+ "file_read",
26309
+ "file_write",
26310
+ "step_start",
26311
+ "step_finish",
26312
+ "snapshot",
26313
+ "retry"
26314
+ ];
26315
+ var SECRET_KEY_RE = /(api[_-]?key|authorization|bearer|credential|password|secret|token)/i;
26316
+ var MAX_PAYLOAD_STRING = 8e3;
26317
+ var MAX_FILE_SCAN_CHARS = 2e4;
26318
+ function isLcmMessagePartKind(value) {
26319
+ return typeof value === "string" && LCM_MESSAGE_PART_KINDS.includes(value);
26320
+ }
26321
+ function parseMessageParts(input, options = {}) {
26322
+ const explicit = normalizeExplicitParts(input);
26323
+ if (explicit.length > 0) return explicit;
26324
+ const format = options.sourceFormat ?? inferSourceFormat(input);
26325
+ switch (format) {
26326
+ case "openai":
26327
+ return withRenderedFallback(parseOpenAiMessageParts(input, options), options);
26328
+ case "anthropic":
26329
+ return withRenderedFallback(parseAnthropicMessageParts(input, options), options);
26330
+ case "openclaw":
26331
+ return withRenderedFallback(parseOpenClawMessageParts(input, options), options);
26332
+ case "lossless-claw":
26333
+ case "remnic":
26334
+ return withRenderedFallback(normalizeExplicitParts(input), options);
26335
+ default:
26336
+ return renderedFallbackParts(options);
26337
+ }
26338
+ }
26339
+ function normalizeExplicitParts(input) {
26340
+ const rawParts = pickArray(input, "parts") ?? pickArray(input, "message_parts");
26341
+ if (!rawParts) return [];
26342
+ const parts = [];
26343
+ rawParts.forEach((raw, index) => {
26344
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
26345
+ const obj = raw;
26346
+ const kind = normalizeKind(obj.kind ?? obj.type);
26347
+ if (!kind) return;
26348
+ const payload = obj.payload && typeof obj.payload === "object" && !Array.isArray(obj.payload) ? obj.payload : { value: sanitizePayload(obj) };
26349
+ const toolName = asNonEmptyString(obj.toolName ?? obj.tool_name ?? obj.name);
26350
+ const filePath = asNonEmptyString(obj.filePath ?? obj.file_path ?? obj.path);
26351
+ const ordinal = typeof obj.ordinal === "number" && Number.isInteger(obj.ordinal) ? Math.max(0, obj.ordinal) : index;
26352
+ parts.push({
26353
+ ordinal,
26354
+ kind,
26355
+ payload: sanitizePayload(payload),
26356
+ toolName,
26357
+ filePath,
26358
+ createdAt: asNonEmptyString(obj.createdAt ?? obj.created_at)
26359
+ });
26360
+ });
26361
+ return parts;
26362
+ }
26363
+ function parseOpenAiMessageParts(input, _options = {}) {
26364
+ const items = gatherOpenAiItems(input);
26365
+ const parts = [];
26366
+ for (const item of items) {
26367
+ const type = asNonEmptyString(item.type) ?? asNonEmptyString(item.kind);
26368
+ if (!type) continue;
26369
+ if (isOpenAiContentBlock(item)) {
26370
+ const text = asNonEmptyString(item.text ?? item.content);
26371
+ if (text) parts.push(makePart("text", { type, text }, { filePath: firstFilePath(text) }));
26372
+ continue;
26373
+ }
26374
+ if (type === "message") {
26375
+ for (const block of gatherContentBlocks(item.content)) {
26376
+ const text = asNonEmptyString(block.text ?? block.content);
26377
+ if (text) parts.push(makePart("text", { type, text }, { filePath: firstFilePath(text) }));
26378
+ }
26379
+ continue;
26380
+ }
26381
+ if (type === "function_call") {
26382
+ const toolName = asNonEmptyString(item.name ?? item.tool_name);
26383
+ const payload = {
26384
+ id: item.id ?? item.call_id,
26385
+ name: toolName,
26386
+ arguments: parseMaybeJson(item.arguments)
26387
+ };
26388
+ parts.push(classifyToolPart(toolName, payload));
26389
+ continue;
26390
+ }
26391
+ if (type === "function_call_output") {
26392
+ const output = asNonEmptyString(item.output) ?? JSON.stringify(sanitizePayload(item.output ?? item));
26393
+ parts.push(makePart("tool_result", { id: item.id ?? item.call_id, output }, {
26394
+ filePath: firstFilePath(output)
26395
+ }));
26396
+ continue;
26397
+ }
26398
+ if (type === "reasoning") {
26399
+ parts.push(makePart("step_start", { type, summary: sanitizePayload(item.summary ?? item) }));
26400
+ continue;
26401
+ }
26402
+ if (type === "retry") {
26403
+ parts.push(makePart("retry", { type, item: sanitizePayload(item) }));
26404
+ }
26405
+ }
26406
+ return withOrdinals(parts);
26407
+ }
26408
+ function parseAnthropicMessageParts(input, _options = {}) {
26409
+ const blocks = gatherContentBlocks(
26410
+ Array.isArray(input) ? input : input && typeof input === "object" ? input.content : input
26411
+ );
26412
+ const parts = [];
26413
+ for (const block of blocks) {
26414
+ const type = asNonEmptyString(block.type);
26415
+ if (type === "text") {
26416
+ const text = asNonEmptyString(block.text);
26417
+ if (text) parts.push(makePart("text", { type, text }, { filePath: firstFilePath(text) }));
26418
+ continue;
26419
+ }
26420
+ if (type === "tool_use") {
26421
+ const toolName = asNonEmptyString(block.name);
26422
+ parts.push(classifyToolPart(toolName, {
26423
+ id: block.id,
26424
+ name: toolName,
26425
+ input: sanitizePayload(block.input)
26426
+ }));
26427
+ continue;
26428
+ }
26429
+ if (type === "tool_result") {
26430
+ const content = block.content;
26431
+ const rendered = renderUnknownContent(content);
26432
+ parts.push(makePart("tool_result", { id: block.tool_use_id, content: sanitizePayload(content) }, {
26433
+ filePath: firstFilePath(rendered)
26434
+ }));
26435
+ continue;
26436
+ }
26437
+ if (type === "thinking") {
26438
+ parts.push(makePart("step_start", {
26439
+ type,
26440
+ thinking: truncateString(asNonEmptyString(block.thinking) ?? ""),
26441
+ signature: asNonEmptyString(block.signature)
26442
+ }));
26443
+ continue;
26444
+ }
26445
+ if (type === "redacted_thinking") {
26446
+ parts.push(makePart("step_finish", { type }));
26447
+ }
26448
+ }
26449
+ return withOrdinals(parts);
26450
+ }
26451
+ function parseOpenClawMessageParts(input, options = {}) {
26452
+ const explicit = normalizeExplicitParts(input);
26453
+ if (explicit.length > 0) return explicit;
26454
+ if (!input || typeof input !== "object") return [];
26455
+ const obj = input;
26456
+ const content = obj.content;
26457
+ if (Array.isArray(content)) {
26458
+ const hasOpenAiBlocks = content.some(isOpenAiContentBlock);
26459
+ if (hasOpenAiBlocks) return parseOpenAiMessageParts(content, options);
26460
+ const hasAnthropicBlocks = content.some(
26461
+ (block) => block && typeof block === "object" && typeof block.type === "string"
26462
+ );
26463
+ if (hasAnthropicBlocks) return parseAnthropicMessageParts({ content }, options);
26464
+ }
26465
+ const toolName = asNonEmptyString(obj.toolName ?? obj.tool_name ?? obj.name);
26466
+ if (toolName) {
26467
+ return withOrdinals([
26468
+ classifyToolPart(toolName, {
26469
+ name: toolName,
26470
+ input: sanitizePayload(obj.input ?? obj.arguments ?? obj.params),
26471
+ output: sanitizePayload(obj.output ?? obj.result)
26472
+ })
26473
+ ]);
26474
+ }
26475
+ const rendered = options.renderedContent ?? asNonEmptyString(obj.content);
26476
+ return rendered ? withOrdinals(partsFromRenderedText(rendered)) : [];
26477
+ }
26478
+ function partsFromRenderedText(text) {
26479
+ if (text.includes("*** Begin Patch")) {
26480
+ const paths2 = extractFilePaths(text);
26481
+ const patchPaths = extractPatchPaths(text);
26482
+ return withOrdinals((patchPaths.length > 0 ? patchPaths : paths2).map(
26483
+ (filePath) => makePart("patch", { text: truncateString(text) }, { filePath })
26484
+ ));
26485
+ }
26486
+ const paths = extractFilePaths(text);
26487
+ if (paths.length === 0) return [];
26488
+ return withOrdinals(paths.map(
26489
+ (filePath) => makePart("file_read", { text: truncateString(text) }, { filePath })
26490
+ ));
26491
+ }
26492
+ function inferSourceFormat(input) {
26493
+ if (input && typeof input === "object") {
26494
+ const obj = input;
26495
+ const explicit = asNonEmptyString(obj.sourceFormat ?? obj.source_format);
26496
+ if (explicit === "openai" || explicit === "anthropic" || explicit === "openclaw" || explicit === "lossless-claw" || explicit === "remnic") {
26497
+ return explicit;
26498
+ }
26499
+ if (Array.isArray(obj.output)) return "openai";
26500
+ if (isOpenAiResponseItem(obj)) return "openai";
26501
+ if (Array.isArray(obj.content) && obj.content.some(isOpenAiContentBlock)) return "openai";
26502
+ if (Array.isArray(obj.content)) return "anthropic";
26503
+ }
26504
+ if (Array.isArray(input)) {
26505
+ return input.some(
26506
+ (item) => isRecord3(item) && (isOpenAiResponseItem(item) || isOpenAiContentBlock(item))
26507
+ ) ? "openai" : "anthropic";
26508
+ }
26509
+ return void 0;
26510
+ }
26511
+ function isOpenAiResponseItem(obj) {
26512
+ const type = asNonEmptyString(obj.type ?? obj.kind);
26513
+ return type === "message" || type === "function_call" || type === "function_call_output" || type === "reasoning" || type === "retry";
26514
+ }
26515
+ function isOpenAiContentBlock(value) {
26516
+ if (!isRecord3(value)) return false;
26517
+ const type = asNonEmptyString(value.type);
26518
+ return type === "input_text" || type === "output_text" || type === "input_image" || type === "input_file" || type === "refusal";
26519
+ }
26520
+ function gatherOpenAiItems(input) {
26521
+ if (Array.isArray(input)) return input.filter(isRecord3);
26522
+ if (!isRecord3(input)) return [];
26523
+ if (Array.isArray(input.output)) return input.output.filter(isRecord3);
26524
+ if (Array.isArray(input.items)) return input.items.filter(isRecord3);
26525
+ return [input];
26526
+ }
26527
+ function gatherContentBlocks(input) {
26528
+ if (Array.isArray(input)) return input.filter(isRecord3);
26529
+ if (typeof input === "string") return [{ type: "text", text: input }];
26530
+ if (isRecord3(input)) return [input];
26531
+ return [];
26532
+ }
26533
+ function classifyToolPart(toolName, payload) {
26534
+ const normalized = (toolName ?? "").toLowerCase();
26535
+ const rendered = renderUnknownContent(payload);
26536
+ const filePath = firstFilePathFromObject(payload) ?? firstFilePath(rendered) ?? null;
26537
+ if (normalized.includes("apply_patch") || rendered.includes("*** Begin Patch")) {
26538
+ return makePart("patch", payload, { toolName, filePath: filePath ?? extractPatchPaths(rendered)[0] ?? null });
26539
+ }
26540
+ if (/(write|edit|multiedit|create|save)/i.test(normalized)) {
26541
+ return makePart("file_write", payload, { toolName, filePath });
26542
+ }
26543
+ if (/(read|grep|glob|search|list|ls)/i.test(normalized)) {
26544
+ return makePart("file_read", payload, { toolName, filePath });
26545
+ }
26546
+ return makePart("tool_call", payload, { toolName, filePath });
26547
+ }
26548
+ function makePart(kind, payload, options = {}) {
26549
+ return {
26550
+ kind,
26551
+ payload: sanitizePayload(payload),
26552
+ toolName: options.toolName ?? null,
26553
+ filePath: options.filePath ?? null
26554
+ };
26555
+ }
26556
+ function withOrdinals(parts) {
26557
+ return parts.map((part, ordinal) => ({ ...part, ordinal: part.ordinal ?? ordinal }));
26558
+ }
26559
+ function withRenderedFallback(parts, options) {
26560
+ return parts.length > 0 ? parts : renderedFallbackParts(options);
26561
+ }
26562
+ function renderedFallbackParts(options) {
26563
+ const rendered = asNonEmptyString(options.renderedContent);
26564
+ return rendered ? partsFromRenderedText(rendered) : [];
26565
+ }
26566
+ function normalizeKind(value) {
26567
+ if (isLcmMessagePartKind(value)) return value;
26568
+ if (value === "tool_use" || value === "function_call") return "tool_call";
26569
+ if (value === "function_call_output") return "tool_result";
26570
+ if (value === "thinking" || value === "reasoning") return "step_start";
26571
+ return null;
26572
+ }
26573
+ function pickArray(input, key) {
26574
+ if (!input || typeof input !== "object" || Array.isArray(input)) return null;
26575
+ const value = input[key];
26576
+ return Array.isArray(value) ? value : null;
26577
+ }
26578
+ function asNonEmptyString(value) {
26579
+ if (typeof value !== "string") return null;
26580
+ const trimmed = value.trim();
26581
+ return trimmed.length > 0 ? trimmed : null;
26582
+ }
26583
+ function isRecord3(value) {
26584
+ return !!value && typeof value === "object" && !Array.isArray(value);
26585
+ }
26586
+ function parseMaybeJson(value) {
26587
+ if (typeof value !== "string") return sanitizePayload(value);
26588
+ try {
26589
+ return sanitizePayload(JSON.parse(value));
26590
+ } catch {
26591
+ return truncateString(value);
26592
+ }
26593
+ }
26594
+ function sanitizePayload(value, depth = 0) {
26595
+ if (value === null || value === void 0) return value;
26596
+ if (typeof value === "string") return truncateString(value);
26597
+ if (typeof value === "number" || typeof value === "boolean") return value;
26598
+ if (Array.isArray(value)) {
26599
+ if (depth >= 4) return "[truncated]";
26600
+ return value.slice(0, 100).map((item) => sanitizePayload(item, depth + 1));
26601
+ }
26602
+ if (typeof value === "object") {
26603
+ if (depth >= 4) return "[truncated]";
26604
+ const out = {};
26605
+ for (const [key, child] of Object.entries(value)) {
26606
+ out[key] = SECRET_KEY_RE.test(key) ? "[redacted]" : sanitizePayload(child, depth + 1);
26607
+ }
26608
+ return out;
26609
+ }
26610
+ return String(value);
26611
+ }
26612
+ function truncateString(value) {
26613
+ return value.length > MAX_PAYLOAD_STRING ? `${value.slice(0, MAX_PAYLOAD_STRING)}...[truncated]` : value;
26614
+ }
26615
+ function renderUnknownContent(value) {
26616
+ if (typeof value === "string") return value;
26617
+ try {
26618
+ return JSON.stringify(value ?? "");
26619
+ } catch {
26620
+ return String(value ?? "");
26621
+ }
26622
+ }
26623
+ function firstFilePathFromObject(value) {
26624
+ if (!isRecord3(value)) return null;
26625
+ const keys = ["file_path", "filePath", "path", "filename", "cwd"];
26626
+ for (const key of keys) {
26627
+ const candidate = asNonEmptyString(value[key]);
26628
+ if (candidate) return candidate;
26629
+ }
26630
+ for (const child of Object.values(value)) {
26631
+ if (typeof child === "string") {
26632
+ const fromText = extractPatchPaths(child)[0] ?? firstFilePath(child);
26633
+ if (fromText) return fromText;
26634
+ }
26635
+ if (isRecord3(child)) {
26636
+ const nested = firstFilePathFromObject(child);
26637
+ if (nested) return nested;
26638
+ }
26639
+ }
26640
+ return null;
26641
+ }
26642
+ function firstFilePath(text) {
26643
+ return extractFilePaths(text)[0] ?? null;
26644
+ }
26645
+ function extractFilePaths(text) {
26646
+ const out = /* @__PURE__ */ new Set();
26647
+ let token = "";
26648
+ const scanLength = Math.min(text.length, MAX_FILE_SCAN_CHARS);
26649
+ for (let index = 0; index <= scanLength; index += 1) {
26650
+ const char = index < scanLength ? text[index] : " ";
26651
+ if (isFilePathTokenSeparator(char)) {
26652
+ addFilePathCandidate(out, token);
26653
+ token = "";
26654
+ continue;
26655
+ }
26656
+ token += char;
26657
+ if (token.length > 512) {
26658
+ addFilePathCandidate(out, token);
26659
+ token = "";
26660
+ }
26661
+ }
26662
+ return [...out].slice(0, 20);
26663
+ }
26664
+ function isFilePathTokenSeparator(char) {
26665
+ return char === " " || char === "\n" || char === "\r" || char === " " || char === '"' || char === "'" || char === "`" || char === "(" || char === ")" || char === "[" || char === "]" || char === "{" || char === "}" || char === "<" || char === ">" || char === ",";
26666
+ }
26667
+ function addFilePathCandidate(out, raw) {
26668
+ const candidate = trimFilePathPunctuation(raw);
26669
+ if (candidate.length === 0 || candidate.includes("://")) return;
26670
+ if (isLikelyFilePath(candidate)) out.add(candidate);
26671
+ }
26672
+ function trimFilePathPunctuation(raw) {
26673
+ let start = 0;
26674
+ let end = raw.length;
26675
+ while (start < end && isLeadingFilePathPunctuation(raw[start])) start += 1;
26676
+ while (end > start && isTrailingFilePathPunctuation(raw[end - 1])) end -= 1;
26677
+ return raw.slice(start, end);
26678
+ }
26679
+ function isLeadingFilePathPunctuation(char) {
26680
+ return char === ":" || char === ";" || char === "!" || char === "?" || char === "|" || char === "*" || char === "=";
26681
+ }
26682
+ function isTrailingFilePathPunctuation(char) {
26683
+ return char === "." || char === ":" || char === ";" || char === "!" || char === "?" || char === "|" || char === "*" || char === "=";
26684
+ }
26685
+ function isLikelyFilePath(value) {
26686
+ if (value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value.startsWith("~/")) {
26687
+ return hasValidFileExtension(value);
26688
+ }
26689
+ if (value.includes("/")) return hasValidFileExtension(value);
26690
+ return hasValidFileExtension(value);
26691
+ }
26692
+ function hasValidFileExtension(value) {
26693
+ const lastSlash = value.lastIndexOf("/");
26694
+ const basename3 = value.slice(lastSlash + 1);
26695
+ const dot = basename3.lastIndexOf(".");
26696
+ if (dot <= 0 || dot === basename3.length - 1) return false;
26697
+ const ext = basename3.slice(dot + 1);
26698
+ if (ext.length < 1 || ext.length > 12) return false;
26699
+ for (const char of ext) {
26700
+ if (!isFileExtensionChar(char)) return false;
26701
+ }
26702
+ return true;
26703
+ }
26704
+ function isFileExtensionChar(char) {
26705
+ const code = char.charCodeAt(0);
26706
+ return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || char === "_" || char === "+" || char === "-";
26707
+ }
26708
+ function extractPatchPaths(text) {
26709
+ const out = /* @__PURE__ */ new Set();
26710
+ for (const line of text.split(/\r?\n/)) {
26711
+ const match = line.match(/^\*\*\* (?:Add|Update|Delete) File: (.+)$/);
26712
+ if (match?.[1]) out.add(match[1].trim());
26713
+ const move = line.match(/^\*\*\* Move to: (.+)$/);
26714
+ if (move?.[1]) out.add(move[1].trim());
26715
+ }
26716
+ return [...out].slice(0, 20);
26717
+ }
26718
+
26274
26719
  // ../remnic-core/src/lcm/archive.ts
26275
26720
  function estimateTokens3(text) {
26276
26721
  return Math.ceil(text.length / 4);
@@ -26281,7 +26726,7 @@ var LcmArchive = class {
26281
26726
  }
26282
26727
  db;
26283
26728
  /** Append a message to the archive. Returns the row id. */
26284
- appendMessage(sessionId, turnIndex, role, content, metadata) {
26729
+ appendMessage(sessionId, turnIndex, role, content, metadata, parts) {
26285
26730
  const tokenCount = estimateTokens3(content);
26286
26731
  const now = (/* @__PURE__ */ new Date()).toISOString();
26287
26732
  const metaJson = metadata ? JSON.stringify(metadata) : null;
@@ -26292,18 +26737,58 @@ var LcmArchive = class {
26292
26737
  const result = stmt.run(sessionId, turnIndex, role, content, tokenCount, now, metaJson);
26293
26738
  const rowId = Number(result.lastInsertRowid);
26294
26739
  this.db.prepare("INSERT INTO lcm_messages_fts (rowid, content) VALUES (?, ?)").run(rowId, content);
26740
+ if (parts && parts.length > 0) {
26741
+ this.insertMessageParts(rowId, parts, now);
26742
+ }
26295
26743
  return rowId;
26296
26744
  }
26297
26745
  /** Append multiple messages in a single transaction. */
26298
- appendMessages(sessionId, messages) {
26746
+ appendMessages(sessionId, messages, options = {}) {
26299
26747
  if (messages.length === 0) return;
26748
+ const captureMessageParts = options.messagePartsEnabled !== false;
26300
26749
  const txn = this.db.transaction(() => {
26301
26750
  for (const msg of messages) {
26302
- this.appendMessage(sessionId, msg.turnIndex, msg.role, msg.content, msg.metadata);
26751
+ const explicitParts = msg.parts && msg.parts.length > 0 ? msg.parts : void 0;
26752
+ const rawContent = msg.rawContent ?? msg.content;
26753
+ const parts = captureMessageParts ? explicitParts ?? parseMessageParts(rawContent, {
26754
+ sourceFormat: msg.sourceFormat,
26755
+ renderedContent: msg.content
26756
+ }) : void 0;
26757
+ this.appendMessage(
26758
+ sessionId,
26759
+ msg.turnIndex,
26760
+ msg.role,
26761
+ msg.content,
26762
+ msg.metadata,
26763
+ parts
26764
+ );
26303
26765
  }
26304
26766
  });
26305
26767
  txn();
26306
26768
  }
26769
+ insertMessageParts(messageId, parts, fallbackCreatedAt) {
26770
+ if (parts.length === 0) return;
26771
+ const stmt = this.db.prepare(`
26772
+ INSERT INTO lcm_message_parts (message_id, ordinal, kind, payload, tool_name, file_path, created_at)
26773
+ VALUES (?, ?, ?, ?, ?, ?, ?)
26774
+ `);
26775
+ for (let index = 0; index < parts.length; index += 1) {
26776
+ const part = parts[index];
26777
+ const rawPart = part;
26778
+ const toolName = part.toolName ?? asNullableString(rawPart.tool_name);
26779
+ const filePath = part.filePath ?? asNullableString(rawPart.file_path);
26780
+ const createdAt = part.createdAt ?? asNullableString(rawPart.created_at);
26781
+ stmt.run(
26782
+ messageId,
26783
+ part.ordinal ?? index,
26784
+ part.kind,
26785
+ JSON.stringify(part.payload ?? {}),
26786
+ toolName ?? null,
26787
+ filePath ?? null,
26788
+ createdAt ?? fallbackCreatedAt
26789
+ );
26790
+ }
26791
+ }
26307
26792
  /** Get the highest turn_index for a session, or -1 if none. */
26308
26793
  getMaxTurnIndex(sessionId) {
26309
26794
  const row = this.db.prepare("SELECT MAX(turn_index) as max_turn FROM lcm_messages WHERE session_id = ?").get(sessionId);
@@ -26425,6 +26910,55 @@ var LcmArchive = class {
26425
26910
  return [];
26426
26911
  }
26427
26912
  }
26913
+ searchStructuredParts(query, limit, sessionId) {
26914
+ const cappedLimit = Math.max(0, Math.min(20, Math.floor(limit)));
26915
+ if (cappedLimit === 0) return [];
26916
+ const fileTerms = extractStructuredFileTerms(query);
26917
+ const toolTerms = extractStructuredToolTerms(query);
26918
+ if (fileTerms.length === 0 && toolTerms.length === 0) return [];
26919
+ const matchWhere = [];
26920
+ const whereParams = [];
26921
+ for (const term of fileTerms) {
26922
+ matchWhere.push("(p.file_path = ? OR p.file_path LIKE ? ESCAPE '\\')");
26923
+ whereParams.push(term, `%${escapeLike(term)}%`);
26924
+ }
26925
+ for (const term of toolTerms) {
26926
+ matchWhere.push("p.tool_name LIKE ? ESCAPE '\\'");
26927
+ whereParams.push(`%${escapeLike(term)}%`);
26928
+ }
26929
+ const where = [`(${matchWhere.join(" OR ")})`];
26930
+ if (sessionId) {
26931
+ where.push("m.session_id = ?");
26932
+ whereParams.push(sessionId);
26933
+ }
26934
+ const exactFileScoreParams = [...fileTerms];
26935
+ const sqlParams = [...exactFileScoreParams, ...whereParams, cappedLimit];
26936
+ const rows = this.db.prepare(`
26937
+ SELECT
26938
+ p.id AS part_id,
26939
+ p.message_id AS message_id,
26940
+ m.turn_index AS turn_index,
26941
+ m.role AS role,
26942
+ m.content AS content,
26943
+ m.session_id AS session_id,
26944
+ p.kind AS kind,
26945
+ p.tool_name AS tool_name,
26946
+ p.file_path AS file_path,
26947
+ p.payload AS payload,
26948
+ CASE
26949
+ WHEN p.file_path IN (${fileTerms.map(() => "?").join(",") || "NULL"}) THEN 3
26950
+ WHEN p.file_path IS NOT NULL THEN 2
26951
+ WHEN p.tool_name IS NOT NULL THEN 1
26952
+ ELSE 0
26953
+ END AS score
26954
+ FROM lcm_message_parts p
26955
+ JOIN lcm_messages m ON m.id = p.message_id
26956
+ WHERE ${where.join(" AND ")}
26957
+ ORDER BY score DESC, m.turn_index DESC, p.ordinal ASC
26958
+ LIMIT ?
26959
+ `).all(...sqlParams);
26960
+ return rows;
26961
+ }
26428
26962
  /** Get total message count for a session. */
26429
26963
  getMessageCount(sessionId) {
26430
26964
  const row = this.db.prepare("SELECT COUNT(*) as cnt FROM lcm_messages WHERE session_id = ?").get(sessionId);
@@ -26576,6 +27110,72 @@ var STOPWORDS = /* @__PURE__ */ new Set([
26576
27110
  "we",
26577
27111
  "they"
26578
27112
  ]);
27113
+ function extractStructuredFileTerms(query) {
27114
+ const terms = /* @__PURE__ */ new Set();
27115
+ for (const raw of splitQueryTerms(query)) {
27116
+ const cleaned = trimStructuredQueryTerm(raw);
27117
+ if (cleaned.includes("/") || hasStructuredFileExtension(cleaned)) {
27118
+ terms.add(cleaned);
27119
+ const basename3 = cleaned.split("/").pop();
27120
+ if (basename3 && basename3 !== cleaned) terms.add(basename3);
27121
+ }
27122
+ }
27123
+ return [...terms].filter((term) => term.length > 1).slice(0, 12);
27124
+ }
27125
+ function splitQueryTerms(query) {
27126
+ const terms = [];
27127
+ let term = "";
27128
+ for (const char of query.slice(0, 2e4)) {
27129
+ if (char === " " || char === "\n" || char === "\r" || char === " ") {
27130
+ if (term.length > 0) terms.push(term);
27131
+ term = "";
27132
+ continue;
27133
+ }
27134
+ term += char;
27135
+ if (term.length > 512) {
27136
+ terms.push(term);
27137
+ term = "";
27138
+ }
27139
+ }
27140
+ if (term.length > 0) terms.push(term);
27141
+ return terms;
27142
+ }
27143
+ function trimStructuredQueryTerm(raw) {
27144
+ const leading = /* @__PURE__ */ new Set(["`", "'", '"', "(", "[", "{"]);
27145
+ const trailing = /* @__PURE__ */ new Set(["`", "'", '"', ",", ".", "?", "!", ":", ";", ")", "]", "}"]);
27146
+ let start = 0;
27147
+ let end = raw.length;
27148
+ while (start < end && leading.has(raw[start])) start += 1;
27149
+ while (end > start && trailing.has(raw[end - 1])) end -= 1;
27150
+ return raw.slice(start, end);
27151
+ }
27152
+ function hasStructuredFileExtension(value) {
27153
+ const slash = value.lastIndexOf("/");
27154
+ const basename3 = value.slice(slash + 1);
27155
+ const dot = basename3.lastIndexOf(".");
27156
+ if (dot <= 0 || dot === basename3.length - 1) return false;
27157
+ const ext = basename3.slice(dot + 1);
27158
+ if (ext.length < 1 || ext.length > 12) return false;
27159
+ for (const char of ext) {
27160
+ const code = char.charCodeAt(0);
27161
+ const valid = code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || char === "_" || char === "+" || char === "-";
27162
+ if (!valid) return false;
27163
+ }
27164
+ return true;
27165
+ }
27166
+ function extractStructuredToolTerms(query) {
27167
+ const lower = query.toLowerCase();
27168
+ if (!/\b(tool|command|invocation|called|used|ran|read|write|patch|edit|grep|search)\b/.test(lower)) {
27169
+ return [];
27170
+ }
27171
+ return query.replace(/[^\w.-]/g, " ").split(/\s+/).filter((term) => term.length > 2 && !STOPWORDS.has(term.toLowerCase())).slice(0, 8);
27172
+ }
27173
+ function escapeLike(value) {
27174
+ return value.replace(/[\\%_]/g, (char) => `\\${char}`);
27175
+ }
27176
+ function asNullableString(value) {
27177
+ return typeof value === "string" && value.trim().length > 0 ? value : null;
27178
+ }
26579
27179
  function sanitizeFtsQuery(raw) {
26580
27180
  const words = raw.replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 1 && !STOPWORDS.has(w.toLowerCase()));
26581
27181
  if (words.length === 0) {
@@ -27045,7 +27645,9 @@ function extractLcmConfig(cfg) {
27045
27645
  maxDepth: cfg.lcmMaxDepth ?? 5,
27046
27646
  deterministicMaxTokens: cfg.lcmDeterministicMaxTokens ?? 512,
27047
27647
  archiveRetentionDays: cfg.lcmArchiveRetentionDays ?? 90,
27048
- recallBudgetShare: cfg.lcmRecallBudgetShare ?? 0.15
27648
+ recallBudgetShare: cfg.lcmRecallBudgetShare ?? 0.15,
27649
+ messagePartsEnabled: cfg.messagePartsEnabled === true,
27650
+ messagePartsRecallMaxResults: typeof cfg.messagePartsRecallMaxResults === "number" ? Math.max(0, Math.floor(cfg.messagePartsRecallMaxResults)) : 6
27049
27651
  };
27050
27652
  }
27051
27653
  var LcmEngine = class {
@@ -27177,9 +27779,14 @@ var LcmEngine = class {
27177
27779
  const newMessages = messages.map((m, i) => ({
27178
27780
  turnIndex: currentMax + 1 + i,
27179
27781
  role: m.role,
27180
- content: m.content
27782
+ content: m.content,
27783
+ parts: this.config.messagePartsEnabled ? m.parts : void 0,
27784
+ rawContent: this.config.messagePartsEnabled ? m.rawContent : void 0,
27785
+ sourceFormat: this.config.messagePartsEnabled ? m.sourceFormat : void 0
27181
27786
  }));
27182
- this.archive.appendMessages(sessionId, newMessages);
27787
+ this.archive.appendMessages(sessionId, newMessages, {
27788
+ messagePartsEnabled: this.config.messagePartsEnabled
27789
+ });
27183
27790
  try {
27184
27791
  await this.summarizer.summarizeIncremental(sessionId);
27185
27792
  } catch (err) {
@@ -27257,6 +27864,28 @@ var LcmEngine = class {
27257
27864
  budgetChars: effectiveBudget
27258
27865
  });
27259
27866
  }
27867
+ async searchStructuredParts(sessionId, query, limit = this.config.messagePartsRecallMaxResults) {
27868
+ if (!this.config.enabled || !this.config.messagePartsEnabled) return [];
27869
+ await this.ensureInitialized();
27870
+ if (!this.archive) return [];
27871
+ return this.archive.searchStructuredParts(query, limit, sessionId);
27872
+ }
27873
+ formatStructuredRecall(matches, budgetChars) {
27874
+ if (matches.length === 0 || budgetChars <= 0) return "";
27875
+ const lines = [];
27876
+ let used = "## Structured Session Matches\n\n".length;
27877
+ for (const match of matches) {
27878
+ const label = match.file_path ? `${match.kind} ${match.file_path}` : match.tool_name ? `${match.kind} ${match.tool_name}` : match.kind;
27879
+ const excerpt = match.content.replace(/\s+/g, " ").slice(0, 220);
27880
+ const line = `- turn ${match.turn_index} (${match.role}): ${label} \u2014 ${excerpt}`;
27881
+ if (used + line.length + 1 > budgetChars) break;
27882
+ lines.push(line);
27883
+ used += line.length + 1;
27884
+ }
27885
+ return lines.length > 0 ? `## Structured Session Matches
27886
+
27887
+ ${lines.join("\n")}` : "";
27888
+ }
27260
27889
  /** Flush pending summaries before compaction (called from before_compaction hook). */
27261
27890
  async preCompactionFlush(sessionId) {
27262
27891
  if (!this.config.enabled) return;
@@ -32443,7 +33072,7 @@ var Orchestrator = class _Orchestrator {
32443
33072
  this.conversationFaiss = conversationIndexRuntime.faiss;
32444
33073
  this.conversationIndexBackend = conversationIndexRuntime.backend;
32445
33074
  this.sharedContext = config.sharedContextEnabled ? new SharedContextManager(config) : void 0;
32446
- this.compounding = config.compoundingEnabled ? new CompoundingEngine(config) : void 0;
33075
+ this.compounding = config.compoundingEnabled ? new CompoundingEngine(config, this.storage) : void 0;
32447
33076
  this.buffer = new SmartBuffer(config, this.storage);
32448
33077
  this.transcript = new TranscriptManager(config);
32449
33078
  this.conversationIndexDir = path43.join(
@@ -32745,8 +33374,7 @@ var Orchestrator = class _Orchestrator {
32745
33374
  promotionByOutcomeEnabled: this.config.promotionByOutcomeEnabled
32746
33375
  });
32747
33376
  if (this.config.factDeduplicationEnabled) {
32748
- const stateDir2 = path43.join(this.config.memoryDir, "state");
32749
- this.contentHashIndex = new ContentHashIndex(stateDir2);
33377
+ this.contentHashIndex = this.storage.createContentHashIndex();
32750
33378
  await this.contentHashIndex.load();
32751
33379
  log.info(
32752
33380
  `content-hash dedup: loaded ${this.contentHashIndex.size} hashes`
@@ -33404,7 +34032,7 @@ ${doc.content}` : doc.content,
33404
34032
  }
33405
34033
  async runDeepSleepGovernanceNow(options) {
33406
34034
  const targetStorage = options?.storage ?? this.storage;
33407
- const { runMemoryGovernance: runMemoryGovernance2 } = await import("./memory-governance-DWGFV4FX.js");
34035
+ const { runMemoryGovernance: runMemoryGovernance2 } = await import("./memory-governance-SJ5DGRB3.js");
33408
34036
  const { summarizeGovernanceResultForDreams } = await import("./dreams-ledger-3I52ISYR.js");
33409
34037
  const govResult = await runMemoryGovernance2({
33410
34038
  memoryDir: targetStorage.dir,
@@ -35498,6 +36126,7 @@ ${r.snippet.trim()}
35498
36126
  let recalledMemoryIds = [];
35499
36127
  let recalledMemoryPaths = [];
35500
36128
  let xrayRecalledResults = [];
36129
+ const lcmStructuredXrayResults = [];
35501
36130
  const xrayBranchPoolSize = {
35502
36131
  hot_qmd: 0,
35503
36132
  hot_embedding: 0,
@@ -37421,6 +38050,32 @@ ${tmtNode.summary}`
37421
38050
  }
37422
38051
  if (this.lcmEngine?.enabled && recallMode !== "minimal" && recallMode !== "no_recall") {
37423
38052
  try {
38053
+ const structuredMatches = await this.lcmEngine.searchStructuredParts(
38054
+ sessionKey ?? "default",
38055
+ retrievalQuery
38056
+ );
38057
+ const structuredSection = this.lcmEngine.formatStructuredRecall(
38058
+ structuredMatches,
38059
+ Math.ceil(this.config.recallBudgetChars * 0.08)
38060
+ );
38061
+ if (structuredSection) {
38062
+ const structuredAppended = this.appendRecallSection(
38063
+ sectionBuckets,
38064
+ "lcm-message-parts",
38065
+ structuredSection
38066
+ );
38067
+ if (structuredAppended) {
38068
+ for (const match of structuredMatches) {
38069
+ lcmStructuredXrayResults.push({
38070
+ memoryId: `lcm-message-part-${match.part_id}`,
38071
+ path: `lcm://${match.session_id}/turn/${match.turn_index}/part/${match.part_id}`,
38072
+ servedBy: match.file_path ? "lcm-file-parts" : "lcm-tool-parts",
38073
+ scoreDecomposition: { final: match.score },
38074
+ admittedBy: ["lcm-message-parts"]
38075
+ });
38076
+ }
38077
+ }
38078
+ }
37424
38079
  const lcmSection = await this.lcmEngine.assembleRecall(
37425
38080
  sessionKey ?? "default",
37426
38081
  this.config.recallBudgetChars
@@ -38321,10 +38976,17 @@ _Context: ${topQuestion.context}_`
38321
38976
  admitted: recalledMemoryIds.length
38322
38977
  }
38323
38978
  ];
38979
+ if (lcmStructuredXrayResults.length > 0) {
38980
+ filters.push({
38981
+ name: "lcm-message-parts",
38982
+ considered: lcmStructuredXrayResults.length,
38983
+ admitted: lcmStructuredXrayResults.length
38984
+ });
38985
+ }
38324
38986
  this.lastXraySnapshot = buildXraySnapshot({
38325
38987
  query: retrievalQuery,
38326
38988
  tierExplain: null,
38327
- results,
38989
+ results: [...results, ...lcmStructuredXrayResults],
38328
38990
  filters,
38329
38991
  budget: {
38330
38992
  chars: this.getRecallBudgetChars(options.budgetCharsOverride),
@@ -38491,13 +39153,28 @@ _Context: ${topQuestion.context}_`
38491
39153
  role: turn.role,
38492
39154
  content: turn.content,
38493
39155
  timestamp: turn.timestamp,
38494
- sessionKey: key
39156
+ sessionKey: key,
39157
+ parts: turn.parts,
39158
+ rawContent: turn.rawContent,
39159
+ sourceFormat: turn.sourceFormat
38495
39160
  });
38496
39161
  bySession.set(key, list);
38497
39162
  }
38498
39163
  const replayTasks = [];
38499
39164
  for (const [key, sessionTurns] of bySession.entries()) {
38500
39165
  if (sessionTurns.length === 0) continue;
39166
+ if (options.archiveLcm !== false && this.lcmEngine?.enabled) {
39167
+ await this.lcmEngine.observeMessages(
39168
+ key,
39169
+ sessionTurns.map((turn) => ({
39170
+ role: turn.role,
39171
+ content: turn.content,
39172
+ parts: turn.parts,
39173
+ rawContent: turn.rawContent,
39174
+ sourceFormat: turn.sourceFormat
39175
+ }))
39176
+ );
39177
+ }
38501
39178
  replayTasks.push(
38502
39179
  new Promise((resolve2, reject) => {
38503
39180
  void this.queueBufferedExtraction(sessionTurns, "trigger_mode", {
@@ -38586,10 +39263,25 @@ _Context: ${topQuestion.context}_`
38586
39263
  role: turn.role,
38587
39264
  content: turn.content,
38588
39265
  timestamp: turn.timestamp,
38589
- sessionKey
39266
+ sessionKey,
39267
+ parts: turn.parts,
39268
+ rawContent: turn.rawContent,
39269
+ sourceFormat: turn.sourceFormat
38590
39270
  });
38591
39271
  }
38592
39272
  if (sessionTurns.length === 0) return;
39273
+ if (this.lcmEngine?.enabled) {
39274
+ await this.lcmEngine.observeMessages(
39275
+ sessionKey,
39276
+ sessionTurns.map((turn) => ({
39277
+ role: turn.role,
39278
+ content: turn.content,
39279
+ parts: turn.parts,
39280
+ rawContent: turn.rawContent,
39281
+ sourceFormat: turn.sourceFormat
39282
+ }))
39283
+ );
39284
+ }
38593
39285
  await new Promise((resolve2, reject) => {
38594
39286
  void this.queueBufferedExtraction(sessionTurns, "trigger_mode", {
38595
39287
  skipDedupeCheck: true,
@@ -43118,13 +43810,13 @@ function toolJsonResult(value, options) {
43118
43810
  function workLayerTextResult(text, options) {
43119
43811
  return toolResult2(wrapWorkLayerContext(text, { linkToMemory: options?.linkToMemory === true }));
43120
43812
  }
43121
- function asNonEmptyString(value) {
43813
+ function asNonEmptyString2(value) {
43122
43814
  if (typeof value !== "string") return void 0;
43123
43815
  const trimmed = value.trim();
43124
43816
  return trimmed.length > 0 ? trimmed : void 0;
43125
43817
  }
43126
43818
  function normalizeToolNamespace(value) {
43127
- return asNonEmptyString(value);
43819
+ return asNonEmptyString2(value);
43128
43820
  }
43129
43821
  function clampUnitInterval(value, fallback) {
43130
43822
  if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
@@ -43173,17 +43865,17 @@ var WORK_TASK_STATUSES = /* @__PURE__ */ new Set(["todo", "in_progress", "blocke
43173
43865
  var WORK_TASK_PRIORITIES = /* @__PURE__ */ new Set(["low", "medium", "high"]);
43174
43866
  var WORK_PROJECT_STATUSES = /* @__PURE__ */ new Set(["active", "on_hold", "completed", "archived"]);
43175
43867
  function asTaskStatus(value) {
43176
- const normalized = asNonEmptyString(value);
43868
+ const normalized = asNonEmptyString2(value);
43177
43869
  if (!normalized || !WORK_TASK_STATUSES.has(normalized)) return void 0;
43178
43870
  return normalized;
43179
43871
  }
43180
43872
  function asTaskPriority(value) {
43181
- const normalized = asNonEmptyString(value);
43873
+ const normalized = asNonEmptyString2(value);
43182
43874
  if (!normalized || !WORK_TASK_PRIORITIES.has(normalized)) return void 0;
43183
43875
  return normalized;
43184
43876
  }
43185
43877
  function asProjectStatus(value) {
43186
- const normalized = asNonEmptyString(value);
43878
+ const normalized = asNonEmptyString2(value);
43187
43879
  if (!normalized || !WORK_PROJECT_STATUSES.has(normalized)) return void 0;
43188
43880
  return normalized;
43189
43881
  }
@@ -43323,7 +44015,7 @@ function registerTools(api, orchestrator) {
43323
44015
  return createHash10("sha256").update(input).digest("hex").slice(0, 16);
43324
44016
  }
43325
44017
  function normalizeMemoryCategory(value, fallback) {
43326
- const normalized = asNonEmptyString(value);
44018
+ const normalized = asNonEmptyString2(value);
43327
44019
  if (!normalized) return fallback;
43328
44020
  if (!VALID_MEMORY_CATEGORIES.has(normalized)) return void 0;
43329
44021
  return normalized;
@@ -43332,8 +44024,8 @@ function registerTools(api, orchestrator) {
43332
44024
  return typeof value === "string" && actionTypeSet.has(value);
43333
44025
  }
43334
44026
  function buildActionInputSummary(action, params) {
43335
- const primary = asNonEmptyString(params.memoryId) ?? asNonEmptyString(params.category) ?? asNonEmptyString(params.linkTargetId);
43336
- const content = asNonEmptyString(params.content);
44027
+ const primary = asNonEmptyString2(params.memoryId) ?? asNonEmptyString2(params.category) ?? asNonEmptyString2(params.linkTargetId);
44028
+ const content = asNonEmptyString2(params.content);
43337
44029
  let summary = primary ? `${action} => ${primary}` : action;
43338
44030
  if (content) {
43339
44031
  summary += ` | ${content.slice(0, 120)}`;
@@ -44218,14 +44910,14 @@ NOTE: You did not provide sessionKey; under concurrency this may not match your
44218
44910
  return toolResult2(`Recorded context checkpoint telemetry in namespace=${ns}.`);
44219
44911
  }
44220
44912
  const validationErrors = [];
44221
- if (!asNonEmptyString(sessionKey)) validationErrors.push("sessionKey is required");
44913
+ if (!asNonEmptyString2(sessionKey)) validationErrors.push("sessionKey is required");
44222
44914
  if (!Array.isArray(turns) || turns.length === 0) validationErrors.push("turns must be a non-empty array");
44223
44915
  const baseEvent = {
44224
44916
  action: "summarize_node",
44225
44917
  namespace: ns,
44226
44918
  actor: "tool.context_checkpoint",
44227
44919
  subsystem: "tools.context_checkpoint",
44228
- sourceSessionKey: asNonEmptyString(sessionKey),
44920
+ sourceSessionKey: asNonEmptyString2(sessionKey),
44229
44921
  inputSummary: summary,
44230
44922
  checkpointTurnCount: Array.isArray(turns) ? turns.length : void 0,
44231
44923
  dryRun: dryRun === true,
@@ -44387,10 +45079,10 @@ NOTE: You did not provide sessionKey; under concurrency this may not match your
44387
45079
  return toolResult2(`Validation failed: invalid action ${String(action)}.`);
44388
45080
  }
44389
45081
  const validationErrors = [];
44390
- const contentValue = asNonEmptyString(content);
44391
- const memoryIdValue = asNonEmptyString(memoryId);
44392
- const linkTargetIdValue = asNonEmptyString(linkTargetId);
44393
- const linkTypeValue = asNonEmptyString(linkType);
45082
+ const contentValue = asNonEmptyString2(content);
45083
+ const memoryIdValue = asNonEmptyString2(memoryId);
45084
+ const linkTargetIdValue = asNonEmptyString2(linkTargetId);
45085
+ const linkTypeValue = asNonEmptyString2(linkType);
44394
45086
  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");
44395
45087
  const baseEvent = {
44396
45088
  action,
@@ -44399,7 +45091,7 @@ NOTE: You did not provide sessionKey; under concurrency this may not match your
44399
45091
  subsystem: "tools.memory_action_apply",
44400
45092
  reason,
44401
45093
  memoryId: memoryIdValue,
44402
- sourceSessionKey: asNonEmptyString(sessionKey),
45094
+ sourceSessionKey: asNonEmptyString2(sessionKey),
44403
45095
  inputSummary: buildActionInputSummary(action, params),
44404
45096
  promptHash: promptHashForTelemetry(sourcePrompt)
44405
45097
  };
@@ -45309,16 +46001,16 @@ Best for:
45309
46001
  description: typeof p.description === "string" ? p.description : void 0,
45310
46002
  status: asTaskStatus(p.status),
45311
46003
  priority: asTaskPriority(p.priority),
45312
- owner: asNonEmptyString(p.owner),
45313
- assignee: asNonEmptyString(p.assignee),
45314
- projectId: asNonEmptyString(p.projectId),
46004
+ owner: asNonEmptyString2(p.owner),
46005
+ assignee: asNonEmptyString2(p.assignee),
46006
+ projectId: asNonEmptyString2(p.projectId),
45315
46007
  tags: Array.isArray(p.tags) ? p.tags.filter((x) => typeof x === "string") : void 0,
45316
- dueAt: asNonEmptyString(p.dueAt)
46008
+ dueAt: asNonEmptyString2(p.dueAt)
45317
46009
  });
45318
46010
  return toolJsonResult({ action, task: created });
45319
46011
  }
45320
46012
  if (action === "get") {
45321
- const taskId = asNonEmptyString(p.id);
46013
+ const taskId = asNonEmptyString2(p.id);
45322
46014
  if (!taskId) {
45323
46015
  return workLayerTextResult("work_task.get requires `id`.");
45324
46016
  }
@@ -45328,14 +46020,14 @@ Best for:
45328
46020
  if (action === "list") {
45329
46021
  const tasks = await storage.listTasks({
45330
46022
  status: asTaskStatus(p.status),
45331
- owner: asNonEmptyString(p.owner),
45332
- assignee: asNonEmptyString(p.assignee),
45333
- projectId: asNonEmptyString(p.projectId)
46023
+ owner: asNonEmptyString2(p.owner),
46024
+ assignee: asNonEmptyString2(p.assignee),
46025
+ projectId: asNonEmptyString2(p.projectId)
45334
46026
  });
45335
46027
  return toolJsonResult({ action, count: tasks.length, tasks });
45336
46028
  }
45337
46029
  if (action === "update") {
45338
- const taskId = asNonEmptyString(p.id);
46030
+ const taskId = asNonEmptyString2(p.id);
45339
46031
  if (!taskId) {
45340
46032
  return workLayerTextResult("work_task.update requires `id`.");
45341
46033
  }
@@ -45359,8 +46051,8 @@ Best for:
45359
46051
  return toolJsonResult({ action, task: updated });
45360
46052
  }
45361
46053
  if (action === "transition") {
45362
- const taskId = asNonEmptyString(p.id);
45363
- const rawStatus = asNonEmptyString(p.status);
46054
+ const taskId = asNonEmptyString2(p.id);
46055
+ const rawStatus = asNonEmptyString2(p.status);
45364
46056
  if (!taskId || !rawStatus) {
45365
46057
  return workLayerTextResult("work_task.transition requires `id` and `status`.");
45366
46058
  }
@@ -45372,7 +46064,7 @@ Best for:
45372
46064
  return toolJsonResult({ action, task });
45373
46065
  }
45374
46066
  if (action === "delete") {
45375
- const taskId = asNonEmptyString(p.id);
46067
+ const taskId = asNonEmptyString2(p.id);
45376
46068
  if (!taskId) {
45377
46069
  return workLayerTextResult("work_task.delete requires `id`.");
45378
46070
  }
@@ -45420,13 +46112,13 @@ Best for:
45420
46112
  name: p.name,
45421
46113
  description: typeof p.description === "string" ? p.description : void 0,
45422
46114
  status: asProjectStatus(p.status),
45423
- owner: asNonEmptyString(p.owner),
46115
+ owner: asNonEmptyString2(p.owner),
45424
46116
  tags: Array.isArray(p.tags) ? p.tags.filter((x) => typeof x === "string") : void 0
45425
46117
  });
45426
46118
  return toolJsonResult({ action, project });
45427
46119
  }
45428
46120
  if (action === "get") {
45429
- const projectId = asNonEmptyString(p.id);
46121
+ const projectId = asNonEmptyString2(p.id);
45430
46122
  if (!projectId) {
45431
46123
  return workLayerTextResult("work_project.get requires `id`.");
45432
46124
  }
@@ -45438,7 +46130,7 @@ Best for:
45438
46130
  return toolJsonResult({ action, count: projects.length, projects });
45439
46131
  }
45440
46132
  if (action === "update") {
45441
- const projectId = asNonEmptyString(p.id);
46133
+ const projectId = asNonEmptyString2(p.id);
45442
46134
  if (!projectId) {
45443
46135
  return workLayerTextResult("work_project.update requires `id`.");
45444
46136
  }
@@ -45454,7 +46146,7 @@ Best for:
45454
46146
  return toolJsonResult({ action, project });
45455
46147
  }
45456
46148
  if (action === "delete") {
45457
- const projectId = asNonEmptyString(p.id);
46149
+ const projectId = asNonEmptyString2(p.id);
45458
46150
  if (!projectId) {
45459
46151
  return workLayerTextResult("work_project.delete requires `id`.");
45460
46152
  }
@@ -45462,8 +46154,8 @@ Best for:
45462
46154
  return toolJsonResult({ action, deleted });
45463
46155
  }
45464
46156
  if (action === "link_task") {
45465
- const taskId = asNonEmptyString(p.taskId);
45466
- const projectId = asNonEmptyString(p.projectId);
46157
+ const taskId = asNonEmptyString2(p.taskId);
46158
+ const projectId = asNonEmptyString2(p.projectId);
45467
46159
  if (!taskId || !projectId) {
45468
46160
  return workLayerTextResult("work_project.link_task requires `taskId` and `projectId`.");
45469
46161
  }
@@ -45499,7 +46191,7 @@ Best for:
45499
46191
  async execute(_toolCallId, params) {
45500
46192
  const p = params;
45501
46193
  const action = String(p.action ?? "");
45502
- const projectId = asNonEmptyString(p.projectId);
46194
+ const projectId = asNonEmptyString2(p.projectId);
45503
46195
  const linkToMemory = p.linkToMemory === true;
45504
46196
  try {
45505
46197
  await new WorkStorage(orchestrator.config.memoryDir).ensureDirectories();
@@ -45519,7 +46211,7 @@ Best for:
45519
46211
  const result = await importWorkBoardSnapshot({
45520
46212
  memoryDir: orchestrator.config.memoryDir,
45521
46213
  snapshot,
45522
- projectId: asNonEmptyString(p.projectId)
46214
+ projectId: asNonEmptyString2(p.projectId)
45523
46215
  });
45524
46216
  return toolJsonResult({ action, result }, { linkToMemory });
45525
46217
  }
@@ -45785,7 +46477,7 @@ Returns: Performance trace data with timing breakdown`,
45785
46477
  "Profiling is disabled. Set profilingEnabled: true in your plugin config to enable."
45786
46478
  );
45787
46479
  }
45788
- const format = asNonEmptyString(params.format) ?? "ascii";
46480
+ const format = asNonEmptyString2(params.format) ?? "ascii";
45789
46481
  const limit = Math.min(typeof params.limit === "number" ? params.limit : 5, 20);
45790
46482
  const traces = profiler.getRecentTraces(limit);
45791
46483
  const stats = profiler.getStats();
@@ -45856,7 +46548,7 @@ Returns: Performance trace data with timing breakdown`,
45856
46548
  "Profiling is disabled. Set profilingEnabled: true in your plugin config to enable."
45857
46549
  );
45858
46550
  }
45859
- const format = asNonEmptyString(params.format) ?? "ascii";
46551
+ const format = asNonEmptyString2(params.format) ?? "ascii";
45860
46552
  const limit = Math.min(typeof params.limit === "number" ? params.limit : 5, 20);
45861
46553
  const traces = profiler.getRecentTraces(limit);
45862
46554
  const stats = profiler.getStats();
@@ -45905,8 +46597,8 @@ Returns: Performance trace data with timing breakdown`,
45905
46597
 
45906
46598
  // ../remnic-core/src/cli.ts
45907
46599
  import path74 from "path";
45908
- import { access as access6, readFile as readFile46, readdir as readdir27, unlink as unlink12 } from "fs/promises";
45909
- import { createHash as createHash13 } from "crypto";
46600
+ import { access as access7, readFile as readFile47, readdir as readdir28, unlink as unlink12 } from "fs/promises";
46601
+ import { createHash as createHash14 } from "crypto";
45910
46602
 
45911
46603
  // ../remnic-core/src/transfer/export-json.ts
45912
46604
  import path45 from "path";
@@ -46020,7 +46712,7 @@ async function backupMemoryDir(opts) {
46020
46712
  const ts = timestampDirName(/* @__PURE__ */ new Date());
46021
46713
  if (opts.encrypt === true) {
46022
46714
  const { listFilesRecursive: listFilesRecursive3, toPosixRelPath: toPosixRelPath2 } = await import("./fs-utils-PZRI2HDZ.js");
46023
- const { readFile: readFile53 } = await import("fs/promises");
46715
+ const { readFile: readFile54 } = await import("fs/promises");
46024
46716
  const memoryDirAbs = path47.resolve(opts.memoryDir);
46025
46717
  const filesAbs = await listFilesRecursive3(memoryDirAbs);
46026
46718
  const includeTranscripts = opts.includeTranscripts === true;
@@ -46030,7 +46722,7 @@ async function backupMemoryDir(opts) {
46030
46722
  const parts = relPosix.split("/");
46031
46723
  if (parts.some((p) => p === "node_modules" || p === ".git" || p === ".secure-store" || p === ".capsules")) continue;
46032
46724
  if (!includeTranscripts && parts[0] === "transcripts") continue;
46033
- const content = await readFile53(abs, "utf-8");
46725
+ const content = await readFile54(abs, "utf-8");
46034
46726
  records.push({ path: relPosix, content });
46035
46727
  }
46036
46728
  records.sort((a, b) => a.path.localeCompare(b.path));
@@ -49052,7 +49744,7 @@ function removeManagedCaptureInstructions(existing) {
49052
49744
  async function runOperatorSetup(options) {
49053
49745
  const now = options.now ?? /* @__PURE__ */ new Date();
49054
49746
  const configStatus = await loadCliPluginConfig(options.configPath);
49055
- const storage = new StorageManager(options.orchestrator.config.memoryDir);
49747
+ const storage = options.orchestrator.storage;
49056
49748
  await storage.ensureDirectories();
49057
49749
  await mkdir42(options.orchestrator.config.workspaceDir, { recursive: true });
49058
49750
  const qmdAvailable = await options.orchestrator.qmd.probe();
@@ -49329,6 +50021,7 @@ async function runOperatorDoctor(options) {
49329
50021
  const configStatus = await loadCliPluginConfig(options.configPath);
49330
50022
  const checks = [];
49331
50023
  const config = options.orchestrator.config;
50024
+ const storage = options.orchestrator.storage;
49332
50025
  const configReview = await buildOperatorConfigReviewReport({
49333
50026
  now,
49334
50027
  configStatus,
@@ -49379,7 +50072,7 @@ async function runOperatorDoctor(options) {
49379
50072
  remediation: conversationIndex.enabled && conversationIndex.status !== "ok" ? "Run `openclaw engram rebuild-index` to refresh the conversation index artifacts." : void 0,
49380
50073
  details: conversationIndex
49381
50074
  });
49382
- const meta = await new StorageManager(config.memoryDir).loadMeta();
50075
+ const meta = await storage.loadMeta();
49383
50076
  checks.push({
49384
50077
  key: "maintenance",
49385
50078
  status: meta.lastExtractionAt || meta.lastConsolidationAt ? "ok" : "warn",
@@ -49412,14 +50105,14 @@ async function runOperatorDoctor(options) {
49412
50105
  warnRatio: config.fileHygiene.lintWarnRatio
49413
50106
  }) : [];
49414
50107
  checks.push(summarizeHygieneWarnings(warnings, config.fileHygiene));
49415
- checks.push(await summarizeMemoryWorthLegacyCounters(new StorageManager(config.memoryDir)));
50108
+ checks.push(await summarizeMemoryWorthLegacyCounters(storage));
49416
50109
  checks.push(
49417
- await summarizeBufferSurpriseDistribution(new StorageManager(config.memoryDir), config)
50110
+ await summarizeBufferSurpriseDistribution(storage, config)
49418
50111
  );
49419
- checks.push(await summarizeConsolidationProvenance(new StorageManager(config.memoryDir), config));
50112
+ checks.push(await summarizeConsolidationProvenance(storage, config));
49420
50113
  checks.push(await summarizeGraphEdgeDecayStatus(config));
49421
50114
  checks.push(await summarizeTierDistribution(options.orchestrator.storage));
49422
- checks.push(await summarizeDreamsPhases(config));
50115
+ checks.push(await summarizeDreamsPhases(config, storage));
49423
50116
  checks.push(summarizeSecurityMitigations(config));
49424
50117
  checks.push(await summarizeObservationThroughput(config.memoryDir));
49425
50118
  const summary = checks.reduce(
@@ -49569,8 +50262,8 @@ async function summarizeTierDistribution(storage) {
49569
50262
  const sevenDaysAgoMs = Date.now() - 7 * 864e5;
49570
50263
  if (journalPath) {
49571
50264
  try {
49572
- const { readFile: readFile53 } = await import("fs/promises");
49573
- const raw = await readFile53(journalPath, "utf-8");
50265
+ const { readFile: readFile54 } = await import("fs/promises");
50266
+ const raw = await readFile54(journalPath, "utf-8");
49574
50267
  for (const line of raw.split("\n")) {
49575
50268
  const trimmed = line.trim();
49576
50269
  if (trimmed.length === 0) continue;
@@ -49615,9 +50308,8 @@ async function summarizeTierDistribution(storage) {
49615
50308
  };
49616
50309
  }
49617
50310
  }
49618
- async function summarizeDreamsPhases(config) {
50311
+ async function summarizeDreamsPhases(config, storage = new StorageManager(config.memoryDir)) {
49619
50312
  const phases = config.dreamsPhases;
49620
- const storage = new StorageManager(config.memoryDir);
49621
50313
  const meta = await storage.loadMeta();
49622
50314
  let deepSleepLastRun = null;
49623
50315
  let deepSleepLastRunWarning = null;
@@ -50868,6 +51560,8 @@ var GraphDashboardServer = class {
50868
51560
  // ../remnic-core/src/access-service.ts
50869
51561
  import { stat as stat19 } from "fs/promises";
50870
51562
  import * as nodeFs from "fs/promises";
51563
+ import { constants as fsConstants2 } from "fs";
51564
+ import { createHash as createHash13 } from "crypto";
50871
51565
 
50872
51566
  // ../remnic-core/src/access-idempotency.ts
50873
51567
  import { mkdir as mkdir44, open as open4, readFile as readFile40, rename as rename7, stat as stat18, unlink as unlink11, utimes as utimes2, writeFile as writeFile39 } from "fs/promises";
@@ -53338,6 +54032,18 @@ async function recordMemoryOutcome(storage, input) {
53338
54032
  // ../remnic-core/src/access-service.ts
53339
54033
  var EngramAccessInputError = class extends Error {
53340
54034
  };
54035
+ var cachedPackageVersion = null;
54036
+ async function getPackageVersion() {
54037
+ if (cachedPackageVersion !== null) return cachedPackageVersion;
54038
+ try {
54039
+ const raw = await nodeFs.readFile(new URL("../package.json", import.meta.url), "utf-8");
54040
+ const parsed = JSON.parse(raw);
54041
+ cachedPackageVersion = typeof parsed.version === "string" && parsed.version.length > 0 ? parsed.version : "unknown";
54042
+ } catch {
54043
+ cachedPackageVersion = "unknown";
54044
+ }
54045
+ return cachedPackageVersion;
54046
+ }
53341
54047
  function normalizeTrustZoneInputError(error) {
53342
54048
  const message = error instanceof Error ? error.message : null;
53343
54049
  if (!message) {
@@ -54942,6 +55648,147 @@ var EngramAccessService = class {
54942
55648
  });
54943
55649
  return { namespace: resolvedNamespace, ...report };
54944
55650
  }
55651
+ async memorySummarizeHourly() {
55652
+ await this.orchestrator.summarizer.runHourly();
55653
+ return {
55654
+ ok: true,
55655
+ message: "Hourly summarization completed. Check the summaries directory for results."
55656
+ };
55657
+ }
55658
+ async conversationIndexUpdate(request = {}) {
55659
+ if (!this.orchestrator.config.conversationIndexEnabled) {
55660
+ return {
55661
+ enabled: false,
55662
+ sessions: 0,
55663
+ chunks: 0,
55664
+ skipped: 0,
55665
+ skippedSessionKeys: [],
55666
+ embeddedRuns: 0,
55667
+ reason: "disabled"
55668
+ };
55669
+ }
55670
+ const hours = typeof request.hours === "number" && Number.isFinite(request.hours) ? Math.max(1, Math.floor(request.hours)) : 24;
55671
+ let sessionKey;
55672
+ if (request.sessionKey !== void 0) {
55673
+ if (typeof request.sessionKey !== "string" || request.sessionKey.trim().length === 0) {
55674
+ throw new EngramAccessInputError("sessionKey must be a non-empty string when provided");
55675
+ }
55676
+ sessionKey = request.sessionKey.trim();
55677
+ }
55678
+ if (sessionKey) {
55679
+ const result = await this.orchestrator.updateConversationIndex(
55680
+ sessionKey,
55681
+ hours,
55682
+ { embed: request.embed }
55683
+ );
55684
+ return {
55685
+ enabled: true,
55686
+ sessionKey,
55687
+ sessions: 1,
55688
+ chunks: result.chunks,
55689
+ skipped: result.skipped ? 1 : 0,
55690
+ skippedSessionKeys: result.skipped ? [sessionKey] : [],
55691
+ embeddedRuns: result.embedded ? 1 : 0,
55692
+ reason: result.reason,
55693
+ retryAfterMs: result.retryAfterMs
55694
+ };
55695
+ }
55696
+ const sessionKeys = await this.orchestrator.transcript.listSessionKeys();
55697
+ let chunks = 0;
55698
+ let skipped = 0;
55699
+ const skippedSessionKeys = [];
55700
+ let embeddedRuns = 0;
55701
+ for (const sessionKey2 of sessionKeys) {
55702
+ const result = await this.orchestrator.updateConversationIndex(
55703
+ sessionKey2,
55704
+ hours,
55705
+ { embed: request.embed }
55706
+ );
55707
+ chunks += result.chunks;
55708
+ if (result.skipped) {
55709
+ skipped += 1;
55710
+ skippedSessionKeys.push(sessionKey2);
55711
+ }
55712
+ if (result.embedded) {
55713
+ embeddedRuns += 1;
55714
+ }
55715
+ }
55716
+ return {
55717
+ enabled: true,
55718
+ sessions: sessionKeys.length,
55719
+ chunks,
55720
+ skipped,
55721
+ skippedSessionKeys,
55722
+ embeddedRuns
55723
+ };
55724
+ }
55725
+ async profilingReport(request = {}) {
55726
+ const profiler = this.orchestrator.profiler;
55727
+ if (!profiler.isEnabled) {
55728
+ return {
55729
+ enabled: false,
55730
+ reason: "disabled",
55731
+ message: "Profiling is disabled. Set profilingEnabled: true in your plugin config to enable."
55732
+ };
55733
+ }
55734
+ const format = request.format ?? "ascii";
55735
+ if (format !== "ascii" && format !== "json") {
55736
+ throw new EngramAccessInputError("format must be one of: ascii, json");
55737
+ }
55738
+ const limit = request.limit ?? 5;
55739
+ if (!Number.isInteger(limit) || limit < 1 || limit > 20) {
55740
+ throw new EngramAccessInputError("limit must be an integer between 1 and 20");
55741
+ }
55742
+ const traces = profiler.getRecentTraces(limit);
55743
+ const stats = profiler.getStats();
55744
+ const bottleneck = profiler.identifyBottleneck();
55745
+ if (format === "json") {
55746
+ return {
55747
+ enabled: true,
55748
+ format,
55749
+ traces,
55750
+ stats,
55751
+ bottleneck
55752
+ };
55753
+ }
55754
+ const lines = [];
55755
+ lines.push("Engram Profiling Report");
55756
+ lines.push("=".repeat(60));
55757
+ lines.push("");
55758
+ const allBuckets = [
55759
+ ["byKind", stats.byKind],
55760
+ ["bySpan", stats.bySpan]
55761
+ ];
55762
+ const hasStats = allBuckets.some(([, entries]) => Object.keys(entries).length > 0);
55763
+ if (hasStats) {
55764
+ lines.push("Aggregate Stats (all retained traces):");
55765
+ for (const [bucket, entries] of allBuckets) {
55766
+ for (const [key, summary] of Object.entries(entries)) {
55767
+ lines.push(
55768
+ ` ${bucket}/${key}: avg=${summary.avgMs}ms p50=${summary.p50Ms}ms p95=${summary.p95Ms}ms max=${summary.maxMs}ms (n=${summary.count})`
55769
+ );
55770
+ }
55771
+ }
55772
+ lines.push("");
55773
+ }
55774
+ if (bottleneck) {
55775
+ lines.push(`Bottleneck: ${bottleneck}`);
55776
+ lines.push("");
55777
+ }
55778
+ if (traces.length === 0) {
55779
+ lines.push("No traces recorded yet. Trigger a recall or extraction to see timing data.");
55780
+ } else {
55781
+ for (const trace of traces) {
55782
+ lines.push(formatProfileTraceAscii(trace));
55783
+ lines.push("");
55784
+ }
55785
+ }
55786
+ return {
55787
+ enabled: true,
55788
+ format,
55789
+ report: lines.join("\n")
55790
+ };
55791
+ }
54945
55792
  async trustZoneStatus(namespace, principal) {
54946
55793
  const resolvedNamespace = this.resolveReadableNamespace(namespace, principal);
54947
55794
  const storage = await this.orchestrator.getStorage(resolvedNamespace);
@@ -55159,10 +56006,15 @@ var EngramAccessService = class {
55159
56006
  sessionKey: lcmSessionKey,
55160
56007
  role: m.role,
55161
56008
  content: m.content,
56009
+ parts: m.parts,
56010
+ rawContent: m.rawContent,
56011
+ sourceFormat: m.sourceFormat,
55162
56012
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
55163
56013
  }));
55164
56014
  try {
55165
- const extractionPromise = this.orchestrator.ingestReplayBatch(turns);
56015
+ const extractionPromise = this.orchestrator.ingestReplayBatch(turns, {
56016
+ archiveLcm: false
56017
+ });
55166
56018
  extractionPromise.catch((err) => {
55167
56019
  log.error(`access-observe background extraction failed: ${err}`);
55168
56020
  });
@@ -55861,22 +56713,82 @@ ${next}`);
55861
56713
  });
55862
56714
  return { promoted: true, memoryId: request.memoryId };
55863
56715
  }
56716
+ async memoryActionApply(request) {
56717
+ const actionTypes = /* @__PURE__ */ new Set([
56718
+ "store_episode",
56719
+ "store_note",
56720
+ "update_note",
56721
+ "create_artifact",
56722
+ "summarize_node",
56723
+ "discard",
56724
+ "link_graph"
56725
+ ]);
56726
+ if (!actionTypes.has(request.action)) {
56727
+ throw new EngramAccessInputError(
56728
+ `memory_action_apply: invalid action ${JSON.stringify(request.action)}`
56729
+ );
56730
+ }
56731
+ if (this.orchestrator.config.contextCompressionActionsEnabled !== true) {
56732
+ throw new EngramAccessInputError(
56733
+ "memory_action_apply is disabled; enable contextCompressionActionsEnabled to use this tool"
56734
+ );
56735
+ }
56736
+ const outcome = request.outcome ?? "skipped";
56737
+ if (outcome !== "applied" && outcome !== "skipped" && outcome !== "failed") {
56738
+ throw new EngramAccessInputError(
56739
+ `memory_action_apply: outcome must be "applied", "skipped", or "failed"; got ${JSON.stringify(outcome)}`
56740
+ );
56741
+ }
56742
+ const resolvedNs = this.resolveWritableNamespace(
56743
+ request.namespace,
56744
+ request.sessionKey,
56745
+ request.principal
56746
+ );
56747
+ const inputSummaryParts = [
56748
+ request.content,
56749
+ request.category ? `category=${request.category}` : void 0,
56750
+ request.linkTargetId ? `linkTargetId=${request.linkTargetId}` : void 0,
56751
+ request.linkType ? `linkType=${request.linkType}` : void 0,
56752
+ typeof request.linkStrength === "number" ? `linkStrength=${request.linkStrength}` : void 0,
56753
+ request.artifactType ? `artifactType=${request.artifactType}` : void 0,
56754
+ typeof request.execute === "boolean" ? `execute=${request.execute}` : void 0
56755
+ ].filter((part) => typeof part === "string" && part.length > 0);
56756
+ const event = {
56757
+ action: request.action,
56758
+ outcome,
56759
+ namespace: resolvedNs,
56760
+ actor: "access.memory_action_apply",
56761
+ subsystem: "access.memory_action_apply",
56762
+ reason: request.reason,
56763
+ memoryId: request.memoryId,
56764
+ sourceSessionKey: request.sessionKey,
56765
+ inputSummary: inputSummaryParts.length > 0 ? inputSummaryParts.join(" | ").slice(0, 500) : void 0,
56766
+ dryRun: request.dryRun === true,
56767
+ promptHash: typeof request.sourcePrompt === "string" && request.sourcePrompt.length > 0 ? createHash13("sha256").update(request.sourcePrompt).digest("hex") : void 0
56768
+ };
56769
+ const preview = this.orchestrator.previewMemoryActionEvent(event);
56770
+ if (request.dryRun === true) {
56771
+ return { recorded: false, dryRun: true, event: preview };
56772
+ }
56773
+ const recorded = await this.orchestrator.appendMemoryActionEvent(event);
56774
+ return { recorded, event: preview };
56775
+ }
55864
56776
  async contextCheckpoint(request) {
55865
56777
  const resolvedNs = this.resolveWritableNamespace(request.namespace, request.sessionKey, request.principal);
55866
56778
  const storage = await this.orchestrator.getStorage(resolvedNs);
55867
56779
  const storageDir = storage.dir;
55868
56780
  const { writeFile: writeFile45, mkdir: mkdir52 } = await import("fs/promises");
55869
- const { join: join5, resolve: resolve2 } = await import("path");
56781
+ const { join: join6, resolve: resolve2 } = await import("path");
55870
56782
  const safeKey = request.sessionKey.replace(/[^a-zA-Z0-9_-]/g, "_");
55871
56783
  if (!safeKey) throw new EngramAccessInputError("sessionKey is required");
55872
- const checkpointDir = join5(storageDir, "checkpoints", safeKey);
56784
+ const checkpointDir = join6(storageDir, "checkpoints", safeKey);
55873
56785
  const resolved = resolve2(checkpointDir);
55874
56786
  if (!resolved.startsWith(resolve2(storageDir))) {
55875
56787
  throw new EngramAccessInputError("Invalid sessionKey");
55876
56788
  }
55877
56789
  await mkdir52(checkpointDir, { recursive: true });
55878
56790
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
55879
- const filePath = join5(checkpointDir, `checkpoint-${ts}.md`);
56791
+ const filePath = join6(checkpointDir, `checkpoint-${ts}.md`);
55880
56792
  await writeFile45(filePath, request.context, "utf-8");
55881
56793
  return { saved: true };
55882
56794
  }
@@ -56157,13 +57069,158 @@ ${next}`);
56157
57069
  * caller having to construct the config object.
56158
57070
  */
56159
57071
  async capsuleImport(opts) {
56160
- const root = opts.root ?? this.orchestrator.config.memoryDir;
56161
- const versioning = opts.versioning ?? {
57072
+ const { namespace, principal, root: explicitRoot, memoryDir: explicitMemoryDir, ...importOptions } = opts;
57073
+ const resolvedNamespace = this.resolveWritableNamespace(namespace, void 0, principal);
57074
+ const storage = await this.orchestrator.getStorage(resolvedNamespace);
57075
+ const root = explicitRoot ?? storage.dir;
57076
+ const memoryDir = explicitMemoryDir ?? this.orchestrator.config.memoryDir;
57077
+ const versioning = importOptions.versioning ?? {
56162
57078
  enabled: this.orchestrator.config.versioningEnabled,
56163
57079
  maxVersionsPerPage: this.orchestrator.config.versioningMaxPerPage,
56164
57080
  sidecarDir: this.orchestrator.config.versioningSidecarDir
56165
57081
  };
56166
- return importCapsule({ ...opts, root, versioning });
57082
+ await this.validateCapsuleImportArchivePath(importOptions.archivePath);
57083
+ try {
57084
+ return await importCapsule({ ...importOptions, root, memoryDir, versioning });
57085
+ } catch (err) {
57086
+ const message = err instanceof Error ? err.message : String(err);
57087
+ if (this.isCapsuleImportArchiveInputError(err, message)) {
57088
+ throw new EngramAccessInputError(`capsule import failed: ${message}`);
57089
+ }
57090
+ throw err;
57091
+ }
57092
+ }
57093
+ async validateCapsuleImportArchivePath(archivePath) {
57094
+ let archiveStat;
57095
+ try {
57096
+ archiveStat = await stat19(archivePath);
57097
+ } catch (err) {
57098
+ if (!this.isCapsuleImportPathInputFsError(err)) throw err;
57099
+ const message = err instanceof Error ? err.message : String(err);
57100
+ throw new EngramAccessInputError(`capsule import failed: archive is not readable: ${message}`);
57101
+ }
57102
+ if (!archiveStat.isFile()) {
57103
+ throw new EngramAccessInputError("capsule import failed: archivePath must point to a file");
57104
+ }
57105
+ try {
57106
+ await nodeFs.access(archivePath, fsConstants2.R_OK);
57107
+ } catch (err) {
57108
+ if (!this.isCapsuleImportPathInputFsError(err)) throw err;
57109
+ const message = err instanceof Error ? err.message : String(err);
57110
+ throw new EngramAccessInputError(`capsule import failed: archive is not readable: ${message}`);
57111
+ }
57112
+ }
57113
+ isCapsuleImportPathInputFsError(err) {
57114
+ const code = typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
57115
+ return code === "ENOENT" || code === "ENOTDIR" || code === "EACCES" || code === "EPERM" || code === "ELOOP";
57116
+ }
57117
+ isCapsuleImportArchiveInputError(err, message) {
57118
+ if (err instanceof ZodError) return true;
57119
+ const code = typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
57120
+ if (typeof code === "string" && code.startsWith("Z_")) return true;
57121
+ return message.startsWith("importCapsule: archive") || message.startsWith("importCapsule: bundle") || message.startsWith("importCapsule: manifest") || message.startsWith("importCapsule: record") || /incorrect header check|invalid stored block lengths|not in gzip format|unexpected end of file/i.test(message);
57122
+ }
57123
+ /**
57124
+ * Export a capsule archive from the orchestrator's memory directory.
57125
+ *
57126
+ * HTTP and future MCP surfaces use this rather than calling the transfer
57127
+ * helper directly so namespace ACL checks stay consistent with the archive
57128
+ * write side effect. The exporter still owns archive construction and
57129
+ * validation.
57130
+ */
57131
+ async capsuleExport(opts) {
57132
+ const { namespace, principal, root: explicitRoot, memoryDir: explicitMemoryDir, ...exportOptions } = opts;
57133
+ const resolvedNamespace = this.resolveWritableNamespace(namespace, void 0, principal);
57134
+ const storage = await this.orchestrator.getStorage(resolvedNamespace);
57135
+ const root = explicitRoot ?? storage.dir;
57136
+ const memoryDir = explicitMemoryDir ?? this.orchestrator.config.memoryDir;
57137
+ const pluginVersion = exportOptions.pluginVersion ?? await getPackageVersion();
57138
+ return exportCapsule({
57139
+ ...exportOptions,
57140
+ pluginVersion,
57141
+ root,
57142
+ memoryDir: exportOptions.encrypt === true ? memoryDir : void 0
57143
+ });
57144
+ }
57145
+ /**
57146
+ * List capsule archives in the namespace-scoped capsule store.
57147
+ *
57148
+ * MCP uses this access-layer method instead of reading arbitrary paths so
57149
+ * capsule discovery remains bound to the same namespace ACLs as export and
57150
+ * import.
57151
+ */
57152
+ async capsuleList(options) {
57153
+ const resolvedNamespace = this.resolveReadableNamespace(options?.namespace, options?.principal);
57154
+ const storage = await this.orchestrator.getStorage(resolvedNamespace);
57155
+ const capsulesDir = defaultCapsulesDir(storage.dir);
57156
+ let dirEntries;
57157
+ try {
57158
+ const capsulesDirStat = await nodeFs.lstat(capsulesDir);
57159
+ if (capsulesDirStat.isSymbolicLink()) {
57160
+ throw new EngramAccessInputError("capsule list failed: capsule store directory must not be a symlink");
57161
+ }
57162
+ if (!capsulesDirStat.isDirectory()) {
57163
+ throw new EngramAccessInputError("capsule list failed: capsule store path must be a directory");
57164
+ }
57165
+ dirEntries = await nodeFs.readdir(capsulesDir, { withFileTypes: true });
57166
+ } catch (err) {
57167
+ const code = typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
57168
+ if (code === "ENOENT") {
57169
+ return { namespace: resolvedNamespace, capsulesDir, capsules: [] };
57170
+ }
57171
+ throw err;
57172
+ }
57173
+ const archiveNames = dirEntries.filter(
57174
+ (entry) => entry.isFile() && (entry.name.endsWith(".capsule.json.gz") || entry.name.endsWith(".capsule.json.gz.enc"))
57175
+ ).map((entry) => entry.name).sort();
57176
+ const capsules = [];
57177
+ for (const archiveName of archiveNames) {
57178
+ const archivePath = nodePath.join(capsulesDir, archiveName);
57179
+ const id = archiveName.replace(/\.capsule\.json\.gz\.enc$/, "").replace(/\.capsule\.json\.gz$/, "");
57180
+ const manifestPath2 = nodePath.join(capsulesDir, `${id}.manifest.json`);
57181
+ let createdAt = null;
57182
+ let pluginVersion = null;
57183
+ let fileCount = null;
57184
+ let description = null;
57185
+ let manifestPathOrNull = manifestPath2;
57186
+ try {
57187
+ const manifestStat = await nodeFs.lstat(manifestPath2);
57188
+ if (manifestStat.isSymbolicLink() || !manifestStat.isFile()) {
57189
+ capsules.push({
57190
+ id,
57191
+ archivePath,
57192
+ manifestPath: manifestPathOrNull,
57193
+ createdAt,
57194
+ pluginVersion,
57195
+ fileCount,
57196
+ description
57197
+ });
57198
+ continue;
57199
+ }
57200
+ const raw = await nodeFs.readFile(manifestPath2, "utf-8");
57201
+ const sidecar = JSON.parse(raw);
57202
+ createdAt = typeof sidecar.createdAt === "string" ? sidecar.createdAt : null;
57203
+ pluginVersion = typeof sidecar.pluginVersion === "string" ? sidecar.pluginVersion : null;
57204
+ fileCount = Array.isArray(sidecar.files) ? sidecar.files.length : null;
57205
+ const capsule = sidecar.capsule;
57206
+ description = capsule && typeof capsule.description === "string" ? capsule.description : null;
57207
+ } catch (err) {
57208
+ const code = typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
57209
+ if (code === "ENOENT") {
57210
+ manifestPathOrNull = null;
57211
+ }
57212
+ }
57213
+ capsules.push({
57214
+ id,
57215
+ archivePath,
57216
+ manifestPath: manifestPathOrNull,
57217
+ createdAt,
57218
+ pluginVersion,
57219
+ fileCount,
57220
+ description
57221
+ });
57222
+ }
57223
+ return { namespace: resolvedNamespace, capsulesDir, capsules };
56167
57224
  }
56168
57225
  // ── Dreams pipeline telemetry surfaces (issue #678 PR 3+4) ──────────────
56169
57226
  /**
@@ -56257,14 +57314,273 @@ import { createServer as createServer3 } from "http";
56257
57314
  import { randomUUID as randomUUID6, timingSafeEqual as timingSafeEqual2 } from "crypto";
56258
57315
  import { AsyncLocalStorage } from "async_hooks";
56259
57316
  import { existsSync as existsSync9 } from "fs";
56260
- import { readFile as readFile43 } from "fs/promises";
57317
+ import { readFile as readFile44 } from "fs/promises";
56261
57318
  import path70 from "path";
56262
57319
  import { fileURLToPath as fileURLToPath3, URL as URL3 } from "url";
56263
57320
 
56264
57321
  // ../remnic-core/src/access-mcp.ts
56265
- import { readFile as readFile42 } from "fs/promises";
57322
+ import { readFile as readFile43 } from "fs/promises";
56266
57323
  import { randomUUID as randomUUID5 } from "crypto";
56267
57324
 
57325
+ // ../remnic-core/src/access-schema.ts
57326
+ function formatZodError(error) {
57327
+ return {
57328
+ error: "request validation failed",
57329
+ code: "validation_error",
57330
+ details: error.issues.map((issue) => ({
57331
+ field: issue.path.join(".") || "(root)",
57332
+ message: issue.message
57333
+ }))
57334
+ };
57335
+ }
57336
+ var namespaceSchema = external_exports.string().trim().max(256).optional();
57337
+ var sessionKeySchema = external_exports.string().trim().min(1).max(512).optional();
57338
+ var idempotencyKeySchema = external_exports.string().trim().min(1).max(256).optional();
57339
+ var dryRunSchema = external_exports.boolean().optional();
57340
+ var schemaVersionSchema = external_exports.number().int().optional();
57341
+ var codingContextSchema = external_exports.object({
57342
+ projectId: external_exports.string().trim().min(1, "codingContext.projectId is required").max(128),
57343
+ branch: external_exports.string().trim().max(256).nullable(),
57344
+ rootPath: external_exports.string().trim().min(1, "codingContext.rootPath is required").max(1024),
57345
+ defaultBranch: external_exports.string().trim().max(256).nullable()
57346
+ }).nullable();
57347
+ var recallDisclosureSchema = external_exports.enum(["chunk", "section", "raw"]);
57348
+ var tagMatchSchema = external_exports.enum(["any", "all"]);
57349
+ var recallRequestSchema = external_exports.object({
57350
+ query: external_exports.string().min(1, "query is required"),
57351
+ sessionKey: sessionKeySchema,
57352
+ namespace: namespaceSchema,
57353
+ topK: external_exports.number().int().min(0).max(200).optional(),
57354
+ mode: external_exports.enum(["auto", "no_recall", "minimal", "full", "graph_mode"]).optional(),
57355
+ includeDebug: external_exports.boolean().optional(),
57356
+ disclosure: recallDisclosureSchema.optional(),
57357
+ codingContext: codingContextSchema.optional(),
57358
+ /** Working directory for auto git-context resolution (issue #569). */
57359
+ cwd: external_exports.string().trim().min(1, "cwd must be non-empty when provided").max(2048).optional(),
57360
+ /**
57361
+ * Arbitrary project tag for non-git-based project scoping (issue #569).
57362
+ * Creates a coding context with `projectId: "tag:<projectTag>"`.
57363
+ */
57364
+ projectTag: external_exports.string().trim().min(1, "projectTag must be non-empty when provided").max(256).optional(),
57365
+ /**
57366
+ * Historical recall pin (issue #680). ISO 8601 timestamp. The
57367
+ * schema only enforces the basic shape; the access service runs
57368
+ * `Date.parse` and emits a structured 400 on malformed input
57369
+ * (CLAUDE.md rule 51).
57370
+ */
57371
+ asOf: external_exports.string().trim().min(1, "asOf must be a non-empty ISO 8601 timestamp").max(64).optional(),
57372
+ /**
57373
+ * Free-form recall tag filter (issue #689). When provided, recall results
57374
+ * whose frontmatter `tags` do not match the filter are removed before the
57375
+ * response is returned. Comparison is case-sensitive exact match.
57376
+ */
57377
+ tags: external_exports.array(external_exports.string().trim().min(1).max(256)).max(50).optional(),
57378
+ /**
57379
+ * Match mode for `tags` (issue #689). Defaults to `"any"` when `tags` is
57380
+ * provided and `tagMatch` is omitted. Ignored when `tags` is absent.
57381
+ */
57382
+ tagMatch: tagMatchSchema.optional(),
57383
+ /**
57384
+ * Include graph edges below `graphTraversalConfidenceFloor` for diagnostic
57385
+ * recall traversal (issue #681). Defaults to false.
57386
+ */
57387
+ includeLowConfidence: external_exports.boolean().optional()
57388
+ });
57389
+ var recallExplainRequestSchema = external_exports.object({
57390
+ sessionKey: sessionKeySchema,
57391
+ namespace: namespaceSchema
57392
+ });
57393
+ var setCodingContextRequestSchema = external_exports.object({
57394
+ sessionKey: external_exports.string().trim().min(1, "sessionKey is required").max(512),
57395
+ codingContext: codingContextSchema
57396
+ });
57397
+ var messageSchema = external_exports.object({
57398
+ role: external_exports.enum(["user", "assistant"]),
57399
+ content: external_exports.string().min(1, "message content must be non-empty"),
57400
+ sourceFormat: external_exports.enum(["openai", "anthropic", "openclaw", "lossless-claw", "remnic"]).nullable().optional(),
57401
+ rawContent: external_exports.unknown().nullable().optional(),
57402
+ parts: external_exports.array(
57403
+ external_exports.object({
57404
+ ordinal: external_exports.number().int().min(0).nullable().optional(),
57405
+ kind: external_exports.enum([
57406
+ "text",
57407
+ "tool_call",
57408
+ "tool_result",
57409
+ "patch",
57410
+ "file_read",
57411
+ "file_write",
57412
+ "step_start",
57413
+ "step_finish",
57414
+ "snapshot",
57415
+ "retry"
57416
+ ]),
57417
+ payload: external_exports.record(external_exports.string(), external_exports.unknown()),
57418
+ toolName: external_exports.string().nullable().optional(),
57419
+ tool_name: external_exports.string().nullable().optional(),
57420
+ filePath: external_exports.string().nullable().optional(),
57421
+ file_path: external_exports.string().nullable().optional(),
57422
+ createdAt: external_exports.string().nullable().optional(),
57423
+ created_at: external_exports.string().nullable().optional()
57424
+ })
57425
+ ).nullable().optional()
57426
+ });
57427
+ var observeRequestSchema = external_exports.object({
57428
+ sessionKey: external_exports.string().trim().min(1, "sessionKey is required").max(512),
57429
+ messages: external_exports.array(messageSchema).min(1, "messages must be a non-empty array"),
57430
+ namespace: namespaceSchema,
57431
+ skipExtraction: external_exports.boolean().optional(),
57432
+ /** Working directory for auto git-context resolution (issue #569). */
57433
+ cwd: external_exports.string().trim().min(1, "cwd must be non-empty when provided").max(2048).optional(),
57434
+ /**
57435
+ * Arbitrary project tag for non-git-based project scoping (issue #569).
57436
+ * Creates a coding context with `projectId: "tag:<projectTag>"`.
57437
+ */
57438
+ projectTag: external_exports.string().trim().min(1, "projectTag must be non-empty when provided").max(256).optional()
57439
+ });
57440
+ var writeContentSchema = external_exports.string().min(1, "content is required").max(5e4);
57441
+ var categorySchema = external_exports.enum([
57442
+ "fact",
57443
+ "preference",
57444
+ "correction",
57445
+ "entity",
57446
+ "decision",
57447
+ "relationship",
57448
+ "principle",
57449
+ "commitment",
57450
+ "moment",
57451
+ "skill",
57452
+ "rule",
57453
+ "procedure",
57454
+ "reasoning_trace"
57455
+ ]).optional();
57456
+ var confidenceSchema = external_exports.number().min(0).max(1).optional();
57457
+ var tagsSchema = external_exports.array(external_exports.string().max(256)).max(50).optional();
57458
+ var entityRefSchema = external_exports.string().trim().max(512).optional();
57459
+ var ttlSchema = external_exports.string().trim().max(128).optional();
57460
+ var sourceReasonSchema = external_exports.string().trim().max(2e3).optional();
57461
+ var memoryStoreRequestSchema = external_exports.object({
57462
+ schemaVersion: schemaVersionSchema,
57463
+ idempotencyKey: idempotencyKeySchema,
57464
+ dryRun: dryRunSchema,
57465
+ sessionKey: sessionKeySchema,
57466
+ content: writeContentSchema,
57467
+ category: categorySchema,
57468
+ confidence: confidenceSchema,
57469
+ namespace: namespaceSchema,
57470
+ tags: tagsSchema,
57471
+ entityRef: entityRefSchema,
57472
+ ttl: ttlSchema,
57473
+ sourceReason: sourceReasonSchema
57474
+ });
57475
+ var suggestionSubmitRequestSchema = memoryStoreRequestSchema;
57476
+ var reviewDispositionRequestSchema = external_exports.object({
57477
+ memoryId: external_exports.string().trim().min(1, "memoryId is required"),
57478
+ status: external_exports.enum([
57479
+ "active",
57480
+ "pending_review",
57481
+ "quarantined",
57482
+ "rejected",
57483
+ "superseded",
57484
+ "archived"
57485
+ ]),
57486
+ reasonCode: external_exports.string().trim().min(1, "reasonCode is required"),
57487
+ namespace: namespaceSchema
57488
+ });
57489
+ var trustZonePromoteRequestSchema = external_exports.object({
57490
+ recordId: external_exports.string().trim().min(1, "recordId is required"),
57491
+ targetZone: external_exports.enum(["working", "trusted"], {
57492
+ errorMap: () => ({ message: "targetZone must be 'working' or 'trusted'" })
57493
+ }),
57494
+ promotionReason: external_exports.string().trim().min(1, "promotionReason is required"),
57495
+ recordedAt: external_exports.string().trim().optional(),
57496
+ summary: external_exports.string().trim().max(5e3).optional(),
57497
+ dryRun: dryRunSchema,
57498
+ namespace: namespaceSchema
57499
+ });
57500
+ var trustZoneDemoSeedRequestSchema = external_exports.object({
57501
+ scenario: external_exports.string().trim().max(256).optional(),
57502
+ recordedAt: external_exports.string().trim().optional(),
57503
+ dryRun: dryRunSchema,
57504
+ namespace: namespaceSchema
57505
+ });
57506
+ var lcmSearchRequestSchema = external_exports.object({
57507
+ query: external_exports.string().min(1, "query is required"),
57508
+ sessionKey: sessionKeySchema,
57509
+ namespace: namespaceSchema,
57510
+ limit: external_exports.number().int().min(1).max(100).optional()
57511
+ });
57512
+ var daySummaryRequestSchema = external_exports.object({
57513
+ memories: external_exports.string().max(1e5).optional(),
57514
+ sessionKey: sessionKeySchema,
57515
+ namespace: namespaceSchema
57516
+ });
57517
+ var capsuleTopLevelSegmentSchema = external_exports.string().trim().min(1).max(128).refine(
57518
+ (value) => !value.includes("/") && !value.includes("\\"),
57519
+ "must be a top-level directory name without path separators"
57520
+ );
57521
+ var capsulePeerIdSchema = external_exports.string().trim().min(1).max(256).refine(
57522
+ (value) => value !== "." && value !== ".." && !value.includes("/") && !value.includes("\\"),
57523
+ "must be a plain peer id without path separators"
57524
+ );
57525
+ var capsuleIsoSinceSchema = external_exports.string().trim().min(1, "since must be a non-empty ISO 8601 timestamp").max(128).refine(
57526
+ isValidCapsuleSince,
57527
+ "since must be a valid ISO 8601 timestamp with no calendar overflow"
57528
+ );
57529
+ var capsuleExportRequestSchema = external_exports.object({
57530
+ name: external_exports.string().trim().min(1, "name is required").max(64, "name must be 64 characters or fewer").regex(
57531
+ CAPSULE_ID_PATTERN,
57532
+ "name must be alphanumeric with single dashes (no spaces, no leading/trailing dashes)"
57533
+ ),
57534
+ namespace: namespaceSchema,
57535
+ since: capsuleIsoSinceSchema.optional(),
57536
+ includeKinds: external_exports.array(capsuleTopLevelSegmentSchema).max(50).optional(),
57537
+ peerIds: external_exports.array(capsulePeerIdSchema).max(100).optional(),
57538
+ includeTranscripts: external_exports.boolean().optional(),
57539
+ encrypt: external_exports.boolean().optional()
57540
+ });
57541
+ var capsuleImportRequestSchema = external_exports.object({
57542
+ archivePath: external_exports.string().trim().min(1, "archivePath is required").max(4096),
57543
+ namespace: namespaceSchema,
57544
+ mode: external_exports.enum(["skip", "overwrite", "fork"]).optional()
57545
+ });
57546
+ var capsuleListRequestSchema = external_exports.object({
57547
+ namespace: namespaceSchema
57548
+ });
57549
+ var schemas = {
57550
+ recall: recallRequestSchema,
57551
+ recallExplain: recallExplainRequestSchema,
57552
+ setCodingContext: setCodingContextRequestSchema,
57553
+ observe: observeRequestSchema,
57554
+ memoryStore: memoryStoreRequestSchema,
57555
+ suggestionSubmit: suggestionSubmitRequestSchema,
57556
+ reviewDisposition: reviewDispositionRequestSchema,
57557
+ trustZonePromote: trustZonePromoteRequestSchema,
57558
+ trustZoneDemoSeed: trustZoneDemoSeedRequestSchema,
57559
+ lcmSearch: lcmSearchRequestSchema,
57560
+ daySummary: daySummaryRequestSchema,
57561
+ capsuleExport: capsuleExportRequestSchema,
57562
+ capsuleImport: capsuleImportRequestSchema,
57563
+ capsuleList: capsuleListRequestSchema
57564
+ };
57565
+ function validateRequest(schemaName, body) {
57566
+ const schema = schemas[schemaName];
57567
+ if (!schema) {
57568
+ return {
57569
+ success: false,
57570
+ error: {
57571
+ error: `unknown schema: ${schemaName}`,
57572
+ code: "validation_error",
57573
+ details: []
57574
+ }
57575
+ };
57576
+ }
57577
+ const result = schema.safeParse(body);
57578
+ if (result.success) {
57579
+ return { success: true, data: result.data };
57580
+ }
57581
+ return { success: false, error: formatZodError(result.error) };
57582
+ }
57583
+
56268
57584
  // ../remnic-core/src/citations.ts
56269
57585
  var OPEN_TAG = "<oai-mem-citation>";
56270
57586
  var CLOSE_TAG = "</oai-mem-citation>";
@@ -56321,12 +57637,43 @@ function withToolAliases(tool) {
56321
57637
  if (canonicalName === tool.name) return [canonicalTool];
56322
57638
  return [canonicalTool, tool];
56323
57639
  }
57640
+ var STRICT_MCP_SCHEMA_KEYS = {
57641
+ capsuleExport: [
57642
+ "name",
57643
+ "namespace",
57644
+ "since",
57645
+ "includeKinds",
57646
+ "peerIds",
57647
+ "includeTranscripts",
57648
+ "encrypt"
57649
+ ],
57650
+ capsuleImport: ["archivePath", "namespace", "mode"],
57651
+ capsuleList: ["namespace"]
57652
+ };
57653
+ function parseMcpRequest(schemaName, args) {
57654
+ const allowedKeys = STRICT_MCP_SCHEMA_KEYS[schemaName];
57655
+ if (allowedKeys) {
57656
+ const allowed = new Set(allowedKeys);
57657
+ const unexpected = Object.keys(args).filter((key) => !allowed.has(key));
57658
+ if (unexpected.length > 0) {
57659
+ throw new EngramAccessInputError(
57660
+ `request validation failed: (root): Unrecognized key(s) in object: ${unexpected.join(", ")}`
57661
+ );
57662
+ }
57663
+ }
57664
+ const validation = validateRequest(schemaName, args);
57665
+ if (validation.success) return validation.data;
57666
+ const details = validation.error.details.map((detail) => `${detail.field}: ${detail.message}`).join("; ");
57667
+ throw new EngramAccessInputError(
57668
+ details.length > 0 ? `${validation.error.error}: ${details}` : validation.error.error
57669
+ );
57670
+ }
56324
57671
  async function getMcpServerVersion() {
56325
57672
  const envVersion = readEnvVar("OPENCLAW_ENGRAM_VERSION")?.trim() || readEnvVar("npm_package_version")?.trim();
56326
57673
  if (envVersion) return envVersion;
56327
57674
  try {
56328
57675
  const pkgPath = new URL("../package.json", import.meta.url);
56329
- const raw = await readFile42(pkgPath, "utf-8");
57676
+ const raw = await readFile43(pkgPath, "utf-8");
56330
57677
  const parsed = JSON.parse(raw);
56331
57678
  return parsed.version?.trim() || "unknown";
56332
57679
  } catch {
@@ -56494,6 +57841,70 @@ var EngramMcpServer = class {
56494
57841
  additionalProperties: false
56495
57842
  }
56496
57843
  },
57844
+ {
57845
+ name: "engram.capsule_export",
57846
+ description: "Export a portable Remnic capsule archive from the namespace-scoped memory store.",
57847
+ inputSchema: {
57848
+ type: "object",
57849
+ properties: {
57850
+ name: {
57851
+ type: "string",
57852
+ description: "Capsule id (alphanumeric with single dashes, max 64 characters)."
57853
+ },
57854
+ namespace: { type: "string" },
57855
+ since: {
57856
+ type: "string",
57857
+ description: "Only include files modified on or after this ISO 8601 timestamp."
57858
+ },
57859
+ includeKinds: {
57860
+ type: "array",
57861
+ items: { type: "string" },
57862
+ description: "Optional top-level directory allow-list."
57863
+ },
57864
+ peerIds: {
57865
+ type: "array",
57866
+ items: { type: "string" },
57867
+ description: "Optional peer id allow-list for the peers/ subtree."
57868
+ },
57869
+ includeTranscripts: { type: "boolean" },
57870
+ encrypt: { type: "boolean" }
57871
+ },
57872
+ required: ["name"],
57873
+ additionalProperties: false
57874
+ }
57875
+ },
57876
+ {
57877
+ name: "engram.capsule_import",
57878
+ description: "Import a Remnic capsule archive into the namespace-scoped memory store.",
57879
+ inputSchema: {
57880
+ type: "object",
57881
+ properties: {
57882
+ archivePath: {
57883
+ type: "string",
57884
+ description: "Path to a .capsule.json.gz or .capsule.json.gz.enc archive."
57885
+ },
57886
+ namespace: { type: "string" },
57887
+ mode: {
57888
+ type: "string",
57889
+ enum: ["skip", "overwrite", "fork"],
57890
+ description: "Conflict handling mode. Defaults to skip."
57891
+ }
57892
+ },
57893
+ required: ["archivePath"],
57894
+ additionalProperties: false
57895
+ }
57896
+ },
57897
+ {
57898
+ name: "engram.capsule_list",
57899
+ description: "List capsule archives in the namespace-scoped capsule store.",
57900
+ inputSchema: {
57901
+ type: "object",
57902
+ properties: {
57903
+ namespace: { type: "string" }
57904
+ },
57905
+ additionalProperties: false
57906
+ }
57907
+ },
56497
57908
  {
56498
57909
  name: "engram.memory_governance_run",
56499
57910
  description: "Run Remnic memory governance in a bounded shadow/apply pass.",
@@ -56657,9 +58068,51 @@ var EngramMcpServer = class {
56657
58068
  type: "object",
56658
58069
  properties: {
56659
58070
  role: { type: "string", enum: ["user", "assistant"] },
56660
- content: { type: "string" }
58071
+ content: { type: "string" },
58072
+ sourceFormat: {
58073
+ type: "string",
58074
+ enum: ["openai", "anthropic", "openclaw", "lossless-claw", "remnic"]
58075
+ },
58076
+ rawContent: {
58077
+ description: "Optional native provider content blocks for structured message-part capture."
58078
+ },
58079
+ parts: {
58080
+ type: "array",
58081
+ description: "Optional normalized Remnic LCM message parts.",
58082
+ items: {
58083
+ type: "object",
58084
+ properties: {
58085
+ ordinal: { type: ["number", "null"], minimum: 0 },
58086
+ kind: {
58087
+ type: "string",
58088
+ enum: [
58089
+ "text",
58090
+ "tool_call",
58091
+ "tool_result",
58092
+ "patch",
58093
+ "file_read",
58094
+ "file_write",
58095
+ "step_start",
58096
+ "step_finish",
58097
+ "snapshot",
58098
+ "retry"
58099
+ ]
58100
+ },
58101
+ payload: { type: "object", additionalProperties: true },
58102
+ toolName: { type: ["string", "null"] },
58103
+ tool_name: { type: ["string", "null"] },
58104
+ filePath: { type: ["string", "null"] },
58105
+ file_path: { type: ["string", "null"] },
58106
+ createdAt: { type: ["string", "null"] },
58107
+ created_at: { type: ["string", "null"] }
58108
+ },
58109
+ required: ["kind", "payload"],
58110
+ additionalProperties: false
58111
+ }
58112
+ }
56661
58113
  },
56662
- required: ["role", "content"]
58114
+ required: ["role", "content"],
58115
+ additionalProperties: false
56663
58116
  },
56664
58117
  description: "Conversation messages to observe"
56665
58118
  },
@@ -57149,6 +58602,43 @@ var EngramMcpServer = class {
57149
58602
  additionalProperties: false
57150
58603
  }
57151
58604
  },
58605
+ {
58606
+ name: "engram.memory_action_apply",
58607
+ description: "Record a memory-action application event for policy-learning telemetry.",
58608
+ inputSchema: {
58609
+ type: "object",
58610
+ properties: {
58611
+ action: {
58612
+ type: "string",
58613
+ enum: [
58614
+ "store_episode",
58615
+ "store_note",
58616
+ "update_note",
58617
+ "create_artifact",
58618
+ "summarize_node",
58619
+ "discard",
58620
+ "link_graph"
58621
+ ]
58622
+ },
58623
+ category: { type: "string" },
58624
+ content: { type: "string" },
58625
+ outcome: { type: "string", enum: ["applied", "skipped", "failed"] },
58626
+ reason: { type: "string" },
58627
+ memoryId: { type: "string" },
58628
+ sessionKey: { type: "string" },
58629
+ linkTargetId: { type: "string" },
58630
+ linkType: { type: "string" },
58631
+ linkStrength: { type: "number" },
58632
+ artifactType: { type: "string" },
58633
+ execute: { type: "boolean" },
58634
+ sourcePrompt: { type: "string" },
58635
+ namespace: { type: "string" },
58636
+ dryRun: { type: "boolean" }
58637
+ },
58638
+ required: ["action"],
58639
+ additionalProperties: false
58640
+ }
58641
+ },
57152
58642
  {
57153
58643
  name: "engram.context_checkpoint",
57154
58644
  description: "Save a structured context checkpoint for a session (preserves conversation state to disk).",
@@ -57220,6 +58710,49 @@ var EngramMcpServer = class {
57220
58710
  additionalProperties: false
57221
58711
  }
57222
58712
  },
58713
+ {
58714
+ name: "engram.memory_summarize_hourly",
58715
+ description: "Generate hourly summaries for recent conversations.",
58716
+ inputSchema: {
58717
+ type: "object",
58718
+ properties: {},
58719
+ additionalProperties: false
58720
+ }
58721
+ },
58722
+ {
58723
+ name: "engram.conversation_index_update",
58724
+ description: "Chunk transcript history into conversation-index documents.",
58725
+ inputSchema: {
58726
+ type: "object",
58727
+ properties: {
58728
+ sessionKey: { type: "string" },
58729
+ hours: { type: "number", description: "How many hours of transcript history to include." },
58730
+ embed: { type: "boolean", description: "If true, run QMD embed after update for this invocation." }
58731
+ },
58732
+ additionalProperties: false
58733
+ }
58734
+ },
58735
+ {
58736
+ name: "engram.profiling_report",
58737
+ description: "Return timing and performance data for Remnic recall and extraction pipelines. Requires profilingEnabled: true.",
58738
+ inputSchema: {
58739
+ type: "object",
58740
+ properties: {
58741
+ format: {
58742
+ type: "string",
58743
+ enum: ["ascii", "json"],
58744
+ description: "Output format. Defaults to ascii."
58745
+ },
58746
+ limit: {
58747
+ type: "integer",
58748
+ minimum: 1,
58749
+ maximum: 20,
58750
+ description: "Number of recent traces to include. Defaults to 5."
58751
+ }
58752
+ },
58753
+ additionalProperties: false
58754
+ }
58755
+ },
57223
58756
  {
57224
58757
  name: "engram.graph_edge_decay_run",
57225
58758
  description: "Run the graph-edge-confidence decay maintenance pass (issue #681 PR 2/3). Respects graphEdgeDecayEnabled; writes a structured telemetry record to state/graph-edge-decay-status.json.",
@@ -57472,8 +59005,14 @@ var EngramMcpServer = class {
57472
59005
  if (method === "tools/call") {
57473
59006
  const params = request.params ?? {};
57474
59007
  const name = typeof params.name === "string" ? params.name : "";
57475
- const argumentsObject = params.arguments && typeof params.arguments === "object" && !Array.isArray(params.arguments) ? params.arguments : {};
57476
59008
  try {
59009
+ let argumentsObject = {};
59010
+ if ("arguments" in params && params.arguments !== void 0) {
59011
+ if (params.arguments === null || typeof params.arguments !== "object" || Array.isArray(params.arguments)) {
59012
+ throw new EngramAccessInputError("tools/call arguments must be an object when provided");
59013
+ }
59014
+ argumentsObject = params.arguments;
59015
+ }
57477
59016
  const effectivePrincipal = options?.principalOverride ?? this.authenticatedPrincipal;
57478
59017
  const result = await this.callTool(name, argumentsObject, effectivePrincipal, options?.sessionId);
57479
59018
  return {
@@ -57792,6 +59331,35 @@ ${body}`;
57792
59331
  sessionKey: typeof args.sessionKey === "string" ? args.sessionKey : void 0,
57793
59332
  namespace: typeof args.namespace === "string" ? args.namespace : void 0
57794
59333
  });
59334
+ case "engram.capsule_export": {
59335
+ const body = parseMcpRequest("capsuleExport", args);
59336
+ return this.service.capsuleExport({
59337
+ name: body.name,
59338
+ namespace: body.namespace,
59339
+ principal: effectivePrincipal,
59340
+ since: body.since,
59341
+ includeKinds: body.includeKinds,
59342
+ peerIds: body.peerIds,
59343
+ includeTranscripts: body.includeTranscripts,
59344
+ encrypt: body.encrypt
59345
+ });
59346
+ }
59347
+ case "engram.capsule_import": {
59348
+ const body = parseMcpRequest("capsuleImport", args);
59349
+ return this.service.capsuleImport({
59350
+ archivePath: expandTildePath(body.archivePath),
59351
+ namespace: body.namespace,
59352
+ principal: effectivePrincipal,
59353
+ mode: body.mode
59354
+ });
59355
+ }
59356
+ case "engram.capsule_list": {
59357
+ const body = parseMcpRequest("capsuleList", args);
59358
+ return this.service.capsuleList({
59359
+ namespace: body.namespace,
59360
+ principal: effectivePrincipal
59361
+ });
59362
+ }
57795
59363
  case "engram.memory_governance_run":
57796
59364
  return this.service.governanceRun({
57797
59365
  namespace: typeof args.namespace === "string" ? args.namespace : void 0,
@@ -57885,20 +59453,21 @@ ${body}`;
57885
59453
  effectivePrincipal
57886
59454
  );
57887
59455
  case "engram.observe": {
57888
- if ("cwd" in args && args.cwd !== void 0 && args.cwd !== null && typeof args.cwd !== "string") {
57889
- throw new EngramAccessInputError("cwd must be a string");
57890
- }
57891
- if ("projectTag" in args && args.projectTag !== void 0 && args.projectTag !== null && typeof args.projectTag !== "string") {
57892
- throw new EngramAccessInputError("projectTag must be a string");
57893
- }
59456
+ const body = parseMcpRequest("observe", args);
57894
59457
  return this.service.observe({
57895
- sessionKey: typeof args.sessionKey === "string" ? args.sessionKey : "",
57896
- messages: Array.isArray(args.messages) ? args.messages : [],
57897
- namespace: typeof args.namespace === "string" ? args.namespace : void 0,
59458
+ sessionKey: body.sessionKey,
59459
+ messages: body.messages.map((message) => ({
59460
+ role: message.role,
59461
+ content: message.content,
59462
+ parts: message.parts ?? void 0,
59463
+ rawContent: message.rawContent ?? void 0,
59464
+ sourceFormat: message.sourceFormat ?? void 0
59465
+ })),
59466
+ namespace: body.namespace,
57898
59467
  authenticatedPrincipal: effectivePrincipal,
57899
- skipExtraction: args.skipExtraction === true,
57900
- cwd: typeof args.cwd === "string" ? args.cwd : void 0,
57901
- projectTag: typeof args.projectTag === "string" ? args.projectTag : void 0
59468
+ skipExtraction: body.skipExtraction === true,
59469
+ cwd: body.cwd,
59470
+ projectTag: body.projectTag
57902
59471
  });
57903
59472
  }
57904
59473
  case "engram.lcm_search":
@@ -58176,6 +59745,25 @@ ${body}`;
58176
59745
  timestamp: typeof args.timestamp === "string" ? args.timestamp : void 0
58177
59746
  });
58178
59747
  }
59748
+ case "engram.memory_action_apply":
59749
+ return this.service.memoryActionApply({
59750
+ action: typeof args.action === "string" ? args.action : "",
59751
+ outcome: typeof args.outcome === "string" ? args.outcome : void 0,
59752
+ reason: typeof args.reason === "string" ? args.reason : void 0,
59753
+ memoryId: typeof args.memoryId === "string" ? args.memoryId : void 0,
59754
+ namespace: typeof args.namespace === "string" ? args.namespace : void 0,
59755
+ principal: effectivePrincipal,
59756
+ sessionKey: typeof args.sessionKey === "string" ? args.sessionKey : void 0,
59757
+ content: typeof args.content === "string" ? args.content : void 0,
59758
+ category: typeof args.category === "string" ? args.category : void 0,
59759
+ linkTargetId: typeof args.linkTargetId === "string" ? args.linkTargetId : void 0,
59760
+ linkType: typeof args.linkType === "string" ? args.linkType : void 0,
59761
+ linkStrength: typeof args.linkStrength === "number" ? args.linkStrength : void 0,
59762
+ artifactType: typeof args.artifactType === "string" ? args.artifactType : void 0,
59763
+ execute: typeof args.execute === "boolean" ? args.execute : void 0,
59764
+ sourcePrompt: typeof args.sourcePrompt === "string" ? args.sourcePrompt : void 0,
59765
+ dryRun: args.dryRun === true
59766
+ });
58179
59767
  case "engram.context_checkpoint":
58180
59768
  return this.service.contextCheckpoint({
58181
59769
  sessionKey: typeof args.sessionKey === "string" ? args.sessionKey : "",
@@ -58235,6 +59823,34 @@ ${body}`;
58235
59823
  namespace: typeof args.namespace === "string" ? args.namespace : void 0
58236
59824
  });
58237
59825
  }
59826
+ case "engram.memory_summarize_hourly":
59827
+ case "remnic.memory_summarize_hourly":
59828
+ return this.service.memorySummarizeHourly();
59829
+ case "engram.conversation_index_update":
59830
+ case "remnic.conversation_index_update": {
59831
+ if ("sessionKey" in args && args.sessionKey !== void 0 && typeof args.sessionKey !== "string") {
59832
+ throw new Error("sessionKey must be a string when provided");
59833
+ }
59834
+ const sessionKey = typeof args.sessionKey === "string" ? args.sessionKey : void 0;
59835
+ return this.service.conversationIndexUpdate({
59836
+ sessionKey,
59837
+ hours: typeof args.hours === "number" && Number.isFinite(args.hours) ? args.hours : void 0,
59838
+ embed: typeof args.embed === "boolean" ? args.embed : void 0
59839
+ });
59840
+ }
59841
+ case "engram.profiling_report":
59842
+ case "remnic.profiling_report": {
59843
+ if ("format" in args && args.format !== void 0 && typeof args.format !== "string") {
59844
+ throw new EngramAccessInputError("format must be a string when provided");
59845
+ }
59846
+ if ("limit" in args && args.limit !== void 0 && typeof args.limit !== "number") {
59847
+ throw new EngramAccessInputError("limit must be a number when provided");
59848
+ }
59849
+ return this.service.profilingReport({
59850
+ format: typeof args.format === "string" ? args.format : void 0,
59851
+ limit: typeof args.limit === "number" ? args.limit : void 0
59852
+ });
59853
+ }
58238
59854
  case "engram.graph_edge_decay_run":
58239
59855
  case "remnic.graph_edge_decay_run": {
58240
59856
  const cfg = this.service.configRef;
@@ -58383,204 +59999,6 @@ ${body}`;
58383
59999
  }
58384
60000
  };
58385
60001
 
58386
- // ../remnic-core/src/access-schema.ts
58387
- function formatZodError(error) {
58388
- return {
58389
- error: "request validation failed",
58390
- code: "validation_error",
58391
- details: error.issues.map((issue) => ({
58392
- field: issue.path.join(".") || "(root)",
58393
- message: issue.message
58394
- }))
58395
- };
58396
- }
58397
- var namespaceSchema = external_exports.string().trim().max(256).optional();
58398
- var sessionKeySchema = external_exports.string().trim().min(1).max(512).optional();
58399
- var idempotencyKeySchema = external_exports.string().trim().min(1).max(256).optional();
58400
- var dryRunSchema = external_exports.boolean().optional();
58401
- var schemaVersionSchema = external_exports.number().int().optional();
58402
- var codingContextSchema = external_exports.object({
58403
- projectId: external_exports.string().trim().min(1, "codingContext.projectId is required").max(128),
58404
- branch: external_exports.string().trim().max(256).nullable(),
58405
- rootPath: external_exports.string().trim().min(1, "codingContext.rootPath is required").max(1024),
58406
- defaultBranch: external_exports.string().trim().max(256).nullable()
58407
- }).nullable();
58408
- var recallDisclosureSchema = external_exports.enum(["chunk", "section", "raw"]);
58409
- var tagMatchSchema = external_exports.enum(["any", "all"]);
58410
- var recallRequestSchema = external_exports.object({
58411
- query: external_exports.string().min(1, "query is required"),
58412
- sessionKey: sessionKeySchema,
58413
- namespace: namespaceSchema,
58414
- topK: external_exports.number().int().min(0).max(200).optional(),
58415
- mode: external_exports.enum(["auto", "no_recall", "minimal", "full", "graph_mode"]).optional(),
58416
- includeDebug: external_exports.boolean().optional(),
58417
- disclosure: recallDisclosureSchema.optional(),
58418
- codingContext: codingContextSchema.optional(),
58419
- /** Working directory for auto git-context resolution (issue #569). */
58420
- cwd: external_exports.string().trim().min(1, "cwd must be non-empty when provided").max(2048).optional(),
58421
- /**
58422
- * Arbitrary project tag for non-git-based project scoping (issue #569).
58423
- * Creates a coding context with `projectId: "tag:<projectTag>"`.
58424
- */
58425
- projectTag: external_exports.string().trim().min(1, "projectTag must be non-empty when provided").max(256).optional(),
58426
- /**
58427
- * Historical recall pin (issue #680). ISO 8601 timestamp. The
58428
- * schema only enforces the basic shape; the access service runs
58429
- * `Date.parse` and emits a structured 400 on malformed input
58430
- * (CLAUDE.md rule 51).
58431
- */
58432
- asOf: external_exports.string().trim().min(1, "asOf must be a non-empty ISO 8601 timestamp").max(64).optional(),
58433
- /**
58434
- * Free-form recall tag filter (issue #689). When provided, recall results
58435
- * whose frontmatter `tags` do not match the filter are removed before the
58436
- * response is returned. Comparison is case-sensitive exact match.
58437
- */
58438
- tags: external_exports.array(external_exports.string().trim().min(1).max(256)).max(50).optional(),
58439
- /**
58440
- * Match mode for `tags` (issue #689). Defaults to `"any"` when `tags` is
58441
- * provided and `tagMatch` is omitted. Ignored when `tags` is absent.
58442
- */
58443
- tagMatch: tagMatchSchema.optional(),
58444
- /**
58445
- * Include graph edges below `graphTraversalConfidenceFloor` for diagnostic
58446
- * recall traversal (issue #681). Defaults to false.
58447
- */
58448
- includeLowConfidence: external_exports.boolean().optional()
58449
- });
58450
- var recallExplainRequestSchema = external_exports.object({
58451
- sessionKey: sessionKeySchema,
58452
- namespace: namespaceSchema
58453
- });
58454
- var setCodingContextRequestSchema = external_exports.object({
58455
- sessionKey: external_exports.string().trim().min(1, "sessionKey is required").max(512),
58456
- codingContext: codingContextSchema
58457
- });
58458
- var messageSchema = external_exports.object({
58459
- role: external_exports.enum(["user", "assistant"]),
58460
- content: external_exports.string().min(1, "message content must be non-empty")
58461
- });
58462
- var observeRequestSchema = external_exports.object({
58463
- sessionKey: external_exports.string().trim().min(1, "sessionKey is required").max(512),
58464
- messages: external_exports.array(messageSchema).min(1, "messages must be a non-empty array"),
58465
- namespace: namespaceSchema,
58466
- skipExtraction: external_exports.boolean().optional(),
58467
- /** Working directory for auto git-context resolution (issue #569). */
58468
- cwd: external_exports.string().trim().min(1, "cwd must be non-empty when provided").max(2048).optional(),
58469
- /**
58470
- * Arbitrary project tag for non-git-based project scoping (issue #569).
58471
- * Creates a coding context with `projectId: "tag:<projectTag>"`.
58472
- */
58473
- projectTag: external_exports.string().trim().min(1, "projectTag must be non-empty when provided").max(256).optional()
58474
- });
58475
- var writeContentSchema = external_exports.string().min(1, "content is required").max(5e4);
58476
- var categorySchema = external_exports.enum([
58477
- "fact",
58478
- "preference",
58479
- "correction",
58480
- "entity",
58481
- "decision",
58482
- "relationship",
58483
- "principle",
58484
- "commitment",
58485
- "moment",
58486
- "skill",
58487
- "rule",
58488
- "procedure",
58489
- "reasoning_trace"
58490
- ]).optional();
58491
- var confidenceSchema = external_exports.number().min(0).max(1).optional();
58492
- var tagsSchema = external_exports.array(external_exports.string().max(256)).max(50).optional();
58493
- var entityRefSchema = external_exports.string().trim().max(512).optional();
58494
- var ttlSchema = external_exports.string().trim().max(128).optional();
58495
- var sourceReasonSchema = external_exports.string().trim().max(2e3).optional();
58496
- var memoryStoreRequestSchema = external_exports.object({
58497
- schemaVersion: schemaVersionSchema,
58498
- idempotencyKey: idempotencyKeySchema,
58499
- dryRun: dryRunSchema,
58500
- sessionKey: sessionKeySchema,
58501
- content: writeContentSchema,
58502
- category: categorySchema,
58503
- confidence: confidenceSchema,
58504
- namespace: namespaceSchema,
58505
- tags: tagsSchema,
58506
- entityRef: entityRefSchema,
58507
- ttl: ttlSchema,
58508
- sourceReason: sourceReasonSchema
58509
- });
58510
- var suggestionSubmitRequestSchema = memoryStoreRequestSchema;
58511
- var reviewDispositionRequestSchema = external_exports.object({
58512
- memoryId: external_exports.string().trim().min(1, "memoryId is required"),
58513
- status: external_exports.enum([
58514
- "active",
58515
- "pending_review",
58516
- "quarantined",
58517
- "rejected",
58518
- "superseded",
58519
- "archived"
58520
- ]),
58521
- reasonCode: external_exports.string().trim().min(1, "reasonCode is required"),
58522
- namespace: namespaceSchema
58523
- });
58524
- var trustZonePromoteRequestSchema = external_exports.object({
58525
- recordId: external_exports.string().trim().min(1, "recordId is required"),
58526
- targetZone: external_exports.enum(["working", "trusted"], {
58527
- errorMap: () => ({ message: "targetZone must be 'working' or 'trusted'" })
58528
- }),
58529
- promotionReason: external_exports.string().trim().min(1, "promotionReason is required"),
58530
- recordedAt: external_exports.string().trim().optional(),
58531
- summary: external_exports.string().trim().max(5e3).optional(),
58532
- dryRun: dryRunSchema,
58533
- namespace: namespaceSchema
58534
- });
58535
- var trustZoneDemoSeedRequestSchema = external_exports.object({
58536
- scenario: external_exports.string().trim().max(256).optional(),
58537
- recordedAt: external_exports.string().trim().optional(),
58538
- dryRun: dryRunSchema,
58539
- namespace: namespaceSchema
58540
- });
58541
- var lcmSearchRequestSchema = external_exports.object({
58542
- query: external_exports.string().min(1, "query is required"),
58543
- sessionKey: sessionKeySchema,
58544
- namespace: namespaceSchema,
58545
- limit: external_exports.number().int().min(1).max(100).optional()
58546
- });
58547
- var daySummaryRequestSchema = external_exports.object({
58548
- memories: external_exports.string().max(1e5).optional(),
58549
- sessionKey: sessionKeySchema,
58550
- namespace: namespaceSchema
58551
- });
58552
- var schemas = {
58553
- recall: recallRequestSchema,
58554
- recallExplain: recallExplainRequestSchema,
58555
- setCodingContext: setCodingContextRequestSchema,
58556
- observe: observeRequestSchema,
58557
- memoryStore: memoryStoreRequestSchema,
58558
- suggestionSubmit: suggestionSubmitRequestSchema,
58559
- reviewDisposition: reviewDispositionRequestSchema,
58560
- trustZonePromote: trustZonePromoteRequestSchema,
58561
- trustZoneDemoSeed: trustZoneDemoSeedRequestSchema,
58562
- lcmSearch: lcmSearchRequestSchema,
58563
- daySummary: daySummaryRequestSchema
58564
- };
58565
- function validateRequest(schemaName, body) {
58566
- const schema = schemas[schemaName];
58567
- if (!schema) {
58568
- return {
58569
- success: false,
58570
- error: {
58571
- error: `unknown schema: ${schemaName}`,
58572
- code: "validation_error",
58573
- details: []
58574
- }
58575
- };
58576
- }
58577
- const result = schema.safeParse(body);
58578
- if (result.success) {
58579
- return { success: true, data: result.data };
58580
- }
58581
- return { success: false, error: formatZodError(result.error) };
58582
- }
58583
-
58584
60002
  // ../remnic-core/src/adapters/types.ts
58585
60003
  function headerValue(headers, key) {
58586
60004
  const raw = headers[key];
@@ -59099,6 +60517,36 @@ var EngramAccessHttpServer = class {
59099
60517
  this.respondJson(res, 200, { ok: true });
59100
60518
  return;
59101
60519
  }
60520
+ if (req.method === "POST" && (pathname === "/engram/v1/capsules/export" || pathname === "/remnic/v1/capsules/export")) {
60521
+ const body = await this.readValidatedBody(req, "capsuleExport");
60522
+ this.ensureWriteRateLimitAvailable();
60523
+ const result = await this.service.capsuleExport({
60524
+ name: body.name,
60525
+ namespace: this.resolveNamespace(req, body.namespace),
60526
+ principal: this.resolveRequestPrincipal(req),
60527
+ since: body.since,
60528
+ includeKinds: body.includeKinds,
60529
+ peerIds: body.peerIds,
60530
+ includeTranscripts: body.includeTranscripts,
60531
+ encrypt: body.encrypt
60532
+ });
60533
+ this.recordWriteRateLimitHit();
60534
+ this.respondJson(res, 200, result);
60535
+ return;
60536
+ }
60537
+ if (req.method === "POST" && (pathname === "/engram/v1/capsules/import" || pathname === "/remnic/v1/capsules/import")) {
60538
+ const body = await this.readValidatedBody(req, "capsuleImport");
60539
+ this.ensureWriteRateLimitAvailable();
60540
+ const result = await this.service.capsuleImport({
60541
+ archivePath: expandTildePath(body.archivePath),
60542
+ namespace: this.resolveNamespace(req, body.namespace),
60543
+ principal: this.resolveRequestPrincipal(req),
60544
+ mode: body.mode
60545
+ });
60546
+ this.recordWriteRateLimitHit();
60547
+ this.respondJson(res, 200, result);
60548
+ return;
60549
+ }
59102
60550
  if (req.method === "POST" && pathname === "/engram/v1/recall/explain") {
59103
60551
  const body = await this.readValidatedBody(req, "recallExplain");
59104
60552
  const response = await this.service.recallExplain({
@@ -59198,7 +60646,13 @@ var EngramAccessHttpServer = class {
59198
60646
  this.ensureWriteRateLimitAvailable();
59199
60647
  const response = await this.service.observe({
59200
60648
  sessionKey: body.sessionKey,
59201
- messages: body.messages,
60649
+ messages: body.messages.map((message) => ({
60650
+ role: message.role,
60651
+ content: message.content,
60652
+ sourceFormat: message.sourceFormat ?? void 0,
60653
+ rawContent: message.rawContent ?? void 0,
60654
+ parts: message.parts ?? void 0
60655
+ })),
59202
60656
  namespace: this.resolveNamespace(req, body.namespace),
59203
60657
  authenticatedPrincipal: this.resolveRequestPrincipal(req),
59204
60658
  skipExtraction: body.skipExtraction === true,
@@ -59882,7 +61336,8 @@ var EngramAccessHttpServer = class {
59882
61336
  const toolName = typeof request.params?.name === "string" ? request.params.name : "";
59883
61337
  const toolArgs = request.params?.arguments;
59884
61338
  const dreamsRunDryRun = (toolName === "engram.dreams_run" || toolName === "remnic.dreams_run") && toolArgs !== null && typeof toolArgs === "object" && !Array.isArray(toolArgs) && toolArgs.dryRun === true;
59885
- const isMcpWrite = request.method === "tools/call" && (toolName === "engram.memory_store" || toolName === "remnic.memory_store" || toolName === "engram.suggestion_submit" || toolName === "remnic.suggestion_submit" || toolName === "engram.observe" || toolName === "remnic.observe" || !dreamsRunDryRun && (toolName === "engram.dreams_run" || toolName === "remnic.dreams_run"));
61339
+ const memoryActionApplyDryRun = (toolName === "engram.memory_action_apply" || toolName === "remnic.memory_action_apply") && toolArgs !== null && typeof toolArgs === "object" && !Array.isArray(toolArgs) && toolArgs.dryRun === true;
61340
+ const isMcpWrite = request.method === "tools/call" && (toolName === "engram.memory_store" || toolName === "remnic.memory_store" || toolName === "engram.suggestion_submit" || toolName === "remnic.suggestion_submit" || toolName === "engram.observe" || toolName === "remnic.observe" || toolName === "engram.capsule_export" || toolName === "remnic.capsule_export" || toolName === "engram.capsule_import" || toolName === "remnic.capsule_import" || !dreamsRunDryRun && (toolName === "engram.dreams_run" || toolName === "remnic.dreams_run") || !memoryActionApplyDryRun && (toolName === "engram.memory_action_apply" || toolName === "remnic.memory_action_apply"));
59886
61341
  if (isMcpWrite) {
59887
61342
  this.ensureWriteRateLimitAvailable();
59888
61343
  }
@@ -59946,7 +61401,7 @@ var EngramAccessHttpServer = class {
59946
61401
  }
59947
61402
  async respondStatic(res, filePath, contentType) {
59948
61403
  try {
59949
- const body = await readFile43(filePath, "utf-8");
61404
+ const body = await readFile44(filePath, "utf-8");
59950
61405
  res.statusCode = 200;
59951
61406
  res.setHeader("content-type", contentType);
59952
61407
  res.setHeader("content-length", String(Buffer.byteLength(body)));
@@ -60121,7 +61576,7 @@ async function resolveAgentAccessAuthToken(value, options = {}) {
60121
61576
  }
60122
61577
 
60123
61578
  // ../remnic-core/src/compat/checks.ts
60124
- import { access as access5, readFile as readFile44 } from "fs/promises";
61579
+ import { access as access6, readFile as readFile45 } from "fs/promises";
60125
61580
  import path71 from "path";
60126
61581
  var REQUIRED_HOOKS_LEGACY = ["before_agent_start", "agent_end"];
60127
61582
  var REQUIRED_HOOKS_NEW = ["before_prompt_build", "agent_end"];
@@ -60303,7 +61758,7 @@ async function runCompatChecks(options) {
60303
61758
  let pluginRaw = "";
60304
61759
  let pluginManifestPresent = false;
60305
61760
  try {
60306
- pluginRaw = await readFile44(pluginJsonPath, "utf-8");
61761
+ pluginRaw = await readFile45(pluginJsonPath, "utf-8");
60307
61762
  pluginManifestPresent = true;
60308
61763
  checks.push({
60309
61764
  id: "plugin-manifest-present",
@@ -60353,7 +61808,7 @@ async function runCompatChecks(options) {
60353
61808
  let packageRaw = "";
60354
61809
  let packageJsonPresent = false;
60355
61810
  try {
60356
- packageRaw = await readFile44(packageJsonPath, "utf-8");
61811
+ packageRaw = await readFile45(packageJsonPath, "utf-8");
60357
61812
  packageJsonPresent = true;
60358
61813
  } catch {
60359
61814
  checks.push({
@@ -60423,8 +61878,8 @@ async function runCompatChecks(options) {
60423
61878
  }
60424
61879
  }
60425
61880
  try {
60426
- await access5(indexPath);
60427
- const indexRaw = await readFile44(indexPath, "utf-8");
61881
+ await access6(indexPath);
61882
+ const indexRaw = await readFile45(indexPath, "utf-8");
60428
61883
  const structuralSource = stripCommentsAndStrings(indexRaw);
60429
61884
  const hooks = parseHookRegistrations(indexRaw);
60430
61885
  const missingLegacy = REQUIRED_HOOKS_LEGACY.filter((hook) => !hooks.has(hook));
@@ -60849,7 +62304,7 @@ async function promoteSemanticRuleFromMemory(options) {
60849
62304
  }
60850
62305
 
60851
62306
  // ../remnic-core/src/training-export/converter.ts
60852
- import { lstat as lstat2, readdir as readdir26, readFile as readFile45, realpath as realpath4 } from "fs/promises";
62307
+ import { lstat as lstat3, readdir as readdir27, readFile as readFile46, realpath as realpath4 } from "fs/promises";
60853
62308
  import path73 from "path";
60854
62309
 
60855
62310
  // ../remnic-core/src/training-export/date-parse.ts
@@ -61455,7 +62910,7 @@ async function runRepairMemoryProjectionCliCommand(options) {
61455
62910
  });
61456
62911
  }
61457
62912
  async function runMemoryTimelineCliCommand(options) {
61458
- const storage = new (await import("./storage-T2OGFUF4.js")).StorageManager(options.memoryDir);
62913
+ const storage = new (await import("./storage-PTQ2H2YJ.js")).StorageManager(options.memoryDir);
61459
62914
  return storage.getMemoryTimeline(options.memoryId, options.limit);
61460
62915
  }
61461
62916
  async function runMemoryGovernanceCliCommand(options) {
@@ -61483,7 +62938,7 @@ async function runMemoryGovernanceRestoreCliCommand(options) {
61483
62938
  });
61484
62939
  }
61485
62940
  async function runMemoryReviewDispositionCliCommand(options) {
61486
- const storage = new (await import("./storage-T2OGFUF4.js")).StorageManager(options.memoryDir);
62941
+ const storage = new (await import("./storage-PTQ2H2YJ.js")).StorageManager(options.memoryDir);
61487
62942
  const memory = await storage.getMemoryById(options.memoryId);
61488
62943
  if (!memory) throw new Error(`memory not found: ${options.memoryId}`);
61489
62944
  const updated = await storage.writeMemoryFrontmatter(memory, {
@@ -61662,7 +63117,7 @@ async function runSemanticRulePromoteCliCommand(options) {
61662
63117
  });
61663
63118
  }
61664
63119
  async function runCompoundingPromoteCliCommand(options) {
61665
- const { CompoundingEngine: CompoundingEngine2 } = await import("./engine-VMTFKFGO.js");
63120
+ const { CompoundingEngine: CompoundingEngine2 } = await import("./engine-BIYI3P4J.js");
61666
63121
  const config = parseConfig({
61667
63122
  memoryDir: options.memoryDir,
61668
63123
  qmdEnabled: false,
@@ -62076,7 +63531,7 @@ function effectivePolicyValuesForVersion(values, config) {
62076
63531
  }
62077
63532
  function policyVersionForValues(values, config) {
62078
63533
  const normalized = effectivePolicyValuesForVersion(values, config);
62079
- return createHash13("sha256").update(JSON.stringify(normalized)).digest("hex").slice(0, 12);
63534
+ return createHash14("sha256").update(JSON.stringify(normalized)).digest("hex").slice(0, 12);
62080
63535
  }
62081
63536
  async function readRuntimePolicySnapshot2(config, fileName) {
62082
63537
  const filePath = path74.join(config.memoryDir, "state", fileName);
@@ -62667,7 +64122,7 @@ async function withTimeout(promise, timeoutMs, timeoutMessage) {
62667
64122
  }
62668
64123
  async function runReplayCliCommand(orchestrator, options) {
62669
64124
  const extractionIdleTimeoutMs = Number.isFinite(options.extractionIdleTimeoutMs) ? Math.max(1e3, Math.floor(options.extractionIdleTimeoutMs)) : 15 * 6e4;
62670
- const inputRaw = await readFile46(options.inputPath, "utf-8");
64125
+ const inputRaw = await readFile47(options.inputPath, "utf-8");
62671
64126
  const registry = buildReplayNormalizerRegistry([
62672
64127
  openclawReplayNormalizer,
62673
64128
  claudeReplayNormalizer,
@@ -62759,7 +64214,7 @@ async function runBulkImportCliCommand(opts) {
62759
64214
  "Bulk import persistence is not wired: no ingestBatch callback was provided by the host CLI. Use --dry-run to validate without persisting, or invoke via `openclaw engram bulk-import` which supplies the orchestrator-backed ingestion path."
62760
64215
  );
62761
64216
  }
62762
- const inputRaw = await readFile46(opts.file, "utf-8");
64217
+ const inputRaw = await readFile47(opts.file, "utf-8");
62763
64218
  let inputParsed;
62764
64219
  try {
62765
64220
  inputParsed = JSON.parse(inputRaw);
@@ -62823,7 +64278,7 @@ async function runBulkImportCliCommand(opts) {
62823
64278
  async function getPluginVersion() {
62824
64279
  try {
62825
64280
  const pkgPath = new URL("../package.json", import.meta.url);
62826
- const raw = await readFile46(pkgPath, "utf-8");
64281
+ const raw = await readFile47(pkgPath, "utf-8");
62827
64282
  const parsed = JSON.parse(raw);
62828
64283
  return parsed.version ?? "unknown";
62829
64284
  } catch {
@@ -62832,7 +64287,7 @@ async function getPluginVersion() {
62832
64287
  }
62833
64288
  async function exists3(p) {
62834
64289
  try {
62835
- await access6(p);
64290
+ await access7(p);
62836
64291
  return true;
62837
64292
  } catch {
62838
64293
  return false;
@@ -62858,7 +64313,7 @@ async function walkMemoryMarkdownFiles(memoryDir, visit) {
62858
64313
  const walk = async (dir) => {
62859
64314
  let entries;
62860
64315
  try {
62861
- entries = await readdir27(dir, { withFileTypes: true });
64316
+ entries = await readdir28(dir, { withFileTypes: true });
62862
64317
  } catch {
62863
64318
  return;
62864
64319
  }
@@ -62888,7 +64343,7 @@ async function readAllMemoryFiles(memoryDir) {
62888
64343
  const out = [];
62889
64344
  await walkMemoryMarkdownFiles(memoryDir, async (fullPath) => {
62890
64345
  try {
62891
- const raw = await readFile46(fullPath, "utf-8");
64346
+ const raw = await readFile47(fullPath, "utf-8");
62892
64347
  const parsed = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
62893
64348
  if (!parsed) return;
62894
64349
  const fmRaw = parsed[1];
@@ -63634,8 +65089,8 @@ function registerCli(api, orchestrator, registerOptions = {}) {
63634
65089
  const memoryDir = await resolveMemoryDirForNamespace(orchestrator, namespace, {
63635
65090
  rejectUnsupportedOverride: true
63636
65091
  });
63637
- const { exportCapsule } = await import("./capsule-export-CVA3CKUQ.js");
63638
- const result = await exportCapsule({
65092
+ const { exportCapsule: exportCapsule2 } = await import("./capsule-export-IXVERCQG.js");
65093
+ const result = await exportCapsule2({
63639
65094
  name,
63640
65095
  root: memoryDir,
63641
65096
  since,
@@ -63680,7 +65135,7 @@ function registerCli(api, orchestrator, registerOptions = {}) {
63680
65135
  const memoryDir = await resolveMemoryDirForNamespace(orchestrator, namespace, {
63681
65136
  rejectUnsupportedOverride: true
63682
65137
  });
63683
- const { importCapsule: importCapsule2 } = await import("./capsule-import-CFX7BY5W.js");
65138
+ const { importCapsule: importCapsule2 } = await import("./capsule-import-IA6VIOPQ.js");
63684
65139
  const result = await importCapsule2({
63685
65140
  archivePath: expandTildePath(archivePath),
63686
65141
  root: memoryDir,
@@ -63703,11 +65158,11 @@ function registerCli(api, orchestrator, registerOptions = {}) {
63703
65158
  const opts = args[1] ?? {};
63704
65159
  const {
63705
65160
  parseCapsuleMergeOptions,
63706
- defaultCapsulesDir
63707
- } = await import("./capsule-cli-TFKLAG3S.js");
65161
+ defaultCapsulesDir: defaultCapsulesDir2
65162
+ } = await import("./capsule-cli-GBM3WPAM.js");
63708
65163
  const parsed = parseCapsuleMergeOptions(archiveArg, opts);
63709
65164
  const memoryDir = expandTildePath(orchestrator.config.memoryDir);
63710
- const capsulesDir = defaultCapsulesDir(memoryDir);
65165
+ const capsulesDir = defaultCapsulesDir2(memoryDir);
63711
65166
  const { stat: statMerge } = await import("fs/promises");
63712
65167
  let sourceArchive = expandTildePath(parsed.archive);
63713
65168
  const looksLikePath = sourceArchive.startsWith("/") || sourceArchive.startsWith("./") || sourceArchive.startsWith("../") || sourceArchive.includes(path74.sep);
@@ -63739,7 +65194,7 @@ function registerCli(api, orchestrator, registerOptions = {}) {
63739
65194
  );
63740
65195
  }
63741
65196
  }
63742
- const { mergeCapsule } = await import("./capsule-merge-7RVOHJK3.js");
65197
+ const { mergeCapsule } = await import("./capsule-merge-IWOQ34KL.js");
63743
65198
  const result = await mergeCapsule({
63744
65199
  sourceArchive,
63745
65200
  targetRoot: memoryDir,
@@ -63769,17 +65224,17 @@ function registerCli(api, orchestrator, registerOptions = {}) {
63769
65224
  const {
63770
65225
  parseCapsuleListOptions,
63771
65226
  renderCapsuleList,
63772
- defaultCapsulesDir
63773
- } = await import("./capsule-cli-TFKLAG3S.js");
65227
+ defaultCapsulesDir: defaultCapsulesDir2
65228
+ } = await import("./capsule-cli-GBM3WPAM.js");
63774
65229
  const memoryDir = expandTildePath(orchestrator.config.memoryDir);
63775
- const defaultDir = defaultCapsulesDir(memoryDir);
65230
+ const defaultDir = defaultCapsulesDir2(memoryDir);
63776
65231
  const dirWasExplicit = typeof opts.dir === "string" && opts.dir.trim() !== "";
63777
65232
  const parsed = parseCapsuleListOptions(opts, defaultDir);
63778
65233
  const capsulesDir = expandTildePath(parsed.capsulesDir);
63779
- const { readdir: readdir29, readFile: readFile53, stat: stat21 } = await import("fs/promises");
65234
+ const { readdir: readdir30, readFile: readFile54, stat: stat21 } = await import("fs/promises");
63780
65235
  let dirEntries;
63781
65236
  try {
63782
- dirEntries = await readdir29(capsulesDir);
65237
+ dirEntries = await readdir30(capsulesDir);
63783
65238
  } catch (err) {
63784
65239
  const code = err.code;
63785
65240
  if (dirWasExplicit) {
@@ -63815,7 +65270,7 @@ function registerCli(api, orchestrator, registerOptions = {}) {
63815
65270
  }
63816
65271
  if (hasManifest) {
63817
65272
  try {
63818
- const raw = await readFile53(manifestPath2, "utf-8");
65273
+ const raw = await readFile54(manifestPath2, "utf-8");
63819
65274
  const sidecar = JSON.parse(raw);
63820
65275
  createdAt = typeof sidecar.createdAt === "string" ? sidecar.createdAt : null;
63821
65276
  pluginVersion = typeof sidecar.pluginVersion === "string" ? sidecar.pluginVersion : null;
@@ -63850,10 +65305,10 @@ function registerCli(api, orchestrator, registerOptions = {}) {
63850
65305
  const {
63851
65306
  parseCapsuleInspectOptions,
63852
65307
  renderCapsuleInspect,
63853
- defaultCapsulesDir
63854
- } = await import("./capsule-cli-TFKLAG3S.js");
65308
+ defaultCapsulesDir: defaultCapsulesDir2
65309
+ } = await import("./capsule-cli-GBM3WPAM.js");
63855
65310
  const memoryDir = expandTildePath(orchestrator.config.memoryDir);
63856
- const capsulesDir = defaultCapsulesDir(memoryDir);
65311
+ const capsulesDir = defaultCapsulesDir2(memoryDir);
63857
65312
  const parsed = parseCapsuleInspectOptions(archiveArg, opts);
63858
65313
  const { stat: stat21 } = await import("fs/promises");
63859
65314
  let archivePath = expandTildePath(parsed.archive);
@@ -63881,8 +65336,8 @@ function registerCli(api, orchestrator, registerOptions = {}) {
63881
65336
  const sidecarPath = archivePath.replace(/\.enc$/, "").replace(/\.capsule\.json\.gz$/, ".manifest.json");
63882
65337
  let sidecar = null;
63883
65338
  try {
63884
- const { readFile: readFile53 } = await import("fs/promises");
63885
- const raw = await readFile53(sidecarPath, "utf-8");
65339
+ const { readFile: readFile54 } = await import("fs/promises");
65340
+ const raw = await readFile54(sidecarPath, "utf-8");
63886
65341
  sidecar = JSON.parse(raw);
63887
65342
  } catch {
63888
65343
  }
@@ -63891,7 +65346,7 @@ function registerCli(api, orchestrator, registerOptions = {}) {
63891
65346
  manifest = sidecar;
63892
65347
  } else if (isEncrypted) {
63893
65348
  const { gunzipSync } = await import("zlib");
63894
- const { parseExportBundle } = await import("./types-H5R5D3WF.js");
65349
+ const { parseExportBundle } = await import("./types-R4DO7AKM.js");
63895
65350
  let decryptedBuf;
63896
65351
  try {
63897
65352
  const { decryptCapsuleFileInMemory } = await import("./capsule-crypto-K3IRTKRH.js");
@@ -63919,10 +65374,10 @@ function registerCli(api, orchestrator, registerOptions = {}) {
63919
65374
  }
63920
65375
  manifest = parsed2.bundle.manifest;
63921
65376
  } else {
63922
- const { readFile: readFile53 } = await import("fs/promises");
65377
+ const { readFile: readFile54 } = await import("fs/promises");
63923
65378
  const { gunzipSync } = await import("zlib");
63924
- const { parseExportBundle } = await import("./types-H5R5D3WF.js");
63925
- const buf = await readFile53(archivePath);
65379
+ const { parseExportBundle } = await import("./types-R4DO7AKM.js");
65380
+ const buf = await readFile54(archivePath);
63926
65381
  const json = gunzipSync(buf).toString("utf-8");
63927
65382
  const parsed2 = parseExportBundle(JSON.parse(json));
63928
65383
  if (parsed2.capsuleVersion !== 2) {
@@ -66689,7 +68144,7 @@ Semantic consolidation complete. clusters=${result.clustersFound}, consolidated=
66689
68144
  runSecureStoreInit,
66690
68145
  createPassphraseReader,
66691
68146
  renderInitReport
66692
- } = await import("./secure-store-FWJ7LBPH.js");
68147
+ } = await import("./secure-store-A4NGCNXV.js");
66693
68148
  const memoryDir = expandTildePath(orchestrator.config.memoryDir);
66694
68149
  const kdf = typeof options.kdf === "string" ? options.kdf.trim() : "argon2id";
66695
68150
  if (kdf !== "argon2id" && kdf !== "scrypt") {
@@ -66717,7 +68172,7 @@ Semantic consolidation complete. clusters=${result.clustersFound}, consolidated=
66717
68172
  runSecureStoreUnlock,
66718
68173
  createPassphraseReader,
66719
68174
  renderUnlockReport
66720
- } = await import("./secure-store-FWJ7LBPH.js");
68175
+ } = await import("./secure-store-A4NGCNXV.js");
66721
68176
  const memoryDir = expandTildePath(orchestrator.config.memoryDir);
66722
68177
  const report = await runSecureStoreUnlock({
66723
68178
  memoryDir,
@@ -66736,7 +68191,7 @@ Semantic consolidation complete. clusters=${result.clustersFound}, consolidated=
66736
68191
  "Lock the secure-store. Clears the master key from the daemon's in-memory keyring. Idempotent \u2014 succeeds even if the store is already locked."
66737
68192
  ).option("--json", "Emit machine-readable JSON only").action(async (...args) => {
66738
68193
  const options = args[0] ?? {};
66739
- const { runSecureStoreLock, renderLockReport } = await import("./secure-store-FWJ7LBPH.js");
68194
+ const { runSecureStoreLock, renderLockReport } = await import("./secure-store-A4NGCNXV.js");
66740
68195
  const memoryDir = expandTildePath(orchestrator.config.memoryDir);
66741
68196
  const report = runSecureStoreLock({ memoryDir });
66742
68197
  if (options.json === true) {
@@ -66749,7 +68204,7 @@ Semantic consolidation complete. clusters=${result.clustersFound}, consolidated=
66749
68204
  "Encrypt existing plaintext storage-managed memory files in an initialized, unlocked secure-store. Idempotent; already-encrypted files are skipped."
66750
68205
  ).option("--json", "Emit machine-readable JSON only").action(async (...args) => {
66751
68206
  const options = args[0] ?? {};
66752
- const { runSecureStoreMigrate, renderMigrateReport } = await import("./secure-store-FWJ7LBPH.js");
68207
+ const { runSecureStoreMigrate, renderMigrateReport } = await import("./secure-store-A4NGCNXV.js");
66753
68208
  const memoryDir = expandTildePath(orchestrator.config.memoryDir);
66754
68209
  const report = await runSecureStoreMigrate({ memoryDir });
66755
68210
  if (options.json === true) {
@@ -66761,11 +68216,34 @@ Semantic consolidation complete. clusters=${result.clustersFound}, consolidated=
66761
68216
  process.exitCode = 1;
66762
68217
  }
66763
68218
  });
68219
+ async function runSecureStoreDisableCommand(options) {
68220
+ const { runSecureStoreDisable, renderDisableReport } = await import("./secure-store-A4NGCNXV.js");
68221
+ const memoryDir = expandTildePath(orchestrator.config.memoryDir);
68222
+ const report = await runSecureStoreDisable({ memoryDir });
68223
+ if (options.json === true) {
68224
+ console.log(JSON.stringify(report, null, 2));
68225
+ } else {
68226
+ console.log(renderDisableReport(report));
68227
+ }
68228
+ if (!report.ok) {
68229
+ process.exitCode = 1;
68230
+ }
68231
+ }
68232
+ secureStoreCmd.command("disable").description(
68233
+ "Decrypt storage-managed secure-store files back to plaintext. Requires an initialized, unlocked secure-store and keeps .secure-store metadata in place."
68234
+ ).option("--json", "Emit machine-readable JSON only").action(async (...args) => {
68235
+ const options = args[0] ?? {};
68236
+ await runSecureStoreDisableCommand(options);
68237
+ });
68238
+ secureStoreCmd.command("decrypt").description("Alias for `secure-store disable`.").option("--json", "Emit machine-readable JSON only").action(async (...args) => {
68239
+ const options = args[0] ?? {};
68240
+ await runSecureStoreDisableCommand(options);
68241
+ });
66764
68242
  secureStoreCmd.command("status").description(
66765
68243
  "Report secure-store status: whether a header exists, whether the daemon currently holds the key, KDF parameters, and last-unlock timestamp."
66766
68244
  ).option("--json", "Emit machine-readable JSON only").action(async (...args) => {
66767
68245
  const options = args[0] ?? {};
66768
- const { runSecureStoreStatus, renderStatusReport } = await import("./secure-store-FWJ7LBPH.js");
68246
+ const { runSecureStoreStatus, renderStatusReport } = await import("./secure-store-A4NGCNXV.js");
66769
68247
  const memoryDir = expandTildePath(orchestrator.config.memoryDir);
66770
68248
  const report = await runSecureStoreStatus({ memoryDir });
66771
68249
  if (options.json === true) {
@@ -67112,7 +68590,7 @@ import crypto2 from "crypto";
67112
68590
  function hashSha256(value) {
67113
68591
  return crypto2.createHash("sha256").update(value).digest("hex");
67114
68592
  }
67115
- function isRecord3(value) {
68593
+ function isRecord4(value) {
67116
68594
  return typeof value === "object" && value !== null && !Array.isArray(value);
67117
68595
  }
67118
68596
  function optionalString2(value) {
@@ -67128,11 +68606,11 @@ function normalizedToolName(toolName) {
67128
68606
  return toolNameTokens(toolName).join("_");
67129
68607
  }
67130
68608
  function parseToolArguments(value) {
67131
- if (isRecord3(value)) return value;
68609
+ if (isRecord4(value)) return value;
67132
68610
  if (typeof value !== "string") return void 0;
67133
68611
  try {
67134
68612
  const parsed = JSON.parse(value);
67135
- return isRecord3(parsed) ? parsed : void 0;
68613
+ return isRecord4(parsed) ? parsed : void 0;
67136
68614
  } catch {
67137
68615
  return void 0;
67138
68616
  }
@@ -67142,13 +68620,13 @@ function extractTextContent(value) {
67142
68620
  if (Array.isArray(value)) {
67143
68621
  return value.map((block) => {
67144
68622
  if (typeof block === "string") return block.trim();
67145
- if (isRecord3(block) && block.type === "text" && typeof block.text === "string") {
68623
+ if (isRecord4(block) && block.type === "text" && typeof block.text === "string") {
67146
68624
  return block.text.trim();
67147
68625
  }
67148
68626
  return "";
67149
68627
  }).filter((item) => item.length > 0).join("\n");
67150
68628
  }
67151
- if (isRecord3(value)) {
68629
+ if (isRecord4(value)) {
67152
68630
  return JSON.stringify(value);
67153
68631
  }
67154
68632
  return "";
@@ -67175,10 +68653,10 @@ function getToolCallContexts(messages) {
67175
68653
  const toolCalls = message.tool_calls ?? message.toolCalls;
67176
68654
  if (!Array.isArray(toolCalls)) continue;
67177
68655
  for (const call of toolCalls) {
67178
- if (!isRecord3(call)) continue;
68656
+ if (!isRecord4(call)) continue;
67179
68657
  const toolCallId = optionalString2(call.id) ?? optionalString2(call.toolCallId);
67180
68658
  if (!toolCallId) continue;
67181
- const fn = isRecord3(call.function) ? call.function : void 0;
68659
+ const fn = isRecord4(call.function) ? call.function : void 0;
67182
68660
  const toolName = optionalString2(fn?.name) ?? optionalString2(call.name);
67183
68661
  const args = parseToolArguments(fn?.arguments) ?? parseToolArguments(call.arguments) ?? parseToolArguments(call.args) ?? parseToolArguments(call.input);
67184
68662
  contexts.set(toolCallId, { toolCallId, toolName, args });
@@ -67221,7 +68699,7 @@ function fileContentHash(args) {
67221
68699
  }
67222
68700
  function inferOutcome(message, parsedPayload) {
67223
68701
  if (message.isError === true) return "failure";
67224
- if (isRecord3(parsedPayload)) {
68702
+ if (isRecord4(parsedPayload)) {
67225
68703
  if (parsedPayload.partial === true || parsedPayload.status === "partial") return "partial";
67226
68704
  if (parsedPayload.success === false || parsedPayload.ok === false) return "failure";
67227
68705
  if (parsedPayload.success === true || parsedPayload.ok === true) return "success";
@@ -67425,13 +68903,13 @@ async function probeQmdAvailability(host) {
67425
68903
  }
67426
68904
 
67427
68905
  // ../../src/index.ts
67428
- import { readFile as readFile52, realpath as realpath7, writeFile as writeFile44 } from "fs/promises";
68906
+ import { readFile as readFile53, realpath as realpath7, writeFile as writeFile44 } from "fs/promises";
67429
68907
  import { existsSync as existsSync12, readFileSync as readFileSync6 } from "fs";
67430
68908
  import path99 from "path";
67431
68909
  import os7 from "os";
67432
68910
 
67433
68911
  // ../remnic-core/src/opik-exporter.ts
67434
- import { createHash as createHash14, randomBytes as randomBytes2 } from "crypto";
68912
+ import { createHash as createHash15, randomBytes as randomBytes2 } from "crypto";
67435
68913
  import { readFileSync as readFileSync4 } from "fs";
67436
68914
  import path75 from "path";
67437
68915
  var OPIK_EXPORTER_SLOT = "__openclawOpikExporter";
@@ -67752,7 +69230,7 @@ var OpikExporter = class _OpikExporter {
67752
69230
  * the version/variant bits, not timestamp ordering.
67753
69231
  */
67754
69232
  sessionToTraceId(sessionKey) {
67755
- const digest = createHash14("sha256").update(sessionKey).digest();
69233
+ const digest = createHash15("sha256").update(sessionKey).digest();
67756
69234
  digest[6] = digest[6] & 15 | 112;
67757
69235
  digest[8] = digest[8] & 63 | 128;
67758
69236
  const hex = digest.slice(0, 16).toString("hex");
@@ -67799,7 +69277,7 @@ function cleanUserMessage(content) {
67799
69277
  }
67800
69278
 
67801
69279
  // src/public-artifacts.ts
67802
- import { readdir as readdir28, access as access7, stat as stat20, lstat as lstat3, realpath as realpath5 } from "fs/promises";
69280
+ import { readdir as readdir29, access as access8, stat as stat20, lstat as lstat4, realpath as realpath5 } from "fs/promises";
67803
69281
  import path76 from "path";
67804
69282
  var PUBLIC_DIRS = [
67805
69283
  { dir: "facts", kind: "fact", contentType: "markdown" },
@@ -67832,7 +69310,7 @@ async function listMarkdownFilesRecursive(rootDir, boundary, ancestorRealPaths)
67832
69310
  nextAncestors.add(resolvedRoot);
67833
69311
  let entries;
67834
69312
  try {
67835
- entries = await readdir28(rootDir, { withFileTypes: true });
69313
+ entries = await readdir29(rootDir, { withFileTypes: true });
67836
69314
  } catch {
67837
69315
  return [];
67838
69316
  }
@@ -67843,7 +69321,7 @@ async function listMarkdownFilesRecursive(rootDir, boundary, ancestorRealPaths)
67843
69321
  let isDir = entry.isDirectory();
67844
69322
  let isFile = entry.isFile();
67845
69323
  try {
67846
- const linkStat = await lstat3(fullPath);
69324
+ const linkStat = await lstat4(fullPath);
67847
69325
  if (linkStat.isSymbolicLink()) {
67848
69326
  if (!await isContainedWithin(fullPath, boundaryDir)) {
67849
69327
  continue;
@@ -67867,7 +69345,7 @@ async function listMarkdownFilesRecursive(rootDir, boundary, ancestorRealPaths)
67867
69345
  }
67868
69346
  async function pathExists2(inputPath) {
67869
69347
  try {
67870
- await access7(inputPath);
69348
+ await access8(inputPath);
67871
69349
  return true;
67872
69350
  } catch {
67873
69351
  return false;
@@ -67907,7 +69385,7 @@ async function listRemnicPublicArtifacts(params) {
67907
69385
  if (!await pathExists2(absolutePath)) continue;
67908
69386
  if (!await isContainedWithin(absolutePath, memoryDir)) continue;
67909
69387
  try {
67910
- const linkStat = await lstat3(absolutePath);
69388
+ const linkStat = await lstat4(absolutePath);
67911
69389
  if (linkStat.isSymbolicLink()) {
67912
69390
  const resolvedPath = await realpath5(absolutePath);
67913
69391
  const expectedParent = await realpath5(memoryDir);
@@ -68158,12 +69636,12 @@ import os6 from "os";
68158
69636
  import path92 from "path";
68159
69637
 
68160
69638
  // ../remnic-core/src/enrichment/audit.ts
68161
- import { mkdir as mkdir47, readFile as readFile47, appendFile as appendFile5 } from "fs/promises";
69639
+ import { mkdir as mkdir47, readFile as readFile48, appendFile as appendFile5 } from "fs/promises";
68162
69640
  import { existsSync as existsSync11 } from "fs";
68163
69641
  import path93 from "path";
68164
69642
 
68165
69643
  // ../remnic-core/src/transfer/capsule-fork.ts
68166
- import { lstat as lstat4, mkdir as mkdir48, readFile as readFile48, realpath as realpath6, writeFile as writeFile41 } from "fs/promises";
69644
+ import { lstat as lstat5, mkdir as mkdir48, readFile as readFile49, realpath as realpath6, writeFile as writeFile41 } from "fs/promises";
68167
69645
  import path94 from "path";
68168
69646
 
68169
69647
  // src/openclaw-tools/shapes.ts
@@ -68943,7 +70421,7 @@ function validateSlotSelection(ctx) {
68943
70421
  }
68944
70422
 
68945
70423
  // ../remnic-core/src/session-toggles.ts
68946
- import { mkdir as mkdir49, readFile as readFile49, writeFile as writeFile42 } from "fs/promises";
70424
+ import { mkdir as mkdir49, readFile as readFile50, writeFile as writeFile42 } from "fs/promises";
68947
70425
  import path95 from "path";
68948
70426
  function encodeToggleKey(sessionKey, agentId) {
68949
70427
  return `${encodeURIComponent(sessionKey)}::${encodeURIComponent(agentId)}`;
@@ -68958,7 +70436,7 @@ function decodeToggleKey(key) {
68958
70436
  }
68959
70437
  async function safeReadToggleFile(filePath) {
68960
70438
  try {
68961
- const raw = await readFile49(filePath, "utf8");
70439
+ const raw = await readFile50(filePath, "utf8");
68962
70440
  const parsed = JSON.parse(raw);
68963
70441
  if (!parsed || typeof parsed !== "object" || typeof parsed.entries !== "object") {
68964
70442
  return { version: 1, entries: {} };
@@ -69441,14 +70919,14 @@ import {
69441
70919
  } from "@remnic/core";
69442
70920
 
69443
70921
  // ../remnic-core/src/surfaces/dreams.ts
69444
- import { createHash as createHash15 } from "crypto";
70922
+ import { createHash as createHash16 } from "crypto";
69445
70923
  import { statSync, watch as watch2 } from "fs";
69446
- import { mkdir as mkdir51, readFile as readFile50, writeFile as writeFile43 } from "fs/promises";
70924
+ import { mkdir as mkdir51, readFile as readFile51, writeFile as writeFile43 } from "fs/promises";
69447
70925
  import path97 from "path";
69448
70926
  var DIARY_START_MARKER = "<!-- openclaw:dreaming:diary:start -->";
69449
70927
  var DIARY_END_MARKER = "<!-- openclaw:dreaming:diary:end -->";
69450
70928
  function stableDreamId(params) {
69451
- const digest = createHash15("sha1").update(
70929
+ const digest = createHash16("sha1").update(
69452
70930
  JSON.stringify({
69453
70931
  timestamp: params.timestamp,
69454
70932
  occurrence: params.occurrence
@@ -69609,7 +71087,7 @@ function createDreamsSurface() {
69609
71087
  return {
69610
71088
  async read(filePath) {
69611
71089
  try {
69612
- const content = await readFile50(filePath, "utf8");
71090
+ const content = await readFile51(filePath, "utf8");
69613
71091
  return parseDreamEntries(content);
69614
71092
  } catch (error) {
69615
71093
  if (error.code === "ENOENT") {
@@ -69622,7 +71100,7 @@ function createDreamsSurface() {
69622
71100
  await mkdir51(path97.dirname(filePath), { recursive: true });
69623
71101
  let content = "";
69624
71102
  try {
69625
- content = await readFile50(filePath, "utf8");
71103
+ content = await readFile51(filePath, "utf8");
69626
71104
  } catch (error) {
69627
71105
  if (error.code !== "ENOENT") throw error;
69628
71106
  }
@@ -69718,12 +71196,12 @@ ${ensured.slice(endIndex)}` : `${ensureDiary("")}${block}`;
69718
71196
  }
69719
71197
 
69720
71198
  // ../remnic-core/src/surfaces/heartbeat.ts
69721
- import { createHash as createHash16 } from "crypto";
71199
+ import { createHash as createHash17 } from "crypto";
69722
71200
  import { statSync as statSync2, watch as watch3 } from "fs";
69723
- import { readFile as readFile51 } from "fs/promises";
71201
+ import { readFile as readFile52 } from "fs/promises";
69724
71202
  import path98 from "path";
69725
71203
  function stableHeartbeatId(params) {
69726
- const digest = createHash16("sha1").update(
71204
+ const digest = createHash17("sha1").update(
69727
71205
  JSON.stringify({
69728
71206
  sourceOffset: params.sourceOffset
69729
71207
  })
@@ -69735,7 +71213,7 @@ function slugify(value) {
69735
71213
  if (normalized.length > 0) return normalized;
69736
71214
  const trimmed = value.trim();
69737
71215
  if (trimmed.length === 0) return "heartbeat-untitled";
69738
- return `heartbeat-${createHash16("sha1").update(trimmed).digest("hex").slice(0, 8)}`;
71216
+ return `heartbeat-${createHash17("sha1").update(trimmed).digest("hex").slice(0, 8)}`;
69739
71217
  }
69740
71218
  function parseTags(line) {
69741
71219
  const match = /^Tags:\s*(.*)$/i.exec(line.trim());
@@ -69884,7 +71362,7 @@ function createHeartbeatSurface() {
69884
71362
  return {
69885
71363
  async read(filePath) {
69886
71364
  try {
69887
- const content = await readFile51(filePath, "utf8");
71365
+ const content = await readFile52(filePath, "utf8");
69888
71366
  return parseHeartbeatEntries(content);
69889
71367
  } catch (error) {
69890
71368
  if (error.code === "ENOENT") {
@@ -71562,7 +73040,7 @@ Keep the reflection grounded in the evidence below.
71562
73040
  async readFile(params) {
71563
73041
  const requestedPath = normalizeWorkspacePath(params.relPath);
71564
73042
  const absolutePath = await resolveReadablePath(params.relPath);
71565
- const text = await readFile52(absolutePath, "utf-8");
73043
+ const text = await readFile53(absolutePath, "utf-8");
71566
73044
  const allLines = text.split(/\r?\n/);
71567
73045
  const from = typeof params.from === "number" ? Math.max(1, Math.floor(params.from)) : 1;
71568
73046
  const lines = typeof params.lines === "number" && Number.isFinite(params.lines) ? Math.max(1, Math.floor(params.lines)) : void 0;
@@ -71813,7 +73291,12 @@ Keep the reflection grounded in the evidence below.
71813
73291
  const lcmMessages = lastTurn.map((msg) => {
71814
73292
  const rawRole = typeof msg.role === "string" ? msg.role : "";
71815
73293
  const content = extractTextContent2(msg);
71816
- return { role: rawRole, content };
73294
+ return {
73295
+ role: rawRole,
73296
+ content,
73297
+ rawContent: msg,
73298
+ sourceFormat: "openclaw"
73299
+ };
71817
73300
  }).filter((m) => m.content.length > 0);
71818
73301
  if (lcmMessages.length > 0) {
71819
73302
  orchestrator.lcmEngine.enqueueObserveMessages(
@@ -72218,7 +73701,7 @@ Keep the reflection grounded in the evidence below.
72218
73701
  jobs: []
72219
73702
  };
72220
73703
  try {
72221
- const content = await readFile52(cronFilePath, "utf-8");
73704
+ const content = await readFile53(cronFilePath, "utf-8");
72222
73705
  jobsData = JSON.parse(content);
72223
73706
  } catch {
72224
73707
  }