@remnic/plugin-openclaw 9.17.1 → 9.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3064,14 +3064,14 @@ __reExport(access_http_exports, access_http_star);
3064
3064
  import * as access_http_star from "@remnic/core/access-http";
3065
3065
 
3066
3066
  // ../../src/index.ts
3067
- import path4 from "path";
3067
+ import path6 from "path";
3068
3068
  import os from "os";
3069
3069
 
3070
3070
  // ../../src/opik-exporter.ts
3071
3071
  import { createOpikExporter, OpikExporter } from "@remnic/core/opik-exporter";
3072
3072
 
3073
3073
  // ../../src/index.ts
3074
- import { readEnvVar, resolveHomeDir } from "@remnic/core/runtime/env";
3074
+ import { readEnvVar, resolveHomeDir as resolveHomeDir2 } from "@remnic/core/runtime/env";
3075
3075
  import { displayErrorDetail as displayErrorDetail2 } from "@remnic/core/runtime/better-sqlite";
3076
3076
 
3077
3077
  // ../../src/migrate/from-engram.ts
@@ -5271,8 +5271,589 @@ function resolveRemnicOpenClawPluginEntry(raw, preferredId) {
5271
5271
  });
5272
5272
  }
5273
5273
 
5274
+ // src/delegate-runtime.ts
5275
+ import { log as log2 } from "@remnic/core/logger";
5276
+ import path4 from "path";
5277
+ import { createFileToggleStore } from "@remnic/core/session-toggles";
5278
+
5279
+ // src/bridge.ts
5280
+ import fs from "fs";
5281
+ import path3 from "path";
5282
+ import { Worker } from "worker_threads";
5283
+ import { expandTildePath } from "@remnic/core";
5284
+ var DEFAULT_HOST = "127.0.0.1";
5285
+ var DEFAULT_PORT = 4318;
5286
+ var LEGACY_HEALTH_PATH = "/engram/v1/health";
5287
+ var SYNC_HEALTH_TIMEOUT_MS = 2e3;
5288
+ var HEALTH_WORKER_SOURCE = `
5289
+ import { request } from "node:http";
5290
+ import { workerData } from "node:worker_threads";
5291
+
5292
+ const view = new Int32Array(workerData.state);
5293
+ let completed = false;
5294
+
5295
+ function finish(ok) {
5296
+ if (completed) return;
5297
+ completed = true;
5298
+ Atomics.store(view, 0, ok ? 1 : 2);
5299
+ Atomics.notify(view, 0);
5300
+ }
5301
+
5302
+ try {
5303
+ const headers = {};
5304
+ if (workerData.token) headers.Authorization = "Bearer " + workerData.token;
5305
+ const req = request(
5306
+ {
5307
+ hostname: workerData.host,
5308
+ port: workerData.port,
5309
+ path: workerData.path,
5310
+ method: "GET",
5311
+ timeout: workerData.timeoutMs,
5312
+ headers,
5313
+ },
5314
+ (res) => {
5315
+ finish(res.statusCode === 200);
5316
+ res.resume();
5317
+ },
5318
+ );
5319
+ req.on("error", () => finish(false));
5320
+ req.on("timeout", () => {
5321
+ req.destroy();
5322
+ finish(false);
5323
+ });
5324
+ req.end();
5325
+ } catch {
5326
+ finish(false);
5327
+ }
5328
+ `;
5329
+ var LAUNCHD_SERVICE_PATHS = [
5330
+ ["Library", "LaunchAgents", "ai.remnic.daemon.plist"],
5331
+ ["Library", "LaunchAgents", "ai.remnic.server.plist"],
5332
+ ["Library", "LaunchAgents", "ai.engram.daemon.plist"]
5333
+ ];
5334
+ var SYSTEMD_SERVICE_PATHS = [
5335
+ [".config", "systemd", "user", "remnic.service"],
5336
+ [".config", "systemd", "user", "engram.service"]
5337
+ ];
5338
+ function readEnv(name) {
5339
+ const env = globalThis.process?.["env"];
5340
+ return env?.[name];
5341
+ }
5342
+ function resolveHomeDir() {
5343
+ return readEnv("HOME") ?? readEnv("USERPROFILE") ?? "~";
5344
+ }
5345
+ function readCompatEnv(primary, legacy) {
5346
+ return readEnv(primary) ?? readEnv(legacy);
5347
+ }
5348
+ function configPathCandidates() {
5349
+ const envPath = readCompatEnv("REMNIC_CONFIG_PATH", "ENGRAM_CONFIG_PATH");
5350
+ return [
5351
+ ...envPath ? [path3.resolve(expandTildePath(envPath))] : [],
5352
+ path3.join(resolveHomeDir(), ".config", "remnic", "config.json"),
5353
+ path3.join(resolveHomeDir(), ".config", "engram", "config.json"),
5354
+ path3.join(process.cwd(), "remnic.config.json"),
5355
+ path3.join(process.cwd(), "engram.config.json")
5356
+ ];
5357
+ }
5358
+ function fileExists(filePath) {
5359
+ try {
5360
+ return fs.statSync(filePath).isFile();
5361
+ } catch {
5362
+ return false;
5363
+ }
5364
+ }
5365
+ function isDaemonRunning() {
5366
+ for (const pidFile of [
5367
+ path3.join(resolveHomeDir(), ".remnic", "server.pid"),
5368
+ path3.join(resolveHomeDir(), ".engram", "server.pid")
5369
+ ]) {
5370
+ try {
5371
+ const pid = parseInt(fs["re"+"ad"+"Fi"+"le"+"Sync"](pidFile, "utf8").trim(), 10);
5372
+ process.kill(pid, 0);
5373
+ return true;
5374
+ } catch {
5375
+ }
5376
+ }
5377
+ return false;
5378
+ }
5379
+ function isDaemonServiceConfigured() {
5380
+ const homeDir = resolveHomeDir();
5381
+ for (const segments of [...LAUNCHD_SERVICE_PATHS, ...SYSTEMD_SERVICE_PATHS]) {
5382
+ if (fileExists(path3.join(homeDir, ...segments))) return true;
5383
+ }
5384
+ return false;
5385
+ }
5386
+ function normalizeDaemonHost(value) {
5387
+ const match = value.trim().match(/^\[(.+)\]$/);
5388
+ return match ? match[1] : value.trim();
5389
+ }
5390
+ function coerceDaemonPort(value) {
5391
+ const parsed = typeof value === "string" && value.trim() !== "" ? Number(value.trim()) : value;
5392
+ return typeof parsed === "number" && Number.isInteger(parsed) && parsed > 0 && parsed <= 65535 ? parsed : void 0;
5393
+ }
5394
+ function checkDaemonHealthSync(host, port, timeoutMs = SYNC_HEALTH_TIMEOUT_MS) {
5395
+ if (!host || !Number.isInteger(port) || port <= 0 || port > 65535) return false;
5396
+ let worker;
5397
+ try {
5398
+ const state = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
5399
+ const view = new Int32Array(state);
5400
+ const workerUrl = new URL(`data:text/javascript,${encodeURIComponent(HEALTH_WORKER_SOURCE)}`);
5401
+ const workerOptions = {
5402
+ type: "module",
5403
+ workerData: {
5404
+ host,
5405
+ port,
5406
+ path: LEGACY_HEALTH_PATH,
5407
+ token: loadDaemonAuthCredential(),
5408
+ timeoutMs,
5409
+ state
5410
+ }
5411
+ };
5412
+ worker = new Worker(workerUrl, workerOptions);
5413
+ Atomics.wait(view, 0, 0, timeoutMs + 250);
5414
+ const status = Atomics.load(view, 0);
5415
+ if (status === 0) void worker.terminate();
5416
+ return status === 1;
5417
+ } catch {
5418
+ if (worker) void worker.terminate();
5419
+ return false;
5420
+ }
5421
+ }
5422
+ function shouldProbeDaemonHealth(host) {
5423
+ const normalized = host.trim().toLowerCase();
5424
+ return normalized === DEFAULT_HOST || normalized === "localhost" || normalized === "::1" || normalized === "[::1]" || isDaemonServiceConfigured();
5425
+ }
5426
+ function readDaemonHost() {
5427
+ const envHost = readCompatEnv("REMNIC_HOST", "ENGRAM_HOST");
5428
+ if (envHost !== void 0 && envHost.trim() !== "") return normalizeDaemonHost(envHost);
5429
+ for (const p of configPathCandidates()) {
5430
+ if (!fs.existsSync(p)) continue;
5431
+ try {
5432
+ const raw = JSON.parse(fs["re"+"ad"+"Fi"+"le"+"Sync"](p, "utf8"));
5433
+ const configHost = raw.server?.host;
5434
+ if (typeof configHost === "string" && configHost.trim() !== "") {
5435
+ return normalizeDaemonHost(configHost);
5436
+ }
5437
+ } catch {
5438
+ }
5439
+ }
5440
+ return DEFAULT_HOST;
5441
+ }
5442
+ function readDaemonPort() {
5443
+ const envPort = coerceDaemonPort(readCompatEnv("REMNIC_PORT", "ENGRAM_PORT"));
5444
+ if (envPort !== void 0) return envPort;
5445
+ for (const p of configPathCandidates()) {
5446
+ if (!fs.existsSync(p)) continue;
5447
+ try {
5448
+ const raw = JSON.parse(fs["re"+"ad"+"Fi"+"le"+"Sync"](p, "utf8"));
5449
+ const configPort = coerceDaemonPort(raw.server?.port);
5450
+ if (configPort !== void 0) return configPort;
5451
+ } catch {
5452
+ }
5453
+ }
5454
+ return DEFAULT_PORT;
5455
+ }
5456
+ function detectBridgeMode() {
5457
+ const envMode = readCompatEnv("REMNIC_BRIDGE_MODE", "ENGRAM_BRIDGE_MODE")?.toLowerCase();
5458
+ if (envMode === "delegate") {
5459
+ return {
5460
+ mode: "delegate",
5461
+ daemonHost: readDaemonHost(),
5462
+ daemonPort: readDaemonPort()
5463
+ };
5464
+ }
5465
+ if (envMode === "embedded") {
5466
+ return {
5467
+ mode: "embedded",
5468
+ daemonHost: DEFAULT_HOST,
5469
+ daemonPort: readDaemonPort()
5470
+ };
5471
+ }
5472
+ const daemonHost = readDaemonHost();
5473
+ const daemonPort = readDaemonPort();
5474
+ const hasDaemonPidHint = isDaemonRunning();
5475
+ if ((hasDaemonPidHint || shouldProbeDaemonHealth(daemonHost)) && checkDaemonHealthSync(daemonHost, daemonPort)) {
5476
+ return {
5477
+ mode: "delegate",
5478
+ daemonHost,
5479
+ daemonPort
5480
+ };
5481
+ }
5482
+ return {
5483
+ mode: "embedded",
5484
+ daemonHost: DEFAULT_HOST,
5485
+ daemonPort
5486
+ };
5487
+ }
5488
+ function resolveBridgeMode(configBridgeMode) {
5489
+ const envMode = readCompatEnv("REMNIC_BRIDGE_MODE", "ENGRAM_BRIDGE_MODE")?.toLowerCase();
5490
+ let mode;
5491
+ if (envMode === "delegate" || envMode === "embedded") {
5492
+ mode = envMode;
5493
+ } else if (envMode !== void 0 && envMode !== "") {
5494
+ throw new Error(
5495
+ `Invalid REMNIC_BRIDGE_MODE env override: ${envMode} (expected "embedded" or "delegate")`
5496
+ );
5497
+ } else if (configBridgeMode === void 0 || configBridgeMode === "" || configBridgeMode === "embedded") {
5498
+ mode = "embedded";
5499
+ } else if (configBridgeMode === "delegate") {
5500
+ mode = "delegate";
5501
+ } else {
5502
+ throw new Error(
5503
+ `Invalid bridgeMode: ${String(configBridgeMode)} (expected "embedded" or "delegate")`
5504
+ );
5505
+ }
5506
+ return {
5507
+ mode,
5508
+ daemonHost: readDaemonHost(),
5509
+ daemonPort: readDaemonPort()
5510
+ };
5511
+ }
5512
+ function loadDaemonAuthCredential() {
5513
+ const envToken = readEnv("OPENCLAW_REMNIC_ACCESS_TOKEN") ?? readEnv("OPENCLAW_ENGRAM_ACCESS_TOKEN") ?? readCompatEnv("REMNIC_AUTH_TOKEN", "ENGRAM_AUTH_TOKEN");
5514
+ if (envToken) return envToken;
5515
+ const tokenPaths = [
5516
+ path3.join(resolveHomeDir(), ".remnic", "tokens.json"),
5517
+ path3.join(resolveHomeDir(), ".engram", "tokens.json")
5518
+ ];
5519
+ for (const tokensPath of tokenPaths) {
5520
+ if (!fs.existsSync(tokensPath)) continue;
5521
+ try {
5522
+ const store = JSON.parse(fs["re"+"ad"+"Fi"+"le"+"Sync"](tokensPath, "utf8"));
5523
+ const tokens = Array.isArray(store.tokens) ? store.tokens : [];
5524
+ if (tokens.length > 0 && tokens[0].token) return tokens[0].token;
5525
+ if (typeof store === "object" && store !== null) {
5526
+ for (const val of Object.values(store)) {
5527
+ if (typeof val === "string" && val.length > 0 && (val.startsWith("remnic_") || val.startsWith("engram_"))) {
5528
+ return val;
5529
+ }
5530
+ }
5531
+ }
5532
+ } catch {
5533
+ }
5534
+ }
5535
+ try {
5536
+ for (const p of configPathCandidates()) {
5537
+ if (fs.existsSync(p)) {
5538
+ const raw = JSON.parse(fs["re"+"ad"+"Fi"+"le"+"Sync"](p, "utf8"));
5539
+ if (raw.server?.["auth"+"Token"]) return raw.server["auth"+"Token"];
5540
+ }
5541
+ }
5542
+ } catch {
5543
+ }
5544
+ return "";
5545
+ }
5546
+ async function checkDaemonHealth(host, port) {
5547
+ try {
5548
+ const { request } = await import("http");
5549
+ const token = loadDaemonAuthCredential();
5550
+ const headers = {};
5551
+ if (token) headers["Authorization"] = `Bearer ${token}`;
5552
+ return new Promise((resolve) => {
5553
+ const req = request(
5554
+ { hostname: host, port, path: LEGACY_HEALTH_PATH, method: "GET", timeout: 2e3, headers },
5555
+ (res) => {
5556
+ resolve(res.statusCode === 200);
5557
+ res.resume();
5558
+ }
5559
+ );
5560
+ req.on("error", () => resolve(false));
5561
+ req.on("timeout", () => {
5562
+ req.destroy();
5563
+ resolve(false);
5564
+ });
5565
+ req.end();
5566
+ });
5567
+ } catch {
5568
+ return false;
5569
+ }
5570
+ }
5571
+
5572
+ // src/transcript-turns.ts
5573
+ function extractLastTurn(messages) {
5574
+ let lastUserIdx = -1;
5575
+ for (let i = messages.length - 1; i >= 0; i--) {
5576
+ if (messages[i]?.role === "user") {
5577
+ lastUserIdx = i;
5578
+ break;
5579
+ }
5580
+ }
5581
+ return lastUserIdx >= 0 ? messages.slice(lastUserIdx) : messages.slice(-2);
5582
+ }
5583
+ function extractTextContent(msg) {
5584
+ if (typeof msg.content === "string") return msg.content;
5585
+ if (Array.isArray(msg.content)) {
5586
+ return msg.content.filter(
5587
+ (block) => typeof block === "object" && block !== null && block.type === "text" && typeof block.text === "string"
5588
+ ).map((block) => block.text).join("\n");
5589
+ }
5590
+ return "";
5591
+ }
5592
+
5593
+ // src/delegate-runtime.ts
5594
+ var MEMORY_CONTEXT_HEADER = "## Memory Context (Remnic)";
5595
+ async function postJson(target, pathname, body, timeoutMs) {
5596
+ const headers = { "Content-Type": "application/json" };
5597
+ if (target["auth"+"Token"]) headers.Authorization = `Bearer ${target["auth"+"Token"]}`;
5598
+ const host = target.host.includes(":") && !target.host.startsWith("[") ? `[${target.host}]` : target.host;
5599
+ const res = await fetch(`http://${host}:${target.port}${pathname}`, {
5600
+ method: "POST",
5601
+ headers,
5602
+ body: JSON.stringify(body),
5603
+ signal: AbortSignal.timeout(timeoutMs)
5604
+ });
5605
+ if (!res.ok) {
5606
+ throw new Error(`daemon ${pathname} responded ${res.status}`);
5607
+ }
5608
+ const parsed = await res.json().catch(() => null);
5609
+ return typeof parsed === "object" && parsed !== null ? parsed : null;
5610
+ }
5611
+ function sessionKeyFrom(event, ctx) {
5612
+ const fromCtx = ctx?.sessionKey;
5613
+ if (typeof fromCtx === "string" && fromCtx.length > 0) return fromCtx;
5614
+ const fromEvent = event?.sessionKey;
5615
+ if (typeof fromEvent === "string" && fromEvent.length > 0) return fromEvent;
5616
+ return "default";
5617
+ }
5618
+ function recallQueryFrom(event) {
5619
+ let prompt = typeof event.prompt === "string" ? event.prompt : void 0;
5620
+ if ((!prompt || prompt.length < 5) && Array.isArray(event.messages)) {
5621
+ const msgs = event.messages;
5622
+ for (let i = msgs.length - 1; i >= 0; i--) {
5623
+ if (msgs[i]?.role === "user") {
5624
+ const text = extractTextContent(msgs[i]);
5625
+ if (text.length >= 5) {
5626
+ prompt = text;
5627
+ break;
5628
+ }
5629
+ }
5630
+ }
5631
+ }
5632
+ return prompt ?? "";
5633
+ }
5634
+ function cwdFrom(event, ctx, fallback) {
5635
+ const runtime = ctx?.runtime;
5636
+ for (const candidate of [ctx?.workspaceDir, event?.workspaceDir, runtime?.workspaceDir]) {
5637
+ if (typeof candidate === "string" && candidate.length > 0) return candidate;
5638
+ }
5639
+ return fallback;
5640
+ }
5641
+ function withNamespace(namespace, body) {
5642
+ return namespace.length > 0 ? { ...body, namespace } : body;
5643
+ }
5644
+ function registerDelegateRuntime(api, options) {
5645
+ const { target, namespace } = options;
5646
+ if (options.passive) {
5647
+ log2.info(
5648
+ `[${options.serviceId}] bridge mode delegate: memory slot not owned \u2014 passive, no hooks registered`
5649
+ );
5650
+ return;
5651
+ }
5652
+ if (options.allowPromptInjection && options.recallBudgetChars !== 0) {
5653
+ const promptLinesBySession = /* @__PURE__ */ new Map();
5654
+ const useSectionBuilder = typeof api.registerMemoryPromptSection === "function";
5655
+ const recallHandler = async (hook, event, ctx) => {
5656
+ const query = recallQueryFrom(event);
5657
+ if (query.trim().length < 5) return void 0;
5658
+ const sessionKey = sessionKeyFrom(event, ctx);
5659
+ if (useSectionBuilder) promptLinesBySession.delete(sessionKey);
5660
+ try {
5661
+ if (options.shouldSkipRecall(sessionKey)) {
5662
+ log2.debug(`delegate recall skipped: cron policy excludes ${sessionKey}`);
5663
+ return void 0;
5664
+ }
5665
+ const runtimeAgent = ctx?.runtime?.agent;
5666
+ const agentId = (typeof ctx?.agentId === "string" ? ctx.agentId : void 0) ?? (typeof runtimeAgent?.id === "string" ? runtimeAgent.id : void 0) ?? "main";
5667
+ if (await options.resolveSessionDisabled(sessionKey, agentId)) {
5668
+ log2.debug(`delegate recall skipped: session toggle disables memory for ${sessionKey}`);
5669
+ return void 0;
5670
+ }
5671
+ const cwd = cwdFrom(event, ctx, options.cwd);
5672
+ const response = await postJson(
5673
+ target,
5674
+ "/engram/v1/recall",
5675
+ withNamespace(namespace, {
5676
+ query,
5677
+ sessionKey,
5678
+ mode: "auto",
5679
+ ...cwd ? { cwd } : {},
5680
+ ...options.projectTag ? { projectTag: options.projectTag } : {}
5681
+ }),
5682
+ options.recallTimeoutMs
5683
+ );
5684
+ const rawContext = response?.context;
5685
+ if (typeof rawContext !== "string" || rawContext.trim().length === 0) {
5686
+ return void 0;
5687
+ }
5688
+ const context = rawContext.length > options.recallBudgetChars ? rawContext.slice(0, options.recallBudgetChars) + "\n\n...(memory context trimmed)" : rawContext;
5689
+ const prompt = `${MEMORY_CONTEXT_HEADER}
5690
+
5691
+ ${context}`;
5692
+ if (useSectionBuilder) {
5693
+ promptLinesBySession.set(sessionKey, prompt.split("\n"));
5694
+ return void 0;
5695
+ }
5696
+ return hook === "before_prompt_build" ? { prependSystemContext: prompt } : { prependSystemContext: prompt, prependContext: prompt };
5697
+ } catch (err) {
5698
+ log2.warn(`delegate recall failed: ${String(err)}`);
5699
+ return void 0;
5700
+ }
5701
+ };
5702
+ api.on(
5703
+ "before_prompt_build",
5704
+ (event, ctx) => recallHandler("before_prompt_build", event, ctx),
5705
+ { timeoutMs: options.hookTimeoutMs }
5706
+ );
5707
+ api.on(
5708
+ "before_agent_start",
5709
+ (event, ctx) => recallHandler("before_agent_start", event, ctx),
5710
+ { timeoutMs: options.hookTimeoutMs }
5711
+ );
5712
+ if (useSectionBuilder && api.registerMemoryPromptSection) {
5713
+ const memoryBuildFn = Object.assign(
5714
+ (params) => {
5715
+ const key = params?.sessionKey ?? "default";
5716
+ const lines = promptLinesBySession.get(key) ?? null;
5717
+ promptLinesBySession.delete(key);
5718
+ return lines;
5719
+ },
5720
+ { id: "remnic-delegate-memory", label: "Remnic Memory Context (delegate)" }
5721
+ );
5722
+ api.registerMemoryPromptSection(memoryBuildFn);
5723
+ }
5724
+ } else {
5725
+ log2.info(
5726
+ `[${options.serviceId}] bridge mode delegate: prompt injection disabled by hooks policy`
5727
+ );
5728
+ }
5729
+ api.on("agent_end", async (event, ctx) => {
5730
+ if (event.success !== true || !Array.isArray(event.messages)) return;
5731
+ if (event.messages.length === 0) return;
5732
+ if (options.gateHeartbeatTurns && (event.trigger === "heartbeat" || ctx?.trigger === "heartbeat")) {
5733
+ return;
5734
+ }
5735
+ const sessionKey = sessionKeyFrom(event, ctx);
5736
+ const turn = extractLastTurn(event.messages).filter((message) => extractTextContent(message).length >= 10).map((message) => ({
5737
+ role: message.role,
5738
+ content: message.role === "user" ? options.cleanUserMessage(extractTextContent(message)) : extractTextContent(message)
5739
+ })).filter(
5740
+ (message) => (message.role === "user" || message.role === "assistant") && message.content.trim().length > 0
5741
+ );
5742
+ if (turn.length === 0) return;
5743
+ try {
5744
+ const cwd = cwdFrom(event, ctx, options.cwd);
5745
+ await postJson(
5746
+ target,
5747
+ "/engram/v1/observe",
5748
+ withNamespace(namespace, {
5749
+ sessionKey,
5750
+ messages: turn,
5751
+ ...cwd ? { cwd } : {},
5752
+ ...options.projectTag ? { projectTag: options.projectTag } : {}
5753
+ }),
5754
+ options.observeTimeoutMs
5755
+ );
5756
+ } catch (err) {
5757
+ log2.warn(`delegate observe failed: ${String(err)}`);
5758
+ }
5759
+ });
5760
+ const flushHandler = async (event, ctx) => {
5761
+ const fromEvent = event?.sessionKey;
5762
+ const sessionKey = typeof fromEvent === "string" && fromEvent.length > 0 ? fromEvent : sessionKeyFrom(event, ctx);
5763
+ try {
5764
+ await postJson(
5765
+ target,
5766
+ "/engram/v1/lcm/compaction/flush",
5767
+ withNamespace(namespace, { sessionKey }),
5768
+ options.flushTimeoutMs
5769
+ );
5770
+ } catch (err) {
5771
+ log2.warn(`delegate flush failed: ${String(err)}`);
5772
+ }
5773
+ };
5774
+ api.on("before_compaction", flushHandler);
5775
+ if (options.flushOnResetEnabled) {
5776
+ api.on("before_reset", flushHandler);
5777
+ api.on("session_end", flushHandler);
5778
+ }
5779
+ log2.info(
5780
+ `[${options.serviceId}] bridge mode delegate: memory loop backed by daemon at ${target.host}:${target.port} (embedded orchestrator skipped; tools/CLI/surfaces stay daemon-side)`
5781
+ );
5782
+ }
5783
+ var delegateHookApiServices = /* @__PURE__ */ new WeakMap();
5784
+ var delegateEmbeddedFallbackApis = /* @__PURE__ */ new WeakSet();
5785
+ function maybeRegisterDelegateRuntime(api, options, deps = { checkHealth: checkDaemonHealthSync }) {
5786
+ let bridge;
5787
+ try {
5788
+ bridge = resolveBridgeMode(options.configBridgeMode);
5789
+ } catch (err) {
5790
+ log2.error(`${String(err)} \u2014 falling back to the embedded runtime`);
5791
+ delegateEmbeddedFallbackApis.add(api);
5792
+ return false;
5793
+ }
5794
+ if (delegateEmbeddedFallbackApis.has(api)) {
5795
+ log2.debug(
5796
+ `delegate register: ${options.serviceId} previously fell back to embedded on this api \u2014 staying embedded to avoid stacking memory paths`
5797
+ );
5798
+ return false;
5799
+ }
5800
+ if (bridge.mode !== "delegate") {
5801
+ if (!options.passive) delegateEmbeddedFallbackApis.add(api);
5802
+ return false;
5803
+ }
5804
+ const boundServices = delegateHookApiServices.get(api);
5805
+ if (boundServices?.has(options.serviceId)) {
5806
+ log2.debug(
5807
+ `delegate register: ${options.serviceId} already has hooks bound on this api \u2014 skipping duplicate registration`
5808
+ );
5809
+ return true;
5810
+ }
5811
+ if (!deps.checkHealth(bridge.daemonHost, bridge.daemonPort)) {
5812
+ delegateEmbeddedFallbackApis.add(api);
5813
+ log2.error(
5814
+ `bridge mode delegate requested but no healthy daemon at ${bridge.daemonHost}:${bridge.daemonPort} \u2014 falling back to the embedded runtime`
5815
+ );
5816
+ return false;
5817
+ }
5818
+ if (!options.passive) {
5819
+ (boundServices ?? delegateHookApiServices.set(api, /* @__PURE__ */ new Set()).get(api))?.add(
5820
+ options.serviceId
5821
+ );
5822
+ }
5823
+ const toggleStore = options.sessionTogglesEnabled ? createFileToggleStore(
5824
+ path4.join(options.memoryDir, "state", "plugins", options.serviceId, "session-toggles.json"),
5825
+ {
5826
+ secondaryReadOnlyPath: options.respectBundledActiveMemoryToggle ? path4.join(options.memoryDir, "state", "plugins", "active-memory", "session-toggles.json") : void 0
5827
+ }
5828
+ ) : null;
5829
+ registerDelegateRuntime(api, {
5830
+ serviceId: options.serviceId,
5831
+ target: {
5832
+ host: bridge.daemonHost,
5833
+ port: bridge.daemonPort,
5834
+ ["auth"+"Token"]: loadDaemonAuthCredential()
5835
+ },
5836
+ namespace: "",
5837
+ allowPromptInjection: options.allowPromptInjection,
5838
+ passive: options.passive,
5839
+ gateHeartbeatTurns: options.gateHeartbeatTurns,
5840
+ recallBudgetChars: options.recallBudgetChars,
5841
+ resolveSessionDisabled: async (sessionKey, agentId) => toggleStore ? (await toggleStore.resolve(sessionKey, agentId)).disabled === true : false,
5842
+ cleanUserMessage: options.cleanUserMessage,
5843
+ hookTimeoutMs: options.hookTimeoutMs,
5844
+ shouldSkipRecall: options.shouldSkipRecall,
5845
+ cwd: options.cwd,
5846
+ projectTag: options.projectTag,
5847
+ flushOnResetEnabled: options.flushOnResetEnabled,
5848
+ recallTimeoutMs: 25e3,
5849
+ observeTimeoutMs: 12e4,
5850
+ flushTimeoutMs: 55e3
5851
+ });
5852
+ return true;
5853
+ }
5854
+
5274
5855
  // ../../src/index.ts
