@remnic/plugin-openclaw 9.69.60 → 9.69.63
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 +1037 -676
- package/openclaw.plugin.json +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ import OpenAI from "openai";
|
|
|
19
19
|
import { createRequire } from "module";
|
|
20
20
|
import { createHash as createHash3 } from "crypto";
|
|
21
21
|
import { parseConfig } from "@remnic/core/config";
|
|
22
|
-
import { initLogger, log as
|
|
22
|
+
import { initLogger, log as log9 } from "@remnic/core/logger";
|
|
23
23
|
import {
|
|
24
24
|
detectSdkCapabilities
|
|
25
25
|
} from "@remnic/core/sdk-compat";
|
|
@@ -450,7 +450,117 @@ function formatContinuityLoopSummary(loop) {
|
|
|
450
450
|
if (loop.notes) lines.push(`Notes: ${loop.notes}`);
|
|
451
451
|
return lines.join("\n");
|
|
452
452
|
}
|
|
453
|
-
function
|
|
453
|
+
function buildLegacyMemorySearchTool(orchestrator) {
|
|
454
|
+
return {
|
|
455
|
+
name: "memory_search",
|
|
456
|
+
label: "Search Memory",
|
|
457
|
+
description: `Search local memory files using QMD's semantic index. Returns matching memories with snippets and relevance scores.
|
|
458
|
+
|
|
459
|
+
Returns: Matching memory entries ranked by relevance
|
|
460
|
+
Cost: Free (local index query)
|
|
461
|
+
Speed: Fast
|
|
462
|
+
|
|
463
|
+
Best for:
|
|
464
|
+
- Finding previously learned facts about the user
|
|
465
|
+
- Checking what you know about a topic
|
|
466
|
+
- Locating past decisions or corrections`,
|
|
467
|
+
parameters: Type2.Object({
|
|
468
|
+
query: Type2.String({
|
|
469
|
+
description: "Search query \u2014 keywords, phrases, or natural language"
|
|
470
|
+
}),
|
|
471
|
+
namespace: Type2.Optional(
|
|
472
|
+
Type2.String({
|
|
473
|
+
description: "Optional namespace filter. When set, only returns results under memoryDir/namespaces/<namespace>/ (default namespace uses legacy root)."
|
|
474
|
+
})
|
|
475
|
+
),
|
|
476
|
+
maxResults: Type2.Optional(
|
|
477
|
+
Type2.Number({
|
|
478
|
+
description: "Maximum results (default: 8)",
|
|
479
|
+
minimum: 1,
|
|
480
|
+
maximum: 50
|
|
481
|
+
})
|
|
482
|
+
),
|
|
483
|
+
collection: Type2.Optional(
|
|
484
|
+
Type2.String({
|
|
485
|
+
description: "QMD collection to search. Omit for memory collection, use 'global' for all collections."
|
|
486
|
+
})
|
|
487
|
+
)
|
|
488
|
+
}),
|
|
489
|
+
async execute(_toolCallId, params) {
|
|
490
|
+
const { query, maxResults, collection, namespace } = params;
|
|
491
|
+
const namespaceFilter = namespace && namespace.length > 0 ? namespace : void 0;
|
|
492
|
+
const resultLimit = normalizeMemorySearchResultLimit(maxResults);
|
|
493
|
+
const searchCandidates = async (limit) => collection === "global" && !namespaceFilter ? await orchestrator.qmd.searchGlobal(query, limit) : await orchestrator.searchAcrossNamespaces({
|
|
494
|
+
query,
|
|
495
|
+
namespaces: namespaceFilter ? [namespaceFilter] : void 0,
|
|
496
|
+
maxResults: limit,
|
|
497
|
+
mode: "search"
|
|
498
|
+
});
|
|
499
|
+
let candidateLimit = resultLimit;
|
|
500
|
+
const privateVisibilityCache = /* @__PURE__ */ new Map();
|
|
501
|
+
let candidates = await searchCandidates(candidateLimit);
|
|
502
|
+
let filtered = await orchestrator.filterPrivateSearchResults(
|
|
503
|
+
candidates,
|
|
504
|
+
namespaceFilter ? [namespaceFilter] : [],
|
|
505
|
+
false,
|
|
506
|
+
privateVisibilityCache
|
|
507
|
+
);
|
|
508
|
+
while (filtered.length < resultLimit && candidates.length >= candidateLimit && candidateLimit < MEMORY_SEARCH_CANDIDATE_CAP) {
|
|
509
|
+
const nextCandidateLimit = Math.min(
|
|
510
|
+
MEMORY_SEARCH_CANDIDATE_CAP,
|
|
511
|
+
Math.max(candidateLimit + 16, candidateLimit * 2)
|
|
512
|
+
);
|
|
513
|
+
if (nextCandidateLimit === candidateLimit) break;
|
|
514
|
+
const nextCandidates = await searchCandidates(nextCandidateLimit);
|
|
515
|
+
if (nextCandidates.length <= candidates.length) break;
|
|
516
|
+
candidateLimit = nextCandidateLimit;
|
|
517
|
+
candidates = nextCandidates;
|
|
518
|
+
filtered = await orchestrator.filterPrivateSearchResults(
|
|
519
|
+
candidates,
|
|
520
|
+
namespaceFilter ? [namespaceFilter] : [],
|
|
521
|
+
false,
|
|
522
|
+
privateVisibilityCache
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
filtered = filtered.slice(0, resultLimit);
|
|
526
|
+
if (filtered.length === 0) {
|
|
527
|
+
return toolResult2(`No memories found matching: "${query}"`);
|
|
528
|
+
}
|
|
529
|
+
const formatted = filtered.map((r, i) => {
|
|
530
|
+
const snippet = r.snippet ? r.snippet.slice(0, 800) : "(no preview)";
|
|
531
|
+
return `### [${i + 1}] ${r.path}
|
|
532
|
+
Score: ${r.score.toFixed(3)}
|
|
533
|
+
|
|
534
|
+
\`\`\`
|
|
535
|
+
${snippet}
|
|
536
|
+
\`\`\``;
|
|
537
|
+
}).join("\n\n");
|
|
538
|
+
return toolResult2(
|
|
539
|
+
`## Memory Search: "${query}"
|
|
540
|
+
|
|
541
|
+
${filtered.length} result(s)
|
|
542
|
+
|
|
543
|
+
${formatted}`
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
function buildLegacyMemorySearchToolForPublicShape(orchestrator) {
|
|
549
|
+
const legacy = buildLegacyMemorySearchTool(orchestrator);
|
|
550
|
+
return {
|
|
551
|
+
...legacy,
|
|
552
|
+
async execute(toolCallId, params) {
|
|
553
|
+
const filters = params.filters && typeof params.filters === "object" ? params.filters : void 0;
|
|
554
|
+
return legacy.execute(toolCallId, {
|
|
555
|
+
query: params.query,
|
|
556
|
+
...typeof params.limit === "number" ? { maxResults: params.limit } : {},
|
|
557
|
+
...typeof filters?.namespace === "string" ? { namespace: filters.namespace } : {},
|
|
558
|
+
...typeof filters?.collection === "string" ? { collection: filters.collection } : {}
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
function registerTools(api, orchestrator, hostRuntimeAgentId, reservedToolNames = []) {
|
|
454
564
|
const useDedicatedOpenClawMemoryTools = orchestrator.config.openclawToolsEnabled !== false;
|
|
455
565
|
const actionTypes = [
|
|
456
566
|
"store_episode",
|
|
@@ -551,103 +661,8 @@ Content: ${content}`
|
|
|
551
661
|
}
|
|
552
662
|
}
|
|
553
663
|
}
|
|
554
|
-
if (!useDedicatedOpenClawMemoryTools) {
|
|
555
|
-
api.registerTool(
|
|
556
|
-
{
|
|
557
|
-
name: "memory_search",
|
|
558
|
-
label: "Search Memory",
|
|
559
|
-
description: `Search local memory files using QMD's semantic index. Returns matching memories with snippets and relevance scores.
|
|
560
|
-
|
|
561
|
-
Returns: Matching memory entries ranked by relevance
|
|
562
|
-
Cost: Free (local index query)
|
|
563
|
-
Speed: Fast
|
|
564
|
-
|
|
565
|
-
Best for:
|
|
566
|
-
- Finding previously learned facts about the user
|
|
567
|
-
- Checking what you know about a topic
|
|
568
|
-
- Locating past decisions or corrections`,
|
|
569
|
-
parameters: Type2.Object({
|
|
570
|
-
query: Type2.String({
|
|
571
|
-
description: "Search query \u2014 keywords, phrases, or natural language"
|
|
572
|
-
}),
|
|
573
|
-
namespace: Type2.Optional(
|
|
574
|
-
Type2.String({
|
|
575
|
-
description: "Optional namespace filter. When set, only returns results under memoryDir/namespaces/<namespace>/ (default namespace uses legacy root)."
|
|
576
|
-
})
|
|
577
|
-
),
|
|
578
|
-
maxResults: Type2.Optional(
|
|
579
|
-
Type2.Number({
|
|
580
|
-
description: "Maximum results (default: 8)",
|
|
581
|
-
minimum: 1,
|
|
582
|
-
maximum: 50
|
|
583
|
-
})
|
|
584
|
-
),
|
|
585
|
-
collection: Type2.Optional(
|
|
586
|
-
Type2.String({
|
|
587
|
-
description: "QMD collection to search. Omit for memory collection, use 'global' for all collections."
|
|
588
|
-
})
|
|
589
|
-
)
|
|
590
|
-
}),
|
|
591
|
-
async execute(_toolCallId, params) {
|
|
592
|
-
const { query, maxResults, collection, namespace } = params;
|
|
593
|
-
const namespaceFilter = namespace && namespace.length > 0 ? namespace : void 0;
|
|
594
|
-
const resultLimit = normalizeMemorySearchResultLimit(maxResults);
|
|
595
|
-
const searchCandidates = async (limit) => collection === "global" && !namespaceFilter ? await orchestrator.qmd.searchGlobal(query, limit) : await orchestrator.searchAcrossNamespaces({
|
|
596
|
-
query,
|
|
597
|
-
namespaces: namespaceFilter ? [namespaceFilter] : void 0,
|
|
598
|
-
maxResults: limit,
|
|
599
|
-
mode: "search"
|
|
600
|
-
});
|
|
601
|
-
let candidateLimit = resultLimit;
|
|
602
|
-
const privateVisibilityCache = /* @__PURE__ */ new Map();
|
|
603
|
-
let candidates = await searchCandidates(candidateLimit);
|
|
604
|
-
let filtered = await orchestrator.filterPrivateSearchResults(
|
|
605
|
-
candidates,
|
|
606
|
-
namespaceFilter ? [namespaceFilter] : [],
|
|
607
|
-
false,
|
|
608
|
-
privateVisibilityCache
|
|
609
|
-
);
|
|
610
|
-
while (filtered.length < resultLimit && candidates.length >= candidateLimit && candidateLimit < MEMORY_SEARCH_CANDIDATE_CAP) {
|
|
611
|
-
const nextCandidateLimit = Math.min(
|
|
612
|
-
MEMORY_SEARCH_CANDIDATE_CAP,
|
|
613
|
-
Math.max(candidateLimit + 16, candidateLimit * 2)
|
|
614
|
-
);
|
|
615
|
-
if (nextCandidateLimit === candidateLimit) break;
|
|
616
|
-
const nextCandidates = await searchCandidates(nextCandidateLimit);
|
|
617
|
-
if (nextCandidates.length <= candidates.length) break;
|
|
618
|
-
candidateLimit = nextCandidateLimit;
|
|
619
|
-
candidates = nextCandidates;
|
|
620
|
-
filtered = await orchestrator.filterPrivateSearchResults(
|
|
621
|
-
candidates,
|
|
622
|
-
namespaceFilter ? [namespaceFilter] : [],
|
|
623
|
-
false,
|
|
624
|
-
privateVisibilityCache
|
|
625
|
-
);
|
|
626
|
-
}
|
|
627
|
-
filtered = filtered.slice(0, resultLimit);
|
|
628
|
-
if (filtered.length === 0) {
|
|
629
|
-
return toolResult2(`No memories found matching: "${query}"`);
|
|
630
|
-
}
|
|
631
|
-
const formatted = filtered.map((r, i) => {
|
|
632
|
-
const snippet = r.snippet ? r.snippet.slice(0, 800) : "(no preview)";
|
|
633
|
-
return `### [${i + 1}] ${r.path}
|
|
634
|
-
Score: ${r.score.toFixed(3)}
|
|
635
|
-
|
|
636
|
-
\`\`\`
|
|
637
|
-
${snippet}
|
|
638
|
-
\`\`\``;
|
|
639
|
-
}).join("\n\n");
|
|
640
|
-
return toolResult2(
|
|
641
|
-
`## Memory Search: "${query}"
|
|
642
|
-
|
|
643
|
-
${filtered.length} result(s)
|
|
644
|
-
|
|
645
|
-
${formatted}`
|
|
646
|
-
);
|
|
647
|
-
}
|
|
648
|
-
},
|
|
649
|
-
{ name: "memory_search" }
|
|
650
|
-
);
|
|
664
|
+
if (!useDedicatedOpenClawMemoryTools && !reservedToolNames.includes("memory_search")) {
|
|
665
|
+
api.registerTool(buildLegacyMemorySearchTool(orchestrator), { name: "memory_search" });
|
|
651
666
|
}
|
|
652
667
|
api.registerTool(
|
|
653
668
|
{
|
|
@@ -3026,8 +3041,8 @@ var EngramAccessService = class extends CoreEngramAccessService {
|
|
|
3026
3041
|
|
|
3027
3042
|
// ../../src/index.ts
|
|
3028
3043
|
import { EngramAccessHttpServer } from "@remnic/core/access-http";
|
|
3029
|
-
import
|
|
3030
|
-
import
|
|
3044
|
+
import path13 from "path";
|
|
3045
|
+
import os2 from "os";
|
|
3031
3046
|
import { createOpikExporter } from "@remnic/core/opik-exporter";
|
|
3032
3047
|
import { readEnvVar, resolveHomeDir as resolveHomeDir3 } from "@remnic/core/runtime/env";
|
|
3033
3048
|
import { displayErrorDetail } from "@remnic/core/runtime/better-sqlite";
|
|
@@ -5232,11 +5247,11 @@ async function processOpenClawFlushPlanFile(params) {
|
|
|
5232
5247
|
}
|
|
5233
5248
|
|
|
5234
5249
|
// src/delegate-runtime.ts
|
|
5235
|
-
import
|
|
5250
|
+
import path11 from "path";
|
|
5236
5251
|
import {
|
|
5237
5252
|
renderMemoryContextPrompt
|
|
5238
5253
|
} from "@remnic/core";
|
|
5239
|
-
import { log as
|
|
5254
|
+
import { log as log8 } from "@remnic/core/logger";
|
|
5240
5255
|
|
|
5241
5256
|
// src/delegate-authorization.ts
|
|
5242
5257
|
import { log as log2 } from "@remnic/core/logger";
|
|
@@ -5244,10 +5259,62 @@ import { log as log2 } from "@remnic/core/logger";
|
|
|
5244
5259
|
// src/bridge.ts
|
|
5245
5260
|
import fs3 from "fs";
|
|
5246
5261
|
import path6 from "path";
|
|
5247
|
-
|
|
5262
|
+
|
|
5263
|
+
// src/bridge-daemon-host.ts
|
|
5264
|
+
import { isIP, isIPv6 } from "net";
|
|
5265
|
+
import os from "os";
|
|
5266
|
+
import { isLoopbackHost } from "@remnic/core/runtime/http-transport.js";
|
|
5267
|
+
var LOOPBACK_V4 = "127.0.0.1";
|
|
5268
|
+
function isLoopbackDaemonHost(host) {
|
|
5269
|
+
const normalized = host.trim().toLowerCase().replace(/^\[/, "").replace(/\]$/, "");
|
|
5270
|
+
if (loopbackForSameHost(normalized) !== void 0) return true;
|
|
5271
|
+
return isLoopbackHost(normalized);
|
|
5272
|
+
}
|
|
5273
|
+
function canonicalIPv6(value) {
|
|
5274
|
+
if (!isIPv6(value)) return void 0;
|
|
5275
|
+
try {
|
|
5276
|
+
return new URL(`http://[${value}]`).hostname.replace(/^\[/, "").replace(/\]$/, "");
|
|
5277
|
+
} catch {
|
|
5278
|
+
return value;
|
|
5279
|
+
}
|
|
5280
|
+
}
|
|
5281
|
+
function loopbackForSameHost(host) {
|
|
5282
|
+
const normalized = host.trim().toLowerCase().replace(/^\[/, "").replace(/\]$/, "");
|
|
5283
|
+
if (normalized === "0.0.0.0") return LOOPBACK_V4;
|
|
5284
|
+
const v6 = canonicalIPv6(normalized);
|
|
5285
|
+
if (v6 === "::") return "::1";
|
|
5286
|
+
const local = localInterfaceFamily(v6 ?? normalized);
|
|
5287
|
+
if (local === void 0) return void 0;
|
|
5288
|
+
return local === "IPv4" ? LOOPBACK_V4 : "::1";
|
|
5289
|
+
}
|
|
5290
|
+
function sameHostDialFallback(host) {
|
|
5291
|
+
const normalized = host.trim().toLowerCase().replace(/^\[/, "").replace(/\]$/, "");
|
|
5292
|
+
return localInterfaceFamily(canonicalIPv6(normalized) ?? normalized) === void 0 ? void 0 : normalized;
|
|
5293
|
+
}
|
|
5294
|
+
function localInterfaceFamily(address) {
|
|
5295
|
+
if (isIP(address) === 0) return void 0;
|
|
5296
|
+
for (const entries of Object.values(os.networkInterfaces())) {
|
|
5297
|
+
for (const entry of entries ?? []) {
|
|
5298
|
+
if (entry.internal) continue;
|
|
5299
|
+
if (entry.family === "IPv6") {
|
|
5300
|
+
if (canonicalIPv6(entry.address.replace(/%.*$/, "")) === address) return "IPv6";
|
|
5301
|
+
continue;
|
|
5302
|
+
}
|
|
5303
|
+
if (entry.address === address || canonicalIPv6(`::ffff:${entry.address}`) === address) {
|
|
5304
|
+
return "IPv4";
|
|
5305
|
+
}
|
|
5306
|
+
}
|
|
5307
|
+
}
|
|
5308
|
+
return void 0;
|
|
5309
|
+
}
|
|
5310
|
+
function normalizeDaemonHost(value) {
|
|
5311
|
+
const match = value.trim().match(/^\[(.+)\]$/);
|
|
5312
|
+
return match ? match[1] : value.trim();
|
|
5313
|
+
}
|
|
5314
|
+
|
|
5315
|
+
// src/bridge.ts
|
|
5248
5316
|
import { Worker } from "worker_threads";
|
|
5249
5317
|
import { configPathCandidates, readCompatEnv } from "@remnic/core";
|
|
5250
|
-
import { isLoopbackHost } from "@remnic/core/runtime/http-transport.js";
|
|
5251
5318
|
|
|
5252
5319
|
// src/bridge-health-worker.ts
|
|
5253
5320
|
function runHealthWorker(request, data) {
|
|
@@ -6047,29 +6114,6 @@ function isDaemonRunning() {
|
|
|
6047
6114
|
}
|
|
6048
6115
|
return false;
|
|
6049
6116
|
}
|
|
6050
|
-
function isLoopbackDaemonHost(host) {
|
|
6051
|
-
const normalized = host.trim().toLowerCase().replace(/^\[/, "").replace(/\]$/, "");
|
|
6052
|
-
if (loopbackForWildcardBind(normalized) !== void 0) return true;
|
|
6053
|
-
return isLoopbackHost(normalized);
|
|
6054
|
-
}
|
|
6055
|
-
function canonicalIPv6(value) {
|
|
6056
|
-
if (!isIPv6(value)) return void 0;
|
|
6057
|
-
try {
|
|
6058
|
-
return new URL(`http://[${value}]`).hostname.replace(/^\[/, "").replace(/\]$/, "");
|
|
6059
|
-
} catch {
|
|
6060
|
-
return value;
|
|
6061
|
-
}
|
|
6062
|
-
}
|
|
6063
|
-
function loopbackForWildcardBind(host) {
|
|
6064
|
-
const normalized = host.trim().toLowerCase().replace(/^\[/, "").replace(/\]$/, "");
|
|
6065
|
-
if (normalized === "0.0.0.0") return DEFAULT_HOST;
|
|
6066
|
-
if (canonicalIPv6(normalized) === "::") return "::1";
|
|
6067
|
-
return void 0;
|
|
6068
|
-
}
|
|
6069
|
-
function normalizeDaemonHost(value) {
|
|
6070
|
-
const match = value.trim().match(/^\[(.+)\]$/);
|
|
6071
|
-
return match ? match[1] : value.trim();
|
|
6072
|
-
}
|
|
6073
6117
|
function coerceDaemonPort(value) {
|
|
6074
6118
|
const parsed = typeof value === "string" && value.trim() !== "" ? Number(value.trim()) : value;
|
|
6075
6119
|
return typeof parsed === "number" && Number.isInteger(parsed) && parsed > 0 && parsed <= 65535 ? parsed : void 0;
|
|
@@ -6158,7 +6202,7 @@ function shouldProbeDaemonHealth(host) {
|
|
|
6158
6202
|
}
|
|
6159
6203
|
function readDaemonHost() {
|
|
6160
6204
|
const resolved = readConfiguredDaemonHost();
|
|
6161
|
-
return
|
|
6205
|
+
return loopbackForSameHost(resolved) ?? resolved;
|
|
6162
6206
|
}
|
|
6163
6207
|
function readDaemonServerConfig() {
|
|
6164
6208
|
for (const candidate of configPathCandidates()) {
|
|
@@ -6196,39 +6240,40 @@ function daemonEndpointCandidates(unitExists) {
|
|
|
6196
6240
|
const resolvedHost = normalizeDaemonHost(
|
|
6197
6241
|
envHost !== void 0 && envHost.trim() !== "" ? envHost : host ?? DEFAULT_HOST
|
|
6198
6242
|
);
|
|
6199
|
-
const dialHost =
|
|
6243
|
+
const dialHost = loopbackForSameHost(resolvedHost) ?? resolvedHost;
|
|
6244
|
+
const dialFallback = sameHostDialFallback(resolvedHost);
|
|
6200
6245
|
const dialPort = envPort ?? port ?? DEFAULT_PORT;
|
|
6201
6246
|
const token = authTokenOverride ?? loadDaemonAuth(configPath).token;
|
|
6202
6247
|
const configToken = configPath === void 0 ? void 0 : readServerBlock(configPath)?.authToken;
|
|
6203
6248
|
const fallbackToken = configToken !== void 0 && configToken !== token ? configToken : void 0;
|
|
6204
|
-
|
|
6205
|
-
(
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6249
|
+
for (const candidateHost of dialFallback === void 0 ? [dialHost] : [dialHost, dialFallback]) {
|
|
6250
|
+
if (candidates.some(
|
|
6251
|
+
(c) => c.host === candidateHost && c.port === dialPort && c.token === token && // The BOUND credential is part of the identity too: when a gateway
|
|
6252
|
+
// token wins for both, two configs on one endpoint resolve the same
|
|
6253
|
+
// primary token but carry different fallbacks, and dropping the
|
|
6254
|
+
// second would leave the daemon's real credential untried.
|
|
6255
|
+
c.fallbackToken === fallbackToken && // So is the UNIT the credential is re-read from per request: two
|
|
6256
|
+
// units can agree today and diverge on the next rotation.
|
|
6257
|
+
c.authTokenUnit?.unitPath === authTokenUnit?.unitPath && // And so is the CONFIG, for the same reason: `daemonConfigPath` is
|
|
6258
|
+
// re-read per request, so collapsing two configs that agree today
|
|
6259
|
+
// would keep sending the retained one's token after the other
|
|
6260
|
+
// rotates.
|
|
6261
|
+
c.configPath === configPath
|
|
6262
|
+
)) {
|
|
6263
|
+
continue;
|
|
6264
|
+
}
|
|
6265
|
+
candidates.push({
|
|
6266
|
+
host: candidateHost,
|
|
6267
|
+
port: dialPort,
|
|
6268
|
+
configPath,
|
|
6269
|
+
token,
|
|
6270
|
+
aliasGroup: `${resolvedHost}\0${dialPort}\0${configPath ?? ""}\0${token}`,
|
|
6271
|
+
...authTokenOverride === void 0 ? {} : { authTokenOverride },
|
|
6272
|
+
...authTokenUnit === void 0 ? {} : { authTokenUnit },
|
|
6273
|
+
...fallbackToken === void 0 ? {} : { fallbackToken }
|
|
6274
|
+
});
|
|
6218
6275
|
}
|
|
6219
|
-
candidates.push({
|
|
6220
|
-
host: dialHost,
|
|
6221
|
-
port: dialPort,
|
|
6222
|
-
configPath,
|
|
6223
|
-
token,
|
|
6224
|
-
...authTokenOverride === void 0 ? {} : { authTokenOverride },
|
|
6225
|
-
...authTokenUnit === void 0 ? {} : { authTokenUnit },
|
|
6226
|
-
...fallbackToken === void 0 ? {} : { fallbackToken }
|
|
6227
|
-
});
|
|
6228
6276
|
};
|
|
6229
|
-
if (envHost !== void 0 && envHost.trim() !== "" || envPort !== void 0) {
|
|
6230
|
-
add(void 0, void 0);
|
|
6231
|
-
}
|
|
6232
6277
|
const configOrder = configPathCandidates();
|
|
6233
6278
|
const envConfigPath = readCompatEnv("REMNIC_CONFIG_PATH", "ENGRAM_CONFIG_PATH") ? configOrder[0] : void 0;
|
|
6234
6279
|
const addConfigCandidate = (candidate) => {
|
|
@@ -6294,14 +6339,16 @@ function detectDaemonBridgeMode(options) {
|
|
|
6294
6339
|
}
|
|
6295
6340
|
probeable.push(candidate);
|
|
6296
6341
|
}
|
|
6297
|
-
|
|
6342
|
+
const groupDeadlines = /* @__PURE__ */ new Map();
|
|
6343
|
+
const totalGroups = new Set(probeable.map((candidate) => candidate.aliasGroup)).size;
|
|
6298
6344
|
for (const {
|
|
6299
6345
|
host: daemonHost,
|
|
6300
6346
|
port: daemonPort,
|
|
6301
6347
|
configPath,
|
|
6302
6348
|
authTokenOverride,
|
|
6303
6349
|
authTokenUnit,
|
|
6304
|
-
fallbackToken
|
|
6350
|
+
fallbackToken,
|
|
6351
|
+
aliasGroup
|
|
6305
6352
|
} of probeable) {
|
|
6306
6353
|
const remainingMs = deadline - Date.now();
|
|
6307
6354
|
if (remainingMs <= 0) {
|
|
@@ -6310,12 +6357,15 @@ function detectDaemonBridgeMode(options) {
|
|
|
6310
6357
|
);
|
|
6311
6358
|
break;
|
|
6312
6359
|
}
|
|
6313
|
-
const
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
6360
|
+
const started = groupDeadlines.get(aliasGroup);
|
|
6361
|
+
if (started === void 0) {
|
|
6362
|
+
const perGroupMs = Math.max(
|
|
6363
|
+
1,
|
|
6364
|
+
Math.ceil(remainingMs / Math.max(1, totalGroups - groupDeadlines.size))
|
|
6365
|
+
);
|
|
6366
|
+
groupDeadlines.set(aliasGroup, Date.now() + Math.min(remainingMs, perGroupMs));
|
|
6367
|
+
}
|
|
6368
|
+
const candidateDeadline = groupDeadlines.get(aliasGroup) ?? Date.now();
|
|
6319
6369
|
const firstAttemptMs = candidateDeadline - Date.now();
|
|
6320
6370
|
if (firstAttemptMs <= 0) {
|
|
6321
6371
|
options.onSkip?.(
|
|
@@ -6421,10 +6471,13 @@ function resolveBridgeMode(configBridgeMode, options = {}) {
|
|
|
6421
6471
|
});
|
|
6422
6472
|
}
|
|
6423
6473
|
const selectedConfig = selectedDaemonConfigPath();
|
|
6474
|
+
const configuredHost = readConfiguredDaemonHost();
|
|
6475
|
+
const fallbackHost = sameHostDialFallback(configuredHost);
|
|
6424
6476
|
return {
|
|
6425
6477
|
mode: requested,
|
|
6426
|
-
daemonHost:
|
|
6478
|
+
daemonHost: loopbackForSameHost(configuredHost) ?? configuredHost,
|
|
6427
6479
|
daemonPort: readDaemonPort(),
|
|
6480
|
+
...fallbackHost === void 0 ? {} : { daemonHostFallback: fallbackHost },
|
|
6428
6481
|
...selectedConfig === void 0 ? {} : { daemonConfigPath: selectedConfig }
|
|
6429
6482
|
};
|
|
6430
6483
|
}
|
|
@@ -6603,114 +6656,234 @@ async function probeDelegateAuthorization(target, namespace = "", operations = D
|
|
|
6603
6656
|
return { state: "unavailable", tokenSource: auth.source };
|
|
6604
6657
|
}
|
|
6605
6658
|
|
|
6606
|
-
// src/delegate-
|
|
6659
|
+
// src/delegate-namespace-bindings.ts
|
|
6660
|
+
import path7 from "path";
|
|
6661
|
+
import { log as log3 } from "@remnic/core/logger";
|
|
6607
6662
|
import {
|
|
6608
6663
|
SESSION_NAMESPACE_BINDING_MAX_ENTRIES,
|
|
6609
|
-
SESSION_NAMESPACE_BINDING_MAX_NAMESPACES
|
|
6664
|
+
SESSION_NAMESPACE_BINDING_MAX_NAMESPACES,
|
|
6610
6665
|
createFileSessionNamespaceBindingStore
|
|
6611
6666
|
} from "@remnic/core/session-namespace-bindings";
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
async function withNamespace(namespace, body, resolveScopedNamespace) {
|
|
6619
|
-
const scoped = await resolveScopedNamespace(namespace || void 0);
|
|
6620
|
-
return scoped === void 0 ? body : { ...body, namespace: scoped };
|
|
6621
|
-
}
|
|
6622
|
-
function explicitSessionNamespaceFrom(sessionKey, event, ctx) {
|
|
6623
|
-
const eventSessionKey = typeof event.sessionKey === "string" ? event.sessionKey : void 0;
|
|
6624
|
-
const ctxSessionKey = typeof ctx.sessionKey === "string" ? ctx.sessionKey : void 0;
|
|
6625
|
-
const sources = eventSessionKey === sessionKey ? [event, ctx] : ctxSessionKey === sessionKey ? [ctx, event] : [ctx, event];
|
|
6626
|
-
for (const source of sources) {
|
|
6627
|
-
const sourceSessionKey = typeof source.sessionKey === "string" ? source.sessionKey : void 0;
|
|
6628
|
-
if (sourceSessionKey !== sessionKey) continue;
|
|
6629
|
-
const runtime = source.runtime;
|
|
6630
|
-
if (typeof runtime !== "object" || runtime === null) continue;
|
|
6631
|
-
const agent = runtime.agent;
|
|
6632
|
-
if (typeof agent !== "object" || agent === null) continue;
|
|
6633
|
-
const session = agent.session;
|
|
6634
|
-
if (typeof session !== "object" || session === null) continue;
|
|
6635
|
-
const namespace = session.namespace;
|
|
6636
|
-
if (namespace !== void 0 && typeof namespace !== "string") {
|
|
6637
|
-
throw new Error("delegate session namespace metadata must be a string");
|
|
6638
|
-
}
|
|
6639
|
-
return { namespace: typeof namespace === "string" ? namespace.trim() || void 0 : void 0 };
|
|
6640
|
-
}
|
|
6641
|
-
return void 0;
|
|
6642
|
-
}
|
|
6643
|
-
async function rememberedNamespacesFor(sessionKey, namespaceBindings) {
|
|
6644
|
-
return namespaceBindings.namespacesFor(sessionKey);
|
|
6645
|
-
}
|
|
6646
|
-
async function rememberNamespace(sessionKey, namespace, namespaceBindings) {
|
|
6647
|
-
if (namespace.length > SESSION_NAMESPACE_BINDING_MAX_NAMESPACE_LENGTH) {
|
|
6648
|
-
throw new Error(
|
|
6649
|
-
`delegate session namespace exceeds the daemon limit of ${SESSION_NAMESPACE_BINDING_MAX_NAMESPACE_LENGTH} characters`
|
|
6650
|
-
);
|
|
6651
|
-
}
|
|
6652
|
-
try {
|
|
6653
|
-
await namespaceBindings.remember(sessionKey, namespace);
|
|
6654
|
-
} catch (err) {
|
|
6655
|
-
log3.warn(`delegate namespace binding persistence failed: ${String(err)}`);
|
|
6656
|
-
throw err;
|
|
6657
|
-
}
|
|
6658
|
-
}
|
|
6659
|
-
async function sessionNamespaceFrom(sessionKey, event, ctx, fallback, namespaceBindings) {
|
|
6660
|
-
const explicit = explicitSessionNamespaceFrom(sessionKey, event, ctx);
|
|
6661
|
-
if (explicit !== void 0) {
|
|
6662
|
-
await rememberNamespace(sessionKey, explicit.namespace ?? "", namespaceBindings);
|
|
6663
|
-
return explicit.namespace;
|
|
6664
|
-
}
|
|
6665
|
-
const remembered = await rememberedNamespacesFor(sessionKey, namespaceBindings);
|
|
6666
|
-
return remembered.length > 0 ? remembered.at(-1) || void 0 : fallback.trim() || void 0;
|
|
6667
|
-
}
|
|
6668
|
-
async function lifecycleSessionNamespacesFrom(sessionKey, event, ctx, fallback, namespaceBindings) {
|
|
6669
|
-
const explicit = explicitSessionNamespaceFrom(sessionKey, event, ctx);
|
|
6670
|
-
if (explicit !== void 0) {
|
|
6671
|
-
await rememberNamespace(sessionKey, explicit.namespace ?? "", namespaceBindings);
|
|
6672
|
-
}
|
|
6673
|
-
const remembered = await rememberedNamespacesFor(sessionKey, namespaceBindings);
|
|
6674
|
-
if (explicit !== void 0) {
|
|
6675
|
-
const explicitNamespace = explicit.namespace ?? "";
|
|
6676
|
-
const namespaces = remembered.includes(explicitNamespace) ? remembered : [...remembered, explicitNamespace];
|
|
6677
|
-
return namespaces.map((namespace) => namespace || void 0);
|
|
6667
|
+
var delegateNamespaceMigrationChains = /* @__PURE__ */ new Map();
|
|
6668
|
+
var queueDelegateNamespaceMigration = (bindingPath, sessionKey, operation) => {
|
|
6669
|
+
let sessionChains = delegateNamespaceMigrationChains.get(bindingPath);
|
|
6670
|
+
if (sessionChains === void 0) {
|
|
6671
|
+
sessionChains = /* @__PURE__ */ new Map();
|
|
6672
|
+
delegateNamespaceMigrationChains.set(bindingPath, sessionChains);
|
|
6678
6673
|
}
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6674
|
+
const prior = sessionChains.get(sessionKey) ?? Promise.resolve();
|
|
6675
|
+
const run = prior.catch(() => void 0).then(operation);
|
|
6676
|
+
const settled = run.then(
|
|
6677
|
+
() => void 0,
|
|
6678
|
+
() => void 0
|
|
6679
|
+
);
|
|
6680
|
+
sessionChains.set(sessionKey, settled);
|
|
6681
|
+
void settled.then(() => {
|
|
6682
|
+
if (sessionChains?.get(sessionKey) !== settled) return;
|
|
6683
|
+
sessionChains.delete(sessionKey);
|
|
6684
|
+
if (sessionChains.size === 0 && delegateNamespaceMigrationChains.get(bindingPath) === sessionChains) {
|
|
6685
|
+
delegateNamespaceMigrationChains.delete(bindingPath);
|
|
6686
|
+
}
|
|
6687
|
+
});
|
|
6688
|
+
return run;
|
|
6689
|
+
};
|
|
6690
|
+
function createDelegateNamespaceBindingStore(memoryDir, serviceId, isLegacyAdapterActive) {
|
|
6691
|
+
const bindingPath = (pluginId) => path7.join(memoryDir, "state", "plugins", pluginId, "session-namespace-bindings.json");
|
|
6692
|
+
const primaryPath = bindingPath(serviceId);
|
|
6693
|
+
const primary = createFileSessionNamespaceBindingStore(primaryPath);
|
|
6694
|
+
if (serviceId !== REMNIC_OPENCLAW_PLUGIN_ID) return primary;
|
|
6695
|
+
const legacy = createFileSessionNamespaceBindingStore(bindingPath(REMNIC_OPENCLAW_LEGACY_PLUGIN_ID));
|
|
6696
|
+
const migratedLegacySessions = /* @__PURE__ */ new Set();
|
|
6697
|
+
const rememberMigratedLegacySession = (sessionKey) => {
|
|
6698
|
+
if (migratedLegacySessions.has(sessionKey)) return;
|
|
6699
|
+
migratedLegacySessions.add(sessionKey);
|
|
6700
|
+
while (migratedLegacySessions.size > SESSION_NAMESPACE_BINDING_MAX_ENTRIES) {
|
|
6701
|
+
const oldest = migratedLegacySessions.values().next().value;
|
|
6702
|
+
if (oldest === void 0) return;
|
|
6703
|
+
migratedLegacySessions.delete(oldest);
|
|
6704
|
+
}
|
|
6705
|
+
};
|
|
6706
|
+
const queueSessionMigration = (sessionKey, operation) => queueDelegateNamespaceMigration(primaryPath, sessionKey, operation);
|
|
6707
|
+
const readLegacyNamespaces = async (sessionKey, current) => {
|
|
6708
|
+
if (!isLegacyAdapterActive() && migratedLegacySessions.has(sessionKey)) return [];
|
|
6709
|
+
try {
|
|
6710
|
+
const previous = await legacy.namespacesFor(sessionKey);
|
|
6711
|
+
if (previous.length === 0) rememberMigratedLegacySession(sessionKey);
|
|
6712
|
+
return previous;
|
|
6713
|
+
} catch (err) {
|
|
6714
|
+
if (current.length > 0) {
|
|
6715
|
+
log3.warn(`[${serviceId}] delegate legacy namespace read failed; using canonical bindings: ${String(err)}`);
|
|
6716
|
+
return [];
|
|
6717
|
+
}
|
|
6718
|
+
throw err;
|
|
6719
|
+
}
|
|
6720
|
+
};
|
|
6721
|
+
const mergeNamespaceHistory = (current, previous) => {
|
|
6722
|
+
const merged = [];
|
|
6723
|
+
for (const remembered of [...previous, ...current]) {
|
|
6724
|
+
const existing = merged.indexOf(remembered);
|
|
6725
|
+
if (existing >= 0) merged.splice(existing, 1);
|
|
6726
|
+
merged.push(remembered);
|
|
6727
|
+
}
|
|
6728
|
+
return merged.slice(-SESSION_NAMESPACE_BINDING_MAX_NAMESPACES);
|
|
6729
|
+
};
|
|
6730
|
+
const persistNamespaceHistory = async (store, sessionKey, namespaces) => {
|
|
6731
|
+
if (store.replace !== void 0) {
|
|
6732
|
+
await store.replace(sessionKey, namespaces);
|
|
6733
|
+
return;
|
|
6734
|
+
}
|
|
6735
|
+
for (const namespace of namespaces) {
|
|
6736
|
+
await store.remember(sessionKey, namespace);
|
|
6737
|
+
}
|
|
6738
|
+
};
|
|
6739
|
+
const completeLegacyMigration = async (sessionKey) => {
|
|
6740
|
+
if (!isLegacyAdapterActive()) {
|
|
6741
|
+
try {
|
|
6742
|
+
await legacy.replace?.(sessionKey, []);
|
|
6743
|
+
} catch (err) {
|
|
6744
|
+
log3.warn(`[${serviceId}] delegate legacy namespace cleanup failed: ${String(err)}`);
|
|
6745
|
+
}
|
|
6746
|
+
}
|
|
6747
|
+
rememberMigratedLegacySession(sessionKey);
|
|
6748
|
+
};
|
|
6749
|
+
return {
|
|
6750
|
+
async namespacesFor(sessionKey) {
|
|
6751
|
+
return queueSessionMigration(sessionKey, async () => {
|
|
6752
|
+
const current = await primary.namespacesFor(sessionKey);
|
|
6753
|
+
const previous = await readLegacyNamespaces(sessionKey, current);
|
|
6754
|
+
if (previous.length === 0) return current;
|
|
6755
|
+
const merged = mergeNamespaceHistory(current, previous);
|
|
6756
|
+
const hasMissingLegacy = previous.some((remembered) => !current.includes(remembered));
|
|
6757
|
+
if (!hasMissingLegacy) {
|
|
6758
|
+
await completeLegacyMigration(sessionKey);
|
|
6759
|
+
return current;
|
|
6760
|
+
}
|
|
6761
|
+
try {
|
|
6762
|
+
await persistNamespaceHistory(primary, sessionKey, merged);
|
|
6763
|
+
await completeLegacyMigration(sessionKey);
|
|
6764
|
+
} catch (err) {
|
|
6765
|
+
log3.warn(`[${serviceId}] delegate namespace migration failed: ${String(err)}`);
|
|
6766
|
+
}
|
|
6767
|
+
return merged;
|
|
6768
|
+
});
|
|
6769
|
+
},
|
|
6770
|
+
async remember(sessionKey, namespace) {
|
|
6771
|
+
return queueSessionMigration(sessionKey, async () => {
|
|
6772
|
+
const current = await primary.namespacesFor(sessionKey);
|
|
6773
|
+
const previous = await readLegacyNamespaces(sessionKey, current);
|
|
6774
|
+
if (previous.length === 0) {
|
|
6775
|
+
await primary.remember(sessionKey, namespace);
|
|
6776
|
+
return;
|
|
6777
|
+
}
|
|
6778
|
+
const merged = mergeNamespaceHistory([...current, namespace], previous);
|
|
6779
|
+
await persistNamespaceHistory(primary, sessionKey, merged);
|
|
6780
|
+
await completeLegacyMigration(sessionKey);
|
|
6781
|
+
});
|
|
6782
|
+
}
|
|
6783
|
+
};
|
|
6784
|
+
}
|
|
6785
|
+
|
|
6786
|
+
// src/delegate-namespaces.ts
|
|
6787
|
+
import {
|
|
6788
|
+
SESSION_NAMESPACE_BINDING_MAX_NAMESPACE_LENGTH
|
|
6789
|
+
} from "@remnic/core/session-namespace-bindings";
|
|
6790
|
+
import { log as log4 } from "@remnic/core/logger";
|
|
6791
|
+
async function withNamespace(namespace, body, resolveScopedNamespace) {
|
|
6792
|
+
const scoped = await resolveScopedNamespace(namespace || void 0);
|
|
6793
|
+
return scoped === void 0 ? body : { ...body, namespace: scoped };
|
|
6794
|
+
}
|
|
6795
|
+
function explicitSessionNamespaceFrom(sessionKey, event, ctx) {
|
|
6796
|
+
const eventSessionKey = typeof event.sessionKey === "string" ? event.sessionKey : void 0;
|
|
6797
|
+
const ctxSessionKey = typeof ctx.sessionKey === "string" ? ctx.sessionKey : void 0;
|
|
6798
|
+
const sources = eventSessionKey === sessionKey ? [event, ctx] : ctxSessionKey === sessionKey ? [ctx, event] : [ctx, event];
|
|
6799
|
+
for (const source of sources) {
|
|
6800
|
+
const sourceSessionKey = typeof source.sessionKey === "string" ? source.sessionKey : void 0;
|
|
6801
|
+
if (sourceSessionKey !== sessionKey) continue;
|
|
6802
|
+
const runtime = source.runtime;
|
|
6803
|
+
if (typeof runtime !== "object" || runtime === null) continue;
|
|
6804
|
+
const agent = runtime.agent;
|
|
6805
|
+
if (typeof agent !== "object" || agent === null) continue;
|
|
6806
|
+
const session = agent.session;
|
|
6807
|
+
if (typeof session !== "object" || session === null) continue;
|
|
6808
|
+
const namespace = session.namespace;
|
|
6809
|
+
if (namespace !== void 0 && typeof namespace !== "string") {
|
|
6810
|
+
throw new Error("delegate session namespace metadata must be a string");
|
|
6811
|
+
}
|
|
6812
|
+
return { namespace: typeof namespace === "string" ? namespace.trim() || void 0 : void 0 };
|
|
6813
|
+
}
|
|
6814
|
+
return void 0;
|
|
6815
|
+
}
|
|
6816
|
+
async function rememberedNamespacesFor(sessionKey, namespaceBindings) {
|
|
6817
|
+
return namespaceBindings.namespacesFor(sessionKey);
|
|
6818
|
+
}
|
|
6819
|
+
async function rememberNamespace(sessionKey, namespace, namespaceBindings) {
|
|
6820
|
+
if (namespace.length > SESSION_NAMESPACE_BINDING_MAX_NAMESPACE_LENGTH) {
|
|
6821
|
+
throw new Error(
|
|
6822
|
+
`delegate session namespace exceeds the daemon limit of ${SESSION_NAMESPACE_BINDING_MAX_NAMESPACE_LENGTH} characters`
|
|
6823
|
+
);
|
|
6824
|
+
}
|
|
6825
|
+
try {
|
|
6826
|
+
await namespaceBindings.remember(sessionKey, namespace);
|
|
6827
|
+
} catch (err) {
|
|
6828
|
+
log4.warn(`delegate namespace binding persistence failed: ${String(err)}`);
|
|
6829
|
+
throw err;
|
|
6830
|
+
}
|
|
6831
|
+
}
|
|
6832
|
+
async function sessionNamespaceFrom(sessionKey, event, ctx, fallback, namespaceBindings) {
|
|
6833
|
+
const explicit = explicitSessionNamespaceFrom(sessionKey, event, ctx);
|
|
6834
|
+
if (explicit !== void 0) {
|
|
6835
|
+
await rememberNamespace(sessionKey, explicit.namespace ?? "", namespaceBindings);
|
|
6836
|
+
return explicit.namespace;
|
|
6837
|
+
}
|
|
6838
|
+
const remembered = await rememberedNamespacesFor(sessionKey, namespaceBindings);
|
|
6839
|
+
return remembered.length > 0 ? remembered.at(-1) || void 0 : fallback.trim() || void 0;
|
|
6840
|
+
}
|
|
6841
|
+
async function lifecycleSessionNamespacesFrom(sessionKey, event, ctx, fallback, namespaceBindings) {
|
|
6842
|
+
const explicit = explicitSessionNamespaceFrom(sessionKey, event, ctx);
|
|
6843
|
+
if (explicit !== void 0) {
|
|
6844
|
+
await rememberNamespace(sessionKey, explicit.namespace ?? "", namespaceBindings);
|
|
6845
|
+
}
|
|
6846
|
+
const remembered = await rememberedNamespacesFor(sessionKey, namespaceBindings);
|
|
6847
|
+
if (explicit !== void 0) {
|
|
6848
|
+
const explicitNamespace = explicit.namespace ?? "";
|
|
6849
|
+
const namespaces = remembered.includes(explicitNamespace) ? remembered : [...remembered, explicitNamespace];
|
|
6850
|
+
return namespaces.map((namespace) => namespace || void 0);
|
|
6851
|
+
}
|
|
6852
|
+
if (remembered.length > 0) return remembered.map((namespace) => namespace || void 0);
|
|
6853
|
+
return [fallback.trim() || void 0];
|
|
6854
|
+
}
|
|
6855
|
+
|
|
6856
|
+
// src/delegate-daemon-target.ts
|
|
6857
|
+
function daemonTargetFor(bridge) {
|
|
6858
|
+
const invalidatedCredentials = /* @__PURE__ */ new Set();
|
|
6859
|
+
const invalidated = (token) => invalidatedCredentials.has(`daemon configuration\0${token}`);
|
|
6860
|
+
return {
|
|
6861
|
+
host: bridge.daemonHost,
|
|
6862
|
+
port: bridge.daemonPort,
|
|
6863
|
+
invalidateAuthToken: (auth) => {
|
|
6864
|
+
invalidatedCredentials.add(`${auth.source}\0${auth.token}`);
|
|
6865
|
+
},
|
|
6866
|
+
resolveAuthToken: () => {
|
|
6867
|
+
if (bridge.daemonAuthTokenOverride !== void 0 && !invalidated(bridge.daemonAuthTokenOverride)) {
|
|
6868
|
+
const unit = bridge.daemonAuthUnit === void 0 ? void 0 : readUnitAuthToken(bridge.daemonAuthUnit);
|
|
6869
|
+
if (unit?.readable === true && unit.token !== void 0 && !invalidated(unit.token)) {
|
|
6870
|
+
return { token: unit.token, source: "daemon configuration" };
|
|
6871
|
+
}
|
|
6872
|
+
if ((unit === void 0 || unit.readable === false) && !invalidated(bridge.daemonAuthTokenOverride)) {
|
|
6873
|
+
return { token: bridge.daemonAuthTokenOverride, source: "daemon configuration" };
|
|
6874
|
+
}
|
|
6875
|
+
if (bridge.daemonConfigPath !== void 0) {
|
|
6876
|
+
const configToken = readDaemonConfigAuthToken(bridge.daemonConfigPath);
|
|
6877
|
+
if (configToken !== void 0 && !invalidated(configToken)) {
|
|
6878
|
+
return { token: configToken, source: "daemon configuration" };
|
|
6879
|
+
}
|
|
6880
|
+
}
|
|
6881
|
+
}
|
|
6882
|
+
if (bridge.daemonAuthPrefersConfig && bridge.daemonConfigPath !== void 0) {
|
|
6883
|
+
const configToken = readDaemonConfigAuthToken(bridge.daemonConfigPath);
|
|
6884
|
+
if (configToken !== void 0 && !invalidated(configToken)) {
|
|
6885
|
+
return { token: configToken, source: "daemon configuration" };
|
|
6886
|
+
}
|
|
6714
6887
|
}
|
|
6715
6888
|
return loadDaemonAuth(bridge.daemonConfigPath, invalidatedCredentials);
|
|
6716
6889
|
}
|
|
@@ -6720,14 +6893,14 @@ function daemonTargetFor(bridge) {
|
|
|
6720
6893
|
// src/delegate-flush-plan-ingest.ts
|
|
6721
6894
|
import { constants as constants2 } from "fs";
|
|
6722
6895
|
import { lstat as lstat4, open as open2, rename as rename2, unlink as unlink2, writeFile as writeFile2 } from "fs/promises";
|
|
6723
|
-
import
|
|
6724
|
-
import { log as
|
|
6896
|
+
import path9 from "path";
|
|
6897
|
+
import { log as log5 } from "@remnic/core/logger";
|
|
6725
6898
|
import { withHeldFileLock } from "@remnic/core/utils/serialize-mutations";
|
|
6726
6899
|
|
|
6727
6900
|
// src/delegate-flush-plan-directory.ts
|
|
6728
6901
|
import { constants } from "fs";
|
|
6729
6902
|
import { lstat as lstat3, open, stat as stat2 } from "fs/promises";
|
|
6730
|
-
import
|
|
6903
|
+
import path8 from "path";
|
|
6731
6904
|
function buildSnapshotPaths(planPath) {
|
|
6732
6905
|
return {
|
|
6733
6906
|
plan: planPath,
|
|
@@ -6746,7 +6919,7 @@ function descriptorDirectoryRoot(platform = process.platform) {
|
|
|
6746
6919
|
async function pinSnapshotDirectory(paths, options = {}) {
|
|
6747
6920
|
const descriptorRoot = options.descriptorRoot ?? descriptorDirectoryRoot();
|
|
6748
6921
|
if (descriptorRoot === void 0) return { kind: "unsupported" };
|
|
6749
|
-
const directory =
|
|
6922
|
+
const directory = path8.dirname(paths.plan);
|
|
6750
6923
|
let before;
|
|
6751
6924
|
try {
|
|
6752
6925
|
before = await lstat3(directory);
|
|
@@ -6767,7 +6940,7 @@ async function pinSnapshotDirectory(paths, options = {}) {
|
|
|
6767
6940
|
await closeQuietly(handle);
|
|
6768
6941
|
return { kind: "unstable" };
|
|
6769
6942
|
}
|
|
6770
|
-
const pinnedDirectory =
|
|
6943
|
+
const pinnedDirectory = path8.join(descriptorRoot, String(handle.fd));
|
|
6771
6944
|
let anchored;
|
|
6772
6945
|
try {
|
|
6773
6946
|
anchored = await stat2(pinnedDirectory);
|
|
@@ -6796,7 +6969,7 @@ async function pinSnapshotDirectory(paths, options = {}) {
|
|
|
6796
6969
|
}
|
|
6797
6970
|
}
|
|
6798
6971
|
function anchoredChild(pinnedDirectory, realPath) {
|
|
6799
|
-
return
|
|
6972
|
+
return path8.join(pinnedDirectory, path8.basename(realPath));
|
|
6800
6973
|
}
|
|
6801
6974
|
function isSameInode(left, right) {
|
|
6802
6975
|
return left.dev === right.dev && left.ino === right.ino;
|
|
@@ -6890,14 +7063,14 @@ var MAX_RECLAIM_PASSES = 4;
|
|
|
6890
7063
|
async function ingestFlushPlanNotes(options) {
|
|
6891
7064
|
if (options.workspaceDir === void 0) return;
|
|
6892
7065
|
const workspaceDir = options.workspaceDir;
|
|
6893
|
-
const planPath =
|
|
7066
|
+
const planPath = path9.join(
|
|
6894
7067
|
workspaceDir,
|
|
6895
7068
|
...buildMemoryFlushPlan({ serviceId: options.serviceId }).relativePath.split("/")
|
|
6896
7069
|
);
|
|
6897
7070
|
const paths = buildSnapshotPaths(planPath);
|
|
6898
7071
|
for (const candidate of [paths.plan, paths.inflight, paths.rotating, paths.oversized]) {
|
|
6899
7072
|
if (await isLinkFreeUnder(workspaceDir, candidate)) continue;
|
|
6900
|
-
|
|
7073
|
+
log5.warn(
|
|
6901
7074
|
`[${options.serviceId}] flush-plan ingestion skipped: ${candidate}, a parent, or the workspace root is a symlink`
|
|
6902
7075
|
);
|
|
6903
7076
|
return;
|
|
@@ -6908,21 +7081,21 @@ async function ingestFlushPlanNotes(options) {
|
|
|
6908
7081
|
{ staleMs: LOCK_STALE_MS, maxWaitMs: lockWaitMs },
|
|
6909
7082
|
async (acquired, lock) => {
|
|
6910
7083
|
if (!acquired) {
|
|
6911
|
-
|
|
7084
|
+
log5.warn(
|
|
6912
7085
|
`[${options.serviceId}] flush-plan ingestion skipped: another flush holds the lock; the notes drain on the next flush`
|
|
6913
7086
|
);
|
|
6914
7087
|
return;
|
|
6915
7088
|
}
|
|
6916
7089
|
for (const candidate of [paths.plan, paths.inflight, paths.rotating, paths.oversized]) {
|
|
6917
7090
|
if (await isLinkFreeUnder(workspaceDir, candidate)) continue;
|
|
6918
|
-
|
|
7091
|
+
log5.warn(
|
|
6919
7092
|
`[${options.serviceId}] flush-plan ingestion skipped: ${candidate}, a parent, or the workspace root became a symlink`
|
|
6920
7093
|
);
|
|
6921
7094
|
return;
|
|
6922
7095
|
}
|
|
6923
7096
|
const pinned = await pinSnapshotDirectory(paths);
|
|
6924
7097
|
if (pinned.kind === "unstable") {
|
|
6925
|
-
|
|
7098
|
+
log5.warn(
|
|
6926
7099
|
`[${options.serviceId}] flush-plan ingestion skipped: the snapshot directory changed identity while it was being opened`
|
|
6927
7100
|
);
|
|
6928
7101
|
return;
|
|
@@ -6941,7 +7114,7 @@ async function ingestUnderLock(options, paths, lock) {
|
|
|
6941
7114
|
if (options.remainingTimeoutMs() <= 0) return;
|
|
6942
7115
|
const claimed = await claimPendingNotes(paths, options.serviceId, lock);
|
|
6943
7116
|
if (claimed === void 0) {
|
|
6944
|
-
|
|
7117
|
+
log5.warn(
|
|
6945
7118
|
`[${options.serviceId}] flush-plan lock was lost during recovery; its new owner drains the snapshot`
|
|
6946
7119
|
);
|
|
6947
7120
|
return;
|
|
@@ -6957,7 +7130,7 @@ async function ingestUnderLock(options, paths, lock) {
|
|
|
6957
7130
|
while (pending.trim().length > 0) {
|
|
6958
7131
|
const timeoutMs = options.remainingTimeoutMs();
|
|
6959
7132
|
if (timeoutMs <= 0) {
|
|
6960
|
-
|
|
7133
|
+
log5.warn(
|
|
6961
7134
|
`[${options.serviceId}] flush-plan ingestion stopped: the caller's deadline is spent; the remainder drains on the next flush`
|
|
6962
7135
|
);
|
|
6963
7136
|
stop = true;
|
|
@@ -6980,7 +7153,7 @@ async function ingestUnderLock(options, paths, lock) {
|
|
|
6980
7153
|
timeoutMs
|
|
6981
7154
|
);
|
|
6982
7155
|
if (isRefusal(response.status)) {
|
|
6983
|
-
|
|
7156
|
+
log5.warn(
|
|
6984
7157
|
`[${options.serviceId}] flush-plan notes were refused by the daemon (${response.status}); keeping them for the next flush`
|
|
6985
7158
|
);
|
|
6986
7159
|
stop = true;
|
|
@@ -6994,7 +7167,7 @@ async function ingestUnderLock(options, paths, lock) {
|
|
|
6994
7167
|
pending = await quarantineOversizedLine(paths, pending, options.serviceId);
|
|
6995
7168
|
chunkBytes = MAX_OBSERVE_CHUNK_BYTES;
|
|
6996
7169
|
if (!await lock.refresh()) {
|
|
6997
|
-
|
|
7170
|
+
log5.warn(
|
|
6998
7171
|
`[${options.serviceId}] flush-plan lock was lost mid-flush; the snapshot is left to its new owner`
|
|
6999
7172
|
);
|
|
7000
7173
|
lockLost = true;
|
|
@@ -7010,7 +7183,7 @@ async function ingestUnderLock(options, paths, lock) {
|
|
|
7010
7183
|
}
|
|
7011
7184
|
pending = pending.slice(chunk.length);
|
|
7012
7185
|
if (!await lock.refresh()) {
|
|
7013
|
-
|
|
7186
|
+
log5.warn(
|
|
7014
7187
|
`[${options.serviceId}] flush-plan lock was lost mid-flush; the snapshot is left to its new owner`
|
|
7015
7188
|
);
|
|
7016
7189
|
lockLost = true;
|
|
@@ -7023,7 +7196,7 @@ async function ingestUnderLock(options, paths, lock) {
|
|
|
7023
7196
|
if (!lockLost && await lock.refresh()) {
|
|
7024
7197
|
await releasePendingNotes(paths, pending, options.serviceId);
|
|
7025
7198
|
} else if (!lockLost) {
|
|
7026
|
-
|
|
7199
|
+
log5.warn(
|
|
7027
7200
|
`[${options.serviceId}] flush-plan lock was lost before the snapshot could be updated; its new owner drains the remainder`
|
|
7028
7201
|
);
|
|
7029
7202
|
}
|
|
@@ -7043,7 +7216,7 @@ async function claimPendingNotes(paths, serviceId, lock) {
|
|
|
7043
7216
|
const recovery = await mergeRotatedIntoInflight(paths, lock);
|
|
7044
7217
|
if (recovery === "lock-lost") return void 0;
|
|
7045
7218
|
if (recovery === "recovered") {
|
|
7046
|
-
|
|
7219
|
+
log5.warn(`[${serviceId}] recovered flush-plan notes left by an interrupted ingestion`);
|
|
7047
7220
|
}
|
|
7048
7221
|
if (!await lock.refresh()) return void 0;
|
|
7049
7222
|
try {
|
|
@@ -7082,7 +7255,7 @@ async function releasePendingNotes(paths, pending, serviceId) {
|
|
|
7082
7255
|
}
|
|
7083
7256
|
await atomicWrite(paths.inflight, pending);
|
|
7084
7257
|
} catch (err) {
|
|
7085
|
-
|
|
7258
|
+
log5.warn(
|
|
7086
7259
|
`[${serviceId}] could not persist unsent flush-plan notes; the next ingestion recovers them: ${String(err)}`
|
|
7087
7260
|
);
|
|
7088
7261
|
}
|
|
@@ -7102,7 +7275,7 @@ async function quarantineOversizedLine(paths, pending, serviceId) {
|
|
|
7102
7275
|
} finally {
|
|
7103
7276
|
await handle.close();
|
|
7104
7277
|
}
|
|
7105
|
-
|
|
7278
|
+
log5.warn(
|
|
7106
7279
|
`[${serviceId}] a flush-plan note exceeds the daemon's body limit; moved it to ${paths.oversizedLabel} so the remaining notes can drain`
|
|
7107
7280
|
);
|
|
7108
7281
|
return rest;
|
|
@@ -7151,16 +7324,16 @@ function chunkOnLineBoundaries(text, limit) {
|
|
|
7151
7324
|
return chunks;
|
|
7152
7325
|
}
|
|
7153
7326
|
async function isLinkFreeUnder(root, target) {
|
|
7154
|
-
const relative =
|
|
7155
|
-
if (relative.startsWith("..") ||
|
|
7327
|
+
const relative = path9.relative(root, target);
|
|
7328
|
+
if (relative.startsWith("..") || path9.isAbsolute(relative)) return false;
|
|
7156
7329
|
let current = root;
|
|
7157
7330
|
try {
|
|
7158
7331
|
if ((await lstat4(current)).isSymbolicLink()) return false;
|
|
7159
7332
|
} catch {
|
|
7160
7333
|
return true;
|
|
7161
7334
|
}
|
|
7162
|
-
for (const segment of relative.split(
|
|
7163
|
-
current =
|
|
7335
|
+
for (const segment of relative.split(path9.sep)) {
|
|
7336
|
+
current = path9.join(current, segment);
|
|
7164
7337
|
try {
|
|
7165
7338
|
if ((await lstat4(current)).isSymbolicLink()) return false;
|
|
7166
7339
|
} catch {
|
|
@@ -7196,7 +7369,7 @@ function extractTextContent(msg) {
|
|
|
7196
7369
|
|
|
7197
7370
|
// src/delegate-capability.ts
|
|
7198
7371
|
import { readFile as readFile2 } from "fs/promises";
|
|
7199
|
-
import { log as
|
|
7372
|
+
import { log as log6 } from "@remnic/core/logger";
|
|
7200
7373
|
var HEALTH_CACHE_TTL_MS = 3e4;
|
|
7201
7374
|
var SEARCH_CANDIDATE_CAP = 25e3;
|
|
7202
7375
|
function searchCandidateCeiling(budget) {
|
|
@@ -7393,7 +7566,7 @@ function createDelegateMemoryCapability(options) {
|
|
|
7393
7566
|
corpusShared = daemonIsLocal && health.memoryDir !== void 0 && daemonServesCorpus(options.memoryDir, health.memoryDir);
|
|
7394
7567
|
if (!corpusShared && daemonIsLocal && !reportedCorpusMismatch) {
|
|
7395
7568
|
reportedCorpusMismatch = true;
|
|
7396
|
-
|
|
7569
|
+
log6.error(
|
|
7397
7570
|
`[${serviceId}] delegate capability: the daemon does not serve this plugin's memoryDir (daemon: ${health.memoryDir ?? "unreported"}, plugin: ${options.memoryDir}) \u2014 file-backed reads and public artifacts are disabled; search still runs through the daemon`
|
|
7398
7571
|
);
|
|
7399
7572
|
}
|
|
@@ -7411,7 +7584,7 @@ function createDelegateMemoryCapability(options) {
|
|
|
7411
7584
|
const message = `[${serviceId}] delegate capability health probe failed: ${String(err)}`;
|
|
7412
7585
|
if (message !== lastHealthFailure) {
|
|
7413
7586
|
lastHealthFailure = message;
|
|
7414
|
-
|
|
7587
|
+
log6.warn(message);
|
|
7415
7588
|
}
|
|
7416
7589
|
} finally {
|
|
7417
7590
|
healthInFlight = void 0;
|
|
@@ -7638,7 +7811,7 @@ function createDelegateMemoryCapability(options) {
|
|
|
7638
7811
|
agentIds: options.agentIds
|
|
7639
7812
|
});
|
|
7640
7813
|
} catch (err) {
|
|
7641
|
-
|
|
7814
|
+
log6.error(`[${serviceId}] delegate publicArtifacts.listArtifacts failed`, err);
|
|
7642
7815
|
return [];
|
|
7643
7816
|
}
|
|
7644
7817
|
},
|
|
@@ -7651,7 +7824,7 @@ function registerDelegateMemoryCapability(api, options) {
|
|
|
7651
7824
|
const hasRuntime = typeof api.registerMemoryRuntime === "function";
|
|
7652
7825
|
const hasFlushPlan = typeof api.registerMemoryFlushPlan === "function";
|
|
7653
7826
|
if (!hasUnified && !hasRuntime && !hasFlushPlan) {
|
|
7654
|
-
|
|
7827
|
+
log6.debug(
|
|
7655
7828
|
`[${options.serviceId}] delegate: host exposes no memory capability surface \u2014 nothing to register`
|
|
7656
7829
|
);
|
|
7657
7830
|
return built;
|
|
@@ -7668,7 +7841,7 @@ function registerDelegateMemoryCapability(api, options) {
|
|
|
7668
7841
|
if (hasFlushPlan) api.registerMemoryFlushPlan?.(built.flushPlanResolver);
|
|
7669
7842
|
const surface = hasUnified ? "memory capability with publicArtifacts provider" : "split memory runtime/flush-plan surfaces";
|
|
7670
7843
|
const builder = options.allowPromptInjection ? " and promptBuilder" : " (promptBuilder omitted \u2014 injection disabled by policy)";
|
|
7671
|
-
|
|
7844
|
+
log6.info(`[${options.serviceId}] delegate: registered daemon-backed ${surface}${builder}`);
|
|
7672
7845
|
return built;
|
|
7673
7846
|
}
|
|
7674
7847
|
|
|
@@ -7680,7 +7853,7 @@ import {
|
|
|
7680
7853
|
acceptsSupportPassportModelResponse,
|
|
7681
7854
|
parseSupportPassportModelJob
|
|
7682
7855
|
} from "@remnic/core";
|
|
7683
|
-
import { log as
|
|
7856
|
+
import { log as log7 } from "@remnic/core/logger";
|
|
7684
7857
|
var MODEL_WORKER_COUNT = 4;
|
|
7685
7858
|
var DEFAULT_REQUEST_TIMEOUT_MS = 25e3;
|
|
7686
7859
|
var RESULT_REQUEST_TIMEOUT_MS = 5e3;
|
|
@@ -7753,7 +7926,7 @@ function createDelegateSupportPassportModelService(options) {
|
|
|
7753
7926
|
]);
|
|
7754
7927
|
} catch (error) {
|
|
7755
7928
|
if (!modelSignal.aborted) {
|
|
7756
|
-
|
|
7929
|
+
log7.warn(`delegate support passport model call failed: ${String(error)}`);
|
|
7757
7930
|
}
|
|
7758
7931
|
return null;
|
|
7759
7932
|
} finally {
|
|
@@ -7898,14 +8071,14 @@ function createDelegateSupportPassportModelService(options) {
|
|
|
7898
8071
|
}
|
|
7899
8072
|
const job = parseSupportPassportModelJob(await response.json());
|
|
7900
8073
|
if (!job) {
|
|
7901
|
-
|
|
8074
|
+
log7.warn("delegate support passport model bridge received an invalid job");
|
|
7902
8075
|
await delayAfterFailure();
|
|
7903
8076
|
continue;
|
|
7904
8077
|
}
|
|
7905
8078
|
consecutiveFailures = 0;
|
|
7906
8079
|
const deadline = Date.now() + job.timeoutMs;
|
|
7907
8080
|
if (!await acknowledge(job, signal, job.claimAckTimeoutMs ?? job.timeoutMs, true)) {
|
|
7908
|
-
|
|
8081
|
+
log7.warn("delegate support passport model bridge could not acknowledge a claimed job");
|
|
7909
8082
|
await delayAfterFailure();
|
|
7910
8083
|
continue;
|
|
7911
8084
|
}
|
|
@@ -7931,7 +8104,7 @@ function createDelegateSupportPassportModelService(options) {
|
|
|
7931
8104
|
});
|
|
7932
8105
|
if (heartbeatError && !completionAccepted) throw heartbeatError;
|
|
7933
8106
|
} catch (error) {
|
|
7934
|
-
|
|
8107
|
+
log7.warn(`delegate support passport model completion failed: ${String(error)}`);
|
|
7935
8108
|
} finally {
|
|
7936
8109
|
heartbeatController.abort();
|
|
7937
8110
|
workController.abort();
|
|
@@ -7939,7 +8112,7 @@ function createDelegateSupportPassportModelService(options) {
|
|
|
7939
8112
|
}
|
|
7940
8113
|
} catch (error) {
|
|
7941
8114
|
if (signal.aborted) break;
|
|
7942
|
-
|
|
8115
|
+
log7.warn(`delegate support passport model bridge failed: ${String(error)}`);
|
|
7943
8116
|
await delayAfterFailure();
|
|
7944
8117
|
}
|
|
7945
8118
|
}
|
|
@@ -7964,9 +8137,228 @@ function createDelegateSupportPassportModelService(options) {
|
|
|
7964
8137
|
};
|
|
7965
8138
|
}
|
|
7966
8139
|
|
|
7967
|
-
// src/delegate-
|
|
7968
|
-
|
|
7969
|
-
|
|
8140
|
+
// src/delegate-tools.ts
|
|
8141
|
+
import path10 from "path";
|
|
8142
|
+
import {
|
|
8143
|
+
collapseWhitespace,
|
|
8144
|
+
truncateCodePointSafe
|
|
8145
|
+
} from "@remnic/core";
|
|
8146
|
+
import { isHandleToken } from "@remnic/core/recall-handles";
|
|
8147
|
+
var DEFAULT_SEARCH_RESULTS = 8;
|
|
8148
|
+
var DEFAULT_SNIPPET_MAX_CHARS = 600;
|
|
8149
|
+
var DAEMON_SEARCH_QUERY_MAX_CHARS = 2048;
|
|
8150
|
+
var DAEMON_MEMORY_GET_MAX_CHARS = 512;
|
|
8151
|
+
function clampLimit(value) {
|
|
8152
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_SEARCH_RESULTS;
|
|
8153
|
+
return Math.max(1, Math.min(50, Math.floor(value)));
|
|
8154
|
+
}
|
|
8155
|
+
function clampSnippetMaxChars(value) {
|
|
8156
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_SNIPPET_MAX_CHARS;
|
|
8157
|
+
return Math.max(1, Math.min(4e3, Math.floor(value)));
|
|
8158
|
+
}
|
|
8159
|
+
function sessionKeyFor(params, ctx) {
|
|
8160
|
+
if (typeof ctx?.sessionKey === "string" && ctx.sessionKey.trim().length > 0) {
|
|
8161
|
+
return ctx.sessionKey;
|
|
8162
|
+
}
|
|
8163
|
+
return typeof params.sessionKey === "string" && params.sessionKey.trim().length > 0 ? params.sessionKey : "default";
|
|
8164
|
+
}
|
|
8165
|
+
function activeMemoryGetOutputFrom(record) {
|
|
8166
|
+
if (typeof record !== "object" || record === null) {
|
|
8167
|
+
throw new Error("daemon memory route responded 2xx without a memory record");
|
|
8168
|
+
}
|
|
8169
|
+
const memory = record;
|
|
8170
|
+
if (typeof memory.id !== "string" || typeof memory.content !== "string") {
|
|
8171
|
+
throw new Error("daemon memory route returned a malformed memory record");
|
|
8172
|
+
}
|
|
8173
|
+
const frontmatter = typeof memory.frontmatter === "object" && memory.frontmatter !== null ? memory.frontmatter : {};
|
|
8174
|
+
const metadata = {};
|
|
8175
|
+
if (frontmatter.category === "fact" || frontmatter.category === "preference") {
|
|
8176
|
+
metadata.type = frontmatter.category;
|
|
8177
|
+
}
|
|
8178
|
+
if (Array.isArray(frontmatter.tags) && typeof frontmatter.tags[0] === "string") {
|
|
8179
|
+
metadata.topic = frontmatter.tags[0];
|
|
8180
|
+
}
|
|
8181
|
+
if (typeof frontmatter.updated === "string") metadata.updatedAt = frontmatter.updated;
|
|
8182
|
+
if (typeof frontmatter.source === "string") metadata.sourceUri = frontmatter.source;
|
|
8183
|
+
return {
|
|
8184
|
+
id: memory.id,
|
|
8185
|
+
text: collapseWhitespace(memory.content),
|
|
8186
|
+
...Object.keys(metadata).length > 0 ? { metadata } : {}
|
|
8187
|
+
};
|
|
8188
|
+
}
|
|
8189
|
+
function buildDelegateMemorySearchTool(options) {
|
|
8190
|
+
const spent = (deadline, signal, stage) => {
|
|
8191
|
+
if (signal?.aborted) throw new Error(`memory_search aborted before ${stage}`);
|
|
8192
|
+
if (deadline - Date.now() <= 0) throw new Error(`memory_search budget of ${options.timeoutMs}ms spent before ${stage}`);
|
|
8193
|
+
};
|
|
8194
|
+
return {
|
|
8195
|
+
name: "memory_search",
|
|
8196
|
+
description: "Search Remnic memories via the Remnic daemon (delegate).",
|
|
8197
|
+
parameters: MemorySearchInputSchema,
|
|
8198
|
+
inputSchema: MemorySearchInputSchema,
|
|
8199
|
+
async execute(_toolCallId, params, signal, ctx) {
|
|
8200
|
+
const query = typeof params.query === "string" ? params.query.trim() : "";
|
|
8201
|
+
if (query.length === 0) throw new Error("memory_search requires a non-empty query");
|
|
8202
|
+
const deadline = Date.now() + options.timeoutMs;
|
|
8203
|
+
const { manager, error } = await options.runtime.getMemorySearchManager({
|
|
8204
|
+
cfg: void 0,
|
|
8205
|
+
agentId: ctx?.agentId ?? options.agentId
|
|
8206
|
+
});
|
|
8207
|
+
if (!manager) throw new Error(error ?? "delegate memory search manager unavailable");
|
|
8208
|
+
spent(deadline, signal, "scope resolution");
|
|
8209
|
+
const limit = clampLimit(params.limit);
|
|
8210
|
+
const sessionKey = sessionKeyFor(params, ctx);
|
|
8211
|
+
const filters = params.filters && typeof params.filters === "object" ? params.filters : void 0;
|
|
8212
|
+
if (typeof filters?.namespace === "string" && filters.namespace.trim().length > 0) {
|
|
8213
|
+
throw new Error(
|
|
8214
|
+
`memory_search filters.namespace "${filters.namespace.trim()}" is not supported in delegate mode: the search is scoped to the session's own namespace`
|
|
8215
|
+
);
|
|
8216
|
+
}
|
|
8217
|
+
spent(deadline, signal, "search");
|
|
8218
|
+
const results = await manager.search(query.slice(0, DAEMON_SEARCH_QUERY_MAX_CHARS), {
|
|
8219
|
+
maxResults: limit + 1,
|
|
8220
|
+
sessionKey
|
|
8221
|
+
});
|
|
8222
|
+
const output = {
|
|
8223
|
+
results: results.slice(0, limit).map((result) => ({
|
|
8224
|
+
id: path10.basename(result.citation ?? result.path, ".md"),
|
|
8225
|
+
score: result.score,
|
|
8226
|
+
// Read per call: the owner's cap can change on an active takeover.
|
|
8227
|
+
text: truncateCodePointSafe(collapseWhitespace(result.snippet), clampSnippetMaxChars(options.snippetMaxChars))
|
|
8228
|
+
})),
|
|
8229
|
+
truncated: results.length > limit
|
|
8230
|
+
};
|
|
8231
|
+
return toolJsonResult2(output);
|
|
8232
|
+
}
|
|
8233
|
+
};
|
|
8234
|
+
}
|
|
8235
|
+
function buildDelegateMemoryGetTool(options) {
|
|
8236
|
+
return {
|
|
8237
|
+
name: "memory_get",
|
|
8238
|
+
description: "Fetch one Remnic memory via the Remnic daemon (delegate).",
|
|
8239
|
+
parameters: MemoryGetInputSchema,
|
|
8240
|
+
inputSchema: MemoryGetInputSchema,
|
|
8241
|
+
async execute(_toolCallId, params, _signal, ctx) {
|
|
8242
|
+
const id = typeof params.id === "string" ? params.id.trim() : "";
|
|
8243
|
+
if (id.length === 0) throw new Error("memory_get requires an id");
|
|
8244
|
+
if (id.length > DAEMON_MEMORY_GET_MAX_CHARS) {
|
|
8245
|
+
return toolJsonResult2({ error: "not_found" });
|
|
8246
|
+
}
|
|
8247
|
+
const sessionKey = sessionKeyFor(params, ctx);
|
|
8248
|
+
if (sessionKey.length > DAEMON_MEMORY_GET_MAX_CHARS) {
|
|
8249
|
+
throw new Error(`memory_get sessionKey exceeds the daemon's ${DAEMON_MEMORY_GET_MAX_CHARS}-char cap`);
|
|
8250
|
+
}
|
|
8251
|
+
const deadline = Date.now() + options.timeoutMs;
|
|
8252
|
+
const namespace = await options.resolveNamespace(sessionKey, Math.max(1, deadline - Date.now()));
|
|
8253
|
+
const requested = typeof params.namespace === "string" && params.namespace.trim().length > 0 ? params.namespace.trim() : void 0;
|
|
8254
|
+
if (requested !== void 0 && requested !== namespace) {
|
|
8255
|
+
throw new Error(
|
|
8256
|
+
`memory_get namespace "${requested}" does not match the session's memory scope${namespace === void 0 ? "" : ` "${namespace}"`}`
|
|
8257
|
+
);
|
|
8258
|
+
}
|
|
8259
|
+
const search = new URLSearchParams({ sessionKey });
|
|
8260
|
+
if (namespace !== void 0) search.set("namespace", namespace);
|
|
8261
|
+
const pathname = `/engram/v1/memories/${encodeURIComponent(id)}?${search}`;
|
|
8262
|
+
const response = await getJson(options.target, options.serviceId, pathname, Math.max(1, deadline - Date.now()));
|
|
8263
|
+
if (response.status === 404) return toolJsonResult2({ error: "not_found" });
|
|
8264
|
+
if (response.status === 400 && isHandleToken(id)) {
|
|
8265
|
+
return toolJsonResult2({ error: "not_found" });
|
|
8266
|
+
}
|
|
8267
|
+
if (response.status < 200 || response.status > 299) {
|
|
8268
|
+
throw new Error(`daemon ${pathname} responded ${response.status}`);
|
|
8269
|
+
}
|
|
8270
|
+
return toolJsonResult2(activeMemoryGetOutputFrom(response.body?.memory));
|
|
8271
|
+
}
|
|
8272
|
+
};
|
|
8273
|
+
}
|
|
8274
|
+
var SHARED_TOOL_SHAPES = {
|
|
8275
|
+
memory_search: {
|
|
8276
|
+
description: "Search Remnic memories for the OpenClaw active-memory surface.",
|
|
8277
|
+
parameters: MemorySearchInputSchema
|
|
8278
|
+
},
|
|
8279
|
+
memory_get: {
|
|
8280
|
+
description: "Fetch one Remnic memory for the OpenClaw active-memory surface.",
|
|
8281
|
+
parameters: MemoryGetInputSchema
|
|
8282
|
+
}
|
|
8283
|
+
};
|
|
8284
|
+
function installSharedTools(registerTool, owner, tools) {
|
|
8285
|
+
for (const tool of tools) owner.serve[tool.name] = tool;
|
|
8286
|
+
for (const tool of tools) {
|
|
8287
|
+
if (owner.installed.has(tool.name)) continue;
|
|
8288
|
+
owner.installed.add(tool.name);
|
|
8289
|
+
const shape = SHARED_TOOL_SHAPES[tool.name];
|
|
8290
|
+
registerTool(
|
|
8291
|
+
{
|
|
8292
|
+
...tool,
|
|
8293
|
+
...shape === void 0 ? {} : { description: shape.description, parameters: shape.parameters, inputSchema: shape.parameters },
|
|
8294
|
+
execute: async (...args) => {
|
|
8295
|
+
const serving = owner.serve[tool.name] ?? tool;
|
|
8296
|
+
if (!owner.enabled && tool.name !== "memory_search") {
|
|
8297
|
+
throw new Error(`${tool.name} is disabled: the memory slot owner set openclawToolsEnabled: false`);
|
|
8298
|
+
}
|
|
8299
|
+
return serving.execute(...args);
|
|
8300
|
+
}
|
|
8301
|
+
},
|
|
8302
|
+
{ name: tool.name }
|
|
8303
|
+
);
|
|
8304
|
+
}
|
|
8305
|
+
}
|
|
8306
|
+
var delegateToolOwners = /* @__PURE__ */ new WeakMap();
|
|
8307
|
+
function registerDelegateTools(api, options) {
|
|
8308
|
+
if (typeof api.registerTool !== "function") return;
|
|
8309
|
+
const registerTool = api.registerTool.bind(api);
|
|
8310
|
+
const existing = delegateToolOwners.get(api);
|
|
8311
|
+
const owner = existing ?? {
|
|
8312
|
+
enabled: options.enabled,
|
|
8313
|
+
passive: options.passive,
|
|
8314
|
+
installed: /* @__PURE__ */ new Set(),
|
|
8315
|
+
serve: {}
|
|
8316
|
+
};
|
|
8317
|
+
if (existing === void 0) delegateToolOwners.set(api, owner);
|
|
8318
|
+
else if (existing.passive && !options.passive) {
|
|
8319
|
+
owner.enabled = options.enabled;
|
|
8320
|
+
owner.passive = options.passive;
|
|
8321
|
+
} else return;
|
|
8322
|
+
const resolveNamespace = (operation) => async (sessionKey, timeoutMs) => {
|
|
8323
|
+
const deadline = Date.now() + timeoutMs;
|
|
8324
|
+
const bound = await options.resolveSearchNamespace(sessionKey);
|
|
8325
|
+
return options.resolveScopedNamespace(bound, Math.max(1, deadline - Date.now()), [operation]);
|
|
8326
|
+
};
|
|
8327
|
+
const tools = [buildDelegateMemorySearchTool(options)];
|
|
8328
|
+
if (owner.enabled) {
|
|
8329
|
+
tools.push(
|
|
8330
|
+
buildDelegateMemoryGetTool({
|
|
8331
|
+
target: options.target,
|
|
8332
|
+
serviceId: options.serviceId,
|
|
8333
|
+
timeoutMs: options.timeoutMs,
|
|
8334
|
+
resolveNamespace: resolveNamespace("memory_get")
|
|
8335
|
+
})
|
|
8336
|
+
);
|
|
8337
|
+
}
|
|
8338
|
+
installSharedTools(registerTool, owner, tools);
|
|
8339
|
+
}
|
|
8340
|
+
function registerEmbeddedTools(api, options) {
|
|
8341
|
+
if (typeof api.registerTool !== "function") return [];
|
|
8342
|
+
const registerTool = api.registerTool.bind(api);
|
|
8343
|
+
const existing = delegateToolOwners.get(api);
|
|
8344
|
+
const owner = existing ?? {
|
|
8345
|
+
enabled: options.enabled,
|
|
8346
|
+
passive: options.passive,
|
|
8347
|
+
installed: /* @__PURE__ */ new Set(),
|
|
8348
|
+
serve: {}
|
|
8349
|
+
};
|
|
8350
|
+
if (existing === void 0) delegateToolOwners.set(api, owner);
|
|
8351
|
+
else if (options.passive && !existing.passive) return [...owner.installed];
|
|
8352
|
+
else if (!options.passive) {
|
|
8353
|
+
owner.enabled = options.enabled;
|
|
8354
|
+
owner.passive = false;
|
|
8355
|
+
}
|
|
8356
|
+
installSharedTools(registerTool, owner, options.tools);
|
|
8357
|
+
return [...owner.installed];
|
|
8358
|
+
}
|
|
8359
|
+
|
|
8360
|
+
// src/delegate-hook-fields.ts
|
|
8361
|
+
var MAX_RECALL_QUERY_CHARS = 1500;
|
|
7970
8362
|
function sessionKeyFrom(event, ctx) {
|
|
7971
8363
|
const fromCtx = ctx?.sessionKey;
|
|
7972
8364
|
if (typeof fromCtx === "string" && fromCtx.length > 0) return fromCtx;
|
|
@@ -7981,13 +8373,13 @@ function lifecycleSessionKeyFrom(event, ctx) {
|
|
|
7981
8373
|
}
|
|
7982
8374
|
return sessionKeyFrom(event, ctx);
|
|
7983
8375
|
}
|
|
7984
|
-
function recallQueryFrom(event) {
|
|
7985
|
-
let prompt = typeof event.prompt === "string" ? event.prompt :
|
|
7986
|
-
if (
|
|
8376
|
+
function recallQueryFrom(event, cleanUserMessage) {
|
|
8377
|
+
let prompt = typeof event.prompt === "string" ? cleanUserMessage(event.prompt).trim() : "";
|
|
8378
|
+
if (prompt.length < 5 && Array.isArray(event.messages)) {
|
|
7987
8379
|
const msgs = event.messages;
|
|
7988
8380
|
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
7989
8381
|
if (msgs[i]?.role === "user") {
|
|
7990
|
-
const text = extractTextContent(msgs[i]);
|
|
8382
|
+
const text = extractTextContent(msgs[i]).trim();
|
|
7991
8383
|
if (text.length >= 5) {
|
|
7992
8384
|
prompt = text;
|
|
7993
8385
|
break;
|
|
@@ -7995,7 +8387,7 @@ function recallQueryFrom(event) {
|
|
|
7995
8387
|
}
|
|
7996
8388
|
}
|
|
7997
8389
|
}
|
|
7998
|
-
return prompt
|
|
8390
|
+
return prompt.length > MAX_RECALL_QUERY_CHARS ? prompt.slice(-MAX_RECALL_QUERY_CHARS) : prompt;
|
|
7999
8391
|
}
|
|
8000
8392
|
function cwdFrom(event, ctx, fallback) {
|
|
8001
8393
|
const runtime = ctx?.runtime;
|
|
@@ -8025,17 +8417,21 @@ function readContextComposition(response, fallbackContext) {
|
|
|
8025
8417
|
if (degradation) composition.degradation = degradation;
|
|
8026
8418
|
return composition;
|
|
8027
8419
|
}
|
|
8420
|
+
|
|
8421
|
+
// src/delegate-runtime.ts
|
|
8422
|
+
var DELEGATE_BATCH_FLUSH_CACHE_TTL_MS = 3e4;
|
|
8423
|
+
var delegatePassportServiceApiServices = /* @__PURE__ */ new WeakMap();
|
|
8028
8424
|
function registerDelegateRuntime(api, options) {
|
|
8029
8425
|
const { target, namespace, namespaceBindings } = options;
|
|
8030
8426
|
const now = options.now ?? Date.now;
|
|
8031
8427
|
if (options.supportPassportModelRoute) {
|
|
8032
8428
|
const registeredServices = delegatePassportServiceApiServices.get(api);
|
|
8033
8429
|
if (registeredServices?.has(options.serviceId)) {
|
|
8034
|
-
|
|
8430
|
+
log8.debug(
|
|
8035
8431
|
`delegate register: ${options.serviceId} already has its support passport model service on this api`
|
|
8036
8432
|
);
|
|
8037
8433
|
} else if (typeof api.registerService !== "function") {
|
|
8038
|
-
|
|
8434
|
+
log8.error(
|
|
8039
8435
|
`[${options.serviceId}] delegate support passport gateway routing is unavailable: host exposes no service registration surface`
|
|
8040
8436
|
);
|
|
8041
8437
|
} else {
|
|
@@ -8053,32 +8449,22 @@ function registerDelegateRuntime(api, options) {
|
|
|
8053
8449
|
}
|
|
8054
8450
|
}
|
|
8055
8451
|
}
|
|
8056
|
-
if (options.passive) {
|
|
8057
|
-
log7.info(
|
|
8058
|
-
`[${options.serviceId}] bridge mode delegate: memory slot not owned \u2014 passive, no memory hooks registered`
|
|
8059
|
-
);
|
|
8060
|
-
return;
|
|
8061
|
-
}
|
|
8062
8452
|
const promptLinesBySession = /* @__PURE__ */ new Map();
|
|
8063
8453
|
const promptInjectionEnabled = options.allowPromptInjection && options.recallBudgetChars !== 0;
|
|
8064
8454
|
const useSectionBuilder = typeof api.registerMemoryPromptSection === "function";
|
|
8065
8455
|
const cachePromptLines = useSectionBuilder;
|
|
8066
|
-
const
|
|
8456
|
+
const resolveSearchNamespace = async (sessionKey) => {
|
|
8457
|
+
if (typeof sessionKey === "string" && sessionKey.trim().length > 0) {
|
|
8458
|
+
const remembered = await rememberedNamespacesFor(sessionKey, namespaceBindings);
|
|
8459
|
+
if (remembered.length > 0) return remembered.at(-1) || void 0;
|
|
8460
|
+
}
|
|
8461
|
+
return namespace.trim() || void 0;
|
|
8462
|
+
};
|
|
8463
|
+
const capabilityOptions = {
|
|
8067
8464
|
serviceId: options.serviceId,
|
|
8068
8465
|
target,
|
|
8069
8466
|
namespace,
|
|
8070
|
-
|
|
8071
|
-
// the hooks use (the non-explicit branch of sessionNamespaceFrom): the
|
|
8072
|
-
// host hands the runtime a sessionKey but no event/ctx to read an explicit
|
|
8073
|
-
// namespace from, so the remembered binding — else the registration-wide
|
|
8074
|
-
// fallback — is the correct scope.
|
|
8075
|
-
resolveSearchNamespace: async (sessionKey) => {
|
|
8076
|
-
if (typeof sessionKey === "string" && sessionKey.trim().length > 0) {
|
|
8077
|
-
const remembered = await rememberedNamespacesFor(sessionKey, namespaceBindings);
|
|
8078
|
-
if (remembered.length > 0) return remembered.at(-1) || void 0;
|
|
8079
|
-
}
|
|
8080
|
-
return namespace.trim() || void 0;
|
|
8081
|
-
},
|
|
8467
|
+
resolveSearchNamespace,
|
|
8082
8468
|
// The daemon's own namespace-aware probe, so a substituted default is
|
|
8083
8469
|
// proven usable before the first search rather than 403-ing on it.
|
|
8084
8470
|
verifyNamespaceAuthorization: async (candidate, timeoutMs, operations) => {
|
|
@@ -8107,24 +8493,44 @@ function registerDelegateRuntime(api, options) {
|
|
|
8107
8493
|
searchTimeoutMs: options.recallTimeoutMs,
|
|
8108
8494
|
healthTimeoutMs: options.recallTimeoutMs,
|
|
8109
8495
|
now: options.now
|
|
8496
|
+
};
|
|
8497
|
+
const capability = options.passive ? createDelegateMemoryCapability(capabilityOptions) : registerDelegateMemoryCapability(api, capabilityOptions);
|
|
8498
|
+
registerDelegateTools(api, {
|
|
8499
|
+
target,
|
|
8500
|
+
serviceId: options.serviceId,
|
|
8501
|
+
enabled: options.openclawToolsEnabled !== false,
|
|
8502
|
+
passive: options.passive,
|
|
8503
|
+
runtime: capability.runtime,
|
|
8504
|
+
agentId: options.capability.agentIds[0] ?? "main",
|
|
8505
|
+
snippetMaxChars: options.openclawToolSnippetMaxChars,
|
|
8506
|
+
timeoutMs: options.recallTimeoutMs,
|
|
8507
|
+
resolveSearchNamespace,
|
|
8508
|
+
resolveScopedNamespace: capability.resolveScopedNamespace
|
|
8110
8509
|
});
|
|
8510
|
+
if (options.passive) {
|
|
8511
|
+
log8.info(
|
|
8512
|
+
`[${options.serviceId}] bridge mode delegate: memory slot not owned \u2014 passive, no memory hooks registered`
|
|
8513
|
+
);
|
|
8514
|
+
return;
|
|
8515
|
+
}
|
|
8516
|
+
const observeChains = /* @__PURE__ */ new Map();
|
|
8517
|
+
const followUpFlushSessions = /* @__PURE__ */ new Map();
|
|
8111
8518
|
if (promptInjectionEnabled) {
|
|
8112
|
-
const
|
|
8113
|
-
const query = recallQueryFrom(event);
|
|
8519
|
+
const recallContext = async (query, event, ctx) => {
|
|
8114
8520
|
const sessionKey = sessionKeyFrom(event, ctx);
|
|
8115
8521
|
if (cachePromptLines) promptLinesBySession.delete(sessionKey);
|
|
8116
|
-
if (query.
|
|
8522
|
+
if (query.length < 5) return void 0;
|
|
8117
8523
|
const promptDeadline = Date.now() + Math.min(options.hookTimeoutMs, options.recallTimeoutMs);
|
|
8118
8524
|
const promptRemaining = () => promptDeadline - Date.now();
|
|
8119
8525
|
try {
|
|
8120
8526
|
if (options.shouldSkipRecall(sessionKey)) {
|
|
8121
|
-
|
|
8527
|
+
log8.debug(`delegate recall skipped: cron policy excludes ${sessionKey}`);
|
|
8122
8528
|
return void 0;
|
|
8123
8529
|
}
|
|
8124
8530
|
const runtimeAgent = ctx?.runtime?.agent;
|
|
8125
8531
|
const agentId = (typeof ctx?.agentId === "string" ? ctx.agentId : void 0) ?? (typeof runtimeAgent?.id === "string" ? runtimeAgent.id : void 0) ?? "main";
|
|
8126
8532
|
if (await options.resolveSessionDisabled(sessionKey, agentId)) {
|
|
8127
|
-
|
|
8533
|
+
log8.debug(`delegate recall skipped: session toggle disables memory for ${sessionKey}`);
|
|
8128
8534
|
return void 0;
|
|
8129
8535
|
}
|
|
8130
8536
|
const cwd = cwdFrom(event, ctx, options.cwd);
|
|
@@ -8165,20 +8571,28 @@ function registerDelegateRuntime(api, options) {
|
|
|
8165
8571
|
maxChars: options.recallBudgetChars
|
|
8166
8572
|
});
|
|
8167
8573
|
if (!rendered) return void 0;
|
|
8168
|
-
const prompt = rendered.prompt;
|
|
8169
|
-
if (cachePromptLines) {
|
|
8170
|
-
promptLinesBySession.set(sessionKey, rendered.lines);
|
|
8171
|
-
return void 0;
|
|
8172
|
-
}
|
|
8173
8574
|
return {
|
|
8174
|
-
|
|
8575
|
+
prompt: rendered.prompt,
|
|
8576
|
+
lines: rendered.lines,
|
|
8175
8577
|
...composition.degradation ? { degradation: composition.degradation } : {}
|
|
8176
8578
|
};
|
|
8177
8579
|
} catch (err) {
|
|
8178
|
-
|
|
8580
|
+
log8.warn(`delegate recall failed: ${String(err)}`);
|
|
8179
8581
|
return void 0;
|
|
8180
8582
|
}
|
|
8181
8583
|
};
|
|
8584
|
+
const recallHandler = async (event, ctx) => {
|
|
8585
|
+
const recalled = await recallContext(recallQueryFrom(event, options.cleanUserMessage), event, ctx);
|
|
8586
|
+
if (!recalled) return void 0;
|
|
8587
|
+
if (cachePromptLines) {
|
|
8588
|
+
promptLinesBySession.set(sessionKeyFrom(event, ctx), recalled.lines);
|
|
8589
|
+
return void 0;
|
|
8590
|
+
}
|
|
8591
|
+
return {
|
|
8592
|
+
prependSystemContext: recalled.prompt,
|
|
8593
|
+
...recalled.degradation ? { degradation: recalled.degradation } : {}
|
|
8594
|
+
};
|
|
8595
|
+
};
|
|
8182
8596
|
api.on(
|
|
8183
8597
|
"before_prompt_build",
|
|
8184
8598
|
(event, ctx) => recallHandler(event, ctx),
|
|
@@ -8197,7 +8611,7 @@ function registerDelegateRuntime(api, options) {
|
|
|
8197
8611
|
api.registerMemoryPromptSection(memoryBuildFn);
|
|
8198
8612
|
}
|
|
8199
8613
|
} else {
|
|
8200
|
-
|
|
8614
|
+
log8.info(
|
|
8201
8615
|
`[${options.serviceId}] bridge mode delegate: prompt injection disabled by hooks policy`
|
|
8202
8616
|
);
|
|
8203
8617
|
}
|
|
@@ -8215,36 +8629,49 @@ function registerDelegateRuntime(api, options) {
|
|
|
8215
8629
|
(message) => (message.role === "user" || message.role === "assistant") && message.content.trim().length > 0
|
|
8216
8630
|
);
|
|
8217
8631
|
if (turn.length === 0) return;
|
|
8632
|
+
const cwd = cwdFrom(event, ctx, options.cwd);
|
|
8633
|
+
let scopedNamespace;
|
|
8218
8634
|
try {
|
|
8219
|
-
|
|
8220
|
-
const observeRemaining = () => observeDeadline - Date.now();
|
|
8221
|
-
const cwd = cwdFrom(event, ctx, options.cwd);
|
|
8222
|
-
const scopedNamespace = await sessionNamespaceFrom(
|
|
8635
|
+
scopedNamespace = await sessionNamespaceFrom(
|
|
8223
8636
|
sessionKey,
|
|
8224
8637
|
event,
|
|
8225
8638
|
ctx,
|
|
8226
8639
|
namespace,
|
|
8227
8640
|
namespaceBindings
|
|
8228
8641
|
);
|
|
8229
|
-
await postJson(
|
|
8230
|
-
target,
|
|
8231
|
-
options.serviceId,
|
|
8232
|
-
"/engram/v1/observe",
|
|
8233
|
-
await withNamespace(
|
|
8234
|
-
scopedNamespace,
|
|
8235
|
-
{
|
|
8236
|
-
sessionKey,
|
|
8237
|
-
messages: turn,
|
|
8238
|
-
...cwd ? { cwd } : {},
|
|
8239
|
-
...options.projectTag ? { projectTag: options.projectTag } : {}
|
|
8240
|
-
},
|
|
8241
|
-
(explicit) => capability.resolveScopedNamespace(explicit, observeRemaining(), ["observe"])
|
|
8242
|
-
),
|
|
8243
|
-
Math.max(1, observeRemaining())
|
|
8244
|
-
);
|
|
8245
8642
|
} catch (err) {
|
|
8246
|
-
|
|
8643
|
+
log8.warn(`delegate observe failed: ${String(err)}`);
|
|
8644
|
+
return;
|
|
8247
8645
|
}
|
|
8646
|
+
const observe = async () => {
|
|
8647
|
+
const observeDeadline = Date.now() + Math.min(options.observeTimeoutMs, options.flushTimeoutMs / 2);
|
|
8648
|
+
const observeRemaining = () => observeDeadline - Date.now();
|
|
8649
|
+
try {
|
|
8650
|
+
await postJson(
|
|
8651
|
+
target,
|
|
8652
|
+
options.serviceId,
|
|
8653
|
+
"/engram/v1/observe",
|
|
8654
|
+
await withNamespace(
|
|
8655
|
+
scopedNamespace,
|
|
8656
|
+
{
|
|
8657
|
+
sessionKey,
|
|
8658
|
+
messages: turn,
|
|
8659
|
+
...cwd ? { cwd } : {},
|
|
8660
|
+
...options.projectTag ? { projectTag: options.projectTag } : {}
|
|
8661
|
+
},
|
|
8662
|
+
(explicit) => capability.resolveScopedNamespace(explicit, observeRemaining(), ["observe"])
|
|
8663
|
+
),
|
|
8664
|
+
Math.max(1, observeRemaining())
|
|
8665
|
+
);
|
|
8666
|
+
} catch (err) {
|
|
8667
|
+
log8.warn(`delegate observe failed: ${String(err)}`);
|
|
8668
|
+
}
|
|
8669
|
+
};
|
|
8670
|
+
const chained = (observeChains.get(sessionKey) ?? Promise.resolve()).then(observe);
|
|
8671
|
+
observeChains.set(sessionKey, chained);
|
|
8672
|
+
void chained.then(() => {
|
|
8673
|
+
if (observeChains.get(sessionKey) === chained) observeChains.delete(sessionKey);
|
|
8674
|
+
});
|
|
8248
8675
|
});
|
|
8249
8676
|
let cachedBatchFlushSupport;
|
|
8250
8677
|
let cachedBatchFlushSupportExpiresAt = 0;
|
|
@@ -8297,9 +8724,38 @@ function registerDelegateRuntime(api, options) {
|
|
|
8297
8724
|
const remainingBudget = () => deadline - Date.now();
|
|
8298
8725
|
const sessionKey = lifecycleSessionKeyFrom(event, ctx);
|
|
8299
8726
|
if (sessionKey === void 0) {
|
|
8300
|
-
|
|
8727
|
+
log8.warn("delegate flush skipped: lifecycle event has malformed session key");
|
|
8301
8728
|
return false;
|
|
8302
8729
|
}
|
|
8730
|
+
const pendingObserve = observeChains.get(sessionKey);
|
|
8731
|
+
if (pendingObserve !== void 0) {
|
|
8732
|
+
const drainDeadline = Promise.withResolvers();
|
|
8733
|
+
const timer = setTimeout(drainDeadline.resolve, Math.max(0, Math.min(remainingBudget(), options.flushTimeoutMs / 2)));
|
|
8734
|
+
timer.unref?.();
|
|
8735
|
+
const drained = await Promise.race([
|
|
8736
|
+
pendingObserve.then(() => true),
|
|
8737
|
+
drainDeadline.promise.then(() => false)
|
|
8738
|
+
]);
|
|
8739
|
+
clearTimeout(timer);
|
|
8740
|
+
if (!drained && !followUpFlushSessions.has(sessionKey)) {
|
|
8741
|
+
const followUp = {};
|
|
8742
|
+
followUpFlushSessions.set(sessionKey, followUp);
|
|
8743
|
+
const releaseIfOwner = () => {
|
|
8744
|
+
if (followUpFlushSessions.get(sessionKey) === followUp) {
|
|
8745
|
+
followUpFlushSessions.delete(sessionKey);
|
|
8746
|
+
}
|
|
8747
|
+
};
|
|
8748
|
+
void pendingObserve.then(async () => {
|
|
8749
|
+
for (let round = 0; round < 8; round += 1) {
|
|
8750
|
+
const queued = observeChains.get(sessionKey);
|
|
8751
|
+
if (queued === void 0) break;
|
|
8752
|
+
await queued;
|
|
8753
|
+
}
|
|
8754
|
+
releaseIfOwner();
|
|
8755
|
+
return flushHandler({ sessionKey }, {});
|
|
8756
|
+
}).catch((err) => log8.warn(`delegate follow-up flush failed: ${String(err)}`)).finally(releaseIfOwner);
|
|
8757
|
+
}
|
|
8758
|
+
}
|
|
8303
8759
|
const namespaces = await lifecycleSessionNamespacesFrom(
|
|
8304
8760
|
sessionKey,
|
|
8305
8761
|
event,
|
|
@@ -8322,7 +8778,7 @@ function registerDelegateRuntime(api, options) {
|
|
|
8322
8778
|
remainingTimeoutMs: remainingBudget
|
|
8323
8779
|
});
|
|
8324
8780
|
} catch (err) {
|
|
8325
|
-
|
|
8781
|
+
log8.warn(`delegate flush-plan ingestion failed: ${String(err)}`);
|
|
8326
8782
|
}
|
|
8327
8783
|
const flushNamespace = async (sessionNamespace) => postJson(
|
|
8328
8784
|
target,
|
|
@@ -8347,7 +8803,7 @@ function registerDelegateRuntime(api, options) {
|
|
|
8347
8803
|
(outcome) => outcome.status === "fulfilled" && outcome.value !== null && outcome.value.flushed === true
|
|
8348
8804
|
);
|
|
8349
8805
|
};
|
|
8350
|
-
if (namespaces.length <= 1 || await supportsBatchFlush(
|
|
8806
|
+
if (namespaces.length <= 1 || await supportsBatchFlush(Math.max(1, Math.floor(remainingBudget() / 2)))) {
|
|
8351
8807
|
if (namespaces.length <= 1) {
|
|
8352
8808
|
const response = await flushNamespace(namespaces[0]);
|
|
8353
8809
|
return response !== null && response.flushed === true;
|
|
@@ -8365,7 +8821,11 @@ function registerDelegateRuntime(api, options) {
|
|
|
8365
8821
|
options.serviceId,
|
|
8366
8822
|
"/engram/v1/lcm/compaction/flush",
|
|
8367
8823
|
{ sessionKey, namespaces: requestNamespaces },
|
|
8368
|
-
|
|
8824
|
+
// Half of what is left, like the support probe: the batch is an
|
|
8825
|
+
// optimization whose failure path is `flushIndividually`, and one
|
|
8826
|
+
// that stalled having spent everything would leave those singular
|
|
8827
|
+
// flushes the 1ms floor and buffer the turns anyway.
|
|
8828
|
+
Math.max(1, Math.floor(remainingBudget() / 2))
|
|
8369
8829
|
);
|
|
8370
8830
|
if (response === null) {
|
|
8371
8831
|
invalidateCachedBatchFlushSupport();
|
|
@@ -8392,7 +8852,7 @@ function registerDelegateRuntime(api, options) {
|
|
|
8392
8852
|
}
|
|
8393
8853
|
return flushIndividually();
|
|
8394
8854
|
} catch (err) {
|
|
8395
|
-
|
|
8855
|
+
log8.warn(`delegate flush failed: ${String(err)}`);
|
|
8396
8856
|
return false;
|
|
8397
8857
|
}
|
|
8398
8858
|
};
|
|
@@ -8402,135 +8862,13 @@ function registerDelegateRuntime(api, options) {
|
|
|
8402
8862
|
api.on("before_reset", flushEndedSession);
|
|
8403
8863
|
api.on("session_end", flushEndedSession);
|
|
8404
8864
|
}
|
|
8405
|
-
|
|
8865
|
+
log8.info(
|
|
8406
8866
|
`[${options.serviceId}] bridge mode delegate: memory loop backed by daemon at ${target.host}:${target.port} (embedded orchestrator skipped; tools/CLI stay daemon-side)`
|
|
8407
8867
|
);
|
|
8408
8868
|
}
|
|
8409
8869
|
function activeDelegateAuthorizationOperations(options) {
|
|
8410
8870
|
return options.allowPromptInjection && options.recallBudgetChars !== 0 ? DEFAULT_DELEGATE_AUTHORIZATION_OPERATIONS : DEFAULT_DELEGATE_AUTHORIZATION_OPERATIONS.filter((operation) => operation !== "recall");
|
|
8411
8871
|
}
|
|
8412
|
-
var delegateNamespaceMigrationChains = /* @__PURE__ */ new Map();
|
|
8413
|
-
var queueDelegateNamespaceMigration = (bindingPath, sessionKey, operation) => {
|
|
8414
|
-
let sessionChains = delegateNamespaceMigrationChains.get(bindingPath);
|
|
8415
|
-
if (sessionChains === void 0) {
|
|
8416
|
-
sessionChains = /* @__PURE__ */ new Map();
|
|
8417
|
-
delegateNamespaceMigrationChains.set(bindingPath, sessionChains);
|
|
8418
|
-
}
|
|
8419
|
-
const prior = sessionChains.get(sessionKey) ?? Promise.resolve();
|
|
8420
|
-
const run = prior.catch(() => void 0).then(operation);
|
|
8421
|
-
const settled = run.then(
|
|
8422
|
-
() => void 0,
|
|
8423
|
-
() => void 0
|
|
8424
|
-
);
|
|
8425
|
-
sessionChains.set(sessionKey, settled);
|
|
8426
|
-
void settled.then(() => {
|
|
8427
|
-
if (sessionChains?.get(sessionKey) !== settled) return;
|
|
8428
|
-
sessionChains.delete(sessionKey);
|
|
8429
|
-
if (sessionChains.size === 0 && delegateNamespaceMigrationChains.get(bindingPath) === sessionChains) {
|
|
8430
|
-
delegateNamespaceMigrationChains.delete(bindingPath);
|
|
8431
|
-
}
|
|
8432
|
-
});
|
|
8433
|
-
return run;
|
|
8434
|
-
};
|
|
8435
|
-
function createDelegateNamespaceBindingStore(memoryDir, serviceId, isLegacyAdapterActive) {
|
|
8436
|
-
const bindingPath = (pluginId) => path9.join(memoryDir, "state", "plugins", pluginId, "session-namespace-bindings.json");
|
|
8437
|
-
const primaryPath = bindingPath(serviceId);
|
|
8438
|
-
const primary = createFileSessionNamespaceBindingStore(primaryPath);
|
|
8439
|
-
if (serviceId !== REMNIC_OPENCLAW_PLUGIN_ID) return primary;
|
|
8440
|
-
const legacy = createFileSessionNamespaceBindingStore(
|
|
8441
|
-
bindingPath(REMNIC_OPENCLAW_LEGACY_PLUGIN_ID)
|
|
8442
|
-
);
|
|
8443
|
-
const migratedLegacySessions = /* @__PURE__ */ new Set();
|
|
8444
|
-
const rememberMigratedLegacySession = (sessionKey) => {
|
|
8445
|
-
if (migratedLegacySessions.has(sessionKey)) return;
|
|
8446
|
-
migratedLegacySessions.add(sessionKey);
|
|
8447
|
-
while (migratedLegacySessions.size > SESSION_NAMESPACE_BINDING_MAX_ENTRIES) {
|
|
8448
|
-
const oldest = migratedLegacySessions.values().next().value;
|
|
8449
|
-
if (oldest === void 0) return;
|
|
8450
|
-
migratedLegacySessions.delete(oldest);
|
|
8451
|
-
}
|
|
8452
|
-
};
|
|
8453
|
-
const queueSessionMigration = (sessionKey, operation) => queueDelegateNamespaceMigration(primaryPath, sessionKey, operation);
|
|
8454
|
-
const readLegacyNamespaces = async (sessionKey, current) => {
|
|
8455
|
-
if (!isLegacyAdapterActive() && migratedLegacySessions.has(sessionKey)) return [];
|
|
8456
|
-
try {
|
|
8457
|
-
const previous = await legacy.namespacesFor(sessionKey);
|
|
8458
|
-
if (previous.length === 0) rememberMigratedLegacySession(sessionKey);
|
|
8459
|
-
return previous;
|
|
8460
|
-
} catch (err) {
|
|
8461
|
-
if (current.length > 0) {
|
|
8462
|
-
log7.warn(
|
|
8463
|
-
`[${serviceId}] delegate legacy namespace read failed; using canonical bindings: ${String(err)}`
|
|
8464
|
-
);
|
|
8465
|
-
return [];
|
|
8466
|
-
}
|
|
8467
|
-
throw err;
|
|
8468
|
-
}
|
|
8469
|
-
};
|
|
8470
|
-
const mergeNamespaceHistory = (current, previous) => {
|
|
8471
|
-
const merged = [];
|
|
8472
|
-
for (const remembered of [...previous, ...current]) {
|
|
8473
|
-
const existing = merged.indexOf(remembered);
|
|
8474
|
-
if (existing >= 0) merged.splice(existing, 1);
|
|
8475
|
-
merged.push(remembered);
|
|
8476
|
-
}
|
|
8477
|
-
return merged.slice(-SESSION_NAMESPACE_BINDING_MAX_NAMESPACES2);
|
|
8478
|
-
};
|
|
8479
|
-
const persistNamespaceHistory = async (store, sessionKey, namespaces) => {
|
|
8480
|
-
if (store.replace !== void 0) {
|
|
8481
|
-
await store.replace(sessionKey, namespaces);
|
|
8482
|
-
return;
|
|
8483
|
-
}
|
|
8484
|
-
for (const namespace of namespaces) {
|
|
8485
|
-
await store.remember(sessionKey, namespace);
|
|
8486
|
-
}
|
|
8487
|
-
};
|
|
8488
|
-
const completeLegacyMigration = async (sessionKey) => {
|
|
8489
|
-
if (!isLegacyAdapterActive()) {
|
|
8490
|
-
try {
|
|
8491
|
-
await legacy.replace?.(sessionKey, []);
|
|
8492
|
-
} catch (err) {
|
|
8493
|
-
log7.warn(`[${serviceId}] delegate legacy namespace cleanup failed: ${String(err)}`);
|
|
8494
|
-
}
|
|
8495
|
-
}
|
|
8496
|
-
rememberMigratedLegacySession(sessionKey);
|
|
8497
|
-
};
|
|
8498
|
-
return {
|
|
8499
|
-
async namespacesFor(sessionKey) {
|
|
8500
|
-
return queueSessionMigration(sessionKey, async () => {
|
|
8501
|
-
const current = await primary.namespacesFor(sessionKey);
|
|
8502
|
-
const previous = await readLegacyNamespaces(sessionKey, current);
|
|
8503
|
-
if (previous.length === 0) return current;
|
|
8504
|
-
const merged = mergeNamespaceHistory(current, previous);
|
|
8505
|
-
const hasMissingLegacy = previous.some((remembered) => !current.includes(remembered));
|
|
8506
|
-
if (!hasMissingLegacy) {
|
|
8507
|
-
await completeLegacyMigration(sessionKey);
|
|
8508
|
-
return current;
|
|
8509
|
-
}
|
|
8510
|
-
try {
|
|
8511
|
-
await persistNamespaceHistory(primary, sessionKey, merged);
|
|
8512
|
-
await completeLegacyMigration(sessionKey);
|
|
8513
|
-
} catch (err) {
|
|
8514
|
-
log7.warn(`[${serviceId}] delegate namespace migration failed: ${String(err)}`);
|
|
8515
|
-
}
|
|
8516
|
-
return merged;
|
|
8517
|
-
});
|
|
8518
|
-
},
|
|
8519
|
-
async remember(sessionKey, namespace) {
|
|
8520
|
-
return queueSessionMigration(sessionKey, async () => {
|
|
8521
|
-
const current = await primary.namespacesFor(sessionKey);
|
|
8522
|
-
const previous = await readLegacyNamespaces(sessionKey, current);
|
|
8523
|
-
if (previous.length === 0) {
|
|
8524
|
-
await primary.remember(sessionKey, namespace);
|
|
8525
|
-
return;
|
|
8526
|
-
}
|
|
8527
|
-
const merged = mergeNamespaceHistory([...current, namespace], previous);
|
|
8528
|
-
await persistNamespaceHistory(primary, sessionKey, merged);
|
|
8529
|
-
await completeLegacyMigration(sessionKey);
|
|
8530
|
-
});
|
|
8531
|
-
}
|
|
8532
|
-
};
|
|
8533
|
-
}
|
|
8534
8872
|
var delegateHookApiServices = /* @__PURE__ */ new WeakMap();
|
|
8535
8873
|
var delegateActiveServiceIds = /* @__PURE__ */ new Set();
|
|
8536
8874
|
var delegateEmbeddedFallbackApis = /* @__PURE__ */ new WeakSet();
|
|
@@ -8539,13 +8877,13 @@ var delegateAuthorizationPreflightServices = /* @__PURE__ */ new WeakMap();
|
|
|
8539
8877
|
function maybeRegisterDelegateRuntime(api, options, deps = { checkHealth: checkDaemonHealthSync }) {
|
|
8540
8878
|
const boundServices = delegateHookApiServices.get(api);
|
|
8541
8879
|
if (boundServices?.has(options.serviceId)) {
|
|
8542
|
-
|
|
8880
|
+
log8.debug(
|
|
8543
8881
|
`delegate register: ${options.serviceId} already has hooks bound on this api \u2014 skipping duplicate registration`
|
|
8544
8882
|
);
|
|
8545
8883
|
return true;
|
|
8546
8884
|
}
|
|
8547
8885
|
if (delegateEmbeddedFallbackApis.has(api)) {
|
|
8548
|
-
|
|
8886
|
+
log8.debug(
|
|
8549
8887
|
`delegate register: ${options.serviceId} previously fell back to embedded on this api \u2014 staying embedded to avoid stacking memory paths`
|
|
8550
8888
|
);
|
|
8551
8889
|
return false;
|
|
@@ -8559,11 +8897,11 @@ function maybeRegisterDelegateRuntime(api, options, deps = { checkHealth: checkD
|
|
|
8559
8897
|
bridge = resolveBridgeMode(options.configBridgeMode, {
|
|
8560
8898
|
memoryDir: options.memoryDir,
|
|
8561
8899
|
timeoutMs: bridgeHealthTimeoutMs,
|
|
8562
|
-
onSkip: (reason) =>
|
|
8900
|
+
onSkip: (reason) => log8.info(`[${options.serviceId}] bridge mode auto: staying embedded \u2014 ${reason}`)
|
|
8563
8901
|
});
|
|
8564
8902
|
} catch (err) {
|
|
8565
8903
|
const wantedDelegate = requestedDelegate(options.configBridgeMode);
|
|
8566
|
-
|
|
8904
|
+
log8.error(
|
|
8567
8905
|
wantedDelegate ? `${String(err)} \u2014 falling back to the embedded runtime` : `${String(err)} \u2014 the deployment is embedded, so this only affects delegate mode`
|
|
8568
8906
|
);
|
|
8569
8907
|
if (!options.passive) delegateEmbeddedFallbackApis.add(api);
|
|
@@ -8571,7 +8909,7 @@ function maybeRegisterDelegateRuntime(api, options, deps = { checkHealth: checkD
|
|
|
8571
8909
|
}
|
|
8572
8910
|
if (bridge.mode !== "delegate") {
|
|
8573
8911
|
if (delegateBoundApis.has(api)) {
|
|
8574
|
-
|
|
8912
|
+
log8.warn(
|
|
8575
8913
|
`[${options.serviceId}] bridge mode resolved embedded, but a sibling service already bound delegate hooks on this api \u2014 reusing them instead of stacking an embedded runtime`
|
|
8576
8914
|
);
|
|
8577
8915
|
return true;
|
|
@@ -8579,25 +8917,39 @@ function maybeRegisterDelegateRuntime(api, options, deps = { checkHealth: checkD
|
|
|
8579
8917
|
if (!options.passive) {
|
|
8580
8918
|
delegateEmbeddedFallbackApis.add(api);
|
|
8581
8919
|
if (resolveRequestedBridgeMode(options.configBridgeMode) === "auto") {
|
|
8582
|
-
|
|
8920
|
+
log8.info(
|
|
8583
8921
|
`[${options.serviceId}] bridge mode auto: embedded hooks are bound on this api \u2014 a daemon that starts later is picked up on the next gateway restart`
|
|
8584
8922
|
);
|
|
8585
8923
|
}
|
|
8586
8924
|
}
|
|
8587
8925
|
return false;
|
|
8588
8926
|
}
|
|
8589
|
-
if (!bridge.healthVerified
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
|
|
8927
|
+
if (!bridge.healthVerified) {
|
|
8928
|
+
const hosts = bridge.daemonHostFallback === void 0 ? [bridge.daemonHost] : [bridge.daemonHost, bridge.daemonHostFallback];
|
|
8929
|
+
const preflightDeadline = Date.now() + bridgeHealthTimeoutMs;
|
|
8930
|
+
const healthyHost = hosts.find((host, index) => {
|
|
8931
|
+
const remaining = index === 0 ? bridgeHealthTimeoutMs : preflightDeadline - Date.now();
|
|
8932
|
+
return remaining > 0 && deps.checkHealth(host, bridge.daemonPort, remaining);
|
|
8933
|
+
});
|
|
8934
|
+
if (healthyHost !== void 0 && healthyHost !== bridge.daemonHost) {
|
|
8935
|
+
log8.info(
|
|
8936
|
+
`[${options.serviceId}] bridge mode delegate: loopback refused, dialing the configured address ${healthyHost}`
|
|
8593
8937
|
);
|
|
8594
|
-
|
|
8938
|
+
bridge = { ...bridge, daemonHost: healthyHost };
|
|
8939
|
+
}
|
|
8940
|
+
if (healthyHost === void 0) {
|
|
8941
|
+
if (delegateBoundApis.has(api)) {
|
|
8942
|
+
log8.warn(
|
|
8943
|
+
`[${options.serviceId}] no healthy daemon at ${hosts.join("/")}:${bridge.daemonPort}, but a sibling service already bound delegate hooks on this api \u2014 reusing them instead of stacking an embedded runtime`
|
|
8944
|
+
);
|
|
8945
|
+
return true;
|
|
8946
|
+
}
|
|
8947
|
+
delegateEmbeddedFallbackApis.add(api);
|
|
8948
|
+
log8.error(
|
|
8949
|
+
`bridge mode delegate requested but no healthy daemon at ${hosts.join("/")}:${bridge.daemonPort} \u2014 falling back to the embedded runtime`
|
|
8950
|
+
);
|
|
8951
|
+
return false;
|
|
8595
8952
|
}
|
|
8596
|
-
delegateEmbeddedFallbackApis.add(api);
|
|
8597
|
-
log7.error(
|
|
8598
|
-
`bridge mode delegate requested but no healthy daemon at ${bridge.daemonHost}:${bridge.daemonPort} \u2014 falling back to the embedded runtime`
|
|
8599
|
-
);
|
|
8600
|
-
return false;
|
|
8601
8953
|
}
|
|
8602
8954
|
if (!options.passive) {
|
|
8603
8955
|
(boundServices ?? delegateHookApiServices.set(api, /* @__PURE__ */ new Set()).get(api))?.add(
|
|
@@ -8607,9 +8959,9 @@ function maybeRegisterDelegateRuntime(api, options, deps = { checkHealth: checkD
|
|
|
8607
8959
|
delegateBoundApis.add(api);
|
|
8608
8960
|
}
|
|
8609
8961
|
const toggleStore = options.sessionTogglesEnabled ? createFileToggleStore(
|
|
8610
|
-
|
|
8962
|
+
path11.join(options.memoryDir, "state", "plugins", options.serviceId, "session-toggles.json"),
|
|
8611
8963
|
{
|
|
8612
|
-
secondaryReadOnlyPath: options.respectBundledActiveMemoryToggle ?
|
|
8964
|
+
secondaryReadOnlyPath: options.respectBundledActiveMemoryToggle ? path11.join(options.memoryDir, "state", "plugins", "active-memory", "session-toggles.json") : void 0
|
|
8613
8965
|
}
|
|
8614
8966
|
) : null;
|
|
8615
8967
|
const target = daemonTargetFor(bridge);
|
|
@@ -8633,6 +8985,8 @@ function maybeRegisterDelegateRuntime(api, options, deps = { checkHealth: checkD
|
|
|
8633
8985
|
cwd: options.cwd,
|
|
8634
8986
|
projectTag: options.projectTag,
|
|
8635
8987
|
flushOnResetEnabled: options.flushOnResetEnabled,
|
|
8988
|
+
openclawToolsEnabled: options.openclawToolsEnabled,
|
|
8989
|
+
openclawToolSnippetMaxChars: options.openclawToolSnippetMaxChars,
|
|
8636
8990
|
capability: options.capability,
|
|
8637
8991
|
recallTimeoutMs: 25e3,
|
|
8638
8992
|
observeTimeoutMs: 12e4,
|
|
@@ -8652,16 +9006,16 @@ function maybeRegisterDelegateRuntime(api, options, deps = { checkHealth: checkD
|
|
|
8652
9006
|
void probe(target, "", operations).then((result) => {
|
|
8653
9007
|
if (result.state === "authorized") return;
|
|
8654
9008
|
if (result.state === "unauthorized") {
|
|
8655
|
-
|
|
9009
|
+
log8.warn(
|
|
8656
9010
|
`delegate authorization preflight rejected ${operationLabel} (${result.status}; token source: ${result.tokenSource}) \u2014 runtime remains active`
|
|
8657
9011
|
);
|
|
8658
9012
|
return;
|
|
8659
9013
|
}
|
|
8660
|
-
|
|
9014
|
+
log8.warn(
|
|
8661
9015
|
`delegate authorization preflight could not verify ${operationLabel} (token source: ${result.tokenSource}) \u2014 runtime remains active`
|
|
8662
9016
|
);
|
|
8663
9017
|
}).catch(() => {
|
|
8664
|
-
|
|
9018
|
+
log8.warn("delegate authorization preflight could not complete \u2014 runtime remains active");
|
|
8665
9019
|
});
|
|
8666
9020
|
}
|
|
8667
9021
|
return true;
|
|
@@ -8782,23 +9136,23 @@ __export(resolve_provider_secret_exports, {
|
|
|
8782
9136
|
findGatewayRuntimeModules: () => findGatewayRuntimeModules
|
|
8783
9137
|
});
|
|
8784
9138
|
__reExport(resolve_provider_secret_exports, resolve_provider_secret_star);
|
|
8785
|
-
import
|
|
9139
|
+
import path12 from "path";
|
|
8786
9140
|
import * as resolve_provider_secret_star from "@remnic/core/resolve-provider-secret";
|
|
8787
9141
|
async function findGatewayRuntimeModules(filePrefix) {
|
|
8788
9142
|
const { existsSync, readFileSync, readdirSync, realpathSync: realpathSync2 } = await import("fs");
|
|
8789
9143
|
const { createRequire: createRequire2 } = await import("module");
|
|
8790
9144
|
const candidates = [];
|
|
8791
9145
|
const isWithinRoot = (root, candidate) => {
|
|
8792
|
-
const relative =
|
|
8793
|
-
return relative.length === 0 || !relative.startsWith("..") && !
|
|
9146
|
+
const relative = path12.relative(root, candidate);
|
|
9147
|
+
return relative.length === 0 || !relative.startsWith("..") && !path12.isAbsolute(relative);
|
|
8794
9148
|
};
|
|
8795
9149
|
let packageRoot;
|
|
8796
9150
|
try {
|
|
8797
9151
|
const req = createRequire2(import.meta.url);
|
|
8798
9152
|
const openclawEntrypoint = realpathSync2(req.resolve("openclaw"));
|
|
8799
|
-
let currentDir =
|
|
9153
|
+
let currentDir = path12.dirname(openclawEntrypoint);
|
|
8800
9154
|
while (true) {
|
|
8801
|
-
const packageJsonPath =
|
|
9155
|
+
const packageJsonPath = path12.join(currentDir, "package.json");
|
|
8802
9156
|
if (existsSync(packageJsonPath)) {
|
|
8803
9157
|
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
8804
9158
|
if (packageJson.name !== "openclaw") {
|
|
@@ -8807,7 +9161,7 @@ async function findGatewayRuntimeModules(filePrefix) {
|
|
|
8807
9161
|
packageRoot = realpathSync2(currentDir);
|
|
8808
9162
|
break;
|
|
8809
9163
|
}
|
|
8810
|
-
const parent =
|
|
9164
|
+
const parent = path12.dirname(currentDir);
|
|
8811
9165
|
if (parent === currentDir) {
|
|
8812
9166
|
return [];
|
|
8813
9167
|
}
|
|
@@ -8817,14 +9171,14 @@ async function findGatewayRuntimeModules(filePrefix) {
|
|
|
8817
9171
|
return [];
|
|
8818
9172
|
}
|
|
8819
9173
|
try {
|
|
8820
|
-
const distDir = realpathSync2(
|
|
9174
|
+
const distDir = realpathSync2(path12.join(packageRoot, "dist"));
|
|
8821
9175
|
if (!isWithinRoot(packageRoot, distDir)) {
|
|
8822
9176
|
return [];
|
|
8823
9177
|
}
|
|
8824
9178
|
const files = readdirSync(distDir);
|
|
8825
9179
|
for (const f of files) {
|
|
8826
9180
|
if (f.startsWith(filePrefix) && f.endsWith(".js")) {
|
|
8827
|
-
const candidate = realpathSync2(
|
|
9181
|
+
const candidate = realpathSync2(path12.join(distDir, f));
|
|
8828
9182
|
if (isWithinRoot(packageRoot, candidate) && isWithinRoot(distDir, candidate)) {
|
|
8829
9183
|
candidates.push(candidate);
|
|
8830
9184
|
}
|
|
@@ -9080,7 +9434,7 @@ async function loadOpenClawProviderAuthRuntimeModule() {
|
|
|
9080
9434
|
if (typeof mod.resolveApiKeyForProvider === "function" || typeof mod.getRuntimeAuthForModel === "function") {
|
|
9081
9435
|
providerAuthRuntimeModule = mod;
|
|
9082
9436
|
providerAuthRuntimeLoaded = true;
|
|
9083
|
-
|
|
9437
|
+
log9.debug("loaded OpenClaw provider auth runtime module");
|
|
9084
9438
|
return providerAuthRuntimeModule;
|
|
9085
9439
|
}
|
|
9086
9440
|
} catch {
|
|
@@ -9089,7 +9443,7 @@ async function loadOpenClawProviderAuthRuntimeModule() {
|
|
|
9089
9443
|
} catch {
|
|
9090
9444
|
}
|
|
9091
9445
|
providerAuthRuntimeNextRetryAt = Date.now() + SECRET_REF_RESOLVER_RETRY_BACKOFF_MS;
|
|
9092
|
-
|
|
9446
|
+
log9.debug(
|
|
9093
9447
|
`OpenClaw provider auth runtime not available \u2014 will retry after ${SECRET_REF_RESOLVER_RETRY_BACKOFF_MS / 1e3}s`
|
|
9094
9448
|
);
|
|
9095
9449
|
return null;
|
|
@@ -9128,7 +9482,7 @@ async function loadOpenClawSecretRefResolver() {
|
|
|
9128
9482
|
if (typeof fn === "function") {
|
|
9129
9483
|
secretRefResolver = fn;
|
|
9130
9484
|
secretRefResolverLoaded = true;
|
|
9131
|
-
|
|
9485
|
+
log9.debug(
|
|
9132
9486
|
`loaded OpenClaw SecretRef resolver "${exportName}" from ${prefix}*.js`
|
|
9133
9487
|
);
|
|
9134
9488
|
return secretRefResolver;
|
|
@@ -9141,7 +9495,7 @@ async function loadOpenClawSecretRefResolver() {
|
|
|
9141
9495
|
} catch {
|
|
9142
9496
|
}
|
|
9143
9497
|
secretRefResolverNextRetryAt = Date.now() + SECRET_REF_RESOLVER_RETRY_BACKOFF_MS;
|
|
9144
|
-
|
|
9498
|
+
log9.debug(
|
|
9145
9499
|
`OpenClaw SecretRef resolver not available \u2014 will retry after ${SECRET_REF_RESOLVER_RETRY_BACKOFF_MS / 1e3}s`
|
|
9146
9500
|
);
|
|
9147
9501
|
return null;
|
|
@@ -9167,7 +9521,7 @@ function resolveOpenClawConfigFilePath() {
|
|
|
9167
9521
|
if (explicitConfigPath && explicitConfigPath.length > 0) {
|
|
9168
9522
|
return expandTildePath3(explicitConfigPath);
|
|
9169
9523
|
}
|
|
9170
|
-
return
|
|
9524
|
+
return path13.join(resolveHomeDir3(), ".openclaw", "openclaw.json");
|
|
9171
9525
|
}
|
|
9172
9526
|
function coerceRawConfigBoolean(value) {
|
|
9173
9527
|
if (typeof value === "boolean") return value;
|
|
@@ -9184,7 +9538,7 @@ function loadPluginEntryFromFile(pluginId) {
|
|
|
9184
9538
|
const config = JSON.parse(content);
|
|
9185
9539
|
return resolveRemnicPluginEntry(config, pluginId);
|
|
9186
9540
|
} catch (err) {
|
|
9187
|
-
|
|
9541
|
+
log9.warn(`Failed to load config from file: ${err}`);
|
|
9188
9542
|
return void 0;
|
|
9189
9543
|
}
|
|
9190
9544
|
}
|
|
@@ -9202,7 +9556,7 @@ function loadRawConfigFromFile() {
|
|
|
9202
9556
|
const config = JSON.parse(content);
|
|
9203
9557
|
return config && typeof config === "object" ? config : void 0;
|
|
9204
9558
|
} catch (err) {
|
|
9205
|
-
|
|
9559
|
+
log9.warn(`Failed to load raw OpenClaw config from file: ${err}`);
|
|
9206
9560
|
return void 0;
|
|
9207
9561
|
}
|
|
9208
9562
|
}
|
|
@@ -9214,10 +9568,10 @@ function readPluginHooksPolicy(apiConfig, pluginId) {
|
|
|
9214
9568
|
}
|
|
9215
9569
|
async function maybeRegisterLiveConnectorCron(orchestrator) {
|
|
9216
9570
|
if (!hasEnabledLiveConnectorConfig(orchestrator.config.connectors)) return;
|
|
9217
|
-
const jobsPath =
|
|
9571
|
+
const jobsPath = path13.join(resolveHomeDir3(), ".openclaw", "cron", "jobs.json");
|
|
9218
9572
|
try {
|
|
9219
9573
|
if (!fileExistsNow(jobsPath)) {
|
|
9220
|
-
|
|
9574
|
+
log9.debug("live connectors cron: jobs.json not found, skipping auto-register");
|
|
9221
9575
|
return;
|
|
9222
9576
|
}
|
|
9223
9577
|
const result = await ensureLiveConnectorCron(jobsPath, {
|
|
@@ -9225,12 +9579,12 @@ async function maybeRegisterLiveConnectorCron(orchestrator) {
|
|
|
9225
9579
|
connectors: orchestrator.config.connectors
|
|
9226
9580
|
});
|
|
9227
9581
|
if (result.created || result.updated) {
|
|
9228
|
-
|
|
9582
|
+
log9.info(`live connectors cron ${result.created ? "auto-registered" : "reconciled"} (${result.jobId})`);
|
|
9229
9583
|
} else {
|
|
9230
|
-
|
|
9584
|
+
log9.debug("live connectors cron already exists, skipping auto-register");
|
|
9231
9585
|
}
|
|
9232
9586
|
} catch (err) {
|
|
9233
|
-
|
|
9587
|
+
log9.debug(`live connectors cron auto-register error: ${err}`);
|
|
9234
9588
|
}
|
|
9235
9589
|
}
|
|
9236
9590
|
function isBundledActiveMemoryEnabledForAgent(runtimeConfig, fileBackedRuntimeConfig, agentId) {
|
|
@@ -9399,13 +9753,13 @@ function registerOpenClawHostEmbeddingProvider(params) {
|
|
|
9399
9753
|
}).then((result) => {
|
|
9400
9754
|
providerInstance = result && typeof result === "object" ? result.provider ?? null : null;
|
|
9401
9755
|
if (!providerInstance) {
|
|
9402
|
-
|
|
9756
|
+
log9.debug(
|
|
9403
9757
|
`OpenClaw host embedding provider ${selected.adapter.id} did not create a provider; using Remnic fallback embeddings`
|
|
9404
9758
|
);
|
|
9405
9759
|
}
|
|
9406
9760
|
return providerInstance;
|
|
9407
9761
|
}).catch((error) => {
|
|
9408
|
-
|
|
9762
|
+
log9.debug(
|
|
9409
9763
|
`OpenClaw host embedding provider ${selected.adapter.id} unavailable: ${error}`
|
|
9410
9764
|
);
|
|
9411
9765
|
return null;
|
|
@@ -9437,7 +9791,7 @@ function registerOpenClawHostEmbeddingProvider(params) {
|
|
|
9437
9791
|
}
|
|
9438
9792
|
};
|
|
9439
9793
|
const unregister = registerHostEmbeddingProvider(cfg.memoryDir, hostProvider);
|
|
9440
|
-
|
|
9794
|
+
log9.info(
|
|
9441
9795
|
`registered OpenClaw host embedding provider bridge (${selected.adapter.id}) for ${serviceId}`
|
|
9442
9796
|
);
|
|
9443
9797
|
return unregister;
|
|
@@ -9572,12 +9926,14 @@ async function embedBatchWithOpenClawProvider(kind, provider, texts, options) {
|
|
|
9572
9926
|
}
|
|
9573
9927
|
var sdkCaps;
|
|
9574
9928
|
var NON_RUNTIME_REGISTRATION_MODES = /* @__PURE__ */ new Set([
|
|
9575
|
-
"discovery",
|
|
9576
|
-
"tool-discovery",
|
|
9577
9929
|
"setup-only",
|
|
9578
9930
|
"setup-runtime",
|
|
9579
9931
|
"cli-metadata"
|
|
9580
9932
|
]);
|
|
9933
|
+
var DISCOVERY_REGISTRATION_MODES = /* @__PURE__ */ new Set([
|
|
9934
|
+
"discovery",
|
|
9935
|
+
"tool-discovery"
|
|
9936
|
+
]);
|
|
9581
9937
|
function isNonRuntimeRegistrationMode(mode) {
|
|
9582
9938
|
return typeof mode === "string" && NON_RUNTIME_REGISTRATION_MODES.has(mode);
|
|
9583
9939
|
}
|
|
@@ -9592,22 +9948,23 @@ var pluginDefinition = {
|
|
|
9592
9948
|
const keys = buildServiceKeys(serviceId);
|
|
9593
9949
|
initLogger(api.logger, false);
|
|
9594
9950
|
sdkCaps = detectSdkCapabilities(api);
|
|
9595
|
-
|
|
9951
|
+
log9.info(
|
|
9596
9952
|
`SDK detection: version=${sdkCaps.sdkVersion}, beforePromptBuild=${sdkCaps.hasBeforePromptBuild}, memoryPromptSection=${sdkCaps.hasRegisterMemoryPromptSection}, memoryCapability=${sdkCaps.hasRegisterMemoryCapability}, typedHooks=${sdkCaps.hasTypedHooks}`
|
|
9597
9953
|
);
|
|
9598
9954
|
if (isNonRuntimeRegistrationMode(sdkCaps.registrationMode)) {
|
|
9599
|
-
|
|
9955
|
+
log9.info(
|
|
9600
9956
|
`registrationMode=${sdkCaps.registrationMode} \u2014 skipping runtime initialization`
|
|
9601
9957
|
);
|
|
9602
9958
|
return;
|
|
9603
9959
|
}
|
|
9604
|
-
const
|
|
9960
|
+
const discoveryPass = typeof sdkCaps.registrationMode === "string" && DISCOVERY_REGISTRATION_MODES.has(sdkCaps.registrationMode);
|
|
9961
|
+
const disableRegisterMigration = readEnvVar("REMNIC_DISABLE_REGISTER_MIGRATION") === "1" || readEnvVar("OPENCLAW_ENGRAM_DISABLE_REGISTER_MIGRATION") === "1" || discoveryPass;
|
|
9605
9962
|
if (!disableRegisterMigration) {
|
|
9606
9963
|
const migrationPromise = globalThis[ENGRAM_MIGRATION_PROMISE] ??= migrateFromEngram({
|
|
9607
9964
|
quiet: true,
|
|
9608
|
-
logger: (message) =>
|
|
9965
|
+
logger: (message) => log9.info(message)
|
|
9609
9966
|
}).catch((error) => {
|
|
9610
|
-
|
|
9967
|
+
log9.warn(`register migration failed: ${error}`);
|
|
9611
9968
|
});
|
|
9612
9969
|
void migrationPromise;
|
|
9613
9970
|
}
|
|
@@ -9641,10 +9998,10 @@ var pluginDefinition = {
|
|
|
9641
9998
|
cfg.providerApiKeyResolver = resolveOpenClawProviderApiKey;
|
|
9642
9999
|
cfg.runtimeAuthForModelResolver = getOpenClawRuntimeAuthForModel;
|
|
9643
10000
|
initLogger(api.logger, cfg.debug);
|
|
9644
|
-
|
|
10001
|
+
log9.info(
|
|
9645
10002
|
`initialized (debug=${cfg.debug}, qmdEnabled=${cfg.qmdEnabled}, transcriptEnabled=${cfg.transcriptEnabled}, hourlySummariesEnabled=${cfg.hourlySummariesEnabled})`
|
|
9646
10003
|
);
|
|
9647
|
-
|
|
10004
|
+
log9.debug(
|
|
9648
10005
|
`init llm routing (modelSource=${cfg.modelSource}, localLlmEnabled=${cfg.localLlmEnabled}${cfg.localLlmFastEnabled ? `, fastLlm=${cfg.localLlmFastModel || "(primary)"}` : ""})`
|
|
9649
10006
|
);
|
|
9650
10007
|
const fileBackedRawRuntimeConfig = loadRawConfigFromFile();
|
|
@@ -9658,7 +10015,7 @@ var pluginDefinition = {
|
|
|
9658
10015
|
});
|
|
9659
10016
|
const passiveMode = slotValidationMode === "passive";
|
|
9660
10017
|
if (passiveMode) {
|
|
9661
|
-
|
|
10018
|
+
log9.info(
|
|
9662
10019
|
`[remnic] memory slot not assigned to ${serviceId}; running passively`
|
|
9663
10020
|
);
|
|
9664
10021
|
}
|
|
@@ -9689,6 +10046,8 @@ var pluginDefinition = {
|
|
|
9689
10046
|
shouldSkipRecall: (sk) => shouldSkipRecallForSession(sk, cfg),
|
|
9690
10047
|
cwd: getOpenClawRuntimeWorkspaceDir(api),
|
|
9691
10048
|
flushOnResetEnabled: cfg.flushOnResetEnabled,
|
|
10049
|
+
openclawToolsEnabled: cfg.openclawToolsEnabled,
|
|
10050
|
+
openclawToolSnippetMaxChars: cfg.openclawToolSnippetMaxChars,
|
|
9692
10051
|
supportPassportModelRoute: delegateSupportPassportGatewayRoute ?? void 0,
|
|
9693
10052
|
// Memory-slot capability inputs. Mirrors the embedded derivation: the
|
|
9694
10053
|
// registration-time runtime agent owns this memory, and QMD is the
|
|
@@ -9740,14 +10099,14 @@ var pluginDefinition = {
|
|
|
9740
10099
|
}
|
|
9741
10100
|
const hookApis = globalThis[keys.HOOK_APIS] ??= /* @__PURE__ */ new WeakSet();
|
|
9742
10101
|
if (hookApis.has(api)) {
|
|
9743
|
-
|
|
10102
|
+
log9.debug(
|
|
9744
10103
|
"register: this api already has hooks bound \u2014 skipping duplicate hook registration"
|
|
9745
10104
|
);
|
|
9746
10105
|
return;
|
|
9747
10106
|
}
|
|
9748
10107
|
hookApis.add(api);
|
|
9749
10108
|
if (!isFirstRegistration) {
|
|
9750
|
-
|
|
10109
|
+
log9.debug(
|
|
9751
10110
|
"register called again (new registry); re-registering hooks with shared orchestrator"
|
|
9752
10111
|
);
|
|
9753
10112
|
}
|
|
@@ -9783,9 +10142,9 @@ var pluginDefinition = {
|
|
|
9783
10142
|
emitLegacyTools: cfg.emitLegacyTools
|
|
9784
10143
|
});
|
|
9785
10144
|
globalThis[keys.ACCESS_HTTP_SERVER] = accessHttpServer;
|
|
9786
|
-
const pluginStateDir =
|
|
9787
|
-
const togglePrimaryPath =
|
|
9788
|
-
const toggleSecondaryPath = cfg.respectBundledActiveMemoryToggle ?
|
|
10145
|
+
const pluginStateDir = path13.join(cfg.memoryDir, "state", "plugins", serviceId);
|
|
10146
|
+
const togglePrimaryPath = path13.join(pluginStateDir, "session-toggles.json");
|
|
10147
|
+
const toggleSecondaryPath = cfg.respectBundledActiveMemoryToggle ? path13.join(cfg.memoryDir, "state", "plugins", "active-memory", "session-toggles.json") : void 0;
|
|
9789
10148
|
const sessionToggleStore = createFileToggleStore2(togglePrimaryPath, {
|
|
9790
10149
|
secondaryReadOnlyPath: toggleSecondaryPath
|
|
9791
10150
|
});
|
|
@@ -9807,11 +10166,11 @@ var pluginDefinition = {
|
|
|
9807
10166
|
}
|
|
9808
10167
|
function resolveDreamJournalPath(runtimeWorkspaceDir) {
|
|
9809
10168
|
const workspaceRoot = resolveWorkspaceRoot(runtimeWorkspaceDir);
|
|
9810
|
-
return
|
|
10169
|
+
return path13.isAbsolute(cfg.dreaming.journalPath) ? cfg.dreaming.journalPath : path13.join(workspaceRoot, cfg.dreaming.journalPath);
|
|
9811
10170
|
}
|
|
9812
10171
|
function resolveHeartbeatJournalPath(runtimeWorkspaceDir) {
|
|
9813
10172
|
const workspaceRoot = resolveWorkspaceRoot(runtimeWorkspaceDir);
|
|
9814
|
-
return
|
|
10173
|
+
return path13.isAbsolute(cfg.heartbeat.journalPath) ? cfg.heartbeat.journalPath : path13.join(workspaceRoot, cfg.heartbeat.journalPath);
|
|
9815
10174
|
}
|
|
9816
10175
|
const existingFlushPlanProcessingChains = globalThis[keys.FLUSH_PLAN_PROCESSING_CHAINS];
|
|
9817
10176
|
const flushPlanProcessingChains = existingFlushPlanProcessingChains instanceof Map ? existingFlushPlanProcessingChains : /* @__PURE__ */ new Map();
|
|
@@ -9831,7 +10190,7 @@ var pluginDefinition = {
|
|
|
9831
10190
|
}
|
|
9832
10191
|
const remainingMs = Math.max(0, deadlineMs - Date.now());
|
|
9833
10192
|
if (remainingMs === 0) {
|
|
9834
|
-
|
|
10193
|
+
log9.warn(
|
|
9835
10194
|
`OpenClaw flush-plan processing timed out before queue wait for ${reason}`
|
|
9836
10195
|
);
|
|
9837
10196
|
return Promise.resolve();
|
|
@@ -9841,7 +10200,7 @@ var pluginDefinition = {
|
|
|
9841
10200
|
task,
|
|
9842
10201
|
new Promise((resolve) => {
|
|
9843
10202
|
timeout = setTimeout(() => {
|
|
9844
|
-
|
|
10203
|
+
log9.warn(
|
|
9845
10204
|
`OpenClaw flush-plan processing timed out while waiting for ${reason}; current drain remains fenced until it settles`
|
|
9846
10205
|
);
|
|
9847
10206
|
resolve();
|
|
@@ -9853,7 +10212,7 @@ var pluginDefinition = {
|
|
|
9853
10212
|
}
|
|
9854
10213
|
async function runOpenClawFlushPlanProcessing(reason, workspaceRoot, deadlineMs) {
|
|
9855
10214
|
if (typeof deadlineMs === "number" && Date.now() >= deadlineMs) {
|
|
9856
|
-
|
|
10215
|
+
log9.warn(
|
|
9857
10216
|
`OpenClaw flush-plan processing timed out before starting for ${reason}`
|
|
9858
10217
|
);
|
|
9859
10218
|
return;
|
|
@@ -9865,26 +10224,26 @@ var pluginDefinition = {
|
|
|
9865
10224
|
serviceId,
|
|
9866
10225
|
ingestor: orchestrator,
|
|
9867
10226
|
logger: {
|
|
9868
|
-
debug: (message) =>
|
|
9869
|
-
info: (message) =>
|
|
9870
|
-
warn: (message) =>
|
|
10227
|
+
debug: (message) => log9.debug(message),
|
|
10228
|
+
info: (message) => log9.info(message),
|
|
10229
|
+
warn: (message) => log9.warn(message)
|
|
9871
10230
|
},
|
|
9872
10231
|
reason,
|
|
9873
10232
|
deadlineMs,
|
|
9874
10233
|
maxTurnChars: cfg.extractionMaxTurnChars
|
|
9875
10234
|
});
|
|
9876
10235
|
if (result.status === "processed" || result.status === "processed_preserved_tail" || result.status === "processed_marker_recovered" || result.status === "processed_marker_recovered_tail" || result.status === "processed_cleanup_deferred") {
|
|
9877
|
-
|
|
10236
|
+
log9.info(
|
|
9878
10237
|
`OpenClaw flush-plan ${result.status}: ${result.bytesProcessed ?? 0} bytes` + (result.preservedBytes ? ` (${result.preservedBytes} bytes preserved)` : "")
|
|
9879
10238
|
);
|
|
9880
10239
|
} else if (result.status === "skipped") {
|
|
9881
|
-
|
|
10240
|
+
log9.warn(
|
|
9882
10241
|
`OpenClaw flush-plan processing skipped: ${result.reason ?? "unknown reason"}`
|
|
9883
10242
|
);
|
|
9884
10243
|
}
|
|
9885
10244
|
} catch (error) {
|
|
9886
10245
|
const detail = displayErrorDetail(error) || "unknown error";
|
|
9887
|
-
|
|
10246
|
+
log9.warn(`OpenClaw flush-plan processing failed: ${detail}`);
|
|
9888
10247
|
}
|
|
9889
10248
|
}
|
|
9890
10249
|
async function queueOpenClawFlushPlanProcessing(reason, runtimeWorkspaceDir, options = {}) {
|
|
@@ -9965,7 +10324,7 @@ var pluginDefinition = {
|
|
|
9965
10324
|
});
|
|
9966
10325
|
},
|
|
9967
10326
|
logger: {
|
|
9968
|
-
debug: (message) =>
|
|
10327
|
+
debug: (message) => log9.debug(message)
|
|
9969
10328
|
}
|
|
9970
10329
|
});
|
|
9971
10330
|
}
|
|
@@ -9977,7 +10336,7 @@ var pluginDefinition = {
|
|
|
9977
10336
|
if (!cfg.dreaming.enabled) return;
|
|
9978
10337
|
const route = resolveDreamNarrativeRoute(cfg, !!dreamNarrativeClient);
|
|
9979
10338
|
if (route.kind === "skip") {
|
|
9980
|
-
|
|
10339
|
+
log9.debug(
|
|
9981
10340
|
"dreaming narrative skipped: no LLM available (set openaiApiKey or use modelSource=gateway)"
|
|
9982
10341
|
);
|
|
9983
10342
|
return;
|
|
@@ -10008,7 +10367,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
10008
10367
|
fallbackLlmRuntimeContextFromConfig2(cfg)
|
|
10009
10368
|
);
|
|
10010
10369
|
if (!route.hasExplicitModel && !llm.isAvailable(route.options)) {
|
|
10011
|
-
|
|
10370
|
+
log9.debug(
|
|
10012
10371
|
"dreaming narrative skipped: no gateway model configured (set dreaming.narrativeModel or taskModelChain)"
|
|
10013
10372
|
);
|
|
10014
10373
|
return;
|
|
@@ -10032,7 +10391,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
10032
10391
|
rawNarrative = typeof response.output_text === "string" ? response.output_text : JSON.stringify(response.output_text ?? "");
|
|
10033
10392
|
}
|
|
10034
10393
|
} catch (error) {
|
|
10035
|
-
|
|
10394
|
+
log9.warn(`dreaming narrative generation failed: ${String(error)}`);
|
|
10036
10395
|
return;
|
|
10037
10396
|
}
|
|
10038
10397
|
const parsed = parseDreamNarrativeResponse(
|
|
@@ -10048,12 +10407,11 @@ Keep the reflection grounded in the evidence below.
|
|
|
10048
10407
|
});
|
|
10049
10408
|
await queueDreamSurfaceSync();
|
|
10050
10409
|
}
|
|
10051
|
-
|
|
10052
|
-
recallAuditDir,
|
|
10053
|
-
|
|
10054
|
-
|
|
10055
|
-
|
|
10056
|
-
});
|
|
10410
|
+
if (!discoveryPass) {
|
|
10411
|
+
void pruneRecallAuditEntries(recallAuditDir, cfg.recallTranscriptRetentionDays).catch((error) => {
|
|
10412
|
+
log9.debug(`recall audit prune failed: ${String(error)}`);
|
|
10413
|
+
});
|
|
10414
|
+
}
|
|
10057
10415
|
const sessionCommandDescriptors = buildSessionCommandDescriptors(serviceId, {
|
|
10058
10416
|
toggles: sessionToggleStore,
|
|
10059
10417
|
getLastRecall: (sessionKey) => orchestrator.getLastRecall(sessionKey),
|
|
@@ -10088,7 +10446,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
10088
10446
|
timeoutMs: cfg.activeRecallTimeoutMs,
|
|
10089
10447
|
cacheTtlMs: cfg.activeRecallCacheTtlMs,
|
|
10090
10448
|
persistTranscripts: cfg.activeRecallPersistTranscripts,
|
|
10091
|
-
transcriptDir:
|
|
10449
|
+
transcriptDir: path13.isAbsolute(cfg.activeRecallTranscriptDir) ? cfg.activeRecallTranscriptDir : path13.join(pluginStateDir, cfg.activeRecallTranscriptDir),
|
|
10092
10450
|
entityGraphDepth: cfg.activeRecallEntityGraphDepth,
|
|
10093
10451
|
includeCausalTrajectories: cfg.activeRecallIncludeCausalTrajectories,
|
|
10094
10452
|
includeDaySummary: cfg.activeRecallIncludeDaySummary,
|
|
@@ -10499,7 +10857,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
10499
10857
|
return;
|
|
10500
10858
|
}
|
|
10501
10859
|
if (typeof orchestrator.flushSession !== "function") {
|
|
10502
|
-
|
|
10860
|
+
log9.warn("codexCompat provider-switch flush unavailable; preserving binding");
|
|
10503
10861
|
return;
|
|
10504
10862
|
}
|
|
10505
10863
|
try {
|
|
@@ -10509,7 +10867,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
10509
10867
|
});
|
|
10510
10868
|
forgetCodexThread2(sessionKey, sessionIdentity.previousCodexThreadId);
|
|
10511
10869
|
} catch (error) {
|
|
10512
|
-
|
|
10870
|
+
log9.warn(`codexCompat provider-switch flush failed: ${String(error)}`);
|
|
10513
10871
|
}
|
|
10514
10872
|
}
|
|
10515
10873
|
async function flushAndForgetRememberedCodexThreadOnMetadataLoss(sessionKey, sessionIdentity) {
|
|
@@ -10527,7 +10885,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
10527
10885
|
return;
|
|
10528
10886
|
}
|
|
10529
10887
|
if (typeof orchestrator.flushSession !== "function") {
|
|
10530
|
-
|
|
10888
|
+
log9.warn("codexCompat metadata-loss flush unavailable; preserving binding");
|
|
10531
10889
|
return;
|
|
10532
10890
|
}
|
|
10533
10891
|
try {
|
|
@@ -10537,7 +10895,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
10537
10895
|
});
|
|
10538
10896
|
forgetCodexThread2(sessionKey, rememberedThreadId);
|
|
10539
10897
|
} catch (error) {
|
|
10540
|
-
|
|
10898
|
+
log9.warn(`codexCompat metadata-loss flush failed: ${String(error)}`);
|
|
10541
10899
|
}
|
|
10542
10900
|
}
|
|
10543
10901
|
async function resolveBeforeResetBufferKeys(sessionKey, sessionIdentity) {
|
|
@@ -10665,7 +11023,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
10665
11023
|
rememberCodexThread2(sessionKey, sessionIdentity.providerThreadId);
|
|
10666
11024
|
if (sessionIdentity.isCodex && !codexCompactionModeLogged) {
|
|
10667
11025
|
const mode = cfg.codexCompat.compactionFlushMode === "auto" ? "auto compaction flush mode (signal + heuristic)" : `${cfg.codexCompat.compactionFlushMode} compaction flush mode`;
|
|
10668
|
-
|
|
11026
|
+
log9.info(
|
|
10669
11027
|
`codexCompat enabled: using ${mode} for bundled Codex sessions`
|
|
10670
11028
|
);
|
|
10671
11029
|
codexCompactionModeLogged = true;
|
|
@@ -10689,12 +11047,12 @@ Keep the reflection grounded in the evidence below.
|
|
|
10689
11047
|
});
|
|
10690
11048
|
clearCodexCompatCaches2(sessionKey, sessionIdentity.providerThreadId);
|
|
10691
11049
|
rememberCodexThread2(sessionKey, sessionIdentity.providerThreadId);
|
|
10692
|
-
|
|
11050
|
+
log9.info(
|
|
10693
11051
|
`codexCompat heuristic flush: thread=${sessionIdentity.providerThreadId} messages ${previousCount} -> ${currentCount}`
|
|
10694
11052
|
);
|
|
10695
11053
|
} catch (error) {
|
|
10696
11054
|
shouldPersistMessageCount = false;
|
|
10697
|
-
|
|
11055
|
+
log9.warn(`codexCompat heuristic flush failed: ${String(error)}`);
|
|
10698
11056
|
}
|
|
10699
11057
|
}
|
|
10700
11058
|
if (typeof currentCount === "number" && shouldPersistMessageCount && codexBaselineKey) {
|
|
@@ -10705,10 +11063,10 @@ Keep the reflection grounded in the evidence below.
|
|
|
10705
11063
|
const runtimeAgent = ctx.runtime?.agent;
|
|
10706
11064
|
const agentId = ctx?.agentId ?? runtimeAgent?.id ?? "main";
|
|
10707
11065
|
const verboseRequested = cfg.verboseRecallVisibility !== false && isVerboseRecallRequested(event, ctx);
|
|
10708
|
-
|
|
11066
|
+
log9.debug(
|
|
10709
11067
|
`${hookLabel}: sessionKey=${sessionKey}, promptLen=${prompt.length}`
|
|
10710
11068
|
);
|
|
10711
|
-
|
|
11069
|
+
log9.debug(
|
|
10712
11070
|
`${hookLabel}: cronRecallMode=${cfg.cronRecallMode}, allowlistCount=${cfg.cronRecallAllowlist.length}`
|
|
10713
11071
|
);
|
|
10714
11072
|
if (sessionKey.includes(":cron:") && cfg.cronRecallMode === "allowlist") {
|
|
@@ -10716,12 +11074,12 @@ Keep the reflection grounded in the evidence below.
|
|
|
10716
11074
|
const re = wildcardToRegExp(pattern);
|
|
10717
11075
|
return re.test(sessionKey);
|
|
10718
11076
|
});
|
|
10719
|
-
|
|
11077
|
+
log9.debug(
|
|
10720
11078
|
`${hookLabel}: cron allowlist match=${matchedPattern ? "yes" : "no"} pattern=${matchedPattern ?? "none"}`
|
|
10721
11079
|
);
|
|
10722
11080
|
}
|
|
10723
11081
|
if (shouldSkipRecallForSession(sessionKey, cfg)) {
|
|
10724
|
-
|
|
11082
|
+
log9.debug(
|
|
10725
11083
|
`${hookLabel}: skip recall for cron session ${sessionKey} (mode=${cfg.cronRecallMode})`
|
|
10726
11084
|
);
|
|
10727
11085
|
return;
|
|
@@ -10745,7 +11103,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
10745
11103
|
injectedChars: 0,
|
|
10746
11104
|
toggleState: auditToggleState
|
|
10747
11105
|
}).catch((error) => {
|
|
10748
|
-
|
|
11106
|
+
log9.debug(`recall audit append failed: ${String(error)}`);
|
|
10749
11107
|
});
|
|
10750
11108
|
}
|
|
10751
11109
|
if (!verboseRequested) return;
|
|
@@ -10799,7 +11157,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
10799
11157
|
const shouldWarnAndSuppressBundledActiveMemoryCollision = cfg.activeRecallEnabled && !cfg.activeRecallAllowChainedActiveMemory && bundledActiveMemoryEnabledForAgent;
|
|
10800
11158
|
if (shouldWarnAndSuppressBundledActiveMemoryCollision && !warnedBundledActiveMemoryCollisionAgents.has(agentId)) {
|
|
10801
11159
|
warnedBundledActiveMemoryCollisionAgents.add(agentId);
|
|
10802
|
-
|
|
11160
|
+
log9.warn(
|
|
10803
11161
|
`active recall suppressed because bundled active-memory plugin is enabled for agent "${agentId}" while activeRecallAllowChainedActiveMemory=false`
|
|
10804
11162
|
);
|
|
10805
11163
|
}
|
|
@@ -10813,7 +11171,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
10813
11171
|
),
|
|
10814
11172
|
currentMessage: prompt
|
|
10815
11173
|
}).catch((error) => {
|
|
10816
|
-
|
|
11174
|
+
log9.debug(`active recall fallback failed: ${String(error)}`);
|
|
10817
11175
|
return null;
|
|
10818
11176
|
});
|
|
10819
11177
|
const activeRecallLines = activeRecallResult?.summary && activeRecallResult.summary.length > 0 ? ["## Active Recall (Remnic)", "", activeRecallResult.summary, ""] : [];
|
|
@@ -10828,7 +11186,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
10828
11186
|
}
|
|
10829
11187
|
});
|
|
10830
11188
|
recallComposition ??= { context };
|
|
10831
|
-
|
|
11189
|
+
log9.debug(
|
|
10832
11190
|
`${hookLabel}: recall returned ${context?.length ?? 0} chars`
|
|
10833
11191
|
);
|
|
10834
11192
|
const lastRecall = orchestrator.getLastRecall(sessionKey);
|
|
@@ -10875,7 +11233,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
10875
11233
|
requestedMode: lastRecall?.requestedMode,
|
|
10876
11234
|
fallbackUsed: lastRecall?.fallbackUsed
|
|
10877
11235
|
}).catch((error) => {
|
|
10878
|
-
|
|
11236
|
+
log9.debug(`recall audit append failed: ${String(error)}`);
|
|
10879
11237
|
});
|
|
10880
11238
|
}
|
|
10881
11239
|
if (mergedLines.length === 0) return;
|
|
@@ -10902,7 +11260,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
10902
11260
|
requestedMode: lastRecall?.requestedMode,
|
|
10903
11261
|
fallbackUsed: lastRecall?.fallbackUsed
|
|
10904
11262
|
}).catch((error) => {
|
|
10905
|
-
|
|
11263
|
+
log9.debug(`recall audit append failed: ${String(error)}`);
|
|
10906
11264
|
});
|
|
10907
11265
|
}
|
|
10908
11266
|
const verboseLines = verboseRequested ? buildVerboseRecallHeader({
|
|
@@ -10922,12 +11280,12 @@ Keep the reflection grounded in the evidence below.
|
|
|
10922
11280
|
const memorySectionLines = memoryContext.lines;
|
|
10923
11281
|
const memoryLines = useMemoryPromptSection ? memorySectionLines : [...auxiliaryLines, ...memorySectionLines];
|
|
10924
11282
|
const promptWithVerbose = useMemoryPromptSection ? auxiliaryLines.length > 0 ? auxiliaryLines.join("\n").replace(/\n$/, "") : void 0 : auxiliaryLines.length > 0 ? [...auxiliaryLines, ...memorySectionLines].join("\n").replace(/\n$/, "") : memoryContext.prompt;
|
|
10925
|
-
|
|
11283
|
+
log9.debug(
|
|
10926
11284
|
`${hookLabel}: returning memory context with ${trimmed.length} chars`
|
|
10927
11285
|
);
|
|
10928
11286
|
return promptWithVerbose ? { prependSystemContext: promptWithVerbose, memoryLines } : { memoryLines };
|
|
10929
11287
|
} catch (err) {
|
|
10930
|
-
|
|
11288
|
+
log9.error("recall failed", err);
|
|
10931
11289
|
lastRecallSummaryBySession.set(sessionKey, null);
|
|
10932
11290
|
clearCodexCompatCaches2(sessionKey, void 0, {
|
|
10933
11291
|
preserveMessageCount: true,
|
|
@@ -10978,7 +11336,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
10978
11336
|
if (useMemoryPromptSection && api.registerMemoryPromptSection) {
|
|
10979
11337
|
if (cfg.activeRecallEnabled && !globalThis[ACTIVE_RECALL_OVERLAP_WARNED]) {
|
|
10980
11338
|
globalThis[ACTIVE_RECALL_OVERLAP_WARNED] = true;
|
|
10981
|
-
|
|
11339
|
+
log9.warn(
|
|
10982
11340
|
"activeRecallEnabled=true while memory-slot prompt injection is active (registerMemoryPromptSection). Prompt injection does NOT require active recall; this runs a second blocking retrieval per turn. Disable activeRecallEnabled unless the secondary pre-reply summary block is intentional (issue #1550)."
|
|
10983
11341
|
);
|
|
10984
11342
|
}
|
|
@@ -11189,7 +11547,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
11189
11547
|
agentIds: capabilityAgentIds
|
|
11190
11548
|
});
|
|
11191
11549
|
} catch (err) {
|
|
11192
|
-
|
|
11550
|
+
log9.error("publicArtifacts.listArtifacts failed", err);
|
|
11193
11551
|
return [];
|
|
11194
11552
|
}
|
|
11195
11553
|
}
|
|
@@ -11206,14 +11564,14 @@ Keep the reflection grounded in the evidence below.
|
|
|
11206
11564
|
}
|
|
11207
11565
|
const builderDesc = !promptInjectionAllowed ? " (promptBuilder omitted \u2014 injection disabled by policy)" : memoryPromptBuilder ? " and promptBuilder (from registerMemoryPromptSection)" : " and promptBuilder (capability-only fallback)";
|
|
11208
11566
|
const capabilityDesc = typeof api.registerMemoryCapability === "function" ? `memory capability with publicArtifacts provider${builderDesc}` : "split memory runtime/flush-plan surfaces";
|
|
11209
|
-
|
|
11567
|
+
log9.info(`registered ${capabilityDesc}`);
|
|
11210
11568
|
}
|
|
11211
11569
|
if (cfg.openclawMessageReceivedCaptureEnabled) {
|
|
11212
11570
|
api.on(
|
|
11213
11571
|
"message_received",
|
|
11214
11572
|
async (event, ctx) => {
|
|
11215
11573
|
if (cfg.heartbeat.enabled && cfg.heartbeat.gateExtractionDuringHeartbeat && isHeartbeatTrigger2(event, ctx)) {
|
|
11216
|
-
|
|
11574
|
+
log9.debug(
|
|
11217
11575
|
`message_received: skipping transcript capture during heartbeat run for ${ctx?.sessionKey ?? "default"}`
|
|
11218
11576
|
);
|
|
11219
11577
|
return;
|
|
@@ -11301,7 +11659,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
11301
11659
|
rememberPendingSparseInboundContentFingerprint2(sparseInboundContentFingerprint);
|
|
11302
11660
|
}
|
|
11303
11661
|
} catch (err) {
|
|
11304
|
-
|
|
11662
|
+
log9.debug(`message_received transcript append failed: ${err}`);
|
|
11305
11663
|
}
|
|
11306
11664
|
}
|
|
11307
11665
|
);
|
|
@@ -11312,7 +11670,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
11312
11670
|
if (!event.success || !Array.isArray(event.messages)) return;
|
|
11313
11671
|
if (event.messages.length === 0) return;
|
|
11314
11672
|
if (cfg.heartbeat.enabled && cfg.heartbeat.gateExtractionDuringHeartbeat && isHeartbeatTrigger2(event, ctx)) {
|
|
11315
|
-
|
|
11673
|
+
log9.debug(
|
|
11316
11674
|
`agent_end: skipping transcript/extraction buffering during heartbeat run for ${ctx?.sessionKey ?? "default"}`
|
|
11317
11675
|
);
|
|
11318
11676
|
return;
|
|
@@ -11375,7 +11733,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
11375
11733
|
messages
|
|
11376
11734
|
});
|
|
11377
11735
|
} catch (error) {
|
|
11378
|
-
|
|
11736
|
+
log9.debug(
|
|
11379
11737
|
`agent_end objective-state writer skipped due to error: ${error}`
|
|
11380
11738
|
);
|
|
11381
11739
|
}
|
|
@@ -11493,11 +11851,11 @@ Keep the reflection grounded in the evidence below.
|
|
|
11493
11851
|
);
|
|
11494
11852
|
}
|
|
11495
11853
|
} catch (lcmErr) {
|
|
11496
|
-
|
|
11854
|
+
log9.debug(`LCM agent_end indexing error: ${lcmErr}`);
|
|
11497
11855
|
}
|
|
11498
11856
|
}
|
|
11499
11857
|
} catch (err) {
|
|
11500
|
-
|
|
11858
|
+
log9.error("agent_end processing failed", err);
|
|
11501
11859
|
}
|
|
11502
11860
|
}
|
|
11503
11861
|
);
|
|
@@ -11523,7 +11881,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
11523
11881
|
clearCodexCompatCaches2(sessionKey, signalThreadId);
|
|
11524
11882
|
rememberCodexThread2(sessionKey, signalThreadId);
|
|
11525
11883
|
} catch (error) {
|
|
11526
|
-
|
|
11884
|
+
log9.warn(`codexCompat signal flush failed: ${String(error)}`);
|
|
11527
11885
|
}
|
|
11528
11886
|
}
|
|
11529
11887
|
if (orchestrator.lcmEngine?.enabled) {
|
|
@@ -11551,7 +11909,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
11551
11909
|
);
|
|
11552
11910
|
await orchestrator.lcmEngine.preCompactionFlush(sessionKey);
|
|
11553
11911
|
} catch (lcmErr) {
|
|
11554
|
-
|
|
11912
|
+
log9.debug(`LCM before_compaction error: ${lcmErr}`);
|
|
11555
11913
|
}
|
|
11556
11914
|
}
|
|
11557
11915
|
if (!orchestrator.config.checkpointEnabled) {
|
|
@@ -11571,10 +11929,10 @@ Keep the reflection grounded in the evidence below.
|
|
|
11571
11929
|
turns: checkpointTurns,
|
|
11572
11930
|
ttl: new Date(Date.now() + 24 * 60 * 60 * 1e3).toISOString()
|
|
11573
11931
|
});
|
|
11574
|
-
|
|
11932
|
+
log9.info(`saved checkpoint for ${sessionKey} before compaction`);
|
|
11575
11933
|
}
|
|
11576
11934
|
} catch (err) {
|
|
11577
|
-
|
|
11935
|
+
log9.error("before_compaction hook failed", err);
|
|
11578
11936
|
}
|
|
11579
11937
|
}
|
|
11580
11938
|
);
|
|
@@ -11614,28 +11972,28 @@ Keep the reflection grounded in the evidence below.
|
|
|
11614
11972
|
);
|
|
11615
11973
|
await orchestrator.lcmEngine.verifyPostCompaction(sessionKey);
|
|
11616
11974
|
} catch (lcmErr) {
|
|
11617
|
-
|
|
11975
|
+
log9.debug(`LCM after_compaction error: ${lcmErr}`);
|
|
11618
11976
|
}
|
|
11619
11977
|
}
|
|
11620
11978
|
void queueOpenClawFlushPlanProcessing("after_compaction", workspaceDir);
|
|
11621
11979
|
if (!orchestrator.config.compactionResetEnabled) {
|
|
11622
|
-
|
|
11980
|
+
log9.debug(
|
|
11623
11981
|
`compaction completed for ${sessionKey}, reset disabled \u2014 skipping`
|
|
11624
11982
|
);
|
|
11625
11983
|
return;
|
|
11626
11984
|
}
|
|
11627
|
-
|
|
11985
|
+
log9.info(
|
|
11628
11986
|
`compaction completed for ${sessionKey}, triggering session reset`
|
|
11629
11987
|
);
|
|
11630
11988
|
const apiAny = api;
|
|
11631
11989
|
if (typeof apiAny.resetSession === "function") {
|
|
11632
11990
|
const result = await apiAny.resetSession(sessionKey, "new");
|
|
11633
11991
|
if (result?.ok === true) {
|
|
11634
|
-
|
|
11992
|
+
log9.info(
|
|
11635
11993
|
`session reset via API for ${sessionKey}, new sessionId=${result.sessionId}`
|
|
11636
11994
|
);
|
|
11637
11995
|
const safeSessionKey = sanitizeSessionKeyForFilename(sessionKey);
|
|
11638
|
-
const signalPath =
|
|
11996
|
+
const signalPath = path13.join(
|
|
11639
11997
|
workspaceDir,
|
|
11640
11998
|
`.compaction-reset-signal-${safeSessionKey}`
|
|
11641
11999
|
);
|
|
@@ -11651,17 +12009,17 @@ Keep the reflection grounded in the evidence below.
|
|
|
11651
12009
|
const errorDetail = result && typeof result === "object" && "error" in result ? String(
|
|
11652
12010
|
result.error ?? "unknown error"
|
|
11653
12011
|
) : `invalid result: ${JSON.stringify(result)}`;
|
|
11654
|
-
|
|
12012
|
+
log9.error(
|
|
11655
12013
|
`api.resetSession failed for ${sessionKey}: ${errorDetail}`
|
|
11656
12014
|
);
|
|
11657
12015
|
}
|
|
11658
12016
|
} else {
|
|
11659
|
-
|
|
12017
|
+
log9.error(
|
|
11660
12018
|
`api.resetSession not available \u2014 compaction reset requires OC fork with PR #29985. Session ${sessionKey} will continue without reset.`
|
|
11661
12019
|
);
|
|
11662
12020
|
}
|
|
11663
12021
|
} catch (err) {
|
|
11664
|
-
|
|
12022
|
+
log9.error("after_compaction reset failed", err);
|
|
11665
12023
|
}
|
|
11666
12024
|
}
|
|
11667
12025
|
);
|
|
@@ -11694,7 +12052,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
11694
12052
|
).catch((error) => {
|
|
11695
12053
|
if (!flushAbort.signal.aborted) {
|
|
11696
12054
|
flushFailed = true;
|
|
11697
|
-
|
|
12055
|
+
log9.warn(
|
|
11698
12056
|
`before_reset flush failed for ${bufferKey}: ${String(error)}`
|
|
11699
12057
|
);
|
|
11700
12058
|
}
|
|
@@ -11727,7 +12085,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
11727
12085
|
);
|
|
11728
12086
|
}
|
|
11729
12087
|
if (flushTimedOut) {
|
|
11730
|
-
|
|
12088
|
+
log9.warn(
|
|
11731
12089
|
`before_reset flush timed out after ${cfg.beforeResetTimeoutMs}ms`
|
|
11732
12090
|
);
|
|
11733
12091
|
}
|
|
@@ -11742,18 +12100,18 @@ Keep the reflection grounded in the evidence below.
|
|
|
11742
12100
|
}
|
|
11743
12101
|
);
|
|
11744
12102
|
} catch (error) {
|
|
11745
|
-
|
|
12103
|
+
log9.debug(`before_reset hook unavailable on this runtime: ${String(error)}`);
|
|
11746
12104
|
}
|
|
11747
12105
|
if (!passiveMode && sdkCaps.hasBeforePromptBuild) {
|
|
11748
12106
|
api.on(
|
|
11749
12107
|
"session_start",
|
|
11750
12108
|
async (event, ctx) => {
|
|
11751
12109
|
const sessionKey = event.sessionKey ?? "default";
|
|
11752
|
-
|
|
12110
|
+
log9.debug(`session_start: ${sessionKey}`);
|
|
11753
12111
|
try {
|
|
11754
12112
|
await orchestrator.maybeRunFileHygiene().catch(() => void 0);
|
|
11755
12113
|
} catch (err) {
|
|
11756
|
-
|
|
12114
|
+
log9.debug(`session_start file hygiene failed: ${err}`);
|
|
11757
12115
|
}
|
|
11758
12116
|
void queueOpenClawFlushPlanProcessing(
|
|
11759
12117
|
"session_start",
|
|
@@ -11766,7 +12124,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
11766
12124
|
"session_end",
|
|
11767
12125
|
async (event, ctx) => {
|
|
11768
12126
|
const sessionKey = event.sessionKey ?? ctx.sessionKey ?? "default";
|
|
11769
|
-
|
|
12127
|
+
log9.debug(`session_end: ${sessionKey}`);
|
|
11770
12128
|
const sessionIdentity = resolveSessionIdentity2(sessionKey, event, ctx);
|
|
11771
12129
|
const rememberedThreadId = sessionIdentity.providerThreadId ?? resolveStoredCodexThreadId2(sessionKey);
|
|
11772
12130
|
const bufferKeys = await resolveBeforeResetBufferKeys(sessionKey, {
|
|
@@ -11784,7 +12142,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
11784
12142
|
});
|
|
11785
12143
|
} catch (error) {
|
|
11786
12144
|
flushFailed = true;
|
|
11787
|
-
|
|
12145
|
+
log9.warn(
|
|
11788
12146
|
`session_end flush failed for ${bufferKey}: ${String(error)}`
|
|
11789
12147
|
);
|
|
11790
12148
|
}
|
|
@@ -11809,7 +12167,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
11809
12167
|
"before_tool_call",
|
|
11810
12168
|
async (event, _ctx) => {
|
|
11811
12169
|
if (event.toolName) {
|
|
11812
|
-
|
|
12170
|
+
log9.debug(`before_tool_call: ${event.toolName}`);
|
|
11813
12171
|
}
|
|
11814
12172
|
}
|
|
11815
12173
|
);
|
|
@@ -11817,7 +12175,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
11817
12175
|
"after_tool_call",
|
|
11818
12176
|
async (event, _ctx) => {
|
|
11819
12177
|
if (event.toolName) {
|
|
11820
|
-
|
|
12178
|
+
log9.debug(
|
|
11821
12179
|
`after_tool_call: ${event.toolName} (${event.durationMs ?? "?"}ms)`
|
|
11822
12180
|
);
|
|
11823
12181
|
}
|
|
@@ -11828,7 +12186,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
11828
12186
|
async (event, ctx) => {
|
|
11829
12187
|
const sessionKey = ctx?.sessionKey ?? "default";
|
|
11830
12188
|
if (event.tokenUsage) {
|
|
11831
|
-
|
|
12189
|
+
log9.debug(
|
|
11832
12190
|
`llm_output: model=${event.model ?? "?"}, tokens=${event.tokenUsage.input ?? 0}/${event.tokenUsage.output ?? 0}, ${event.durationMs ?? "?"}ms, session=${sessionKey}`
|
|
11833
12191
|
);
|
|
11834
12192
|
}
|
|
@@ -11837,7 +12195,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
11837
12195
|
api.on(
|
|
11838
12196
|
"subagent_spawned",
|
|
11839
12197
|
async (event, _ctx) => {
|
|
11840
|
-
|
|
12198
|
+
log9.debug(
|
|
11841
12199
|
`subagent_spawned: ${event.subagentId ?? "?"} purpose=${event.purpose ?? "?"} parent=${event.parentSessionKey ?? "?"}`
|
|
11842
12200
|
);
|
|
11843
12201
|
}
|
|
@@ -11845,7 +12203,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
11845
12203
|
api.on(
|
|
11846
12204
|
"subagent_ended",
|
|
11847
12205
|
async (event, _ctx) => {
|
|
11848
|
-
|
|
12206
|
+
log9.debug(
|
|
11849
12207
|
`subagent_ended: ${event.subagentId ?? "?"} success=${event.success ?? "?"} ${event.durationMs ?? "?"}ms`
|
|
11850
12208
|
);
|
|
11851
12209
|
}
|
|
@@ -11854,8 +12212,8 @@ Keep the reflection grounded in the evidence below.
|
|
|
11854
12212
|
}
|
|
11855
12213
|
async function ensureHourlySummaryCron(api2) {
|
|
11856
12214
|
const jobId = "engram-hourly-summary";
|
|
11857
|
-
const cronFilePath =
|
|
11858
|
-
|
|
12215
|
+
const cronFilePath = path13.join(
|
|
12216
|
+
os2.homedir(),
|
|
11859
12217
|
".openclaw",
|
|
11860
12218
|
"cron",
|
|
11861
12219
|
"jobs.json"
|
|
@@ -11863,7 +12221,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
11863
12221
|
try {
|
|
11864
12222
|
const loadedJobs = await loadHourlySummaryCronJobsData(cronFilePath);
|
|
11865
12223
|
if (loadedJobs.status === "invalid") {
|
|
11866
|
-
|
|
12224
|
+
log9.error(
|
|
11867
12225
|
`hourly summary cron auto-registration skipped: existing jobs file is invalid at ${cronFilePath}`,
|
|
11868
12226
|
loadedJobs.error
|
|
11869
12227
|
);
|
|
@@ -11878,7 +12236,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
11878
12236
|
{ nowMs: Date.now() }
|
|
11879
12237
|
);
|
|
11880
12238
|
if (!changed) {
|
|
11881
|
-
|
|
12239
|
+
log9.debug("hourly summary cron job already up to date");
|
|
11882
12240
|
return;
|
|
11883
12241
|
}
|
|
11884
12242
|
jobsData.jobs[existingIndex] = job;
|
|
@@ -11886,7 +12244,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
11886
12244
|
cronFilePath,
|
|
11887
12245
|
JSON.stringify(jobsData, null, 2)
|
|
11888
12246
|
);
|
|
11889
|
-
|
|
12247
|
+
log9.info("reconciled hourly summary cron job routing to configured task model");
|
|
11890
12248
|
return;
|
|
11891
12249
|
}
|
|
11892
12250
|
const randomMinute = Math.floor(Math.random() * 59) + 1;
|
|
@@ -11900,9 +12258,9 @@ Keep the reflection grounded in the evidence below.
|
|
|
11900
12258
|
cronFilePath,
|
|
11901
12259
|
JSON.stringify(jobsData, null, 2)
|
|
11902
12260
|
);
|
|
11903
|
-
|
|
12261
|
+
log9.info("auto-registered hourly summary cron job");
|
|
11904
12262
|
} catch (err) {
|
|
11905
|
-
|
|
12263
|
+
log9.error("failed to auto-register hourly summary cron job:", err);
|
|
11906
12264
|
}
|
|
11907
12265
|
}
|
|
11908
12266
|
if (typeof api.registerMemoryCorpusSupplement === "function") {
|
|
@@ -11913,32 +12271,32 @@ Keep the reflection grounded in the evidence below.
|
|
|
11913
12271
|
};
|
|
11914
12272
|
const normalizeCorpusPath = (value) => value.trim().replace(/\\/g, "/").replace(/^\.\//, "");
|
|
11915
12273
|
const pathIsInside = (root, candidate) => {
|
|
11916
|
-
const relative =
|
|
11917
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
12274
|
+
const relative = path13.relative(root, candidate);
|
|
12275
|
+
return relative === "" || !relative.startsWith("..") && !path13.isAbsolute(relative);
|
|
11918
12276
|
};
|
|
11919
12277
|
const corpusPathCandidates = (rawPath, storageDir) => {
|
|
11920
12278
|
const candidates = /* @__PURE__ */ new Set();
|
|
11921
12279
|
const trimmed = rawPath.trim();
|
|
11922
12280
|
if (!trimmed) return [];
|
|
11923
12281
|
candidates.add(normalizeCorpusPath(trimmed));
|
|
11924
|
-
if (
|
|
11925
|
-
const absolutePath =
|
|
11926
|
-
const absoluteStorageDir =
|
|
12282
|
+
if (path13.isAbsolute(trimmed)) {
|
|
12283
|
+
const absolutePath = path13.resolve(trimmed);
|
|
12284
|
+
const absoluteStorageDir = path13.resolve(storageDir);
|
|
11927
12285
|
if (pathIsInside(absoluteStorageDir, absolutePath)) {
|
|
11928
|
-
candidates.add(normalizeCorpusPath(
|
|
12286
|
+
candidates.add(normalizeCorpusPath(path13.relative(absoluteStorageDir, absolutePath)));
|
|
11929
12287
|
}
|
|
11930
12288
|
}
|
|
11931
|
-
candidates.add(
|
|
12289
|
+
candidates.add(path13.basename(trimmed));
|
|
11932
12290
|
return [...candidates].filter((candidate) => candidate.length > 0);
|
|
11933
12291
|
};
|
|
11934
12292
|
const displayCorpusPath = (rawPath, storageDir) => {
|
|
11935
12293
|
const trimmed = rawPath.trim();
|
|
11936
12294
|
if (!trimmed) return "";
|
|
11937
|
-
if (
|
|
11938
|
-
const absolutePath =
|
|
11939
|
-
const absoluteStorageDir =
|
|
12295
|
+
if (path13.isAbsolute(trimmed)) {
|
|
12296
|
+
const absolutePath = path13.resolve(trimmed);
|
|
12297
|
+
const absoluteStorageDir = path13.resolve(storageDir);
|
|
11940
12298
|
if (pathIsInside(absoluteStorageDir, absolutePath)) {
|
|
11941
|
-
return normalizeCorpusPath(
|
|
12299
|
+
return normalizeCorpusPath(path13.relative(absoluteStorageDir, absolutePath));
|
|
11942
12300
|
}
|
|
11943
12301
|
}
|
|
11944
12302
|
return normalizeCorpusPath(trimmed);
|
|
@@ -12012,7 +12370,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
12012
12370
|
};
|
|
12013
12371
|
});
|
|
12014
12372
|
} catch (err) {
|
|
12015
|
-
|
|
12373
|
+
log9.warn(`memory corpus search failed: ${err}`);
|
|
12016
12374
|
throw corpusBackendError("search", err);
|
|
12017
12375
|
}
|
|
12018
12376
|
},
|
|
@@ -12048,30 +12406,31 @@ Keep the reflection grounded in the evidence below.
|
|
|
12048
12406
|
updatedAt: memory.frontmatter.updated
|
|
12049
12407
|
};
|
|
12050
12408
|
} catch (err) {
|
|
12051
|
-
|
|
12409
|
+
log9.warn(`memory corpus get failed: ${err}`);
|
|
12052
12410
|
throw corpusBackendError("get", err);
|
|
12053
12411
|
}
|
|
12054
12412
|
}
|
|
12055
12413
|
};
|
|
12056
12414
|
api.registerMemoryCorpusSupplement(remnicCorpusSupplement);
|
|
12057
12415
|
}
|
|
12058
|
-
|
|
12059
|
-
|
|
12060
|
-
|
|
12061
|
-
|
|
12062
|
-
|
|
12063
|
-
|
|
12064
|
-
|
|
12065
|
-
|
|
12416
|
+
const adaptersEnabled = cfg.openclawToolsEnabled !== false;
|
|
12417
|
+
const ownedToolNames = registerEmbeddedTools(api, {
|
|
12418
|
+
enabled: adaptersEnabled,
|
|
12419
|
+
passive: passiveMode,
|
|
12420
|
+
tools: adaptersEnabled ? [
|
|
12421
|
+
buildMemorySearchTool(orchestrator, { snippetMaxChars: cfg.openclawToolSnippetMaxChars }),
|
|
12422
|
+
buildMemoryGetTool(orchestrator)
|
|
12423
|
+
] : [buildLegacyMemorySearchToolForPublicShape(orchestrator)]
|
|
12424
|
+
});
|
|
12066
12425
|
const commandApi = api;
|
|
12067
|
-
if (!passiveMode && cfg.commandsListEnabled && cfg.sessionTogglesEnabled !== false) {
|
|
12426
|
+
if (!discoveryPass && !passiveMode && cfg.commandsListEnabled && cfg.sessionTogglesEnabled !== false) {
|
|
12068
12427
|
if (typeof commandApi.registerCommand === "function" && !globalThis[SESSION_COMMANDS_REGISTERED_GUARD]) {
|
|
12069
12428
|
globalThis[SESSION_COMMANDS_REGISTERED_GUARD] = true;
|
|
12070
12429
|
for (const descriptor of sessionCommandDescriptors) {
|
|
12071
12430
|
commandApi.registerCommand(descriptor);
|
|
12072
12431
|
}
|
|
12073
12432
|
} else if (typeof commandApi.registerCommand !== "function") {
|
|
12074
|
-
|
|
12433
|
+
log9.debug(
|
|
12075
12434
|
"registerCommand unavailable on this runtime; skipping Remnic session command descriptors because commands.list is a gateway RPC surface, not a typed plugin hook"
|
|
12076
12435
|
);
|
|
12077
12436
|
}
|
|
@@ -12080,7 +12439,9 @@ Keep the reflection grounded in the evidence below.
|
|
|
12080
12439
|
api,
|
|
12081
12440
|
orchestrator,
|
|
12082
12441
|
// Host-native, model-inaccessible origin for shared-context tool writes.
|
|
12083
|
-
getOpenClawRuntimeAgentId(api)
|
|
12442
|
+
getOpenClawRuntimeAgentId(api),
|
|
12443
|
+
// Names the shared ownership record already carries.
|
|
12444
|
+
ownedToolNames
|
|
12084
12445
|
);
|
|
12085
12446
|
if (orchestrator.lcmEngine?.enabled) {
|
|
12086
12447
|
registerLcmTools(
|
|
@@ -12088,7 +12449,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
12088
12449
|
orchestrator.lcmEngine
|
|
12089
12450
|
);
|
|
12090
12451
|
}
|
|
12091
|
-
if (!globalThis[CLI_REGISTERED_GUARD]) {
|
|
12452
|
+
if (!discoveryPass && !globalThis[CLI_REGISTERED_GUARD]) {
|
|
12092
12453
|
globalThis[CLI_REGISTERED_GUARD] = true;
|
|
12093
12454
|
registerCli(
|
|
12094
12455
|
api,
|
|
@@ -12110,7 +12471,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
12110
12471
|
if (globalThis[keys.SERVICE_STARTED]) return;
|
|
12111
12472
|
}
|
|
12112
12473
|
if (globalThis[keys.SERVICE_STARTED]) {
|
|
12113
|
-
|
|
12474
|
+
log9.debug(
|
|
12114
12475
|
`${serviceId}: service.start() called again \u2014 skipping duplicate init`
|
|
12115
12476
|
);
|
|
12116
12477
|
return;
|
|
@@ -12123,10 +12484,10 @@ Keep the reflection grounded in the evidence below.
|
|
|
12123
12484
|
globalThis[keys.ORCHESTRATOR] = orchestrator;
|
|
12124
12485
|
const initPromise = (async () => {
|
|
12125
12486
|
try {
|
|
12126
|
-
|
|
12487
|
+
log9.info("initializing engram memory system...");
|
|
12127
12488
|
await orchestrator.initialize();
|
|
12128
12489
|
if (!didCountStart) return;
|
|
12129
|
-
activeOpikExporter = createOpikExporter({},
|
|
12490
|
+
activeOpikExporter = createOpikExporter({}, log9);
|
|
12130
12491
|
if (activeOpikExporter) activeOpikExporter.subscribe();
|
|
12131
12492
|
if (orchestrator.config.transcriptEnabled) {
|
|
12132
12493
|
await orchestrator.transcript.cleanup(
|
|
@@ -12138,7 +12499,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
12138
12499
|
await ensureHourlySummaryCron(api);
|
|
12139
12500
|
if (!didCountStart) return;
|
|
12140
12501
|
} else if (orchestrator.config.hourlySummariesEnabled) {
|
|
12141
|
-
|
|
12502
|
+
log9.info(
|
|
12142
12503
|
"hourly summaries enabled; cron auto-register is disabled. To schedule summaries, create an isolated/agentTurn cron job that calls `memory_summarize_hourly`."
|
|
12143
12504
|
);
|
|
12144
12505
|
}
|
|
@@ -12154,7 +12515,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
12154
12515
|
resolveDreamJournalPath(),
|
|
12155
12516
|
() => {
|
|
12156
12517
|
void queueDreamSurfaceSync().catch((error) => {
|
|
12157
|
-
|
|
12518
|
+
log9.debug(`dream surface watch sync failed: ${String(error)}`);
|
|
12158
12519
|
});
|
|
12159
12520
|
}
|
|
12160
12521
|
);
|
|
@@ -12174,7 +12535,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
12174
12535
|
resolveHeartbeatJournalPath(),
|
|
12175
12536
|
() => {
|
|
12176
12537
|
void queueHeartbeatSurfaceSync().catch((error) => {
|
|
12177
|
-
|
|
12538
|
+
log9.debug(`heartbeat surface watch sync failed: ${String(error)}`);
|
|
12178
12539
|
});
|
|
12179
12540
|
}
|
|
12180
12541
|
);
|
|
@@ -12195,17 +12556,17 @@ Keep the reflection grounded in the evidence below.
|
|
|
12195
12556
|
}
|
|
12196
12557
|
try {
|
|
12197
12558
|
const status = await accessHttpServer.start();
|
|
12198
|
-
|
|
12559
|
+
log9.info(
|
|
12199
12560
|
`engram access HTTP ready at http://${status.host}:${status.port}`
|
|
12200
12561
|
);
|
|
12201
12562
|
} catch (err) {
|
|
12202
|
-
|
|
12563
|
+
log9.error("failed to start engram access HTTP server", err);
|
|
12203
12564
|
}
|
|
12204
12565
|
}
|
|
12205
12566
|
if (!didCountStart) return;
|
|
12206
12567
|
globalThis[keys.SERVICE_STARTED] = true;
|
|
12207
|
-
|
|
12208
|
-
|
|
12568
|
+
log9.info("engram memory system ready");
|
|
12569
|
+
log9.info(
|
|
12209
12570
|
`gateway_start fired \u2014 Remnic memory plugin is active (id=${pluginDefinition.id}, memoryDir=${cfg.memoryDir})`
|
|
12210
12571
|
);
|
|
12211
12572
|
void queueOpenClawFlushPlanProcessing(
|
|
@@ -12279,7 +12640,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
12279
12640
|
try {
|
|
12280
12641
|
activeOpikExporter?.unsubscribe();
|
|
12281
12642
|
} catch (err) {
|
|
12282
|
-
|
|
12643
|
+
log9.debug(`engram opik exporter unsubscribe failed: ${err}`);
|
|
12283
12644
|
}
|
|
12284
12645
|
activeOpikExporter = null;
|
|
12285
12646
|
const finishCodex = beginCodexSubscriptionShutdown(getCodexSubscriptionRunnerForOwner(cfg));
|
|
@@ -12287,7 +12648,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
12287
12648
|
try {
|
|
12288
12649
|
await accessHttpServer.stop();
|
|
12289
12650
|
} catch (err) {
|
|
12290
|
-
|
|
12651
|
+
log9.debug(`engram access HTTP stop failed: ${err}`);
|
|
12291
12652
|
}
|
|
12292
12653
|
stopDreamWatcher?.();
|
|
12293
12654
|
stopDreamWatcher = null;
|
|
@@ -12298,7 +12659,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
12298
12659
|
try {
|
|
12299
12660
|
await orchestrator.destroy();
|
|
12300
12661
|
} catch (err) {
|
|
12301
|
-
|
|
12662
|
+
log9.debug(`engram orchestrator destroy on stop failed: ${err}`);
|
|
12302
12663
|
}
|
|
12303
12664
|
} finally {
|
|
12304
12665
|
finishCodex();
|
|
@@ -12324,7 +12685,7 @@ Keep the reflection grounded in the evidence below.
|
|
|
12324
12685
|
if (!secondaryTookOver) {
|
|
12325
12686
|
globalThis[keys.SERVICE_STARTED] = false;
|
|
12326
12687
|
}
|
|
12327
|
-
|
|
12688
|
+
log9.info("stopped");
|
|
12328
12689
|
}
|
|
12329
12690
|
});
|
|
12330
12691
|
}
|
|
@@ -12460,9 +12821,9 @@ function truncateMetadataValue(value, maxChars) {
|
|
|
12460
12821
|
return value.length <= maxChars ? value : value.slice(0, maxChars);
|
|
12461
12822
|
}
|
|
12462
12823
|
async function resolveFlushPlanProcessingChainKey(workspaceRoot) {
|
|
12463
|
-
const lexicalRoot =
|
|
12824
|
+
const lexicalRoot = path13.resolve(workspaceRoot);
|
|
12464
12825
|
try {
|
|
12465
|
-
return
|
|
12826
|
+
return path13.resolve(await realPathLater(lexicalRoot));
|
|
12466
12827
|
} catch {
|
|
12467
12828
|
return lexicalRoot;
|
|
12468
12829
|
}
|