@joshuaswarren/openclaw-engram 9.0.84 → 9.0.86
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 +171 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1096,6 +1096,7 @@ function buildRecallPipelineConfig(cfg) {
|
|
|
1096
1096
|
import path42 from "path";
|
|
1097
1097
|
import os3 from "os";
|
|
1098
1098
|
import { createHash as createHash7 } from "crypto";
|
|
1099
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1099
1100
|
import { mkdir as mkdir29, readdir as readdir14, readFile as readFile23, stat as stat6, unlink as unlink5, writeFile as writeFile28 } from "fs/promises";
|
|
1100
1101
|
|
|
1101
1102
|
// src/signal.ts
|
|
@@ -20978,6 +20979,11 @@ var Orchestrator = class _Orchestrator {
|
|
|
20978
20979
|
if (this.compounding) {
|
|
20979
20980
|
await this.compounding.ensureDirs();
|
|
20980
20981
|
}
|
|
20982
|
+
if (this.resolveInit) {
|
|
20983
|
+
this.resolveInit();
|
|
20984
|
+
this.resolveInit = null;
|
|
20985
|
+
log.info("init gate opened (essential state loaded)");
|
|
20986
|
+
}
|
|
20981
20987
|
{
|
|
20982
20988
|
const available = await this.qmd.probe();
|
|
20983
20989
|
if (available) {
|
|
@@ -21047,10 +21053,58 @@ var Orchestrator = class _Orchestrator {
|
|
|
21047
21053
|
log.debug("initialize: stale signal sweep failed:", err);
|
|
21048
21054
|
}
|
|
21049
21055
|
}
|
|
21050
|
-
log.info("orchestrator initialized");
|
|
21051
|
-
if (this.
|
|
21052
|
-
this.
|
|
21053
|
-
|
|
21056
|
+
log.info("orchestrator initialized (full)");
|
|
21057
|
+
if (this.config.daySummaryEnabled) {
|
|
21058
|
+
this.autoRegisterDaySummaryCron().catch((err) => {
|
|
21059
|
+
log.debug(`day-summary cron auto-register failed (non-fatal): ${err}`);
|
|
21060
|
+
});
|
|
21061
|
+
}
|
|
21062
|
+
}
|
|
21063
|
+
/**
|
|
21064
|
+
* Auto-register the engram-day-summary cron job in OpenClaw if it doesn't exist.
|
|
21065
|
+
* Fire-and-forget — never blocks init or crashes on failure.
|
|
21066
|
+
*/
|
|
21067
|
+
async autoRegisterDaySummaryCron() {
|
|
21068
|
+
const CRON_ID = "engram-day-summary";
|
|
21069
|
+
const home = process.env.HOME || os3.homedir();
|
|
21070
|
+
const jobsPath = path42.join(home, ".openclaw", "cron", "jobs.json");
|
|
21071
|
+
try {
|
|
21072
|
+
if (!existsSync6(jobsPath)) {
|
|
21073
|
+
log.debug("day-summary cron: jobs.json not found, skipping auto-register");
|
|
21074
|
+
return;
|
|
21075
|
+
}
|
|
21076
|
+
const raw = await readFile23(jobsPath, "utf-8");
|
|
21077
|
+
const parsed = JSON.parse(raw);
|
|
21078
|
+
const jobsArray = Array.isArray(parsed) ? parsed : Array.isArray(parsed?.jobs) ? parsed.jobs : null;
|
|
21079
|
+
if (!jobsArray) {
|
|
21080
|
+
log.debug("day-summary cron: jobs.json has unexpected structure, skipping auto-register");
|
|
21081
|
+
return;
|
|
21082
|
+
}
|
|
21083
|
+
if (jobsArray.some((j) => j.id === CRON_ID)) {
|
|
21084
|
+
log.debug("day-summary cron already exists, skipping auto-register");
|
|
21085
|
+
return;
|
|
21086
|
+
}
|
|
21087
|
+
jobsArray.push({
|
|
21088
|
+
id: CRON_ID,
|
|
21089
|
+
agentId: "main",
|
|
21090
|
+
name: "Engram Day Summary (auto)",
|
|
21091
|
+
enabled: true,
|
|
21092
|
+
schedule: { kind: "cron", expr: "47 23 * * *", tz: Intl.DateTimeFormat().resolvedOptions().timeZone },
|
|
21093
|
+
sessionTarget: "isolated",
|
|
21094
|
+
wakeMode: "now",
|
|
21095
|
+
payload: {
|
|
21096
|
+
kind: "agentTurn",
|
|
21097
|
+
timeoutSeconds: 900,
|
|
21098
|
+
thinking: "off",
|
|
21099
|
+
message: "You are OpenClaw automation. Call tool engram.day_summary with empty params (it will auto-gather today's facts). If successful output exactly NO_REPLY. On error output one concise line. Do NOT use message tool."
|
|
21100
|
+
},
|
|
21101
|
+
delivery: { mode: "none" }
|
|
21102
|
+
});
|
|
21103
|
+
const output = Array.isArray(parsed) ? jobsArray : { ...parsed, jobs: jobsArray };
|
|
21104
|
+
await writeFile28(jobsPath, JSON.stringify(output, null, 2) + "\n", "utf-8");
|
|
21105
|
+
log.info(`day-summary cron auto-registered (engram-day-summary, 23:47 ${Intl.DateTimeFormat().resolvedOptions().timeZone})`);
|
|
21106
|
+
} catch (err) {
|
|
21107
|
+
log.debug(`day-summary cron auto-register error: ${err}`);
|
|
21054
21108
|
}
|
|
21055
21109
|
}
|
|
21056
21110
|
async applyBehaviorRuntimePolicy(state) {
|
|
@@ -21168,6 +21222,112 @@ var Orchestrator = class _Orchestrator {
|
|
|
21168
21222
|
}
|
|
21169
21223
|
return this.extraction.generateDaySummary(memories);
|
|
21170
21224
|
}
|
|
21225
|
+
/**
|
|
21226
|
+
* Auto-gather today's facts and hourly summaries from storage, then generate a day summary.
|
|
21227
|
+
* Returns null if no facts are found for today.
|
|
21228
|
+
*/
|
|
21229
|
+
async generateDaySummaryAuto(namespace) {
|
|
21230
|
+
const gathered = await this.gatherTodayFacts(namespace);
|
|
21231
|
+
if (!gathered || !gathered.trim()) {
|
|
21232
|
+
log.warn("generateDaySummaryAuto: no facts found for today, skipping");
|
|
21233
|
+
return null;
|
|
21234
|
+
}
|
|
21235
|
+
return this.generateDaySummary(gathered);
|
|
21236
|
+
}
|
|
21237
|
+
/**
|
|
21238
|
+
* Read today's facts and hourly summaries from storage, returning them
|
|
21239
|
+
* as a formatted string suitable for generateDaySummary().
|
|
21240
|
+
*/
|
|
21241
|
+
async gatherTodayFacts(namespace) {
|
|
21242
|
+
const ns = namespace && namespace.length > 0 ? namespace : this.config.defaultNamespace;
|
|
21243
|
+
const storage = await this.storageRouter.storageFor(ns);
|
|
21244
|
+
const now = /* @__PURE__ */ new Date();
|
|
21245
|
+
const utcToday = now.toISOString().slice(0, 10);
|
|
21246
|
+
const yesterday = new Date(now.getTime() - 864e5).toISOString().slice(0, 10);
|
|
21247
|
+
const datesToScan = [yesterday, utcToday].filter((v, i, a) => a.indexOf(v) === i);
|
|
21248
|
+
const factsBaseDir = path42.join(storage.dir, "facts");
|
|
21249
|
+
const MAX_CHARS = 1e5;
|
|
21250
|
+
const facts = [];
|
|
21251
|
+
for (const date of datesToScan) {
|
|
21252
|
+
const factsDir = path42.join(factsBaseDir, date);
|
|
21253
|
+
try {
|
|
21254
|
+
const entries = await readdir14(factsDir, { withFileTypes: true });
|
|
21255
|
+
for (const entry of entries) {
|
|
21256
|
+
if (!entry.name.endsWith(".md")) continue;
|
|
21257
|
+
const fullPath = path42.join(factsDir, entry.name);
|
|
21258
|
+
try {
|
|
21259
|
+
const raw = await readFile23(fullPath, "utf-8");
|
|
21260
|
+
const fmMatch = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
21261
|
+
if (!fmMatch) continue;
|
|
21262
|
+
const fmBlock = fmMatch[1];
|
|
21263
|
+
const content = fmMatch[2].trim();
|
|
21264
|
+
const fm = {};
|
|
21265
|
+
for (const line of fmBlock.split("\n")) {
|
|
21266
|
+
const colonIdx = line.indexOf(":");
|
|
21267
|
+
if (colonIdx === -1) continue;
|
|
21268
|
+
fm[line.slice(0, colonIdx).trim()] = line.slice(colonIdx + 1).trim();
|
|
21269
|
+
}
|
|
21270
|
+
facts.push({
|
|
21271
|
+
path: fullPath,
|
|
21272
|
+
frontmatter: {
|
|
21273
|
+
id: fm.id || path42.basename(entry.name, ".md"),
|
|
21274
|
+
category: fm.category || "fact",
|
|
21275
|
+
created: fm.created || "unknown",
|
|
21276
|
+
updated: fm.updated || fm.created || "unknown",
|
|
21277
|
+
source: fm.source || "unknown",
|
|
21278
|
+
confidence: parseFloat(fm.confidence || "0.8"),
|
|
21279
|
+
confidenceTier: fm.confidenceTier || "implied",
|
|
21280
|
+
tags: []
|
|
21281
|
+
},
|
|
21282
|
+
content
|
|
21283
|
+
});
|
|
21284
|
+
} catch {
|
|
21285
|
+
}
|
|
21286
|
+
}
|
|
21287
|
+
} catch {
|
|
21288
|
+
}
|
|
21289
|
+
}
|
|
21290
|
+
facts.sort((a, b) => a.frontmatter.created < b.frontmatter.created ? -1 : 1);
|
|
21291
|
+
const hourlySummaries = [];
|
|
21292
|
+
const hourlyBaseDir = path42.join(storage.dir, "summaries", "hourly");
|
|
21293
|
+
try {
|
|
21294
|
+
const sessionKeys = await readdir14(hourlyBaseDir, { withFileTypes: true });
|
|
21295
|
+
for (const sk of sessionKeys) {
|
|
21296
|
+
if (!sk.isDirectory()) continue;
|
|
21297
|
+
for (const date of datesToScan) {
|
|
21298
|
+
const summaryFile = path42.join(hourlyBaseDir, sk.name, `${date}.md`);
|
|
21299
|
+
try {
|
|
21300
|
+
const raw = await readFile23(summaryFile, "utf-8");
|
|
21301
|
+
if (raw.trim().length > 0) {
|
|
21302
|
+
hourlySummaries.push(raw.trim());
|
|
21303
|
+
}
|
|
21304
|
+
} catch {
|
|
21305
|
+
}
|
|
21306
|
+
}
|
|
21307
|
+
}
|
|
21308
|
+
} catch {
|
|
21309
|
+
}
|
|
21310
|
+
let formatted = formatDaySummaryMemories(facts);
|
|
21311
|
+
if (hourlySummaries.length > 0) {
|
|
21312
|
+
formatted += "\n\n---\n## Hourly Summaries\n\n" + hourlySummaries.join("\n\n---\n\n");
|
|
21313
|
+
}
|
|
21314
|
+
if (formatted.length > MAX_CHARS) {
|
|
21315
|
+
while (facts.length > 1 && formatted.length > MAX_CHARS) {
|
|
21316
|
+
facts.shift();
|
|
21317
|
+
formatted = formatDaySummaryMemories(facts);
|
|
21318
|
+
if (hourlySummaries.length > 0) {
|
|
21319
|
+
formatted += "\n\n---\n## Hourly Summaries\n\n" + hourlySummaries.join("\n\n---\n\n");
|
|
21320
|
+
}
|
|
21321
|
+
}
|
|
21322
|
+
if (formatted.length > MAX_CHARS) {
|
|
21323
|
+
formatted = formatted.slice(0, MAX_CHARS);
|
|
21324
|
+
}
|
|
21325
|
+
}
|
|
21326
|
+
log.info(
|
|
21327
|
+
`gatherTodayFacts: collected ${facts.length} facts, ${hourlySummaries.length} hourly summaries (${formatted.length} chars)`
|
|
21328
|
+
);
|
|
21329
|
+
return formatted;
|
|
21330
|
+
}
|
|
21171
21331
|
previewMemoryActionEvent(event) {
|
|
21172
21332
|
const namespace = typeof event.namespace === "string" && event.namespace.length > 0 ? event.namespace : this.config.defaultNamespace;
|
|
21173
21333
|
const eligibility = parseMemoryActionEligibilityContext(event.policyEligibility);
|
|
@@ -34932,11 +35092,11 @@ var EngramAccessService = class {
|
|
|
34932
35092
|
if (!this.orchestrator.config.daySummaryEnabled) {
|
|
34933
35093
|
throw new EngramAccessInputError("day summary is disabled");
|
|
34934
35094
|
}
|
|
34935
|
-
const memories = request.memories.trim();
|
|
35095
|
+
const memories = (request.memories ?? "").trim();
|
|
35096
|
+
const namespace = this.resolveRecallNamespace(request.namespace, request.sessionKey);
|
|
34936
35097
|
if (memories.length === 0) {
|
|
34937
|
-
|
|
35098
|
+
return this.orchestrator.generateDaySummaryAuto(namespace);
|
|
34938
35099
|
}
|
|
34939
|
-
this.resolveRecallNamespace(request.namespace, request.sessionKey);
|
|
34940
35100
|
return this.orchestrator.generateDaySummary(memories);
|
|
34941
35101
|
}
|
|
34942
35102
|
async recall(request) {
|
|
@@ -35554,7 +35714,7 @@ var EngramAccessService = class {
|
|
|
35554
35714
|
// src/access-http.ts
|
|
35555
35715
|
import { createServer as createServer3 } from "http";
|
|
35556
35716
|
import { timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
35557
|
-
import { existsSync as
|
|
35717
|
+
import { existsSync as existsSync7 } from "fs";
|
|
35558
35718
|
import { readFile as readFile40 } from "fs/promises";
|
|
35559
35719
|
import path66 from "path";
|
|
35560
35720
|
import { fileURLToPath as fileURLToPath3, URL as URL3 } from "url";
|
|
@@ -35610,7 +35770,7 @@ var EngramMcpServer = class {
|
|
|
35610
35770
|
},
|
|
35611
35771
|
{
|
|
35612
35772
|
name: "engram.day_summary",
|
|
35613
|
-
description: "Generate a structured end-of-day summary
|
|
35773
|
+
description: "Generate a structured end-of-day summary. When memories is omitted or empty, auto-gathers today's facts and hourly summaries from storage.",
|
|
35614
35774
|
inputSchema: {
|
|
35615
35775
|
type: "object",
|
|
35616
35776
|
properties: {
|
|
@@ -35618,7 +35778,7 @@ var EngramMcpServer = class {
|
|
|
35618
35778
|
sessionKey: { type: "string" },
|
|
35619
35779
|
namespace: { type: "string" }
|
|
35620
35780
|
},
|
|
35621
|
-
required: [
|
|
35781
|
+
required: [],
|
|
35622
35782
|
additionalProperties: false
|
|
35623
35783
|
}
|
|
35624
35784
|
},
|
|
@@ -35969,7 +36129,7 @@ function resolveDefaultAdminConsolePublicDir() {
|
|
|
35969
36129
|
fileURLToPath3(new URL3("../admin-console/public", import.meta.url)),
|
|
35970
36130
|
fileURLToPath3(new URL3("./admin-console/public", import.meta.url))
|
|
35971
36131
|
];
|
|
35972
|
-
return candidates.find((candidate) =>
|
|
36132
|
+
return candidates.find((candidate) => existsSync7(candidate)) ?? candidates[0];
|
|
35973
36133
|
}
|
|
35974
36134
|
var defaultAdminConsolePublicDir = resolveDefaultAdminConsolePublicDir();
|
|
35975
36135
|
var WRITE_RATE_LIMIT_WINDOW_MS = 6e4;
|