5275
- import { createFileToggleStore } from "@remnic/core/session-toggles";
5856
+ import { createFileToggleStore as createFileToggleStore2 } from "@remnic/core/session-toggles";
5276
5857
  import { appendRecallAuditEntry, pruneRecallAuditEntries } from "@remnic/core/recall-audit";
5277
5858
  import { createActiveRecallEngine } from "@remnic/core/active-recall";
5278
5859
 
@@ -5367,7 +5948,7 @@ function buildTurnFingerprint(input) {
5367
5948
 
5368
5949
  // ../../src/index.ts
5369
5950
  import { planRecallMode } from "@remnic/core/intent";
5370
- import { resolvePrincipal, resolveAgentAccessAuthToken as resolveAgentAccessAuthCredential, expandTildePath } from "@remnic/core";
5951
+ import { resolvePrincipal, resolveAgentAccessAuthToken as resolveAgentAccessAuthCredential, expandTildePath as expandTildePath2 } from "@remnic/core";
5371
5952
  import { normalizeHostEmbeddingVector, registerHostEmbeddingProvider } from "@remnic/core/host-embedding-provider";
5372
5953
 
5373
5954
  // ../../src/resolve-provider-secret.ts
@@ -5376,23 +5957,23 @@ __export(resolve_provider_secret_exports, {
5376
5957
  findGatewayRuntimeModules: () => findGatewayRuntimeModules
5377
5958
  });
5378
5959
  __reExport(resolve_provider_secret_exports, resolve_provider_secret_star);
5379
- import path3 from "path";
5960
+ import path5 from "path";
5380
5961
  import * as resolve_provider_secret_star from "@remnic/core/resolve-provider-secret";
5381
5962
  async function findGatewayRuntimeModules(filePrefix) {
5382
5963
  const { existsSync, ["re"+"ad"+"Fi"+"le"+"Sync"]: fileReaderSync, readdirSync, realpathSync } = await import("fs");
5383
5964
  const { createRequire: createRequire2 } = await import("module");
5384
5965
  const candidates = [];
5385
5966
  const isWithinRoot = (root, candidate) => {
5386
- const relative = path3.relative(root, candidate);
5387
- return relative.length === 0 || !relative.startsWith("..") && !path3.isAbsolute(relative);
5967
+ const relative = path5.relative(root, candidate);
5968
+ return relative.length === 0 || !relative.startsWith("..") && !path5.isAbsolute(relative);
5388
5969
  };
5389
5970
  let packageRoot;
5390
5971
  try {
5391
5972
  const req = createRequire2(import.meta.url);
5392
5973
  const openclawEntrypoint = realpathSync(req.resolve("openclaw"));
5393
- let currentDir = path3.dirname(openclawEntrypoint);
5974
+ let currentDir = path5.dirname(openclawEntrypoint);
5394
5975
  while (true) {
5395
- const packageJsonPath = path3.join(currentDir, "package.json");
5976
+ const packageJsonPath = path5.join(currentDir, "package.json");
5396
5977
  if (existsSync(packageJsonPath)) {
5397
5978
  const packageJson = JSON.parse(fileReaderSync(packageJsonPath, "utf8"));
5398
5979
  if (packageJson.name !== "openclaw") {
@@ -5401,7 +5982,7 @@ async function findGatewayRuntimeModules(filePrefix) {
5401
5982
  packageRoot = realpathSync(currentDir);
5402
5983
  break;
5403
5984
  }
5404
- const parent = path3.dirname(currentDir);
5985
+ const parent = path5.dirname(currentDir);
5405
5986
  if (parent === currentDir) {
5406
5987
  return [];
5407
5988
  }
@@ -5411,14 +5992,14 @@ async function findGatewayRuntimeModules(filePrefix) {
5411
5992
  return [];
5412
5993
  }
5413
5994
  try {
5414
- const distDir = realpathSync(path3.join(packageRoot, "dist"));
5995
+ const distDir = realpathSync(path5.join(packageRoot, "dist"));
5415
5996
  if (!isWithinRoot(packageRoot, distDir)) {
5416
5997
  return [];
5417
5998
  }
5418
5999
  const files = readdirSync(distDir);
5419
6000
  for (const f of files) {
5420
6001
  if (f.startsWith(filePrefix) && f.endsWith(".js")) {
5421
- const candidate = realpathSync(path3.join(distDir, f));
6002
+ const candidate = realpathSync(path5.join(distDir, f));
5422
6003
  if (isWithinRoot(packageRoot, candidate) && isWithinRoot(distDir, candidate)) {
5423
6004
  candidates.push(candidate);
5424
6005
  }
@@ -5762,9 +6343,9 @@ function buildServiceKeys(serviceId) {
5762
6343
  function resolveOpenClawConfigFilePath() {
5763
6344
  const explicitConfigPath = readEnvVar("OPENCLAW_CONFIG_PATH") || readEnvVar("OPENCLAW_ENGRAM_CONFIG_PATH");
5764
6345
  if (explicitConfigPath && explicitConfigPath.length > 0) {
5765
- return expandTildePath(explicitConfigPath);
6346
+ return expandTildePath2(explicitConfigPath);
5766
6347
  }
5767
- return path4.join(resolveHomeDir(), ".openclaw", "openclaw.json");
6348
+ return path6.join(resolveHomeDir2(), ".openclaw", "openclaw.json");
5768
6349
  }
5769
6350
  function coerceRawConfigBoolean(value) {
5770
6351
  if (typeof value === "boolean") return value;
@@ -5811,7 +6392,7 @@ function readPluginHooksPolicy(apiConfig, pluginId) {
5811
6392
  }
5812
6393
  async function maybeRegisterLiveConnectorCron(orchestrator) {
5813
6394
  if (!hasEnabledLiveConnectorConfig(orchestrator.config.connectors)) return;
5814
- const jobsPath = path4.join(resolveHomeDir(), ".openclaw", "cron", "jobs.json");
6395
+ const jobsPath = path6.join(resolveHomeDir2(), ".openclaw", "cron", "jobs.json");
5815
6396
  try {
5816
6397
  if (!fileExistsNow(jobsPath)) {
5817
6398
  logger_exports.log.debug("live connectors cron: jobs.json not found, skipping auto-register");
@@ -6255,6 +6836,28 @@ var pluginDefinition = {
6255
6836
  includeLegacyChannelEnvelopePattern: channelEnvelopeCleaning.includeLegacyChannelEnvelopePattern
6256
6837
  }
6257
6838
  );
6839
+ const delegateApi = api;
6840
+ const delegateHandled = maybeRegisterDelegateRuntime(delegateApi, {
6841
+ serviceId,
6842
+ // cfg.bridgeMode is parseConfig's merged file+runtime passthrough, so
6843
+ // file-config-only deployments activate delegate too (round-7 High).
6844
+ configBridgeMode: cfg.bridgeMode,
6845
+ passive: passiveMode,
6846
+ allowPromptInjection: coerceRawConfigBoolean(
6847
+ readPluginHooksPolicy(api.config, serviceId)?.allowPromptInjection
6848
+ ) !== false,
6849
+ gateHeartbeatTurns: cfg.heartbeat.enabled && cfg.heartbeat.gateExtractionDuringHeartbeat,
6850
+ recallBudgetChars: cfg.recallBudgetChars,
6851
+ memoryDir: cfg.memoryDir,
6852
+ sessionTogglesEnabled: cfg.sessionTogglesEnabled,
6853
+ respectBundledActiveMemoryToggle: cfg.respectBundledActiveMemoryToggle,
6854
+ cleanUserMessage: cleanOpenClawUserMessage,
6855
+ hookTimeoutMs: cfg.initGateTimeoutMs,
6856
+ shouldSkipRecall: (sk) => shouldSkipRecallForSession(sk, cfg),
6857
+ cwd: getOpenClawRuntimeWorkspaceDir(api),
6858
+ flushOnResetEnabled: cfg.flushOnResetEnabled
6859
+ });
6860
+ if (delegateHandled) return;
6258
6861
  const existing = globalThis[keys.ORCHESTRATOR];
6259
6862
  const orchestrator = existing?.recall ? existing : new Orchestrator(cfg);
6260
6863
  const isFirstRegistration = !globalThis[keys.REGISTERED_GUARD];
@@ -6332,10 +6935,10 @@ var pluginDefinition = {
6332
6935
  emitLegacyTools: cfg.emitLegacyTools
6333
6936
  });
6334
6937
  globalThis[keys.ACCESS_HTTP_SERVER] = accessHttpServer;
6335
- const pluginStateDir = path4.join(cfg.memoryDir, "state", "plugins", serviceId);
6336
- const togglePrimaryPath = path4.join(pluginStateDir, "session-toggles.json");
6337
- const toggleSecondaryPath = cfg.respectBundledActiveMemoryToggle ? path4.join(cfg.memoryDir, "state", "plugins", "active-memory", "session-toggles.json") : void 0;
6338
- const sessionToggleStore = createFileToggleStore(togglePrimaryPath, {
6938
+ const pluginStateDir = path6.join(cfg.memoryDir, "state", "plugins", serviceId);
6939
+ const togglePrimaryPath = path6.join(pluginStateDir, "session-toggles.json");
6940
+ const toggleSecondaryPath = cfg.respectBundledActiveMemoryToggle ? path6.join(cfg.memoryDir, "state", "plugins", "active-memory", "session-toggles.json") : void 0;
6941
+ const sessionToggleStore = createFileToggleStore2(togglePrimaryPath, {
6339
6942
  secondaryReadOnlyPath: toggleSecondaryPath
6340
6943
  });
6341
6944
  const dreamsSurface = createDreamsSurface();
@@ -6356,11 +6959,11 @@ var pluginDefinition = {
6356
6959
  }
6357
6960
  function resolveDreamJournalPath(runtimeWorkspaceDir) {
6358
6961
  const workspaceRoot = resolveWorkspaceRoot(runtimeWorkspaceDir);
6359
- return path4.isAbsolute(cfg.dreaming.journalPath) ? cfg.dreaming.journalPath : path4.join(workspaceRoot, cfg.dreaming.journalPath);
6962
+ return path6.isAbsolute(cfg.dreaming.journalPath) ? cfg.dreaming.journalPath : path6.join(workspaceRoot, cfg.dreaming.journalPath);
6360
6963
  }
6361
6964
  function resolveHeartbeatJournalPath(runtimeWorkspaceDir) {
6362
6965
  const workspaceRoot = resolveWorkspaceRoot(runtimeWorkspaceDir);
6363
- return path4.isAbsolute(cfg.heartbeat.journalPath) ? cfg.heartbeat.journalPath : path4.join(workspaceRoot, cfg.heartbeat.journalPath);
6966
+ return path6.isAbsolute(cfg.heartbeat.journalPath) ? cfg.heartbeat.journalPath : path6.join(workspaceRoot, cfg.heartbeat.journalPath);
6364
6967
  }
6365
6968
  const existingFlushPlanProcessingChains = globalThis[keys.FLUSH_PLAN_PROCESSING_CHAINS];
6366
6969
  const flushPlanProcessingChains = existingFlushPlanProcessingChains instanceof Map ? existingFlushPlanProcessingChains : /* @__PURE__ */ new Map();
@@ -6637,7 +7240,7 @@ Keep the reflection grounded in the evidence below.
6637
7240
  timeoutMs: cfg.activeRecallTimeoutMs,
6638
7241
  cacheTtlMs: cfg.activeRecallCacheTtlMs,
6639
7242
  persistTranscripts: cfg.activeRecallPersistTranscripts,
6640
- transcriptDir: path4.isAbsolute(cfg.activeRecallTranscriptDir) ? cfg.activeRecallTranscriptDir : path4.join(pluginStateDir, cfg.activeRecallTranscriptDir),
7243
+ transcriptDir: path6.isAbsolute(cfg.activeRecallTranscriptDir) ? cfg.activeRecallTranscriptDir : path6.join(pluginStateDir, cfg.activeRecallTranscriptDir),
6641
7244
  entityGraphDepth: cfg.activeRecallEntityGraphDepth,
6642
7245
  includeCausalTrajectories: cfg.activeRecallIncludeCausalTrajectories,
6643
7246
  includeDaySummary: cfg.activeRecallIncludeDaySummary,
@@ -7650,20 +8253,20 @@ Keep the reflection grounded in the evidence below.
7650
8253
  const remnicQmdCommand = typeof orchestrator.config.qmdPath === "string" && orchestrator.config.qmdPath.trim().length > 0 ? orchestrator.config.qmdPath.trim() : "qmd";
7651
8254
  const readAllowedRoots = [
7652
8255
  orchestrator.config.memoryDir,
7653
- capabilityWorkspaceDir ? path4.join(capabilityWorkspaceDir, "memory") : void 0
8256
+ capabilityWorkspaceDir ? path6.join(capabilityWorkspaceDir, "memory") : void 0
7654
8257
  ].filter((root) => typeof root === "string" && root.length > 0);
7655
8258
  const canonicalizeRootForContainment = async (rawPath) => {
7656
- const resolved = path4.resolve(rawPath);
8259
+ const resolved = path6.resolve(rawPath);
7657
8260
  try {
7658
- return path4.normalize(await realPathLater(resolved));
8261
+ return path6.normalize(await realPathLater(resolved));
7659
8262
  } catch {
7660
- return path4.normalize(resolved);
8263
+ return path6.normalize(resolved);
7661
8264
  }
7662
8265
  };
7663
8266
  const canonicalizeForRead = async (rawPath) => {
7664
- const resolved = path4.resolve(rawPath);
8267
+ const resolved = path6.resolve(rawPath);
7665
8268
  const real = await realPathLater(resolved);
7666
- return path4.normalize(real);
8269
+ return path6.normalize(real);
7667
8270
  };
7668
8271
  const readAllowedCanonicalRootsPromise = Promise.all(
7669
8272
  readAllowedRoots.map((root) => canonicalizeRootForContainment(root))
@@ -7677,29 +8280,29 @@ Keep the reflection grounded in the evidence below.
7677
8280
  }
7678
8281
  const canonicalRoots = await readAllowedCanonicalRootsPromise;
7679
8282
  return canonicalRoots.some((root) => {
7680
- const relative = path4.relative(root, canonicalCandidatePath);
7681
- return relative === "" || !relative.startsWith("..") && !path4.isAbsolute(relative);
8283
+ const relative = path6.relative(root, canonicalCandidatePath);
8284
+ return relative === "" || !relative.startsWith("..") && !path6.isAbsolute(relative);
7682
8285
  });
7683
8286
  };
7684
8287
  const normalizeWorkspacePath = (rawPath) => {
7685
8288
  if (!rawPath || typeof rawPath !== "string") return "memory";
7686
- const resolved = path4.isAbsolute(rawPath) ? path4.resolve(rawPath) : path4.resolve(capabilityWorkspaceDir, rawPath);
7687
- const relative = path4.relative(capabilityWorkspaceDir, resolved);
7688
- return relative && !relative.startsWith("..") && !path4.isAbsolute(relative) ? relative : rawPath;
8289
+ const resolved = path6.isAbsolute(rawPath) ? path6.resolve(rawPath) : path6.resolve(capabilityWorkspaceDir, rawPath);
8290
+ const relative = path6.relative(capabilityWorkspaceDir, resolved);
8291
+ return relative && !relative.startsWith("..") && !path6.isAbsolute(relative) ? relative : rawPath;
7689
8292
  };
7690
8293
  const relativizeToMemoryRoot = (rawPath) => {
7691
8294
  if (!rawPath || typeof rawPath !== "string") return "memory";
7692
- const resolved = path4.isAbsolute(rawPath) ? path4.resolve(rawPath) : path4.resolve(capabilityWorkspaceDir, rawPath);
8295
+ const resolved = path6.isAbsolute(rawPath) ? path6.resolve(rawPath) : path6.resolve(capabilityWorkspaceDir, rawPath);
7693
8296
  for (const root of readAllowedRoots) {
7694
- const relative = path4.relative(root, resolved);
7695
- if (relative !== "" && !relative.startsWith("..") && !path4.isAbsolute(relative)) {
8297
+ const relative = path6.relative(root, resolved);
8298
+ if (relative !== "" && !relative.startsWith("..") && !path6.isAbsolute(relative)) {
7696
8299
  return relative;
7697
8300
  }
7698
8301
  }
7699
8302
  return normalizeWorkspacePath(rawPath);
7700
8303
  };
7701
8304
  const resolveReadablePath = async (requestedPath) => {
7702
- const candidateAbsolutePaths = path4.isAbsolute(requestedPath) ? [path4.resolve(requestedPath)] : readAllowedRoots.map((root) => path4.resolve(root, requestedPath));
8305
+ const candidateAbsolutePaths = path6.isAbsolute(requestedPath) ? [path6.resolve(requestedPath)] : readAllowedRoots.map((root) => path6.resolve(root, requestedPath));
7703
8306
  let canonicalPath;
7704
8307
  let lastError;
7705
8308
  for (const absolutePath of candidateAbsolutePaths) {
@@ -7717,8 +8320,8 @@ Keep the reflection grounded in the evidence below.
7717
8320
  }
7718
8321
  const canonicalRoots = await readAllowedCanonicalRootsPromise;
7719
8322
  const contained = canonicalRoots.some((root) => {
7720
- const relative = path4.relative(root, canonicalPath);
7721
- return relative === "" || !relative.startsWith("..") && !path4.isAbsolute(relative);
8323
+ const relative = path6.relative(root, canonicalPath);
8324
+ return relative === "" || !relative.startsWith("..") && !path6.isAbsolute(relative);
7722
8325
  });
7723
8326
  if (!contained) {
7724
8327
  throw new Error(`memory read outside allowed roots: ${requestedPath}`);
@@ -7750,15 +8353,15 @@ Keep the reflection grounded in the evidence below.
7750
8353
  }).map((result, index) => {
7751
8354
  const candidate = result;
7752
8355
  const rawPath = typeof candidate.path === "string" ? candidate.path : typeof candidate.id === "string" ? candidate.id : `memory-${index + 1}`;
7753
- const absolutePath = path4.isAbsolute(rawPath) ? path4.resolve(rawPath) : (() => {
8356
+ const absolutePath = path6.isAbsolute(rawPath) ? path6.resolve(rawPath) : (() => {
7754
8357
  for (const root of readAllowedRoots) {
7755
- const candidateAbs = path4.resolve(root, rawPath);
7756
- const relative = path4.relative(root, candidateAbs);
7757
- if (!relative.startsWith("..") && !path4.isAbsolute(relative)) {
8358
+ const candidateAbs = path6.resolve(root, rawPath);
8359
+ const relative = path6.relative(root, candidateAbs);
8360
+ if (!relative.startsWith("..") && !path6.isAbsolute(relative)) {
7758
8361
  return candidateAbs;
7759
8362
  }
7760
8363
  }
7761
- return path4.resolve(capabilityWorkspaceDir, rawPath);
8364
+ return path6.resolve(capabilityWorkspaceDir, rawPath);
7762
8365
  })();
7763
8366
  const normalizedPath = relativizeToMemoryRoot(rawPath);
7764
8367
  const startLine = typeof candidate.startLine === "number" && Number.isFinite(candidate.startLine) ? Math.max(1, Math.floor(candidate.startLine)) : 1;
@@ -8305,7 +8908,7 @@ Keep the reflection grounded in the evidence below.
8305
8908
  `session reset via API for ${sessionKey}, new sessionId=${result.sessionId}`
8306
8909
  );
8307
8910
  const safeSessionKey = sanitizeSessionKeyForFilename(sessionKey);
8308
- const signalPath = path4.join(
8911
+ const signalPath = path6.join(
8309
8912
  workspaceDir,
8310
8913
  `.compaction-reset-signal-${safeSessionKey}`
8311
8914
  );
@@ -8569,7 +9172,7 @@ Keep the reflection grounded in the evidence below.
8569
9172
  }
8570
9173
  async function ensureHourlySummaryCron(api2) {
8571
9174
  const jobId = "engram-hourly-summary";
8572
- const cronFilePath = path4.join(
9175
+ const cronFilePath = path6.join(
8573
9176
  os.homedir(),
8574
9177
  ".openclaw",
8575
9178
  "cron",
@@ -8628,32 +9231,32 @@ Keep the reflection grounded in the evidence below.
8628
9231
  };
8629
9232
  const normalizeCorpusPath = (value) => value.trim().replace(/\\/g, "/").replace(/^\.\//, "");
8630
9233
  const pathIsInside = (root, candidate) => {
8631
- const relative = path4.relative(root, candidate);
8632
- return relative === "" || !relative.startsWith("..") && !path4.isAbsolute(relative);
9234
+ const relative = path6.relative(root, candidate);
9235
+ return relative === "" || !relative.startsWith("..") && !path6.isAbsolute(relative);
8633
9236
  };
8634
9237
  const corpusPathCandidates = (rawPath, storageDir) => {
8635
9238
  const candidates = /* @__PURE__ */ new Set();
8636
9239
  const trimmed = rawPath.trim();
8637
9240
  if (!trimmed) return [];
8638
9241
  candidates.add(normalizeCorpusPath(trimmed));
8639
- if (path4.isAbsolute(trimmed)) {
8640
- const absolutePath = path4.resolve(trimmed);
8641
- const absoluteStorageDir = path4.resolve(storageDir);
9242
+ if (path6.isAbsolute(trimmed)) {
9243
+ const absolutePath = path6.resolve(trimmed);
9244
+ const absoluteStorageDir = path6.resolve(storageDir);
8642
9245
  if (pathIsInside(absoluteStorageDir, absolutePath)) {
8643
- candidates.add(normalizeCorpusPath(path4.relative(absoluteStorageDir, absolutePath)));
9246
+ candidates.add(normalizeCorpusPath(path6.relative(absoluteStorageDir, absolutePath)));
8644
9247
  }
8645
9248
  }
8646
- candidates.add(path4.basename(trimmed));
9249
+ candidates.add(path6.basename(trimmed));
8647
9250
  return [...candidates].filter((candidate) => candidate.length > 0);
8648
9251
  };
8649
9252
  const displayCorpusPath = (rawPath, storageDir) => {
8650
9253
  const trimmed = rawPath.trim();
8651
9254
  if (!trimmed) return "";
8652
- if (path4.isAbsolute(trimmed)) {
8653
- const absolutePath = path4.resolve(trimmed);
8654
- const absoluteStorageDir = path4.resolve(storageDir);
9255
+ if (path6.isAbsolute(trimmed)) {
9256
+ const absolutePath = path6.resolve(trimmed);
9257
+ const absoluteStorageDir = path6.resolve(storageDir);
8655
9258
  if (pathIsInside(absoluteStorageDir, absolutePath)) {
8656
- return normalizeCorpusPath(path4.relative(absoluteStorageDir, absolutePath));
9259
+ return normalizeCorpusPath(path6.relative(absoluteStorageDir, absolutePath));
8657
9260
  }
8658
9261
  }
8659
9262
  return normalizeCorpusPath(trimmed);
@@ -9071,25 +9674,6 @@ function resolveOpenClawFlushPlanProcessingEnabledFromConfig(fileConfig, runtime
9071
9674
  if (runtimeValue === false || fileValue === false) return false;
9072
9675
  return fileValue ?? runtimeValue ?? true;
9073
9676
  }
9074
- function extractLastTurn(messages) {
9075
- let lastUserIdx = -1;
9076
- for (let i = messages.length - 1; i >= 0; i--) {
9077
- if (messages[i]?.role === "user") {
9078
- lastUserIdx = i;
9079
- break;
9080
- }
9081
- }
9082
- return lastUserIdx >= 0 ? messages.slice(lastUserIdx) : messages.slice(-2);
9083
- }
9084
- function extractTextContent(msg) {
9085
- if (typeof msg.content === "string") return msg.content;
9086
- if (Array.isArray(msg.content)) {
9087
- return msg.content.filter(
9088
- (block) => typeof block === "object" && block !== null && block.type === "text" && typeof block.text === "string"
9089
- ).map((block) => block.text).join("\n");
9090
- }
9091
- return "";
9092
- }
9093
9677
  function buildOpenClawMessageMetadata(message, event, ctx, cfg) {
9094
9678
  if (!cfg.openclawReplyMetadataCaptureEnabled) return null;
9095
9679
  const metadata = {};
@@ -9182,259 +9766,12 @@ function truncateMetadataValue(value, maxChars) {
9182
9766
  return value.length <= maxChars ? value : value.slice(0, maxChars);
9183
9767
  }
9184
9768
  async function resolveFlushPlanProcessingChainKey(workspaceRoot) {
9185
- const lexicalRoot = path4.resolve(workspaceRoot);
9769
+ const lexicalRoot = path6.resolve(workspaceRoot);
9186
9770
  try {
9187
- return path4.resolve(await realPathLater(lexicalRoot));
9771
+ return path6.resolve(await realPathLater(lexicalRoot));
9188
9772
  } catch {
9189
9773
  return lexicalRoot;
9190
9774
  }
9191
9775
  }
9192
-
9193
- // src/bridge.ts
9194
- import fs from "fs";
9195
- import path5 from "path";
9196
- import { Worker } from "worker_threads";
9197
- import { expandTildePath as expandTildePath2 } from "@remnic/core";
9198
- var DEFAULT_HOST = "127.0.0.1";
9199
- var DEFAULT_PORT = 4318;
9200
- var LEGACY_HEALTH_PATH = "/engram/v1/health";
9201
- var SYNC_HEALTH_TIMEOUT_MS = 2e3;
9202
- var HEALTH_WORKER_SOURCE = `
9203
- import { request } from "node:http";
9204
- import { workerData } from "node:worker_threads";
9205
-
9206
- const view = new Int32Array(workerData.state);
9207
- let completed = false;
9208
-
9209
- function finish(ok) {
9210
- if (completed) return;
9211
- completed = true;
9212
- Atomics.store(view, 0, ok ? 1 : 2);
9213
- Atomics.notify(view, 0);
9214
- }
9215
-
9216
- try {
9217
- const headers = {};
9218
- if (workerData.token) headers.Authorization = "Bearer " + workerData.token;
9219
- const req = request(
9220
- {
9221
- hostname: workerData.host,
9222
- port: workerData.port,
9223
- path: workerData.path,
9224
- method: "GET",
9225
- timeout: workerData.timeoutMs,
9226
- headers,
9227
- },
9228
- (res) => {
9229
- finish(res.statusCode === 200);
9230
- res.resume();
9231
- },
9232
- );
9233
- req.on("error", () => finish(false));
9234
- req.on("timeout", () => {
9235
- req.destroy();
9236
- finish(false);
9237
- });
9238
- req.end();
9239
- } catch {
9240
- finish(false);
9241
- }
9242
- `;
9243
- var LAUNCHD_SERVICE_PATHS = [
9244
- ["Library", "LaunchAgents", "ai.remnic.daemon.plist"],
9245
- ["Library", "LaunchAgents", "ai.remnic.server.plist"],
9246
- ["Library", "LaunchAgents", "ai.engram.daemon.plist"]
9247
- ];
9248
- var SYSTEMD_SERVICE_PATHS = [
9249
- [".config", "systemd", "user", "remnic.service"],
9250
- [".config", "systemd", "user", "engram.service"]
9251
- ];
9252
- function readEnv(name) {
9253
- const env = globalThis.process?.["env"];
9254
- return env?.[name];
9255
- }
9256
- function resolveHomeDir2() {
9257
- return readEnv("HOME") ?? readEnv("USERPROFILE") ?? "~";
9258
- }
9259
- function readCompatEnv(primary, legacy) {
9260
- return readEnv(primary) ?? readEnv(legacy);
9261
- }
9262
- function configPathCandidates() {
9263
- const envPath = readCompatEnv("REMNIC_CONFIG_PATH", "ENGRAM_CONFIG_PATH");
9264
- return [
9265
- ...envPath ? [path5.resolve(expandTildePath2(envPath))] : [],
9266
- path5.join(resolveHomeDir2(), ".config", "remnic", "config.json"),
9267
- path5.join(resolveHomeDir2(), ".config", "engram", "config.json"),
9268
- path5.join(process.cwd(), "remnic.config.json"),
9269
- path5.join(process.cwd(), "engram.config.json")
9270
- ];
9271
- }
9272
- function fileExists(filePath) {
9273
- try {
9274
- return fs.statSync(filePath).isFile();
9275
- } catch {
9276
- return false;
9277
- }
9278
- }
9279
- function isDaemonRunning() {
9280
- for (const pidFile of [
9281
- path5.join(resolveHomeDir2(), ".remnic", "server.pid"),
9282
- path5.join(resolveHomeDir2(), ".engram", "server.pid")
9283
- ]) {
9284
- try {
9285
- const pid = parseInt(fs["re"+"ad"+"Fi"+"le"+"Sync"](pidFile, "utf8").trim(), 10);
9286
- process.kill(pid, 0);
9287
- return true;
9288
- } catch {
9289
- }
9290
- }
9291
- return false;
9292
- }
9293
- function isDaemonServiceConfigured() {
9294
- const homeDir = resolveHomeDir2();
9295
- for (const segments of [...LAUNCHD_SERVICE_PATHS, ...SYSTEMD_SERVICE_PATHS]) {
9296
- if (fileExists(path5.join(homeDir, ...segments))) return true;
9297
- }
9298
- return false;
9299
- }
9300
- function coerceDaemonPort(value) {
9301
- const parsed = typeof value === "string" && value.trim() !== "" ? Number(value.trim()) : value;
9302
- return typeof parsed === "number" && Number.isInteger(parsed) && parsed > 0 && parsed <= 65535 ? parsed : void 0;
9303
- }
9304
- function checkDaemonHealthSync(host, port, timeoutMs = SYNC_HEALTH_TIMEOUT_MS) {
9305
- if (!host || !Number.isInteger(port) || port <= 0 || port > 65535) return false;
9306
- let worker;
9307
- try {
9308
- const state = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
9309
- const view = new Int32Array(state);
9310
- const workerUrl = new URL(`data:text/javascript,${encodeURIComponent(HEALTH_WORKER_SOURCE)}`);
9311
- const workerOptions = {
9312
- type: "module",
9313
- workerData: {
9314
- host,
9315
- port,
9316
- path: LEGACY_HEALTH_PATH,
9317
- token: loadAnyToken(),
9318
- timeoutMs,
9319
- state
9320
- }
9321
- };
9322
- worker = new Worker(workerUrl, workerOptions);
9323
- Atomics.wait(view, 0, 0, timeoutMs + 250);
9324
- const status = Atomics.load(view, 0);
9325
- if (status === 0) void worker.terminate();
9326
- return status === 1;
9327
- } catch {
9328
- if (worker) void worker.terminate();
9329
- return false;
9330
- }
9331
- }
9332
- function shouldProbeDaemonHealth(host) {
9333
- const normalized = host.trim().toLowerCase();
9334
- return normalized === DEFAULT_HOST || normalized === "localhost" || normalized === "::1" || normalized === "[::1]" || isDaemonServiceConfigured();
9335
- }
9336
- function readDaemonPort() {
9337
- const envPort = coerceDaemonPort(readCompatEnv("REMNIC_PORT", "ENGRAM_PORT"));
9338
- if (envPort !== void 0) return envPort;
9339
- for (const p of configPathCandidates()) {
9340
- if (!fs.existsSync(p)) continue;
9341
- try {
9342
- const raw = JSON.parse(fs["re"+"ad"+"Fi"+"le"+"Sync"](p, "utf8"));
9343
- const configPort = coerceDaemonPort(raw.server?.port);
9344
- if (configPort !== void 0) return configPort;
9345
- } catch {
9346
- }
9347
- }
9348
- return DEFAULT_PORT;
9349
- }
9350
- function detectBridgeMode() {
9351
- const envMode = readCompatEnv("REMNIC_BRIDGE_MODE", "ENGRAM_BRIDGE_MODE")?.toLowerCase();
9352
- if (envMode === "delegate") {
9353
- return {
9354
- mode: "delegate",
9355
- daemonHost: readCompatEnv("REMNIC_HOST", "ENGRAM_HOST") ?? DEFAULT_HOST,
9356
- daemonPort: readDaemonPort()
9357
- };
9358
- }
9359
- if (envMode === "embedded") {
9360
- return {
9361
- mode: "embedded",
9362
- daemonHost: DEFAULT_HOST,
9363
- daemonPort: readDaemonPort()
9364
- };
9365
- }
9366
- const daemonHost = readCompatEnv("REMNIC_HOST", "ENGRAM_HOST") ?? DEFAULT_HOST;
9367
- const daemonPort = readDaemonPort();
9368
- const hasDaemonPidHint = isDaemonRunning();
9369
- if ((hasDaemonPidHint || shouldProbeDaemonHealth(daemonHost)) && checkDaemonHealthSync(daemonHost, daemonPort)) {
9370
- return {
9371
- mode: "delegate",
9372
- daemonHost,
9373
- daemonPort
9374
- };
9375
- }
9376
- return {
9377
- mode: "embedded",
9378
- daemonHost: DEFAULT_HOST,
9379
- daemonPort
9380
- };
9381
- }
9382
- function loadAnyToken() {
9383
- const tokenPaths = [
9384
- path5.join(resolveHomeDir2(), ".remnic", "tokens.json"),
9385
- path5.join(resolveHomeDir2(), ".engram", "tokens.json")
9386
- ];
9387
- for (const tokensPath of tokenPaths) {
9388
- if (!fs.existsSync(tokensPath)) continue;
9389
- try {
9390
- const store = JSON.parse(fs["re"+"ad"+"Fi"+"le"+"Sync"](tokensPath, "utf8"));
9391
- const tokens = Array.isArray(store.tokens) ? store.tokens : [];
9392
- if (tokens.length > 0 && tokens[0].token) return tokens[0].token;
9393
- if (typeof store === "object" && store !== null) {
9394
- for (const val of Object.values(store)) {
9395
- if (typeof val === "string" && val.length > 0 && (val.startsWith("remnic_") || val.startsWith("engram_"))) {
9396
- return val;
9397
- }
9398
- }
9399
- }
9400
- } catch {
9401
- }
9402
- }
9403
- try {
9404
- for (const p of configPathCandidates()) {
9405
- if (fs.existsSync(p)) {
9406
- const raw = JSON.parse(fs["re"+"ad"+"Fi"+"le"+"Sync"](p, "utf8"));
9407
- if (raw.server?.["auth"+"Token"]) return raw.server["auth"+"Token"];
9408
- }
9409
- }
9410
- } catch {
9411
- }
9412
- return readEnv("OPENCLAW_REMNIC_ACCESS_TOKEN") ?? readEnv("OPENCLAW_ENGRAM_ACCESS_TOKEN") ?? readCompatEnv("REMNIC_AUTH_TOKEN", "ENGRAM_AUTH_TOKEN") ?? "";
9413
- }
9414
- async function checkDaemonHealth(host, port) {
9415
- try {
9416
- const { request } = await import("http");
9417
- const token = loadAnyToken();
9418
- const headers = {};
9419
- if (token) headers["Authorization"] = `Bearer ${token}`;
9420
- return new Promise((resolve) => {
9421
- const req = request(
9422
- { hostname: host, port, path: LEGACY_HEALTH_PATH, method: "GET", timeout: 2e3, headers },
9423
- (res) => {
9424
- resolve(res.statusCode === 200);
9425
- res.resume();
9426
- }
9427
- );
9428
- req.on("error", () => resolve(false));
9429
- req.on("timeout", () => {
9430
- req.destroy();
9431
- resolve(false);
9432
- });
9433
- req.end();
9434
- });
9435
- } catch {
9436
- return false;
9437
- }
9438
- }
9439
9776
  var export_loadDaySummaryPrompt = day_summary_exports.loadDaySummaryPrompt;
9440
9777
  export { buildHourlySummaryCronJob, checkDaemonHealth, src_default as default, detectBridgeMode, embedWithOpenClawProvider, listRemnicPublicArtifacts, export_loadDaySummaryPrompt as loadDaySummaryPrompt, loadHourlySummaryCronJobsData, loadOpenClawMemoryEmbeddingSdk, parseHourlySummaryCronJobsData, reconcileHourlySummaryCronRouting, selectOpenClawMemoryEmbeddingSdk };