omnius 1.0.65 → 1.0.66
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 +321 -14
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -609476,6 +609476,147 @@ function parseTelegramInteractionDecision(text, forcedRoute, options2 = {}) {
|
|
|
609476
609476
|
return null;
|
|
609477
609477
|
}
|
|
609478
609478
|
}
|
|
609479
|
+
function parseTelegramTimeRangeQuery(query, now = /* @__PURE__ */ new Date()) {
|
|
609480
|
+
const original = String(query || "");
|
|
609481
|
+
const lower = original.toLowerCase();
|
|
609482
|
+
const nowMs = now.getTime();
|
|
609483
|
+
const dayMs = 864e5;
|
|
609484
|
+
const hourMs = 36e5;
|
|
609485
|
+
const minMs = 6e4;
|
|
609486
|
+
const weekMs = 7 * dayMs;
|
|
609487
|
+
const startOfDay = (d2) => {
|
|
609488
|
+
const x = new Date(d2);
|
|
609489
|
+
x.setHours(0, 0, 0, 0);
|
|
609490
|
+
return x;
|
|
609491
|
+
};
|
|
609492
|
+
const endOfDay = (d2) => {
|
|
609493
|
+
const x = new Date(d2);
|
|
609494
|
+
x.setHours(23, 59, 59, 999);
|
|
609495
|
+
return x;
|
|
609496
|
+
};
|
|
609497
|
+
let since;
|
|
609498
|
+
let until;
|
|
609499
|
+
let bucket;
|
|
609500
|
+
let label = "";
|
|
609501
|
+
let residual = original;
|
|
609502
|
+
const strip = (re) => {
|
|
609503
|
+
residual = residual.replace(re, " ").replace(/\s+/g, " ").trim();
|
|
609504
|
+
};
|
|
609505
|
+
if (/\b(oldest|earliest|first)\b/.test(lower)) {
|
|
609506
|
+
bucket = "earliest";
|
|
609507
|
+
label = label || "earliest";
|
|
609508
|
+
strip(/\b(?:the\s+)?(?:oldest|earliest|first)(?:\s+(?:memory|entry|turn|message|memories))?\b/gi);
|
|
609509
|
+
} else if (/\b(newest|latest|most\s+recent|last\s+memory|last\s+turn|last\s+message)\b/.test(lower)) {
|
|
609510
|
+
bucket = "latest";
|
|
609511
|
+
label = label || "latest";
|
|
609512
|
+
strip(/\b(?:the\s+)?(?:newest|latest|most\s+recent|last\s+(?:memory|turn|message))\b/gi);
|
|
609513
|
+
}
|
|
609514
|
+
if (/\byesterday\b/.test(lower)) {
|
|
609515
|
+
const y = new Date(now);
|
|
609516
|
+
y.setDate(y.getDate() - 1);
|
|
609517
|
+
since = startOfDay(y).getTime();
|
|
609518
|
+
until = endOfDay(y).getTime();
|
|
609519
|
+
label = label ? `${label}, yesterday` : "yesterday";
|
|
609520
|
+
strip(/\byesterday\b/gi);
|
|
609521
|
+
} else if (/\btoday\b/.test(lower)) {
|
|
609522
|
+
since = startOfDay(now).getTime();
|
|
609523
|
+
until = endOfDay(now).getTime();
|
|
609524
|
+
label = label ? `${label}, today` : "today";
|
|
609525
|
+
strip(/\btoday\b/gi);
|
|
609526
|
+
}
|
|
609527
|
+
const partMatch = lower.match(/\bthis\s+(morning|afternoon|evening|night|tonight)\b/);
|
|
609528
|
+
if (partMatch) {
|
|
609529
|
+
const today0 = startOfDay(now).getTime();
|
|
609530
|
+
if (partMatch[1] === "morning") {
|
|
609531
|
+
since = today0;
|
|
609532
|
+
until = today0 + 12 * hourMs;
|
|
609533
|
+
} else if (partMatch[1] === "afternoon") {
|
|
609534
|
+
since = today0 + 12 * hourMs;
|
|
609535
|
+
until = today0 + 18 * hourMs;
|
|
609536
|
+
} else if (partMatch[1] === "evening") {
|
|
609537
|
+
since = today0 + 17 * hourMs;
|
|
609538
|
+
until = today0 + 22 * hourMs;
|
|
609539
|
+
} else {
|
|
609540
|
+
since = today0 + 20 * hourMs;
|
|
609541
|
+
until = endOfDay(now).getTime();
|
|
609542
|
+
}
|
|
609543
|
+
label = label ? `${label}, this ${partMatch[1]}` : `this ${partMatch[1]}`;
|
|
609544
|
+
strip(/\bthis\s+(?:morning|afternoon|evening|night|tonight)\b/gi);
|
|
609545
|
+
}
|
|
609546
|
+
const nMatch = lower.match(/\b(?:last|past|in\s+the\s+last|in\s+the\s+past)\s+(\d+)?\s*(minute|min|hour|hr|day|week|wk)s?\b/);
|
|
609547
|
+
if (nMatch) {
|
|
609548
|
+
const n2 = nMatch[1] ? parseInt(nMatch[1], 10) : 1;
|
|
609549
|
+
const u = nMatch[2];
|
|
609550
|
+
const stepMs = u.startsWith("min") ? minMs : u.startsWith("h") ? hourMs : u.startsWith("d") ? dayMs : weekMs;
|
|
609551
|
+
since = nowMs - n2 * stepMs;
|
|
609552
|
+
until = nowMs;
|
|
609553
|
+
label = label ? `${label}, last ${n2} ${u}${n2 === 1 ? "" : "s"}` : `last ${n2} ${u}${n2 === 1 ? "" : "s"}`;
|
|
609554
|
+
strip(/\b(?:last|past|in\s+the\s+last|in\s+the\s+past)\s+\d*\s*(?:minute|min|hour|hr|day|week|wk)s?\b/gi);
|
|
609555
|
+
}
|
|
609556
|
+
const agoMatch = lower.match(/\b(\d+)\s+(minute|min|hour|hr|day|week|wk)s?\s+ago\b/);
|
|
609557
|
+
if (agoMatch) {
|
|
609558
|
+
const n2 = parseInt(agoMatch[1], 10);
|
|
609559
|
+
const u = agoMatch[2];
|
|
609560
|
+
const stepMs = u.startsWith("min") ? minMs : u.startsWith("h") ? hourMs : u.startsWith("d") ? dayMs : weekMs;
|
|
609561
|
+
const center = nowMs - n2 * stepMs;
|
|
609562
|
+
if (u.startsWith("d")) {
|
|
609563
|
+
const at = new Date(center);
|
|
609564
|
+
since = startOfDay(at).getTime();
|
|
609565
|
+
until = endOfDay(at).getTime();
|
|
609566
|
+
} else {
|
|
609567
|
+
since = center - stepMs;
|
|
609568
|
+
until = center + stepMs;
|
|
609569
|
+
}
|
|
609570
|
+
label = label ? `${label}, ${n2} ${u}${n2 === 1 ? "" : "s"} ago` : `${n2} ${u}${n2 === 1 ? "" : "s"} ago`;
|
|
609571
|
+
strip(/\b\d+\s+(?:minute|min|hour|hr|day|week|wk)s?\s+ago\b/gi);
|
|
609572
|
+
}
|
|
609573
|
+
if (/\bearlier\s+today\b/.test(lower)) {
|
|
609574
|
+
const today0 = startOfDay(now).getTime();
|
|
609575
|
+
since = today0;
|
|
609576
|
+
until = nowMs - 30 * minMs;
|
|
609577
|
+
label = label ? `${label}, earlier today` : "earlier today";
|
|
609578
|
+
strip(/\bearlier\s+today\b/gi);
|
|
609579
|
+
} else if (/\bearlier\b/.test(lower) && since === void 0 && bucket === void 0) {
|
|
609580
|
+
since = nowMs - dayMs;
|
|
609581
|
+
until = nowMs - 30 * minMs;
|
|
609582
|
+
label = label ? `${label}, earlier` : "earlier";
|
|
609583
|
+
strip(/\bearlier\b/gi);
|
|
609584
|
+
}
|
|
609585
|
+
const sinceIsoMatch = original.match(/\bsince\s+(\d{4}-\d{2}-\d{2}(?:[T\s]\d{2}:\d{2}(?::\d{2})?)?)\b/i);
|
|
609586
|
+
if (sinceIsoMatch) {
|
|
609587
|
+
const t2 = Date.parse(sinceIsoMatch[1]);
|
|
609588
|
+
if (Number.isFinite(t2)) {
|
|
609589
|
+
since = t2;
|
|
609590
|
+
until = nowMs;
|
|
609591
|
+
label = label ? `${label}, since ${sinceIsoMatch[1]}` : `since ${sinceIsoMatch[1]}`;
|
|
609592
|
+
strip(new RegExp(`\\bsince\\s+${sinceIsoMatch[1].replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "gi"));
|
|
609593
|
+
}
|
|
609594
|
+
}
|
|
609595
|
+
const onIsoMatch = original.match(/\bon\s+(\d{4}-\d{2}-\d{2})\b/i);
|
|
609596
|
+
if (onIsoMatch) {
|
|
609597
|
+
const t2 = Date.parse(onIsoMatch[1]);
|
|
609598
|
+
if (Number.isFinite(t2)) {
|
|
609599
|
+
const d2 = new Date(t2);
|
|
609600
|
+
since = startOfDay(d2).getTime();
|
|
609601
|
+
until = endOfDay(d2).getTime();
|
|
609602
|
+
label = label ? `${label}, on ${onIsoMatch[1]}` : `on ${onIsoMatch[1]}`;
|
|
609603
|
+
strip(new RegExp(`\\bon\\s+${onIsoMatch[1]}\\b`, "gi"));
|
|
609604
|
+
}
|
|
609605
|
+
}
|
|
609606
|
+
const betweenMatch = original.match(/\bbetween\s+(\d{4}-\d{2}-\d{2})\s+and\s+(\d{4}-\d{2}-\d{2})\b/i);
|
|
609607
|
+
if (betweenMatch) {
|
|
609608
|
+
const a2 = Date.parse(betweenMatch[1]);
|
|
609609
|
+
const b = Date.parse(betweenMatch[2]);
|
|
609610
|
+
if (Number.isFinite(a2) && Number.isFinite(b)) {
|
|
609611
|
+
since = Math.min(a2, b);
|
|
609612
|
+
until = Math.max(a2, b) + dayMs;
|
|
609613
|
+
label = label ? `${label}, between ${betweenMatch[1]} and ${betweenMatch[2]}` : `between ${betweenMatch[1]} and ${betweenMatch[2]}`;
|
|
609614
|
+
strip(/\bbetween\s+\d{4}-\d{2}-\d{2}\s+and\s+\d{4}-\d{2}-\d{2}\b/gi);
|
|
609615
|
+
}
|
|
609616
|
+
}
|
|
609617
|
+
if (since === void 0 && until === void 0 && bucket === void 0) return null;
|
|
609618
|
+
return { since, until, bucket, label: label || "time-range", residualQuery: residual };
|
|
609619
|
+
}
|
|
609479
609620
|
function parseTelegramReflectionFollowupDecision(text) {
|
|
609480
609621
|
const cleaned = stripTelegramHiddenThinking(text).replace(/```(?:json)?/gi, "").replace(/```/g, "").trim();
|
|
609481
609622
|
const jsonText = cleaned.startsWith("{") ? cleaned : cleaned.match(/\{[\s\S]*\}/)?.[0] ?? "";
|
|
@@ -613332,11 +613473,116 @@ ${lines.join("\n")}`);
|
|
|
613332
613473
|
lines.push(` - ${topic}`);
|
|
613333
613474
|
}
|
|
613334
613475
|
}
|
|
613476
|
+
const sourceHistory = this.chatHistory.get(sessionKey) ?? [];
|
|
613477
|
+
const sortedByTs = [...sourceHistory].filter((e2) => typeof e2.ts === "number" && Number.isFinite(e2.ts)).sort((a2, b) => (a2.ts ?? 0) - (b.ts ?? 0));
|
|
613478
|
+
const fmtHistoryAnchor = (entry) => {
|
|
613479
|
+
const when = entry.ts ? new Date(entry.ts).toISOString() : "(unknown ts)";
|
|
613480
|
+
const speaker = telegramHistorySpeaker(entry);
|
|
613481
|
+
const mode = entry.mode ? `/${entry.mode}` : "";
|
|
613482
|
+
const messageId = entry.messageId ? ` msg:${entry.messageId}` : "";
|
|
613483
|
+
return `${when} ${speaker}${mode}${messageId}: ${telegramContextJsonString(String(entry.text || ""), 320)}`;
|
|
613484
|
+
};
|
|
613485
|
+
if (sortedByTs.length > 0) {
|
|
613486
|
+
lines.push("");
|
|
613487
|
+
lines.push("Chronological anchors — Telegram conversation history (ground truth for 'oldest/newest memory' questions):");
|
|
613488
|
+
lines.push(` EARLIEST turn: ${fmtHistoryAnchor(sortedByTs[0])}`);
|
|
613489
|
+
if (sortedByTs.length > 1) {
|
|
613490
|
+
lines.push(` LATEST turn: ${fmtHistoryAnchor(sortedByTs[sortedByTs.length - 1])}`);
|
|
613491
|
+
}
|
|
613492
|
+
if (sortedByTs.length > 2) {
|
|
613493
|
+
lines.push(` 2nd earliest: ${fmtHistoryAnchor(sortedByTs[1])}`);
|
|
613494
|
+
}
|
|
613495
|
+
if (sortedByTs.length > 3) {
|
|
613496
|
+
lines.push(` 3rd earliest: ${fmtHistoryAnchor(sortedByTs[2])}`);
|
|
613497
|
+
}
|
|
613498
|
+
}
|
|
613499
|
+
if (this.repoRoot) {
|
|
613500
|
+
try {
|
|
613501
|
+
const paths = omniusMemoryDbPaths(this.repoRoot);
|
|
613502
|
+
if (existsSync113(paths.episodes)) {
|
|
613503
|
+
const graph = new TemporalGraph(paths.knowledge);
|
|
613504
|
+
const store2 = new EpisodeStore(paths.episodes, graph);
|
|
613505
|
+
try {
|
|
613506
|
+
const dbAny = store2.db;
|
|
613507
|
+
if (dbAny && typeof dbAny.prepare === "function") {
|
|
613508
|
+
const earliest = dbAny.prepare(
|
|
613509
|
+
"SELECT timestamp, modality, tool_name, content, gist FROM episodes WHERE session_id = ? ORDER BY timestamp ASC LIMIT 1"
|
|
613510
|
+
).get(sessionKey);
|
|
613511
|
+
const latest = dbAny.prepare(
|
|
613512
|
+
"SELECT timestamp, modality, tool_name, content, gist FROM episodes WHERE session_id = ? ORDER BY timestamp DESC LIMIT 1"
|
|
613513
|
+
).get(sessionKey);
|
|
613514
|
+
if (earliest || latest) {
|
|
613515
|
+
lines.push("");
|
|
613516
|
+
lines.push("Chronological anchors — episodes.db (durable, may reach further back than rolling history):");
|
|
613517
|
+
const fmtEp = (row) => {
|
|
613518
|
+
const when = row.timestamp ? new Date(row.timestamp).toISOString() : "(unknown ts)";
|
|
613519
|
+
const tag = `[${row.modality || "?"}${row.tool_name ? ":" + row.tool_name : ""}]`;
|
|
613520
|
+
const text = (row.gist || row.content || "").split("\n").filter((ln) => !/^(Telegram|session:|chat:|message_id:|thread_id:|speaker:|mode:)/i.test(ln.trim())).join(" ").replace(/\s+/g, " ").trim();
|
|
613521
|
+
return `${when} ${tag} ${telegramContextJsonString(text, 320)}`;
|
|
613522
|
+
};
|
|
613523
|
+
if (earliest) lines.push(` EARLIEST episode: ${fmtEp(earliest)}`);
|
|
613524
|
+
if (latest && (!earliest || earliest.timestamp !== latest.timestamp)) lines.push(` LATEST episode: ${fmtEp(latest)}`);
|
|
613525
|
+
}
|
|
613526
|
+
}
|
|
613527
|
+
} finally {
|
|
613528
|
+
try {
|
|
613529
|
+
store2.close();
|
|
613530
|
+
} catch {
|
|
613531
|
+
}
|
|
613532
|
+
try {
|
|
613533
|
+
graph.close();
|
|
613534
|
+
} catch {
|
|
613535
|
+
}
|
|
613536
|
+
}
|
|
613537
|
+
}
|
|
613538
|
+
} catch {
|
|
613539
|
+
}
|
|
613540
|
+
}
|
|
613541
|
+
if (this.repoRoot && chatId !== void 0 && topicFiles.length > 0) {
|
|
613542
|
+
try {
|
|
613543
|
+
const memDir = resolve43(this.repoRoot, ".omnius", "memory");
|
|
613544
|
+
const prefix = this.telegramScopedMemoryPrefix(chatId);
|
|
613545
|
+
let earliestEntry = null;
|
|
613546
|
+
let latestEntry = null;
|
|
613547
|
+
for (const topic of topicFiles) {
|
|
613548
|
+
const file = join127(memDir, `${prefix}${topic}.json`);
|
|
613549
|
+
if (!existsSync113(file)) continue;
|
|
613550
|
+
let parsed;
|
|
613551
|
+
try {
|
|
613552
|
+
parsed = JSON.parse(readFileSync92(file, "utf8"));
|
|
613553
|
+
} catch {
|
|
613554
|
+
continue;
|
|
613555
|
+
}
|
|
613556
|
+
for (const [key, entry] of Object.entries(parsed)) {
|
|
613557
|
+
if (!entry || typeof entry !== "object") continue;
|
|
613558
|
+
const rawTs = entry.timestamp;
|
|
613559
|
+
const ts = typeof rawTs === "string" ? Date.parse(rawTs) : typeof rawTs === "number" ? rawTs : NaN;
|
|
613560
|
+
if (!Number.isFinite(ts)) continue;
|
|
613561
|
+
const value2 = String(entry.value ?? "");
|
|
613562
|
+
if (!earliestEntry || ts < earliestEntry.ts) earliestEntry = { topic, key, value: value2, ts };
|
|
613563
|
+
if (!latestEntry || ts > latestEntry.ts) latestEntry = { topic, key, value: value2, ts };
|
|
613564
|
+
}
|
|
613565
|
+
}
|
|
613566
|
+
if (earliestEntry || latestEntry) {
|
|
613567
|
+
lines.push("");
|
|
613568
|
+
lines.push("Chronological anchors — memory_write entries (most-trusted, agent-asserted):");
|
|
613569
|
+
const fmtMem = (e2) => {
|
|
613570
|
+
const when = new Date(e2.ts).toISOString();
|
|
613571
|
+
return `${when} topic="${e2.topic}" key="${e2.key}" → ${telegramContextJsonString(e2.value, 320)}`;
|
|
613572
|
+
};
|
|
613573
|
+
if (earliestEntry) lines.push(` EARLIEST memory_write: ${fmtMem(earliestEntry)}`);
|
|
613574
|
+
if (latestEntry && (!earliestEntry || earliestEntry.ts !== latestEntry.ts)) lines.push(` LATEST memory_write: ${fmtMem(latestEntry)}`);
|
|
613575
|
+
}
|
|
613576
|
+
} catch {
|
|
613577
|
+
}
|
|
613578
|
+
}
|
|
613335
613579
|
lines.push("");
|
|
613336
613580
|
lines.push("RULES:");
|
|
613337
613581
|
lines.push(" 1. NEVER tell the user 'memory is empty' or 'nothing has been stored' for this chat without first calling memory_search and memory_read on a relevant topic from the list above.");
|
|
613338
613582
|
lines.push(" 2. If the structured sections (cards/facts/sqlite/episodes) above did not surface what the user asked about, that is a SCORING miss, not absence. Call memory_search with broader tokens or pick a topic above with memory_read.");
|
|
613339
613583
|
lines.push(" 3. The rolling-history block is base context; the cards/facts/episodes are retrieval-augmented. Treat them as the same memory, surfaced different ways.");
|
|
613584
|
+
lines.push(" 4. For 'what is your oldest/earliest memory' or 'most recent memory' questions: answer DIRECTLY from the 'Chronological anchors' lines above. Quote the timestamp and content. Do NOT call tools first and do NOT report 'empty'.");
|
|
613585
|
+
lines.push(" 5. memory_search accepts NATURAL-LANGUAGE TIME phrases inside the `query` argument or explicit `since`/`until`/`bucket` args. Examples: query='what did manitcor say yesterday', query='last 3 hours', query='earlier today', query='2 days ago', query='since 2026-05-15', query='between 2026-05-15 and 2026-05-16', query='oldest memory about github', query='most recent flux discussion'. Use these for chronological/'how far back' style queries instead of guessing — the tool parses the phrase, filters by time, and returns the right window.");
|
|
613340
613586
|
return lines.join("\n");
|
|
613341
613587
|
}
|
|
613342
613588
|
buildTelegramConversationContextStream(sessionKey, msg, maxRecent = TELEGRAM_CONTEXT_RECENT_DEFAULT) {
|
|
@@ -615180,7 +615426,7 @@ ${result.llmContent ?? result.output}` };
|
|
|
615180
615426
|
if (tool.name === "memory_search") {
|
|
615181
615427
|
return {
|
|
615182
615428
|
...tool,
|
|
615183
|
-
description: "Search only this Telegram chat's isolated durable memory: raw SQLite message mirror, episode/knowledge graph recall, associative user facts, and memory cards. Supports scope=group/current_chat or scope=user with user_id/username,
|
|
615429
|
+
description: "Search only this Telegram chat's isolated durable memory: raw SQLite message mirror, episode/knowledge graph recall, associative user facts, and memory cards. Supports scope=group/current_chat or scope=user with user_id/username; also supports natural-language time phrases inside the query string ('yesterday', 'last 3 hours', '2 days ago', 'earlier today', 'since 2026-05-15', 'between A and B', 'oldest', 'most recent') OR explicit since/until/bucket arguments. Never crosses into admin/private/global memory.",
|
|
615184
615430
|
parameters: (() => {
|
|
615185
615431
|
const base3 = tool.parameters ?? {};
|
|
615186
615432
|
const props = base3["properties"] && typeof base3["properties"] === "object" && !Array.isArray(base3["properties"]) ? base3["properties"] : {};
|
|
@@ -615191,15 +615437,57 @@ ${result.llmContent ?? result.output}` };
|
|
|
615191
615437
|
scope: { type: "string", enum: ["group", "current_chat", "user"], description: "Search scope inside the current Telegram chat. scope=user defaults to the current sender when no user_id/username is supplied." },
|
|
615192
615438
|
user_id: { type: "number", description: "Optional Telegram user id to search within the current group only." },
|
|
615193
615439
|
username: { type: "string", description: "Optional Telegram username to search within the current group only." },
|
|
615194
|
-
group_id: { type: "string", description: "Optional Telegram group/chat id. Must match the current chat id; other groups are not accessible here." }
|
|
615440
|
+
group_id: { type: "string", description: "Optional Telegram group/chat id. Must match the current chat id; other groups are not accessible here." },
|
|
615441
|
+
since: { type: "string", description: "Optional lower-bound timestamp (ISO 8601 or epoch ms). Combine with `until` for an explicit window. If omitted, natural-language phrases inside `query` are auto-parsed (e.g. 'yesterday', '3 days ago')." },
|
|
615442
|
+
until: { type: "string", description: "Optional upper-bound timestamp (ISO 8601 or epoch ms)." },
|
|
615443
|
+
bucket: { type: "string", enum: ["earliest", "latest"], description: "Chronological ordering hint when there is no specific window. 'earliest' returns the oldest entries on record; 'latest' returns the newest." }
|
|
615195
615444
|
}
|
|
615196
615445
|
};
|
|
615197
615446
|
})(),
|
|
615198
615447
|
execute: async (args) => {
|
|
615199
|
-
const
|
|
615448
|
+
const rawQuery = String(args["query"] || "").trim();
|
|
615200
615449
|
const maxResults = typeof args["max_results"] === "number" && Number.isFinite(args["max_results"]) ? Math.max(1, Math.min(20, Math.floor(args["max_results"]))) : 8;
|
|
615201
|
-
if (!
|
|
615450
|
+
if (!rawQuery) return { success: true, output: "Search query is required." };
|
|
615202
615451
|
this.ensureTelegramConversationLoaded(msgSessionKey);
|
|
615452
|
+
const parseTimeArg = (v) => {
|
|
615453
|
+
if (v === void 0 || v === null || v === "") return void 0;
|
|
615454
|
+
if (typeof v === "number" && Number.isFinite(v)) return Math.trunc(v);
|
|
615455
|
+
if (typeof v === "string") {
|
|
615456
|
+
if (/^\d+$/.test(v.trim())) return Number(v.trim());
|
|
615457
|
+
const t2 = Date.parse(v);
|
|
615458
|
+
if (Number.isFinite(t2)) return t2;
|
|
615459
|
+
}
|
|
615460
|
+
return void 0;
|
|
615461
|
+
};
|
|
615462
|
+
let since = parseTimeArg(args["since"]);
|
|
615463
|
+
let until = parseTimeArg(args["until"]);
|
|
615464
|
+
let bucket = args["bucket"] === "earliest" || args["bucket"] === "latest" ? args["bucket"] : void 0;
|
|
615465
|
+
let timeLabel = "";
|
|
615466
|
+
let effectiveQuery = rawQuery;
|
|
615467
|
+
if (since === void 0 && until === void 0 && bucket === void 0) {
|
|
615468
|
+
const parsed = parseTelegramTimeRangeQuery(rawQuery);
|
|
615469
|
+
if (parsed) {
|
|
615470
|
+
since = parsed.since;
|
|
615471
|
+
until = parsed.until;
|
|
615472
|
+
bucket = parsed.bucket;
|
|
615473
|
+
timeLabel = parsed.label;
|
|
615474
|
+
effectiveQuery = parsed.residualQuery || rawQuery;
|
|
615475
|
+
}
|
|
615476
|
+
} else if (since !== void 0 || until !== void 0 || bucket !== void 0) {
|
|
615477
|
+
timeLabel = [
|
|
615478
|
+
since !== void 0 ? `since=${new Date(since).toISOString()}` : "",
|
|
615479
|
+
until !== void 0 ? `until=${new Date(until).toISOString()}` : "",
|
|
615480
|
+
bucket ? `bucket=${bucket}` : ""
|
|
615481
|
+
].filter(Boolean).join(", ");
|
|
615482
|
+
}
|
|
615483
|
+
const inWindow = (ts) => {
|
|
615484
|
+
if (since === void 0 && until === void 0) return true;
|
|
615485
|
+
if (ts == null || !Number.isFinite(ts)) return false;
|
|
615486
|
+
if (since !== void 0 && ts < since) return false;
|
|
615487
|
+
if (until !== void 0 && ts > until) return false;
|
|
615488
|
+
return true;
|
|
615489
|
+
};
|
|
615490
|
+
const query = effectiveQuery.trim();
|
|
615203
615491
|
const currentGroupId = chatId === void 0 ? "" : String(chatId);
|
|
615204
615492
|
const requestedGroupId = String(args["group_id"] ?? args["chat_id"] ?? "").trim();
|
|
615205
615493
|
if (requestedGroupId && currentGroupId && requestedGroupId !== currentGroupId) {
|
|
@@ -615228,13 +615516,17 @@ ${result.llmContent ?? result.output}` };
|
|
|
615228
615516
|
return false;
|
|
615229
615517
|
});
|
|
615230
615518
|
const queryTokens = telegramMemoryTokens(query);
|
|
615231
|
-
const cardResults = cards.map((card) => ({
|
|
615519
|
+
const cardResults = cards.filter((card) => inWindow(card.updatedAt ?? card.createdAt)).map((card) => ({
|
|
615232
615520
|
card,
|
|
615233
615521
|
score: telegramMemorySimilarity(
|
|
615234
615522
|
queryTokens,
|
|
615235
615523
|
telegramMemoryTokens([card.title, card.tags.join(" "), card.speakers.join(" "), card.notes.join(" ")].join(" "))
|
|
615236
615524
|
)
|
|
615237
|
-
})).filter((item) => item.score > 0
|
|
615525
|
+
})).filter((item) => item.score > 0 || since !== void 0 || until !== void 0 || bucket !== void 0).sort((a2, b) => {
|
|
615526
|
+
if (bucket === "earliest") return (a2.card.updatedAt ?? 0) - (b.card.updatedAt ?? 0);
|
|
615527
|
+
if (bucket === "latest") return (b.card.updatedAt ?? 0) - (a2.card.updatedAt ?? 0);
|
|
615528
|
+
return b.score - a2.score || b.card.updatedAt - a2.card.updatedAt;
|
|
615529
|
+
}).slice(0, maxResults);
|
|
615238
615530
|
const cardLines = cardResults.map(({ card, score }) => {
|
|
615239
615531
|
const notes2 = card.notes.slice(-4).map((note) => ` - ${truncateTelegramContextLine(note, 220)}`).join("\n");
|
|
615240
615532
|
const tags = card.tags.length ? ` tags:${card.tags.slice(0, 8).join(",")}` : "";
|
|
@@ -615247,13 +615539,17 @@ ${notes2}`;
|
|
|
615247
615539
|
if (!wantsUserScope) return true;
|
|
615248
615540
|
if (effectiveUserId !== void 0 && fact.userIds.includes(effectiveUserId)) return true;
|
|
615249
615541
|
return !!effectiveUsername && fact.usernames.includes(effectiveUsername);
|
|
615250
|
-
}).map((fact) => ({
|
|
615542
|
+
}).filter((fact) => inWindow(fact.updatedAt ?? fact.createdAt)).map((fact) => ({
|
|
615251
615543
|
fact,
|
|
615252
615544
|
score: telegramMemorySimilarity(
|
|
615253
615545
|
queryTokens,
|
|
615254
615546
|
telegramMemoryTokens([fact.text, fact.tags.join(" "), fact.speakers.join(" ")].join(" "))
|
|
615255
615547
|
) + (effectiveUserId !== void 0 && fact.userIds.includes(effectiveUserId) ? 0.3 : 0) + (effectiveUsername && fact.usernames.includes(effectiveUsername) ? 0.3 : 0)
|
|
615256
|
-
})).filter((item) => item.score > 0
|
|
615548
|
+
})).filter((item) => item.score > 0 || since !== void 0 || until !== void 0 || bucket !== void 0).sort((a2, b) => {
|
|
615549
|
+
if (bucket === "earliest") return (a2.fact.updatedAt ?? 0) - (b.fact.updatedAt ?? 0);
|
|
615550
|
+
if (bucket === "latest") return (b.fact.updatedAt ?? 0) - (a2.fact.updatedAt ?? 0);
|
|
615551
|
+
return b.score - a2.score || b.fact.updatedAt - a2.fact.updatedAt;
|
|
615552
|
+
}).slice(0, maxResults) : [];
|
|
615257
615553
|
const rawRows = this.searchTelegramSqliteMirrorRows(msgSessionKey, query, {
|
|
615258
615554
|
limit: maxResults,
|
|
615259
615555
|
userId: effectiveUserId,
|
|
@@ -615263,8 +615559,9 @@ ${notes2}`;
|
|
|
615263
615559
|
const historyForScan = this.chatHistory.get(msgSessionKey) ?? [];
|
|
615264
615560
|
const lowerQuery = query.toLowerCase().trim();
|
|
615265
615561
|
const queryWords = lowerQuery.split(/\s+/).filter((w) => w.length >= 3);
|
|
615562
|
+
const hasTimeConstraint = since !== void 0 || until !== void 0 || bucket !== void 0;
|
|
615266
615563
|
const rawHistoryMatches = [];
|
|
615267
|
-
if (historyForScan.length > 0
|
|
615564
|
+
if (historyForScan.length > 0) {
|
|
615268
615565
|
for (const entry of historyForScan) {
|
|
615269
615566
|
if (wantsUserScope) {
|
|
615270
615567
|
const eUser = entry.fromUserId;
|
|
@@ -615272,15 +615569,24 @@ ${notes2}`;
|
|
|
615272
615569
|
const matchesUser = effectiveUserId !== void 0 && eUser === effectiveUserId || effectiveUsername.length > 0 && eName === effectiveUsername;
|
|
615273
615570
|
if (!matchesUser) continue;
|
|
615274
615571
|
}
|
|
615572
|
+
if (!inWindow(entry.ts ?? null)) continue;
|
|
615275
615573
|
const hay = String(entry.text || "").toLowerCase();
|
|
615276
615574
|
if (!hay) continue;
|
|
615277
|
-
|
|
615278
|
-
|
|
615279
|
-
|
|
615575
|
+
let lexMatch = false;
|
|
615576
|
+
if (lowerQuery.length > 0) {
|
|
615577
|
+
const hasExact = hay.includes(lowerQuery);
|
|
615578
|
+
const tokenHits = queryWords.filter((w) => hay.includes(w)).length;
|
|
615579
|
+
lexMatch = hasExact || queryWords.length > 0 && tokenHits >= Math.min(2, queryWords.length);
|
|
615580
|
+
}
|
|
615581
|
+
if (lexMatch || hasTimeConstraint) {
|
|
615280
615582
|
rawHistoryMatches.push(entry);
|
|
615281
615583
|
}
|
|
615282
615584
|
}
|
|
615283
|
-
|
|
615585
|
+
if (bucket === "earliest") {
|
|
615586
|
+
rawHistoryMatches.sort((a2, b) => (a2.ts ?? 0) - (b.ts ?? 0));
|
|
615587
|
+
} else {
|
|
615588
|
+
rawHistoryMatches.sort((a2, b) => (b.ts ?? 0) - (a2.ts ?? 0));
|
|
615589
|
+
}
|
|
615284
615590
|
}
|
|
615285
615591
|
const rawHistorySliced = rawHistoryMatches.slice(0, maxResults);
|
|
615286
615592
|
const scopeLabel = wantsUserScope ? `user ${effectiveUsername ? `@${effectiveUsername}` : `id ${effectiveUserId}`}` : `chat ${currentGroupId || msgSessionKey}`;
|
|
@@ -615327,8 +615633,9 @@ ${lines.join("\n")}`
|
|
|
615327
615633
|
output: `No structured matches for "${query}" in ${scopeLabel}, but the scope IS populated: ${scannedCount} memory cards, ${associativeCount} associative facts, ${historyCount} history entries scanned. Try a broader query (single keyword), a different angle (related concept), or memory_read with topic="${this.telegramScopedTopic(chatId, "general")}" to enumerate. If you're looking for an older turn, ask for the speaker name plus a topic word.`
|
|
615328
615634
|
};
|
|
615329
615635
|
}
|
|
615636
|
+
const timeBanner = timeLabel ? ` (time-range: ${timeLabel}${since !== void 0 || until !== void 0 ? `; window=${since !== void 0 ? new Date(since).toISOString() : "−∞"} → ${until !== void 0 ? new Date(until).toISOString() : "+∞"}` : ""})` : "";
|
|
615330
615637
|
const output = [
|
|
615331
|
-
`Scoped Telegram memory search for "${query}" in ${scopeLabel}.`,
|
|
615638
|
+
`Scoped Telegram memory search for "${query}" in ${scopeLabel}${timeBanner}.`,
|
|
615332
615639
|
"Results are scoped to this Telegram chat and may include raw message evidence, graph episodes, associative facts, and memory cards.",
|
|
615333
615640
|
"",
|
|
615334
615641
|
sections.join("\n\n")
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omnius",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.66",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "omnius",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.66",
|
|
10
10
|
"bundleDependencies": [
|
|
11
11
|
"image-to-ascii"
|
|
12
12
|
],
|
package/package.json
CHANGED