omnius 1.0.59 → 1.0.61
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 +1365 -78
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12159,17 +12159,26 @@ process.on('unhandledRejection', (reason) => {
|
|
|
12159
12159
|
|
|
12160
12160
|
dlog('COHERE response: ' + _cData.queryId + ' model=' + _cModel + ' tier=' + ['trivial','moderate','complex','expert'][_cTier] + ' ' + _cLatency + 'ms' + (_cReviewed ? ' (reviewed)' : ''));
|
|
12161
12161
|
// IK-01: Update identity on query success
|
|
12162
|
+
// Store full query + response content; downstream renderers (narrative summary, prompts)
|
|
12163
|
+
// are responsible for compaction. Truncating at write time destroys identity history.
|
|
12164
|
+
// NOTE: '\\n' is double-escaped because this whole block runs inside the
|
|
12165
|
+
// outer DAEMON_SCRIPT template literal — see comment near line 440.
|
|
12162
12166
|
_updateIdentity({ type: 'query_served', outcome: 1.0, details: {
|
|
12163
12167
|
latencyMs: _cLatency, toolsUsed: _cToolsUsed, model: _cModel,
|
|
12164
|
-
|
|
12168
|
+
query: _cData.query || '',
|
|
12169
|
+
response: _cFinalContent || '',
|
|
12170
|
+
summary: (_cData.query || '') + (_cFinalContent ? '\\n→ ' + _cFinalContent : '')
|
|
12165
12171
|
}});
|
|
12166
12172
|
} catch (_cErr) {
|
|
12167
12173
|
dlog('COHERE inference error: ' + (_cErr.message || _cErr));
|
|
12168
12174
|
_cohereStats.queriesErrors++;
|
|
12169
12175
|
_saveStats();
|
|
12170
12176
|
// IK-01: Update identity on query failure
|
|
12177
|
+
// Preserve the full error so post-hoc diagnosis is possible.
|
|
12171
12178
|
_updateIdentity({ type: 'query_failed', outcome: 0, details: {
|
|
12172
|
-
error: (_cErr.message || String(_cErr)
|
|
12179
|
+
error: (_cErr && _cErr.message) || String(_cErr),
|
|
12180
|
+
query: _cData.query || '',
|
|
12181
|
+
summary: 'inference error: ' + ((_cErr && _cErr.message) || String(_cErr))
|
|
12173
12182
|
}});
|
|
12174
12183
|
}
|
|
12175
12184
|
} catch (_cParseErr) {
|
|
@@ -561982,8 +561991,7 @@ function wrapTaskCompleteSummary(summary) {
|
|
|
561982
561991
|
}
|
|
561983
561992
|
function wrapPlainLine(line, width) {
|
|
561984
561993
|
const out = [];
|
|
561985
|
-
const
|
|
561986
|
-
const continuationIndent = indent.length > 0 ? indent : "";
|
|
561994
|
+
const continuationIndent = hangingIndentForPlainLine(line, width);
|
|
561987
561995
|
let remaining = line;
|
|
561988
561996
|
while (remaining.length > width) {
|
|
561989
561997
|
let breakAt = remaining.lastIndexOf(" ", width);
|
|
@@ -561994,6 +562002,20 @@ function wrapPlainLine(line, width) {
|
|
|
561994
562002
|
out.push(remaining);
|
|
561995
562003
|
return out;
|
|
561996
562004
|
}
|
|
562005
|
+
function hangingIndentForPlainLine(line, width) {
|
|
562006
|
+
const capped = (n2) => " ".repeat(Math.max(0, Math.min(n2, width - 4)));
|
|
562007
|
+
const patterns = [
|
|
562008
|
+
/^(\s*(?:[-*+○•]\s+))/,
|
|
562009
|
+
/^(\s*(?:\d+[.)]\s+))/,
|
|
562010
|
+
/^(\s*(?:>\s*))/,
|
|
562011
|
+
/^(\s*(?:[A-Za-z][\w.-]{0,28}:\s+))/
|
|
562012
|
+
];
|
|
562013
|
+
for (const pattern of patterns) {
|
|
562014
|
+
const match = line.match(pattern);
|
|
562015
|
+
if (match?.[1]) return capped(match[1].length);
|
|
562016
|
+
}
|
|
562017
|
+
return capped(line.match(/^\s*/)?.[0].length ?? 0);
|
|
562018
|
+
}
|
|
561997
562019
|
function renderTaskIncomplete(turns, toolCalls, durationMs, tokens) {
|
|
561998
562020
|
const duration = formatDuration2(durationMs);
|
|
561999
562021
|
const tokenStr = tokens ? ` ${formatTokenCount(tokens)}` : "";
|
|
@@ -563699,7 +563721,7 @@ var init_voice_session = __esm({
|
|
|
563699
563721
|
|
|
563700
563722
|
// packages/cli/src/tui/scoped-personality.ts
|
|
563701
563723
|
import { createHash as createHash17 } from "node:crypto";
|
|
563702
|
-
import { existsSync as existsSync82, mkdirSync as mkdirSync46, readFileSync as readFileSync65, writeFileSync as writeFileSync41 } from "node:fs";
|
|
563724
|
+
import { appendFileSync as appendFileSync4, existsSync as existsSync82, mkdirSync as mkdirSync46, readFileSync as readFileSync65, writeFileSync as writeFileSync41 } from "node:fs";
|
|
563703
563725
|
import { join as join98, resolve as resolve37 } from "node:path";
|
|
563704
563726
|
function safeName(input) {
|
|
563705
563727
|
return input.replace(/[^A-Za-z0-9_.-]/g, "-").slice(0, 80) || "default";
|
|
@@ -563715,7 +563737,8 @@ function scopedPersonalityPaths(scope) {
|
|
|
563715
563737
|
const prefix = `${safeName(scope.label)}-${scopeHash(scope)}`;
|
|
563716
563738
|
return {
|
|
563717
563739
|
json: join98(dir, `${prefix}.json`),
|
|
563718
|
-
markdown: join98(dir, `${prefix}.md`)
|
|
563740
|
+
markdown: join98(dir, `${prefix}.md`),
|
|
563741
|
+
events: join98(dir, `${prefix}.events.jsonl`)
|
|
563719
563742
|
};
|
|
563720
563743
|
}
|
|
563721
563744
|
function inferToneTags(text) {
|
|
@@ -563776,16 +563799,49 @@ function newDocument(scope) {
|
|
|
563776
563799
|
"Do not expose private local files, secrets, credentials, or hidden reasoning in public or group scopes.",
|
|
563777
563800
|
"Do not claim memory is unavailable when scoped context is present."
|
|
563778
563801
|
],
|
|
563802
|
+
topicCounts: {},
|
|
563803
|
+
durableObservations: [],
|
|
563804
|
+
relationshipEvents: [],
|
|
563779
563805
|
recentObservations: []
|
|
563780
563806
|
};
|
|
563781
563807
|
}
|
|
563808
|
+
function normalizeDocument(scope, parsed) {
|
|
563809
|
+
const fresh = newDocument(scope);
|
|
563810
|
+
const doc = {
|
|
563811
|
+
...fresh,
|
|
563812
|
+
...parsed,
|
|
563813
|
+
scope: {
|
|
563814
|
+
kind: scope.kind,
|
|
563815
|
+
idHash: scopeHash(scope),
|
|
563816
|
+
label: scope.label || parsed.scope?.label || fresh.scope.label
|
|
563817
|
+
},
|
|
563818
|
+
participants: parsed.participants && typeof parsed.participants === "object" ? parsed.participants : {},
|
|
563819
|
+
dominantTone: Array.isArray(parsed.dominantTone) ? parsed.dominantTone : [],
|
|
563820
|
+
responseStyle: Array.isArray(parsed.responseStyle) && parsed.responseStyle.length > 0 ? parsed.responseStyle : fresh.responseStyle,
|
|
563821
|
+
relationshipModel: Array.isArray(parsed.relationshipModel) && parsed.relationshipModel.length > 0 ? parsed.relationshipModel : fresh.relationshipModel,
|
|
563822
|
+
behavioralGuidance: Array.isArray(parsed.behavioralGuidance) && parsed.behavioralGuidance.length > 0 ? parsed.behavioralGuidance : fresh.behavioralGuidance,
|
|
563823
|
+
boundaries: Array.isArray(parsed.boundaries) && parsed.boundaries.length > 0 ? parsed.boundaries : fresh.boundaries,
|
|
563824
|
+
topicCounts: parsed.topicCounts && typeof parsed.topicCounts === "object" ? parsed.topicCounts : {},
|
|
563825
|
+
durableObservations: Array.isArray(parsed.durableObservations) ? parsed.durableObservations.slice(-MAX_PROFILE_DURABLE_OBSERVATIONS) : [],
|
|
563826
|
+
relationshipEvents: Array.isArray(parsed.relationshipEvents) ? parsed.relationshipEvents.slice(-MAX_PROFILE_RELATIONSHIP_EVENTS) : [],
|
|
563827
|
+
recentObservations: Array.isArray(parsed.recentObservations) ? parsed.recentObservations.slice(-MAX_PROFILE_OBSERVATIONS) : []
|
|
563828
|
+
};
|
|
563829
|
+
doc.messageCount = Number.isFinite(doc.messageCount) ? doc.messageCount : 0;
|
|
563830
|
+
for (const profile of Object.values(doc.participants)) {
|
|
563831
|
+
if (!Array.isArray(profile.toneTags)) profile.toneTags = [];
|
|
563832
|
+
if (!Array.isArray(profile.samples)) profile.samples = [];
|
|
563833
|
+
profile.toneTags = profile.toneTags.slice(0, MAX_PROFILE_TAGS);
|
|
563834
|
+
profile.samples = profile.samples.slice(-MAX_PROFILE_SAMPLES);
|
|
563835
|
+
}
|
|
563836
|
+
return doc;
|
|
563837
|
+
}
|
|
563782
563838
|
function loadScopedPersonality(scope) {
|
|
563783
563839
|
const paths = scopedPersonalityPaths(scope);
|
|
563784
563840
|
if (!existsSync82(paths.json)) return newDocument(scope);
|
|
563785
563841
|
try {
|
|
563786
563842
|
const parsed = JSON.parse(readFileSync65(paths.json, "utf8"));
|
|
563787
563843
|
if (parsed.version !== PROFILE_VERSION) return newDocument(scope);
|
|
563788
|
-
return parsed;
|
|
563844
|
+
return normalizeDocument(scope, parsed);
|
|
563789
563845
|
} catch {
|
|
563790
563846
|
return newDocument(scope);
|
|
563791
563847
|
}
|
|
@@ -563796,6 +563852,14 @@ function saveScopedPersonality(scope, doc) {
|
|
|
563796
563852
|
writeFileSync41(paths.json, JSON.stringify(doc, null, 2) + "\n", "utf8");
|
|
563797
563853
|
writeFileSync41(paths.markdown, renderScopedPersonalityMarkdown(doc) + "\n", "utf8");
|
|
563798
563854
|
}
|
|
563855
|
+
function appendScopedPersonalityEvent(scope, event) {
|
|
563856
|
+
try {
|
|
563857
|
+
const paths = scopedPersonalityPaths(scope);
|
|
563858
|
+
mkdirSync46(scopedPersonalityDir(scope.repoRoot, scope.kind), { recursive: true });
|
|
563859
|
+
appendFileSync4(paths.events, JSON.stringify(event) + "\n", "utf8");
|
|
563860
|
+
} catch {
|
|
563861
|
+
}
|
|
563862
|
+
}
|
|
563799
563863
|
function updateScopedPersonality(scope, observation) {
|
|
563800
563864
|
const doc = loadScopedPersonality(scope);
|
|
563801
563865
|
const now = new Date(observation.ts ?? Date.now()).toISOString();
|
|
@@ -563806,6 +563870,7 @@ function updateScopedPersonality(scope, observation) {
|
|
|
563806
563870
|
...inferToneTags(observation.text),
|
|
563807
563871
|
...keywords(observation.text)
|
|
563808
563872
|
];
|
|
563873
|
+
const topicTags = keywords(observation.text);
|
|
563809
563874
|
doc.updatedAt = now;
|
|
563810
563875
|
doc.messageCount += 1;
|
|
563811
563876
|
doc.scope.label = scope.label;
|
|
@@ -563831,6 +563896,9 @@ function updateScopedPersonality(scope, observation) {
|
|
|
563831
563896
|
for (const tag of profile.toneTags) allTags.set(tag, (allTags.get(tag) ?? 0) + profile.count);
|
|
563832
563897
|
}
|
|
563833
563898
|
doc.dominantTone = [...allTags.entries()].sort((a2, b) => b[1] - a2[1]).map(([tag]) => tag).slice(0, MAX_PROFILE_TAGS);
|
|
563899
|
+
for (const tag of topicTags) {
|
|
563900
|
+
doc.topicCounts[tag] = (doc.topicCounts[tag] ?? 0) + 1;
|
|
563901
|
+
}
|
|
563834
563902
|
if (doc.dominantTone.includes("frustrated")) {
|
|
563835
563903
|
doc.responseStyle = [
|
|
563836
563904
|
"Start with the concrete fix or answer; avoid soothing filler.",
|
|
@@ -563852,15 +563920,31 @@ function updateScopedPersonality(scope, observation) {
|
|
|
563852
563920
|
for (const hint of relationshipHints) {
|
|
563853
563921
|
const clean5 = compactLine(hint, 180);
|
|
563854
563922
|
if (clean5 && !doc.relationshipModel.includes(clean5)) doc.relationshipModel.push(clean5);
|
|
563923
|
+
if (clean5 && !doc.relationshipEvents.includes(`${now} ${clean5}`)) doc.relationshipEvents.push(`${now} ${clean5}`);
|
|
563855
563924
|
}
|
|
563856
563925
|
doc.relationshipModel = doc.relationshipModel.slice(-12);
|
|
563926
|
+
doc.relationshipEvents = doc.relationshipEvents.slice(-MAX_PROFILE_RELATIONSHIP_EVENTS);
|
|
563857
563927
|
if (text) {
|
|
563858
563928
|
const line = `${now} ${speaker}${observation.mode ? `/${observation.mode}` : ""}: ${text}`;
|
|
563859
563929
|
doc.recentObservations.push(line);
|
|
563860
563930
|
if (doc.recentObservations.length > MAX_PROFILE_OBSERVATIONS) {
|
|
563861
563931
|
doc.recentObservations.splice(0, doc.recentObservations.length - MAX_PROFILE_OBSERVATIONS);
|
|
563862
563932
|
}
|
|
563933
|
+
doc.durableObservations.push(line);
|
|
563934
|
+
if (doc.durableObservations.length > MAX_PROFILE_DURABLE_OBSERVATIONS) {
|
|
563935
|
+
doc.durableObservations.splice(0, doc.durableObservations.length - MAX_PROFILE_DURABLE_OBSERVATIONS);
|
|
563936
|
+
}
|
|
563863
563937
|
}
|
|
563938
|
+
appendScopedPersonalityEvent(scope, {
|
|
563939
|
+
ts: observation.ts ?? Date.now(),
|
|
563940
|
+
iso: now,
|
|
563941
|
+
speaker,
|
|
563942
|
+
role: observation.role,
|
|
563943
|
+
mode: observation.mode,
|
|
563944
|
+
text,
|
|
563945
|
+
toneTags: [...new Set(toneTags)].slice(0, 32),
|
|
563946
|
+
relationshipHints
|
|
563947
|
+
});
|
|
563864
563948
|
saveScopedPersonality(scope, doc);
|
|
563865
563949
|
return doc;
|
|
563866
563950
|
}
|
|
@@ -563871,6 +563955,9 @@ function renderScopedPersonalityMarkdown(doc) {
|
|
|
563871
563955
|
return `- ${name10}: messages=${profile.count}; last=${profile.lastSeenAt};${tags}${samples ? `
|
|
563872
563956
|
${samples}` : ""}`;
|
|
563873
563957
|
});
|
|
563958
|
+
const topTopics = Object.entries(doc.topicCounts ?? {}).sort((a2, b) => b[1] - a2[1]).slice(0, 18).map(([topic, count]) => `- ${topic}: ${count}`);
|
|
563959
|
+
const durable = doc.durableObservations.slice(-18).map((line) => `- ${line}`);
|
|
563960
|
+
const relationships = doc.relationshipEvents.slice(-10).map((line) => `- ${line}`);
|
|
563874
563961
|
return [
|
|
563875
563962
|
`# Scoped Personality Profile`,
|
|
563876
563963
|
``,
|
|
@@ -563887,6 +563974,11 @@ ${samples}` : ""}`;
|
|
|
563887
563974
|
`## Relationship Model`,
|
|
563888
563975
|
doc.relationshipModel.map((line) => `- ${line}`).join("\n"),
|
|
563889
563976
|
``,
|
|
563977
|
+
`## Durable Profile Memory`,
|
|
563978
|
+
topTopics.length ? [`Top recurring topics:`, ...topTopics].join("\n") : "Top recurring topics:\n- none",
|
|
563979
|
+
durable.length ? [`Longer-term observations:`, ...durable].join("\n") : "Longer-term observations:\n- none",
|
|
563980
|
+
relationships.length ? [`Relationship events:`, ...relationships].join("\n") : "Relationship events:\n- none",
|
|
563981
|
+
``,
|
|
563890
563982
|
`## Behavioral Guidance`,
|
|
563891
563983
|
doc.behavioralGuidance.map((line) => `- ${line}`).join("\n"),
|
|
563892
563984
|
``,
|
|
@@ -563910,13 +564002,15 @@ function renderScopedPersonalityContext(doc) {
|
|
|
563910
564002
|
function buildScopedPersonalityContext(scope) {
|
|
563911
564003
|
return renderScopedPersonalityContext(loadScopedPersonality(scope));
|
|
563912
564004
|
}
|
|
563913
|
-
var PROFILE_VERSION, MAX_PROFILE_OBSERVATIONS, MAX_PROFILE_SAMPLES, MAX_PROFILE_TAGS, STOPWORDS2;
|
|
564005
|
+
var PROFILE_VERSION, MAX_PROFILE_OBSERVATIONS, MAX_PROFILE_DURABLE_OBSERVATIONS, MAX_PROFILE_RELATIONSHIP_EVENTS, MAX_PROFILE_SAMPLES, MAX_PROFILE_TAGS, STOPWORDS2;
|
|
563914
564006
|
var init_scoped_personality = __esm({
|
|
563915
564007
|
"packages/cli/src/tui/scoped-personality.ts"() {
|
|
563916
564008
|
"use strict";
|
|
563917
564009
|
PROFILE_VERSION = 1;
|
|
563918
|
-
MAX_PROFILE_OBSERVATIONS =
|
|
563919
|
-
|
|
564010
|
+
MAX_PROFILE_OBSERVATIONS = 40;
|
|
564011
|
+
MAX_PROFILE_DURABLE_OBSERVATIONS = 240;
|
|
564012
|
+
MAX_PROFILE_RELATIONSHIP_EVENTS = 120;
|
|
564013
|
+
MAX_PROFILE_SAMPLES = 16;
|
|
563920
564014
|
MAX_PROFILE_TAGS = 12;
|
|
563921
564015
|
STOPWORDS2 = /* @__PURE__ */ new Set([
|
|
563922
564016
|
"about",
|
|
@@ -567727,7 +567821,7 @@ __export(omnius_directory_exports, {
|
|
|
567727
567821
|
writeIndexMeta: () => writeIndexMeta,
|
|
567728
567822
|
writeTaskHandoff: () => writeTaskHandoff2
|
|
567729
567823
|
});
|
|
567730
|
-
import { cpSync as cpSync2, existsSync as existsSync87, mkdirSync as mkdirSync50, readFileSync as readFileSync70, writeFileSync as writeFileSync45, readdirSync as readdirSync29, statSync as statSync32, unlinkSync as unlinkSync15, openSync as openSync2, closeSync as closeSync2, renameSync as renameSync4 } from "node:fs";
|
|
567824
|
+
import { appendFileSync as appendFileSync5, cpSync as cpSync2, existsSync as existsSync87, mkdirSync as mkdirSync50, readFileSync as readFileSync70, writeFileSync as writeFileSync45, readdirSync as readdirSync29, statSync as statSync32, unlinkSync as unlinkSync15, openSync as openSync2, closeSync as closeSync2, renameSync as renameSync4 } from "node:fs";
|
|
567731
567825
|
import { join as join104, relative as relative9, basename as basename17, dirname as dirname29 } from "node:path";
|
|
567732
567826
|
import { homedir as homedir31 } from "node:os";
|
|
567733
567827
|
import { createHash as createHash20 } from "node:crypto";
|
|
@@ -568248,6 +568342,7 @@ function saveSessionContext(repoRoot, entry) {
|
|
|
568248
568342
|
const contextDir = join104(repoRoot, OMNIUS_DIR, "context");
|
|
568249
568343
|
mkdirSync50(contextDir, { recursive: true });
|
|
568250
568344
|
const filePath = join104(contextDir, CONTEXT_SAVE_FILE);
|
|
568345
|
+
const ledgerPath = join104(contextDir, CONTEXT_LEDGER_FILE);
|
|
568251
568346
|
const lockPath = join104(contextDir, CONTEXT_SAVE_FILE + ".lock");
|
|
568252
568347
|
const locked = acquireLock(lockPath);
|
|
568253
568348
|
if (!locked) {
|
|
@@ -568264,7 +568359,23 @@ function saveSessionContext(repoRoot, entry) {
|
|
|
568264
568359
|
} catch {
|
|
568265
568360
|
ctx3 = { entries: [], maxEntries: MAX_CONTEXT_ENTRIES, updatedAt: "" };
|
|
568266
568361
|
}
|
|
568362
|
+
ctx3.maxEntries = Math.max(
|
|
568363
|
+
Number.isFinite(ctx3.maxEntries) ? ctx3.maxEntries : 0,
|
|
568364
|
+
MAX_CONTEXT_ENTRIES
|
|
568365
|
+
);
|
|
568267
568366
|
const normalizedEntry = normalizeSessionContextEntry(entry);
|
|
568367
|
+
try {
|
|
568368
|
+
appendFileSync5(
|
|
568369
|
+
ledgerPath,
|
|
568370
|
+
JSON.stringify({
|
|
568371
|
+
...normalizedEntry,
|
|
568372
|
+
ledgerSavedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
568373
|
+
ledgerVersion: 1
|
|
568374
|
+
}) + "\n",
|
|
568375
|
+
"utf-8"
|
|
568376
|
+
);
|
|
568377
|
+
} catch {
|
|
568378
|
+
}
|
|
568268
568379
|
const hashToIndex = /* @__PURE__ */ new Map();
|
|
568269
568380
|
for (let i2 = 0; i2 < ctx3.entries.length; i2++) {
|
|
568270
568381
|
const existing = ctx3.entries[i2];
|
|
@@ -568316,7 +568427,7 @@ function saveSessionContext(repoRoot, entry) {
|
|
|
568316
568427
|
try {
|
|
568317
568428
|
writeFileSync45(
|
|
568318
568429
|
join104(contextDir, "session-diary.md"),
|
|
568319
|
-
renderSessionDiary(ctx3.entries.slice(-
|
|
568430
|
+
renderSessionDiary(ctx3.entries.slice(-MAX_SESSION_DIARY_ENTRIES)),
|
|
568320
568431
|
"utf-8"
|
|
568321
568432
|
);
|
|
568322
568433
|
} catch {
|
|
@@ -568744,7 +568855,7 @@ function deleteUsageRecord(kind, value2, repoRoot) {
|
|
|
568744
568855
|
remove(join104(repoRoot, OMNIUS_DIR, USAGE_HISTORY_FILE));
|
|
568745
568856
|
}
|
|
568746
568857
|
}
|
|
568747
|
-
var OMNIUS_DIR, LEGACY_DIRS, SUBDIRS, CONTEXT_FILES, PENDING_TASK_FILE, HANDOFF_FILE, CONTEXT_SAVE_FILE, MAX_CONTEXT_ENTRIES, SAME_TASK_REPLACE_WINDOW_MS, LOCK_TIMEOUT_MS, LOCK_RETRY_MS, LOCK_RETRY_MAX, SESSIONS_DIR, SESSIONS_INDEX, SKIP_DIRS2, HOME_SKIP_DIRS, USAGE_HISTORY_FILE, MAX_HISTORY_RECORDS;
|
|
568858
|
+
var OMNIUS_DIR, LEGACY_DIRS, SUBDIRS, CONTEXT_FILES, PENDING_TASK_FILE, HANDOFF_FILE, CONTEXT_SAVE_FILE, CONTEXT_LEDGER_FILE, MAX_CONTEXT_ENTRIES, MAX_SESSION_DIARY_ENTRIES, SAME_TASK_REPLACE_WINDOW_MS, LOCK_TIMEOUT_MS, LOCK_RETRY_MS, LOCK_RETRY_MAX, SESSIONS_DIR, SESSIONS_INDEX, SKIP_DIRS2, HOME_SKIP_DIRS, USAGE_HISTORY_FILE, MAX_HISTORY_RECORDS;
|
|
568748
568859
|
var init_omnius_directory = __esm({
|
|
568749
568860
|
"packages/cli/src/tui/omnius-directory.ts"() {
|
|
568750
568861
|
"use strict";
|
|
@@ -568764,7 +568875,9 @@ var init_omnius_directory = __esm({
|
|
|
568764
568875
|
PENDING_TASK_FILE = "pending-task.json";
|
|
568765
568876
|
HANDOFF_FILE = "task-handoff.json";
|
|
568766
568877
|
CONTEXT_SAVE_FILE = "session-context.json";
|
|
568767
|
-
|
|
568878
|
+
CONTEXT_LEDGER_FILE = "session-context.events.jsonl";
|
|
568879
|
+
MAX_CONTEXT_ENTRIES = 200;
|
|
568880
|
+
MAX_SESSION_DIARY_ENTRIES = 80;
|
|
568768
568881
|
SAME_TASK_REPLACE_WINDOW_MS = 12 * 60 * 60 * 1e3;
|
|
568769
568882
|
LOCK_TIMEOUT_MS = 5e3;
|
|
568770
568883
|
LOCK_RETRY_MS = 50;
|
|
@@ -572786,6 +572899,106 @@ ${CONTENT_BG_SEQ}`);
|
|
|
572786
572899
|
(seq) => seq.endsWith("m") ? seq : ""
|
|
572787
572900
|
);
|
|
572788
572901
|
}
|
|
572902
|
+
reflowContentLines(livePartialLine, width) {
|
|
572903
|
+
const maxWidth = Math.max(16, width);
|
|
572904
|
+
const source = livePartialLine ? [...this._contentLines, livePartialLine] : this._contentLines;
|
|
572905
|
+
return source.flatMap(
|
|
572906
|
+
(line, idx) => this.reflowContentLine(line, maxWidth).map((segment) => ({
|
|
572907
|
+
line: segment,
|
|
572908
|
+
bufferIdx: idx
|
|
572909
|
+
}))
|
|
572910
|
+
);
|
|
572911
|
+
}
|
|
572912
|
+
reflowContentLine(line, width) {
|
|
572913
|
+
const visible = stripAnsi(line);
|
|
572914
|
+
if (visible.length <= width) return [line];
|
|
572915
|
+
const continuationIndent = this.hangingIndentForVisibleLine(
|
|
572916
|
+
visible,
|
|
572917
|
+
width
|
|
572918
|
+
);
|
|
572919
|
+
const ranges = this.visibleWrapRanges(
|
|
572920
|
+
visible,
|
|
572921
|
+
width,
|
|
572922
|
+
continuationIndent.length
|
|
572923
|
+
);
|
|
572924
|
+
if (ranges.length <= 1) return [line];
|
|
572925
|
+
return ranges.map((range, idx) => {
|
|
572926
|
+
const raw = this.rawSliceForVisibleRange(line, range.start, range.end);
|
|
572927
|
+
return idx === 0 ? raw : continuationIndent + raw.trimStart();
|
|
572928
|
+
});
|
|
572929
|
+
}
|
|
572930
|
+
hangingIndentForVisibleLine(visible, width) {
|
|
572931
|
+
const capped = (n2) => " ".repeat(Math.max(0, Math.min(n2, width - 4)));
|
|
572932
|
+
const patterns = [
|
|
572933
|
+
/^(\s*(?:[│├└]\s*)?(?:[-*+○•]\s+))/,
|
|
572934
|
+
/^(\s*(?:[│├└]\s*)?(?:\d+[.)]\s+))/,
|
|
572935
|
+
/^(\s*(?:[│├└]\s*)?(?:>\s*))/,
|
|
572936
|
+
/^(\s*(?:[│├└]\s*)?(?:[A-Za-z][\w.-]{0,28}:\s+))/
|
|
572937
|
+
];
|
|
572938
|
+
for (const pattern of patterns) {
|
|
572939
|
+
const match = visible.match(pattern);
|
|
572940
|
+
if (match?.[1]) return capped(match[1].length);
|
|
572941
|
+
}
|
|
572942
|
+
return capped(visible.match(/^\s*/)?.[0].length ?? 0);
|
|
572943
|
+
}
|
|
572944
|
+
visibleWrapRanges(visible, width, continuationIndent) {
|
|
572945
|
+
const ranges = [];
|
|
572946
|
+
let start2 = 0;
|
|
572947
|
+
let available = width;
|
|
572948
|
+
while (start2 < visible.length) {
|
|
572949
|
+
if (visible.length - start2 <= available) {
|
|
572950
|
+
ranges.push({ start: start2, end: visible.length });
|
|
572951
|
+
break;
|
|
572952
|
+
}
|
|
572953
|
+
const limit = start2 + available;
|
|
572954
|
+
let breakAt = visible.lastIndexOf(" ", limit);
|
|
572955
|
+
if (breakAt <= start2 + 2) breakAt = limit;
|
|
572956
|
+
let end = breakAt;
|
|
572957
|
+
while (end > start2 && /\s/.test(visible[end - 1] ?? "")) end--;
|
|
572958
|
+
ranges.push({ start: start2, end: Math.max(start2 + 1, end) });
|
|
572959
|
+
start2 = breakAt;
|
|
572960
|
+
while (start2 < visible.length && /\s/.test(visible[start2] ?? "")) start2++;
|
|
572961
|
+
available = Math.max(8, width - continuationIndent);
|
|
572962
|
+
}
|
|
572963
|
+
return ranges;
|
|
572964
|
+
}
|
|
572965
|
+
rawSliceForVisibleRange(line, start2, end) {
|
|
572966
|
+
const startRaw = this.rawIndexForVisibleColumn(line, start2);
|
|
572967
|
+
const endRaw = this.rawIndexForVisibleColumn(line, end);
|
|
572968
|
+
const activeStyle = this.activeSgrAt(line, startRaw);
|
|
572969
|
+
const raw = line.slice(startRaw, endRaw);
|
|
572970
|
+
return activeStyle ? `${activeStyle}${raw}${RESET2}` : raw;
|
|
572971
|
+
}
|
|
572972
|
+
rawIndexForVisibleColumn(line, target) {
|
|
572973
|
+
if (target <= 0) return 0;
|
|
572974
|
+
let visible = 0;
|
|
572975
|
+
for (let i2 = 0; i2 < line.length; i2++) {
|
|
572976
|
+
if (line.charCodeAt(i2) === 27) {
|
|
572977
|
+
const match = line.slice(i2).match(/^\x1B\[[0-?]*[ -/]*[@-~]/);
|
|
572978
|
+
if (match) {
|
|
572979
|
+
i2 += match[0].length - 1;
|
|
572980
|
+
continue;
|
|
572981
|
+
}
|
|
572982
|
+
}
|
|
572983
|
+
if (visible >= target) return i2;
|
|
572984
|
+
visible++;
|
|
572985
|
+
}
|
|
572986
|
+
return line.length;
|
|
572987
|
+
}
|
|
572988
|
+
activeSgrAt(line, rawIndex) {
|
|
572989
|
+
let active = "";
|
|
572990
|
+
const sgr = /\x1B\[[0-9;]*m/g;
|
|
572991
|
+
let match;
|
|
572992
|
+
while ((match = sgr.exec(line)) && match.index < rawIndex) {
|
|
572993
|
+
const seq = match[0];
|
|
572994
|
+
if (seq === RESET2 || /\x1B\[(?:0|39|49)(?:;0)?m/.test(seq)) {
|
|
572995
|
+
active = "";
|
|
572996
|
+
} else {
|
|
572997
|
+
active += seq;
|
|
572998
|
+
}
|
|
572999
|
+
}
|
|
573000
|
+
return active;
|
|
573001
|
+
}
|
|
572789
573002
|
/**
|
|
572790
573003
|
* Remove the last N lines from the content scrollback buffer and repaint.
|
|
572791
573004
|
* Used by Esc-to-recall to erase the just-rendered user prompt.
|
|
@@ -572876,9 +573089,10 @@ ${CONTENT_BG_SEQ}`);
|
|
|
572876
573089
|
repaintContent() {
|
|
572877
573090
|
const h = this.contentHeight;
|
|
572878
573091
|
const livePartialLine = this.getLiveBufferedLine();
|
|
572879
|
-
const totalLines = this._contentLines.length + (livePartialLine ? 1 : 0);
|
|
572880
|
-
const startIdx = Math.max(0, totalLines - h - this._contentScrollOffset);
|
|
572881
573092
|
const w = termCols();
|
|
573093
|
+
const reflowedLines = this.reflowContentLines(livePartialLine, w);
|
|
573094
|
+
const totalLines = reflowedLines.length;
|
|
573095
|
+
const startIdx = Math.max(0, totalLines - h - this._contentScrollOffset);
|
|
572882
573096
|
const headerSafeFloor = layout().headerBottom + 1;
|
|
572883
573097
|
let buf = "\x1B[?2026h";
|
|
572884
573098
|
buf += "\x1B7";
|
|
@@ -572886,16 +573100,12 @@ ${CONTENT_BG_SEQ}`);
|
|
|
572886
573100
|
const selRanges = this._textSelection.getSelectedRanges();
|
|
572887
573101
|
for (let row = 0; row < h; row++) {
|
|
572888
573102
|
const lineIdx = startIdx + row;
|
|
572889
|
-
|
|
572890
|
-
|
|
572891
|
-
line = this._contentLines[lineIdx];
|
|
572892
|
-
} else if (livePartialLine && lineIdx === this._contentLines.length) {
|
|
572893
|
-
line = livePartialLine;
|
|
572894
|
-
}
|
|
573103
|
+
const reflowed = reflowedLines[lineIdx];
|
|
573104
|
+
let line = reflowed?.line ?? "";
|
|
572895
573105
|
const screenRow = this.scrollRegionTop + row;
|
|
572896
573106
|
if (screenRow < headerSafeFloor) continue;
|
|
572897
573107
|
line = line.replace(/\x1B\[0m/g, `\x1B[0m${CONTENT_BG_SEQ}`);
|
|
572898
|
-
const sel = selRanges.find((r2) => r2.bufferIdx ===
|
|
573108
|
+
const sel = selRanges.find((r2) => r2.bufferIdx === reflowed?.bufferIdx);
|
|
572899
573109
|
if (sel) {
|
|
572900
573110
|
line = TextSelection.applyHighlight(line, sel.startCol, sel.endCol);
|
|
572901
573111
|
}
|
|
@@ -575667,7 +575877,7 @@ __export(setup_exports, {
|
|
|
575667
575877
|
import * as readline from "node:readline";
|
|
575668
575878
|
import { execSync as execSync50, spawn as spawn26, exec as exec4 } from "node:child_process";
|
|
575669
575879
|
import { promisify as promisify6 } from "node:util";
|
|
575670
|
-
import { existsSync as existsSync90, writeFileSync as writeFileSync47, readFileSync as readFileSync74, appendFileSync as
|
|
575880
|
+
import { existsSync as existsSync90, writeFileSync as writeFileSync47, readFileSync as readFileSync74, appendFileSync as appendFileSync6, mkdirSync as mkdirSync52 } from "node:fs";
|
|
575671
575881
|
import { join as join107 } from "node:path";
|
|
575672
575882
|
import { homedir as homedir34, platform as platform5 } from "node:os";
|
|
575673
575883
|
function wrapText(value2, width) {
|
|
@@ -578034,7 +578244,7 @@ function ensurePathInShellRc(binDir) {
|
|
|
578034
578244
|
const exportLine = `
|
|
578035
578245
|
export PATH="${binDir}:$PATH" # Added by omnius for nvim
|
|
578036
578246
|
`;
|
|
578037
|
-
|
|
578247
|
+
appendFileSync6(rcFile, exportLine, "utf8");
|
|
578038
578248
|
console.log(` Added ${binDir} to ${rcFile}`);
|
|
578039
578249
|
} catch {
|
|
578040
578250
|
}
|
|
@@ -587604,7 +587814,7 @@ import {
|
|
|
587604
587814
|
lstatSync,
|
|
587605
587815
|
statSync as statSync37,
|
|
587606
587816
|
rmSync as rmSync4,
|
|
587607
|
-
appendFileSync as
|
|
587817
|
+
appendFileSync as appendFileSync7
|
|
587608
587818
|
} from "node:fs";
|
|
587609
587819
|
import { relative as relative11, join as join114 } from "node:path";
|
|
587610
587820
|
async function _immediateReregister(newUrl) {
|
|
@@ -592004,7 +592214,7 @@ sleep 1
|
|
|
592004
592214
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
592005
592215
|
`;
|
|
592006
592216
|
try {
|
|
592007
|
-
|
|
592217
|
+
appendFileSync7(_spLogFile, line);
|
|
592008
592218
|
} catch {
|
|
592009
592219
|
}
|
|
592010
592220
|
};
|
|
@@ -592967,12 +593177,16 @@ sleep 1
|
|
|
592967
593177
|
return "handled";
|
|
592968
593178
|
}
|
|
592969
593179
|
if (requested !== "auto" && requested !== "chat" && requested !== "action") {
|
|
592970
|
-
renderWarning("Usage: /telegram mode auto|chat|action [--
|
|
593180
|
+
renderWarning("Usage: /telegram mode auto|chat|action [--global]");
|
|
592971
593181
|
return "handled";
|
|
592972
593182
|
}
|
|
592973
|
-
|
|
593183
|
+
const hasGlobalFlagMode = parts.includes("--global");
|
|
593184
|
+
const projectRootMode = ctx3.repoRoot || process.cwd();
|
|
593185
|
+
const projectHasOmniusMode = existsSync100(join114(projectRootMode, ".omnius"));
|
|
593186
|
+
const wantsLocalMode = hasGlobalFlagMode ? false : isLocal || projectHasOmniusMode;
|
|
593187
|
+
ctx3.saveTelegramSettings?.({ mode: requested, local: wantsLocalMode });
|
|
592974
593188
|
ctx3.telegramSetInteractionMode?.(requested);
|
|
592975
|
-
renderInfo(`Telegram interaction mode set to ${c3.bold(requested)}${
|
|
593189
|
+
renderInfo(`Telegram interaction mode set to ${c3.bold(requested)}${wantsLocalMode ? " (project)" : " (global)"}.`);
|
|
592976
593190
|
return "handled";
|
|
592977
593191
|
}
|
|
592978
593192
|
if (parts[0] === "auth" || parts[0] === "authenticate") {
|
|
@@ -593219,18 +593433,23 @@ sleep 1
|
|
|
593219
593433
|
}
|
|
593220
593434
|
const keyIdx = parts.indexOf("--key");
|
|
593221
593435
|
const adminIdx = parts.indexOf("--admin");
|
|
593436
|
+
const hasGlobalFlag = parts.includes("--global");
|
|
593437
|
+
const projectRoot = ctx3.repoRoot || process.cwd();
|
|
593438
|
+
const projectHasOmnius = existsSync100(join114(projectRoot, ".omnius"));
|
|
593439
|
+
const wantsLocal = hasGlobalFlag ? false : isLocal || projectHasOmnius;
|
|
593222
593440
|
if (keyIdx !== -1 || adminIdx !== -1) {
|
|
593223
593441
|
const settings = {
|
|
593224
|
-
local:
|
|
593442
|
+
local: wantsLocal
|
|
593225
593443
|
};
|
|
593226
|
-
const scope =
|
|
593444
|
+
const scope = wantsLocal ? "project" : "global";
|
|
593227
593445
|
if (keyIdx !== -1) {
|
|
593228
593446
|
const token = parts[keyIdx + 1];
|
|
593229
593447
|
if (!token || token.startsWith("--")) {
|
|
593230
593448
|
renderWarning(
|
|
593231
|
-
"Usage: /telegram --key <bot-token> [--admin <id>] [--
|
|
593449
|
+
"Usage: /telegram --key <bot-token> [--admin <id>] [--global]"
|
|
593232
593450
|
);
|
|
593233
593451
|
renderInfo("Get a bot token from @BotFather on Telegram.");
|
|
593452
|
+
renderInfo("Default scope: project-local when .omnius/ exists; use --global to override.");
|
|
593234
593453
|
return "handled";
|
|
593235
593454
|
}
|
|
593236
593455
|
settings.key = token;
|
|
@@ -593239,7 +593458,7 @@ sleep 1
|
|
|
593239
593458
|
const userId = parts[adminIdx + 1];
|
|
593240
593459
|
if (!userId || userId.startsWith("--")) {
|
|
593241
593460
|
renderWarning(
|
|
593242
|
-
"Usage: /telegram --admin <user-id-or-username> [--key <token>] [--
|
|
593461
|
+
"Usage: /telegram --admin <user-id-or-username> [--key <token>] [--global]"
|
|
593243
593462
|
);
|
|
593244
593463
|
return "handled";
|
|
593245
593464
|
}
|
|
@@ -593254,6 +593473,11 @@ sleep 1
|
|
|
593254
593473
|
renderInfo(
|
|
593255
593474
|
`Telegram admin set to ${c3.bold(settings.admin)} (${scope}).`
|
|
593256
593475
|
);
|
|
593476
|
+
if (wantsLocal && !projectHasOmnius) {
|
|
593477
|
+
renderWarning(
|
|
593478
|
+
"No .omnius/ in the current directory; this 'local' setting was written to ./.omnius/settings.json and only applies when omnius is invoked from this folder."
|
|
593479
|
+
);
|
|
593480
|
+
}
|
|
593257
593481
|
if (!ctx3.isTelegramActive?.() && settings.key)
|
|
593258
593482
|
renderInfo("Use /telegram to start.");
|
|
593259
593483
|
return "handled";
|
|
@@ -593267,9 +593491,30 @@ sleep 1
|
|
|
593267
593491
|
if (!settings.key) {
|
|
593268
593492
|
renderWarning("No Telegram bot token configured.");
|
|
593269
593493
|
renderInfo("Set one first: /telegram --key <bot-token>");
|
|
593270
|
-
renderInfo("Get a token from @BotFather on Telegram.");
|
|
593494
|
+
renderInfo("Get a token from @BotFather on Telegram. The token saves project-locally by default when .omnius/ exists.");
|
|
593271
593495
|
return "handled";
|
|
593272
593496
|
}
|
|
593497
|
+
try {
|
|
593498
|
+
const startProjectRoot = ctx3.repoRoot || process.cwd();
|
|
593499
|
+
if (existsSync100(join114(startProjectRoot, ".omnius"))) {
|
|
593500
|
+
const projectSettingsPath = join114(startProjectRoot, ".omnius", "settings.json");
|
|
593501
|
+
let projectHasKey = false;
|
|
593502
|
+
if (existsSync100(projectSettingsPath)) {
|
|
593503
|
+
try {
|
|
593504
|
+
const projectJson = JSON.parse(readFileSync81(projectSettingsPath, "utf8"));
|
|
593505
|
+
projectHasKey = typeof projectJson?.telegramKey === "string" && projectJson.telegramKey.length > 0;
|
|
593506
|
+
} catch {
|
|
593507
|
+
}
|
|
593508
|
+
}
|
|
593509
|
+
if (!projectHasKey) {
|
|
593510
|
+
renderWarning(
|
|
593511
|
+
"Using GLOBAL Telegram token in a project that has no local key. Another omnius instance running with the same token will steal getUpdates and you'll see 409 conflicts."
|
|
593512
|
+
);
|
|
593513
|
+
renderInfo("Scope it locally: /telegram --key <token> (writes to ./.omnius/settings.json)");
|
|
593514
|
+
}
|
|
593515
|
+
}
|
|
593516
|
+
} catch {
|
|
593517
|
+
}
|
|
593273
593518
|
try {
|
|
593274
593519
|
await ctx3.telegramStart?.(settings.key, settings.admin);
|
|
593275
593520
|
} catch (err) {
|
|
@@ -593281,8 +593526,8 @@ sleep 1
|
|
|
593281
593526
|
}
|
|
593282
593527
|
renderWarning(`Unknown argument: "${arg}"`);
|
|
593283
593528
|
renderInfo("Usage:");
|
|
593284
|
-
renderInfo(" /telegram --key <token> Save bot token (
|
|
593285
|
-
renderInfo(" /telegram --admin <id> Set admin filter (
|
|
593529
|
+
renderInfo(" /telegram --key <token> Save bot token (project-local when .omnius/ exists)");
|
|
593530
|
+
renderInfo(" /telegram --admin <id> Set admin filter (project-local by default)");
|
|
593286
593531
|
renderInfo(" /telegram Toggle on/off");
|
|
593287
593532
|
renderInfo(" /telegram stop Stop bridge");
|
|
593288
593533
|
renderInfo(" /telegram status Show status");
|
|
@@ -593301,7 +593546,7 @@ sleep 1
|
|
|
593301
593546
|
renderInfo(" /telegram delete-messages <chat> <msg,msg> [reason] Delete multiple messages");
|
|
593302
593547
|
renderInfo(" /telegram delete-reaction <chat> <msg> --user <id> Delete a reaction");
|
|
593303
593548
|
renderInfo(" /telegram delete-reactions <chat> --user <id> Delete recent reactions");
|
|
593304
|
-
renderInfo(" Add --
|
|
593549
|
+
renderInfo(" Add --global to write to ~/.omnius/ (shared across all omnius instances)");
|
|
593305
593550
|
return "handled";
|
|
593306
593551
|
}
|
|
593307
593552
|
case "platforms":
|
|
@@ -602934,10 +603179,13 @@ var init_stream_renderer = __esm({
|
|
|
602934
603179
|
const words = text.split(/(\s+)/);
|
|
602935
603180
|
const lines = [];
|
|
602936
603181
|
let currentLine = "";
|
|
603182
|
+
const continuationIndent = this.hangingIndentForText(text, maxWidth);
|
|
603183
|
+
let available = maxWidth;
|
|
602937
603184
|
for (const segment of words) {
|
|
602938
|
-
if (currentLine.length + segment.length >
|
|
603185
|
+
if (currentLine.length + segment.length > available && currentLine.trim()) {
|
|
602939
603186
|
lines.push(currentLine.trimEnd());
|
|
602940
|
-
currentLine = segment.replace(/^\s+/, "");
|
|
603187
|
+
currentLine = continuationIndent + segment.replace(/^\s+/, "");
|
|
603188
|
+
available = maxWidth;
|
|
602941
603189
|
} else {
|
|
602942
603190
|
currentLine += segment;
|
|
602943
603191
|
}
|
|
@@ -602947,12 +603195,26 @@ var init_stream_renderer = __esm({
|
|
|
602947
603195
|
}
|
|
602948
603196
|
return lines.length > 0 ? lines : [text];
|
|
602949
603197
|
}
|
|
603198
|
+
hangingIndentForText(text, maxWidth) {
|
|
603199
|
+
const capped = (n2) => " ".repeat(Math.max(0, Math.min(n2, maxWidth - 4)));
|
|
603200
|
+
const patterns = [
|
|
603201
|
+
/^(\s*(?:[-*+○•]\s+))/,
|
|
603202
|
+
/^(\s*(?:\d+[.)]\s+))/,
|
|
603203
|
+
/^(\s*(?:>\s*))/,
|
|
603204
|
+
/^(\s*(?:[A-Za-z][\w.-]{0,28}:\s+))/
|
|
603205
|
+
];
|
|
603206
|
+
for (const pattern of patterns) {
|
|
603207
|
+
const match = text.match(pattern);
|
|
603208
|
+
if (match?.[1]) return capped(match[1].length);
|
|
603209
|
+
}
|
|
603210
|
+
return capped(text.match(/^\s*/)?.[0].length ?? 0);
|
|
603211
|
+
}
|
|
602950
603212
|
};
|
|
602951
603213
|
}
|
|
602952
603214
|
});
|
|
602953
603215
|
|
|
602954
603216
|
// packages/cli/src/tui/edit-history.ts
|
|
602955
|
-
import { appendFileSync as
|
|
603217
|
+
import { appendFileSync as appendFileSync8, mkdirSync as mkdirSync60 } from "node:fs";
|
|
602956
603218
|
import { join as join119 } from "node:path";
|
|
602957
603219
|
function createEditHistoryLogger(repoRoot, sessionId) {
|
|
602958
603220
|
const historyDir = join119(repoRoot, ".omnius", "history");
|
|
@@ -602973,7 +603235,7 @@ function createEditHistoryLogger(repoRoot, sessionId) {
|
|
|
602973
603235
|
args: sanitizeArgs(toolName, toolArgs)
|
|
602974
603236
|
};
|
|
602975
603237
|
try {
|
|
602976
|
-
|
|
603238
|
+
appendFileSync8(logPath3, JSON.stringify(entry) + "\n", "utf-8");
|
|
602977
603239
|
} catch {
|
|
602978
603240
|
}
|
|
602979
603241
|
}
|
|
@@ -608356,6 +608618,9 @@ function upsertTelegramReflectionMessage(repoRoot, sessionKey, entry, options2)
|
|
|
608356
608618
|
content,
|
|
608357
608619
|
labels: [entry.mode, entry.mediaSummary, senderLabel(entry)].filter((value2) => Boolean(value2))
|
|
608358
608620
|
});
|
|
608621
|
+
if (typeof entry.ts === "number" && Number.isFinite(entry.ts) && result.episodeId) {
|
|
608622
|
+
store2.getDb().prepare("UPDATE episodes SET timestamp = ? WHERE id = ?").run(entry.ts, result.episodeId);
|
|
608623
|
+
}
|
|
608359
608624
|
addTextEdges(graph, result, entry);
|
|
608360
608625
|
const after = store2.count(sessionKey);
|
|
608361
608626
|
return { episodeId: result.episodeId, reused: after === before };
|
|
@@ -608418,6 +608683,9 @@ async function buildTelegramReflectionCorpus(options2) {
|
|
|
608418
608683
|
content,
|
|
608419
608684
|
labels: [entry.mode, entry.mediaSummary, senderLabel(entry)].filter((value2) => Boolean(value2))
|
|
608420
608685
|
});
|
|
608686
|
+
if (typeof entry.ts === "number" && Number.isFinite(entry.ts) && result.episodeId) {
|
|
608687
|
+
store2.getDb().prepare("UPDATE episodes SET timestamp = ? WHERE id = ?").run(entry.ts, result.episodeId);
|
|
608688
|
+
}
|
|
608421
608689
|
addTextEdges(graph, result, entry);
|
|
608422
608690
|
const after = store2.count(options2.sessionKey);
|
|
608423
608691
|
if (after === before) reusedEpisodes++;
|
|
@@ -609061,7 +609329,7 @@ var init_vision_ingress = __esm({
|
|
|
609061
609329
|
});
|
|
609062
609330
|
|
|
609063
609331
|
// packages/cli/src/tui/telegram-bridge.ts
|
|
609064
|
-
import { mkdirSync as mkdirSync65, existsSync as existsSync112, unlinkSync as unlinkSync22, readdirSync as readdirSync40, statSync as statSync39, statfsSync as statfsSync5, readFileSync as readFileSync92, writeFileSync as writeFileSync59 } from "node:fs";
|
|
609332
|
+
import { mkdirSync as mkdirSync65, existsSync as existsSync112, unlinkSync as unlinkSync22, readdirSync as readdirSync40, statSync as statSync39, statfsSync as statfsSync5, readFileSync as readFileSync92, writeFileSync as writeFileSync59, appendFileSync as appendFileSync9 } from "node:fs";
|
|
609065
609333
|
import { join as join127, resolve as resolve43, basename as basename27, relative as relative13, isAbsolute as isAbsolute8, extname as extname16 } from "node:path";
|
|
609066
609334
|
import { writeFile as writeFileAsync } from "node:fs/promises";
|
|
609067
609335
|
import { createHash as createHash23, randomBytes as randomBytes22, randomInt } from "node:crypto";
|
|
@@ -610277,7 +610545,7 @@ function renderTelegramSubAgentError(username, error) {
|
|
|
610277
610545
|
process.stdout.write(` ${c3.dim("│")} ${c3.red("✘")} @${username}: ${c3.dim(preview)}
|
|
610278
610546
|
`);
|
|
610279
610547
|
}
|
|
610280
|
-
var TELEGRAM_TOOL_ACTION_GROUPS, TELEGRAM_TOOL_ACTION_GROUP, TELEGRAM_TOOL_MUTATING_GROUPS, DEFAULT_TELEGRAM_TOOL_GROUP_POLICY, TELEGRAM_TOOL_BUTTON_LABELS, TELEGRAM_SAFETY_PROMPT, ADMIN_DM_PROMPT, ADMIN_GROUP_PROMPT, TELEGRAM_PUBLIC_SOUL_PROFILE, TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT, TELEGRAM_PUBLIC_MEMORY_SCOPE_CONTRACT, TELEGRAM_PUBLIC_VISION_STACK_CONTRACT, GROUP_REPLY_DISCRETION_PROMPT, TELEGRAM_CHAT_MODE_PROMPT, ADMIN_CHAT_PROFILE_PROMPT, TELEGRAM_ACTION_RESPONSE_CONTRACT, TELEGRAM_EXTERNAL_ACQUISITION_CONTRACT, TELEGRAM_STUCK_SELF_TALK_PREFIXES, TELEGRAM_CHAT_HISTORY_LIMIT, TELEGRAM_CONTEXT_RECENT_DEFAULT, TELEGRAM_CONTEXT_LINE_LIMIT, TELEGRAM_CONTEXT_SAMPLE_LIMIT, TELEGRAM_MEMORY_CARD_LIMIT, TELEGRAM_MEMORY_NOTE_LIMIT, TELEGRAM_MEMORY_STOPWORDS, TELEGRAM_SUB_AGENT_BOUNDED_OPTIONS, TELEGRAM_PUBLIC_HELP_COMMANDS, TELEGRAM_REMINDER_SLASH_COMMANDS, TELEGRAM_REFLECTION_SLASH_COMMANDS, TELEGRAM_IMAGE_EXTENSIONS, MEDIA_CACHE_TTL_MS, TELEGRAM_CHANNEL_DMN_SWEEP_MS, TELEGRAM_CHANNEL_DMN_IDLE_AFTER_MS, TELEGRAM_CHANNEL_DMN_MIN_INTERVAL_MS, TELEGRAM_CHANNEL_DMN_MIN_MESSAGES, TELEGRAM_PUBLIC_TOOL_QUOTAS, TelegramBridge;
|
|
610548
|
+
var TELEGRAM_TOOL_ACTION_GROUPS, TELEGRAM_TOOL_ACTION_GROUP, TELEGRAM_TOOL_MUTATING_GROUPS, DEFAULT_TELEGRAM_TOOL_GROUP_POLICY, TELEGRAM_TOOL_BUTTON_LABELS, TELEGRAM_SAFETY_PROMPT, ADMIN_DM_PROMPT, ADMIN_GROUP_PROMPT, TELEGRAM_PUBLIC_SOUL_PROFILE, TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT, TELEGRAM_PUBLIC_MEMORY_SCOPE_CONTRACT, TELEGRAM_PUBLIC_VISION_STACK_CONTRACT, GROUP_REPLY_DISCRETION_PROMPT, TELEGRAM_CHAT_MODE_PROMPT, ADMIN_CHAT_PROFILE_PROMPT, TELEGRAM_ACTION_RESPONSE_CONTRACT, TELEGRAM_EXTERNAL_ACQUISITION_CONTRACT, TELEGRAM_STUCK_SELF_TALK_PREFIXES, TELEGRAM_CHAT_HISTORY_LIMIT, TELEGRAM_CONTEXT_RECENT_DEFAULT, TELEGRAM_CONTEXT_LINE_LIMIT, TELEGRAM_CONTEXT_SAMPLE_LIMIT, TELEGRAM_MEMORY_CARD_LIMIT, TELEGRAM_MEMORY_NOTE_LIMIT, TELEGRAM_ASSOCIATIVE_FACT_LIMIT, TELEGRAM_ASSOCIATIVE_USER_FACT_LIMIT, TELEGRAM_ASSOCIATIVE_ACTION_LIMIT, TELEGRAM_ASSOCIATIVE_RELATION_LIMIT, TELEGRAM_MEMORY_STOPWORDS, TELEGRAM_SUB_AGENT_BOUNDED_OPTIONS, TELEGRAM_PUBLIC_HELP_COMMANDS, TELEGRAM_REMINDER_SLASH_COMMANDS, TELEGRAM_REFLECTION_SLASH_COMMANDS, TELEGRAM_IMAGE_EXTENSIONS, MEDIA_CACHE_TTL_MS, TELEGRAM_CHANNEL_DMN_SWEEP_MS, TELEGRAM_CHANNEL_DMN_IDLE_AFTER_MS, TELEGRAM_CHANNEL_DMN_MIN_INTERVAL_MS, TELEGRAM_CHANNEL_DMN_MIN_MESSAGES, TELEGRAM_PUBLIC_TOOL_QUOTAS, TelegramBridge;
|
|
610281
610549
|
var init_telegram_bridge = __esm({
|
|
610282
610550
|
"packages/cli/src/tui/telegram-bridge.ts"() {
|
|
610283
610551
|
"use strict";
|
|
@@ -610296,7 +610564,9 @@ var init_telegram_bridge = __esm({
|
|
|
610296
610564
|
init_visual_identity_association();
|
|
610297
610565
|
init_telegram_channel_dmn();
|
|
610298
610566
|
init_telegram_reflection_corpus();
|
|
610567
|
+
init_memory_paths();
|
|
610299
610568
|
init_telegram_reflection_extraction();
|
|
610569
|
+
init_dist7();
|
|
610300
610570
|
TELEGRAM_TOOL_ACTION_GROUPS = [
|
|
610301
610571
|
"read",
|
|
610302
610572
|
"message",
|
|
@@ -610458,6 +610728,7 @@ PUBLIC TELEGRAM MEMORY SCOPE
|
|
|
610458
610728
|
|
|
610459
610729
|
This turn may use memory and conversation history for the current Telegram group/private chat scope only.
|
|
610460
610730
|
Users in a shared public group may ask questions about that shared group history and group memory, scoped by the current group id or by a user id/username inside that same group.
|
|
610731
|
+
Durable associative memory, participant profiles, relationships, and action ledgers persist on disk across Omnius terminal sessions and package updates. Use those recalled facts when the current sender or topic matches them; treat user assertions as scoped memory evidence, not universal truth.
|
|
610461
610732
|
Private chats, admin DMs, other groups, local terminal sessions, and fragmented private contexts are not visible from this public group. Do not imply they exist and do not answer from them.
|
|
610462
610733
|
`.trim();
|
|
610463
610734
|
TELEGRAM_PUBLIC_VISION_STACK_CONTRACT = `
|
|
@@ -610538,12 +610809,16 @@ External acquisition contract:
|
|
|
610538
610809
|
/^hmm,?\s+(let me|maybe|wait)\b/i,
|
|
610539
610810
|
/^ok(ay)?,?\s+let me try\b/i
|
|
610540
610811
|
];
|
|
610541
|
-
TELEGRAM_CHAT_HISTORY_LIMIT =
|
|
610812
|
+
TELEGRAM_CHAT_HISTORY_LIMIT = 5e3;
|
|
610542
610813
|
TELEGRAM_CONTEXT_RECENT_DEFAULT = 36;
|
|
610543
610814
|
TELEGRAM_CONTEXT_LINE_LIMIT = 320;
|
|
610544
610815
|
TELEGRAM_CONTEXT_SAMPLE_LIMIT = 10;
|
|
610545
|
-
TELEGRAM_MEMORY_CARD_LIMIT =
|
|
610546
|
-
TELEGRAM_MEMORY_NOTE_LIMIT =
|
|
610816
|
+
TELEGRAM_MEMORY_CARD_LIMIT = 240;
|
|
610817
|
+
TELEGRAM_MEMORY_NOTE_LIMIT = 24;
|
|
610818
|
+
TELEGRAM_ASSOCIATIVE_FACT_LIMIT = 600;
|
|
610819
|
+
TELEGRAM_ASSOCIATIVE_USER_FACT_LIMIT = 80;
|
|
610820
|
+
TELEGRAM_ASSOCIATIVE_ACTION_LIMIT = 5e3;
|
|
610821
|
+
TELEGRAM_ASSOCIATIVE_RELATION_LIMIT = 300;
|
|
610547
610822
|
TELEGRAM_MEMORY_STOPWORDS = /* @__PURE__ */ new Set([
|
|
610548
610823
|
"about",
|
|
610549
610824
|
"after",
|
|
@@ -610627,6 +610902,7 @@ External acquisition contract:
|
|
|
610627
610902
|
this.telegramToolPolicy = resolveSettings(repoRoot || ".").telegramToolPolicy ?? {};
|
|
610628
610903
|
this.mediaCacheDir = resolve43(repoRoot || ".", ".omnius", "telegram-media-cache");
|
|
610629
610904
|
this.telegramConversationDir = resolve43(repoRoot || ".", ".omnius", "telegram-conversations");
|
|
610905
|
+
this.telegramSqlitePath = resolve43(repoRoot || ".", ".omnius", "telegram.sqlite");
|
|
610630
610906
|
this.telegramToolButtonDir = resolve43(repoRoot || ".", ".omnius", "telegram-tool-buttons");
|
|
610631
610907
|
}
|
|
610632
610908
|
botToken;
|
|
@@ -610658,6 +610934,8 @@ External acquisition contract:
|
|
|
610658
610934
|
chatParticipants = /* @__PURE__ */ new Map();
|
|
610659
610935
|
/** Lightweight Zettelkasten-style memory cards by chat/guest session key */
|
|
610660
610936
|
chatMemoryCards = /* @__PURE__ */ new Map();
|
|
610937
|
+
/** Durable associative memory by scoped Telegram chat key. */
|
|
610938
|
+
chatAssociativeMemory = /* @__PURE__ */ new Map();
|
|
610661
610939
|
/** Generic chronological attention cadence shared by live surfaces. */
|
|
610662
610940
|
stimulation = new StimulationController();
|
|
610663
610941
|
/** Throttles noisy "skipped group chatter" waterfall logs */
|
|
@@ -610707,6 +610985,15 @@ External acquisition contract:
|
|
|
610707
610985
|
mediaCacheDir;
|
|
610708
610986
|
/** Persistent conversation memory directory */
|
|
610709
610987
|
telegramConversationDir;
|
|
610988
|
+
/** Durable SQLite mirror for raw Telegram messages and metadata. */
|
|
610989
|
+
telegramSqlitePath;
|
|
610990
|
+
telegramSqliteDb = null;
|
|
610991
|
+
/**
|
|
610992
|
+
* Per-bot ownership lockfile under .omnius/telegram-runner-state/.
|
|
610993
|
+
* Prevents two omnius instances from polling the same bot token concurrently
|
|
610994
|
+
* (which would cause silent 409 conflicts on getUpdates). Released in stop().
|
|
610995
|
+
*/
|
|
610996
|
+
telegramOwnerLockFile = null;
|
|
610710
610997
|
/** Session keys loaded from persistent conversation memory */
|
|
610711
610998
|
loadedConversationState = /* @__PURE__ */ new Set();
|
|
610712
610999
|
/** True once persisted Telegram conversation scopes have been bulk-loaded. */
|
|
@@ -610993,12 +611280,26 @@ No scoped reflection artifact exists yet for this chat. Use <code>/reflect</code
|
|
|
610993
611280
|
}
|
|
610994
611281
|
recordChatHistory(sessionKey, entry) {
|
|
610995
611282
|
this.ensureTelegramConversationLoaded(sessionKey);
|
|
611283
|
+
const stamped = { ...entry, ts: entry.ts ?? Date.now() };
|
|
610996
611284
|
const existing = this.chatHistory.get(sessionKey) ?? [];
|
|
610997
|
-
existing.push(
|
|
611285
|
+
existing.push(stamped);
|
|
610998
611286
|
if (existing.length > TELEGRAM_CHAT_HISTORY_LIMIT) {
|
|
610999
611287
|
existing.splice(0, existing.length - TELEGRAM_CHAT_HISTORY_LIMIT);
|
|
611000
611288
|
}
|
|
611001
611289
|
this.chatHistory.set(sessionKey, existing);
|
|
611290
|
+
this.appendTelegramConversationLedger(sessionKey, stamped);
|
|
611291
|
+
}
|
|
611292
|
+
appendTelegramConversationLedger(sessionKey, entry) {
|
|
611293
|
+
if (!this.repoRoot) return;
|
|
611294
|
+
try {
|
|
611295
|
+
mkdirSync65(this.telegramConversationDir, { recursive: true });
|
|
611296
|
+
appendFileSync9(
|
|
611297
|
+
this.telegramConversationLedgerPath(sessionKey),
|
|
611298
|
+
JSON.stringify({ sessionKey, ...entry }) + "\n",
|
|
611299
|
+
"utf8"
|
|
611300
|
+
);
|
|
611301
|
+
} catch {
|
|
611302
|
+
}
|
|
611002
611303
|
}
|
|
611003
611304
|
telegramReplySenderWithSelfFlag(sender) {
|
|
611004
611305
|
if (!sender) return void 0;
|
|
@@ -611226,6 +611527,272 @@ ${mediaContext}` : ""
|
|
|
611226
611527
|
const safe = createHash23("sha1").update(sessionKey).digest("hex").slice(0, 20);
|
|
611227
611528
|
return join127(this.telegramConversationDir, `${safe}.json`);
|
|
611228
611529
|
}
|
|
611530
|
+
telegramConversationLedgerPath(sessionKey) {
|
|
611531
|
+
const safe = createHash23("sha1").update(sessionKey).digest("hex").slice(0, 20);
|
|
611532
|
+
return join127(this.telegramConversationDir, `${safe}.events.jsonl`);
|
|
611533
|
+
}
|
|
611534
|
+
telegramDb() {
|
|
611535
|
+
if (this.telegramSqliteDb === false) return null;
|
|
611536
|
+
if (this.telegramSqliteDb) return this.telegramSqliteDb;
|
|
611537
|
+
if (!this.repoRoot) {
|
|
611538
|
+
this.telegramSqliteDb = false;
|
|
611539
|
+
return null;
|
|
611540
|
+
}
|
|
611541
|
+
try {
|
|
611542
|
+
mkdirSync65(resolve43(this.repoRoot, ".omnius"), { recursive: true });
|
|
611543
|
+
const db = initDb(this.telegramSqlitePath);
|
|
611544
|
+
db.exec(`
|
|
611545
|
+
CREATE TABLE IF NOT EXISTS telegram_messages (
|
|
611546
|
+
session_key TEXT NOT NULL,
|
|
611547
|
+
chat_id TEXT NOT NULL,
|
|
611548
|
+
chat_type TEXT,
|
|
611549
|
+
chat_title TEXT,
|
|
611550
|
+
message_id INTEGER NOT NULL,
|
|
611551
|
+
message_thread_id INTEGER,
|
|
611552
|
+
update_id INTEGER,
|
|
611553
|
+
telegram_date INTEGER,
|
|
611554
|
+
received_at INTEGER NOT NULL,
|
|
611555
|
+
role TEXT NOT NULL DEFAULT 'user',
|
|
611556
|
+
from_user_id INTEGER,
|
|
611557
|
+
username TEXT,
|
|
611558
|
+
first_name TEXT,
|
|
611559
|
+
text TEXT,
|
|
611560
|
+
raw_json TEXT NOT NULL,
|
|
611561
|
+
normalized_json TEXT NOT NULL,
|
|
611562
|
+
reply_to_message_id INTEGER,
|
|
611563
|
+
reply_to_username TEXT,
|
|
611564
|
+
media_json TEXT,
|
|
611565
|
+
PRIMARY KEY (chat_id, message_id, role)
|
|
611566
|
+
);
|
|
611567
|
+
CREATE INDEX IF NOT EXISTS idx_telegram_messages_session_time ON telegram_messages(session_key, received_at);
|
|
611568
|
+
CREATE INDEX IF NOT EXISTS idx_telegram_messages_user_time ON telegram_messages(session_key, from_user_id, received_at);
|
|
611569
|
+
CREATE INDEX IF NOT EXISTS idx_telegram_messages_username_time ON telegram_messages(session_key, username, received_at);
|
|
611570
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS telegram_messages_fts USING fts5(
|
|
611571
|
+
session_key UNINDEXED,
|
|
611572
|
+
chat_id UNINDEXED,
|
|
611573
|
+
message_id UNINDEXED,
|
|
611574
|
+
username,
|
|
611575
|
+
first_name,
|
|
611576
|
+
text,
|
|
611577
|
+
content='telegram_messages',
|
|
611578
|
+
content_rowid='rowid'
|
|
611579
|
+
);
|
|
611580
|
+
CREATE TRIGGER IF NOT EXISTS telegram_messages_ai AFTER INSERT ON telegram_messages BEGIN
|
|
611581
|
+
INSERT INTO telegram_messages_fts(rowid, session_key, chat_id, message_id, username, first_name, text)
|
|
611582
|
+
VALUES (new.rowid, new.session_key, new.chat_id, new.message_id, new.username, new.first_name, new.text);
|
|
611583
|
+
END;
|
|
611584
|
+
CREATE TRIGGER IF NOT EXISTS telegram_messages_ad AFTER DELETE ON telegram_messages BEGIN
|
|
611585
|
+
INSERT INTO telegram_messages_fts(telegram_messages_fts, rowid, session_key, chat_id, message_id, username, first_name, text)
|
|
611586
|
+
VALUES('delete', old.rowid, old.session_key, old.chat_id, old.message_id, old.username, old.first_name, old.text);
|
|
611587
|
+
END;
|
|
611588
|
+
CREATE TRIGGER IF NOT EXISTS telegram_messages_au AFTER UPDATE ON telegram_messages BEGIN
|
|
611589
|
+
INSERT INTO telegram_messages_fts(telegram_messages_fts, rowid, session_key, chat_id, message_id, username, first_name, text)
|
|
611590
|
+
VALUES('delete', old.rowid, old.session_key, old.chat_id, old.message_id, old.username, old.first_name, old.text);
|
|
611591
|
+
INSERT INTO telegram_messages_fts(rowid, session_key, chat_id, message_id, username, first_name, text)
|
|
611592
|
+
VALUES (new.rowid, new.session_key, new.chat_id, new.message_id, new.username, new.first_name, new.text);
|
|
611593
|
+
END;
|
|
611594
|
+
`);
|
|
611595
|
+
this.telegramSqliteDb = db;
|
|
611596
|
+
return db;
|
|
611597
|
+
} catch {
|
|
611598
|
+
this.telegramSqliteDb = false;
|
|
611599
|
+
return null;
|
|
611600
|
+
}
|
|
611601
|
+
}
|
|
611602
|
+
persistTelegramRawMessage(update2, msg) {
|
|
611603
|
+
const db = this.telegramDb();
|
|
611604
|
+
if (!db) return;
|
|
611605
|
+
const rawMessage = update2.message ?? update2.guest_message ?? update2.edited_message ?? update2.channel_post ?? {};
|
|
611606
|
+
const sessionKey = this.sessionKeyForMessage(msg);
|
|
611607
|
+
const media = msg.media || msg.replyToMedia || msg.livePhoto;
|
|
611608
|
+
try {
|
|
611609
|
+
db.prepare(`
|
|
611610
|
+
INSERT INTO telegram_messages (
|
|
611611
|
+
session_key, chat_id, chat_type, chat_title, message_id, message_thread_id,
|
|
611612
|
+
update_id, telegram_date, received_at, role, from_user_id, username, first_name,
|
|
611613
|
+
text, raw_json, normalized_json, reply_to_message_id, reply_to_username, media_json
|
|
611614
|
+
) VALUES (
|
|
611615
|
+
@session_key, @chat_id, @chat_type, @chat_title, @message_id, @message_thread_id,
|
|
611616
|
+
@update_id, @telegram_date, @received_at, 'user', @from_user_id, @username, @first_name,
|
|
611617
|
+
@text, @raw_json, @normalized_json, @reply_to_message_id, @reply_to_username, @media_json
|
|
611618
|
+
)
|
|
611619
|
+
ON CONFLICT(chat_id, message_id, role) DO UPDATE SET
|
|
611620
|
+
session_key=excluded.session_key,
|
|
611621
|
+
chat_type=excluded.chat_type,
|
|
611622
|
+
chat_title=excluded.chat_title,
|
|
611623
|
+
message_thread_id=excluded.message_thread_id,
|
|
611624
|
+
update_id=excluded.update_id,
|
|
611625
|
+
telegram_date=excluded.telegram_date,
|
|
611626
|
+
received_at=excluded.received_at,
|
|
611627
|
+
from_user_id=excluded.from_user_id,
|
|
611628
|
+
username=excluded.username,
|
|
611629
|
+
first_name=excluded.first_name,
|
|
611630
|
+
text=excluded.text,
|
|
611631
|
+
raw_json=excluded.raw_json,
|
|
611632
|
+
normalized_json=excluded.normalized_json,
|
|
611633
|
+
reply_to_message_id=excluded.reply_to_message_id,
|
|
611634
|
+
reply_to_username=excluded.reply_to_username,
|
|
611635
|
+
media_json=excluded.media_json
|
|
611636
|
+
`).run({
|
|
611637
|
+
session_key: sessionKey,
|
|
611638
|
+
chat_id: String(msg.chatId),
|
|
611639
|
+
chat_type: msg.chatType,
|
|
611640
|
+
chat_title: msg.chatTitle ?? null,
|
|
611641
|
+
message_id: msg.messageId,
|
|
611642
|
+
message_thread_id: msg.messageThreadId ?? null,
|
|
611643
|
+
update_id: typeof update2.update_id === "number" ? update2.update_id : null,
|
|
611644
|
+
telegram_date: typeof rawMessage.date === "number" ? rawMessage.date : null,
|
|
611645
|
+
received_at: Date.now(),
|
|
611646
|
+
from_user_id: msg.fromUserId ?? null,
|
|
611647
|
+
username: msg.username || null,
|
|
611648
|
+
first_name: msg.firstName || null,
|
|
611649
|
+
text: msg.text || "",
|
|
611650
|
+
raw_json: JSON.stringify(rawMessage),
|
|
611651
|
+
normalized_json: JSON.stringify(msg),
|
|
611652
|
+
reply_to_message_id: msg.replyToMessageId ?? null,
|
|
611653
|
+
reply_to_username: msg.replyToUsername || null,
|
|
611654
|
+
media_json: media ? JSON.stringify(media) : null
|
|
611655
|
+
});
|
|
611656
|
+
} catch {
|
|
611657
|
+
}
|
|
611658
|
+
}
|
|
611659
|
+
persistTelegramAssistantMessage(msg, text, messageId, replyToMessageId) {
|
|
611660
|
+
if (!messageId) return;
|
|
611661
|
+
const db = this.telegramDb();
|
|
611662
|
+
if (!db) return;
|
|
611663
|
+
const sessionKey = this.sessionKeyForMessage(msg);
|
|
611664
|
+
const now = Date.now();
|
|
611665
|
+
const normalized = {
|
|
611666
|
+
role: "assistant",
|
|
611667
|
+
chatId: msg.chatId,
|
|
611668
|
+
chatType: msg.chatType,
|
|
611669
|
+
chatTitle: msg.chatTitle,
|
|
611670
|
+
messageId,
|
|
611671
|
+
messageThreadId: msg.messageThreadId,
|
|
611672
|
+
replyToMessageId,
|
|
611673
|
+
username: this.state.botUsername || "omnius",
|
|
611674
|
+
text
|
|
611675
|
+
};
|
|
611676
|
+
try {
|
|
611677
|
+
db.prepare(`
|
|
611678
|
+
INSERT INTO telegram_messages (
|
|
611679
|
+
session_key, chat_id, chat_type, chat_title, message_id, message_thread_id,
|
|
611680
|
+
update_id, telegram_date, received_at, role, from_user_id, username, first_name,
|
|
611681
|
+
text, raw_json, normalized_json, reply_to_message_id, reply_to_username, media_json
|
|
611682
|
+
) VALUES (
|
|
611683
|
+
@session_key, @chat_id, @chat_type, @chat_title, @message_id, @message_thread_id,
|
|
611684
|
+
NULL, NULL, @received_at, 'assistant', NULL, @username, 'Omnius',
|
|
611685
|
+
@text, @raw_json, @normalized_json, @reply_to_message_id, NULL, NULL
|
|
611686
|
+
)
|
|
611687
|
+
ON CONFLICT(chat_id, message_id, role) DO UPDATE SET
|
|
611688
|
+
session_key=excluded.session_key,
|
|
611689
|
+
chat_type=excluded.chat_type,
|
|
611690
|
+
chat_title=excluded.chat_title,
|
|
611691
|
+
message_thread_id=excluded.message_thread_id,
|
|
611692
|
+
received_at=excluded.received_at,
|
|
611693
|
+
username=excluded.username,
|
|
611694
|
+
text=excluded.text,
|
|
611695
|
+
raw_json=excluded.raw_json,
|
|
611696
|
+
normalized_json=excluded.normalized_json,
|
|
611697
|
+
reply_to_message_id=excluded.reply_to_message_id
|
|
611698
|
+
`).run({
|
|
611699
|
+
session_key: sessionKey,
|
|
611700
|
+
chat_id: String(msg.chatId),
|
|
611701
|
+
chat_type: msg.chatType,
|
|
611702
|
+
chat_title: msg.chatTitle ?? null,
|
|
611703
|
+
message_id: messageId,
|
|
611704
|
+
message_thread_id: msg.messageThreadId ?? null,
|
|
611705
|
+
received_at: now,
|
|
611706
|
+
username: this.state.botUsername || "omnius",
|
|
611707
|
+
text,
|
|
611708
|
+
raw_json: JSON.stringify(normalized),
|
|
611709
|
+
normalized_json: JSON.stringify(normalized),
|
|
611710
|
+
reply_to_message_id: replyToMessageId ?? null
|
|
611711
|
+
});
|
|
611712
|
+
} catch {
|
|
611713
|
+
}
|
|
611714
|
+
}
|
|
611715
|
+
createTelegramAssociativeMemory() {
|
|
611716
|
+
const now = Date.now();
|
|
611717
|
+
return {
|
|
611718
|
+
version: 1,
|
|
611719
|
+
createdAt: now,
|
|
611720
|
+
updatedAt: now,
|
|
611721
|
+
facts: [],
|
|
611722
|
+
users: {},
|
|
611723
|
+
relationships: [],
|
|
611724
|
+
actions: []
|
|
611725
|
+
};
|
|
611726
|
+
}
|
|
611727
|
+
normalizeTelegramAssociativeMemory(raw) {
|
|
611728
|
+
const created = this.createTelegramAssociativeMemory();
|
|
611729
|
+
const users = {};
|
|
611730
|
+
const rawUsers = raw.users && typeof raw.users === "object" ? raw.users : {};
|
|
611731
|
+
for (const [key, user] of Object.entries(rawUsers)) {
|
|
611732
|
+
if (!user || typeof user !== "object") continue;
|
|
611733
|
+
users[key] = {
|
|
611734
|
+
username: String(user.username || "unknown"),
|
|
611735
|
+
displayName: user.displayName,
|
|
611736
|
+
userId: typeof user.userId === "number" ? user.userId : void 0,
|
|
611737
|
+
aliases: Array.isArray(user.aliases) ? user.aliases.map(String).slice(0, 20) : [],
|
|
611738
|
+
firstSeenTs: typeof user.firstSeenTs === "number" ? user.firstSeenTs : created.createdAt,
|
|
611739
|
+
lastSeenTs: typeof user.lastSeenTs === "number" ? user.lastSeenTs : created.updatedAt,
|
|
611740
|
+
messageCount: typeof user.messageCount === "number" ? user.messageCount : 0,
|
|
611741
|
+
directAddressCount: typeof user.directAddressCount === "number" ? user.directAddressCount : 0,
|
|
611742
|
+
replyCount: typeof user.replyCount === "number" ? user.replyCount : 0,
|
|
611743
|
+
toneTags: Array.isArray(user.toneTags) ? user.toneTags.map(String).slice(0, 20) : [],
|
|
611744
|
+
facts: Array.isArray(user.facts) ? user.facts.slice(0, TELEGRAM_ASSOCIATIVE_USER_FACT_LIMIT).map((fact) => this.normalizeTelegramAssociativeFact(fact)) : [],
|
|
611745
|
+
relationshipHints: Array.isArray(user.relationshipHints) ? user.relationshipHints.map(String).slice(0, 80) : [],
|
|
611746
|
+
recentTopics: Array.isArray(user.recentTopics) ? user.recentTopics.map(String).slice(0, 80) : [],
|
|
611747
|
+
lastMessages: Array.isArray(user.lastMessages) ? user.lastMessages.map(String).slice(-40) : []
|
|
611748
|
+
};
|
|
611749
|
+
}
|
|
611750
|
+
return {
|
|
611751
|
+
version: typeof raw.version === "number" ? raw.version : 1,
|
|
611752
|
+
createdAt: typeof raw.createdAt === "number" ? raw.createdAt : created.createdAt,
|
|
611753
|
+
updatedAt: typeof raw.updatedAt === "number" ? raw.updatedAt : created.updatedAt,
|
|
611754
|
+
facts: Array.isArray(raw.facts) ? raw.facts.slice(0, TELEGRAM_ASSOCIATIVE_FACT_LIMIT).map((fact) => this.normalizeTelegramAssociativeFact(fact)) : [],
|
|
611755
|
+
users,
|
|
611756
|
+
relationships: Array.isArray(raw.relationships) ? raw.relationships.slice(0, TELEGRAM_ASSOCIATIVE_RELATION_LIMIT).map((fact) => this.normalizeTelegramAssociativeFact(fact)) : [],
|
|
611757
|
+
actions: Array.isArray(raw.actions) ? raw.actions.slice(-TELEGRAM_ASSOCIATIVE_ACTION_LIMIT).map((action) => ({
|
|
611758
|
+
id: String(action.id || createHash23("sha1").update(JSON.stringify(action)).digest("hex").slice(0, 12)),
|
|
611759
|
+
ts: typeof action.ts === "number" ? action.ts : Date.now(),
|
|
611760
|
+
role: action.role === "assistant" ? "assistant" : "user",
|
|
611761
|
+
speaker: String(action.speaker || "unknown"),
|
|
611762
|
+
mode: action.mode,
|
|
611763
|
+
text: String(action.text || ""),
|
|
611764
|
+
messageId: typeof action.messageId === "number" ? action.messageId : void 0,
|
|
611765
|
+
replyToMessageId: typeof action.replyToMessageId === "number" ? action.replyToMessageId : void 0,
|
|
611766
|
+
userId: typeof action.userId === "number" ? action.userId : void 0,
|
|
611767
|
+
username: typeof action.username === "string" ? action.username : void 0
|
|
611768
|
+
})) : []
|
|
611769
|
+
};
|
|
611770
|
+
}
|
|
611771
|
+
normalizeTelegramAssociativeFact(raw) {
|
|
611772
|
+
const text = String(raw.text || "").trim();
|
|
611773
|
+
const now = Date.now();
|
|
611774
|
+
return {
|
|
611775
|
+
id: String(raw.id || createHash23("sha1").update(text || String(now)).digest("hex").slice(0, 12)),
|
|
611776
|
+
text,
|
|
611777
|
+
tags: Array.isArray(raw.tags) ? raw.tags.map(String).slice(0, 16) : [],
|
|
611778
|
+
speakers: Array.isArray(raw.speakers) ? raw.speakers.map(String).slice(0, 16) : [],
|
|
611779
|
+
userIds: Array.isArray(raw.userIds) ? raw.userIds.filter((id) => typeof id === "number").slice(0, 32) : [],
|
|
611780
|
+
usernames: Array.isArray(raw.usernames) ? raw.usernames.map(String).slice(0, 32) : [],
|
|
611781
|
+
messageIds: Array.isArray(raw.messageIds) ? raw.messageIds.filter((id) => typeof id === "number").slice(0, 80) : [],
|
|
611782
|
+
createdAt: typeof raw.createdAt === "number" ? raw.createdAt : now,
|
|
611783
|
+
updatedAt: typeof raw.updatedAt === "number" ? raw.updatedAt : now,
|
|
611784
|
+
weight: typeof raw.weight === "number" ? raw.weight : 1
|
|
611785
|
+
};
|
|
611786
|
+
}
|
|
611787
|
+
telegramAssociativeMemoryForSession(sessionKey) {
|
|
611788
|
+
this.ensureTelegramConversationLoaded(sessionKey);
|
|
611789
|
+
let memory = this.chatAssociativeMemory.get(sessionKey);
|
|
611790
|
+
if (!memory) {
|
|
611791
|
+
memory = this.createTelegramAssociativeMemory();
|
|
611792
|
+
this.chatAssociativeMemory.set(sessionKey, memory);
|
|
611793
|
+
}
|
|
611794
|
+
return memory;
|
|
611795
|
+
}
|
|
611229
611796
|
telegramPersonalityScope(sessionKey, msg) {
|
|
611230
611797
|
const label = msg.chatType !== "private" ? `${msg.chatTitle || msg.chatType}-${String(msg.chatId)}` : `private-${msg.username || msg.fromUserId || msg.chatId}`;
|
|
611231
611798
|
return {
|
|
@@ -611242,8 +611809,9 @@ ${mediaContext}` : ""
|
|
|
611242
611809
|
if (!existsSync112(path12)) return;
|
|
611243
611810
|
try {
|
|
611244
611811
|
const parsed = JSON.parse(readFileSync92(path12, "utf8"));
|
|
611812
|
+
const loadedHistory = Array.isArray(parsed.history) ? parsed.history : [];
|
|
611245
611813
|
if (Array.isArray(parsed.history)) {
|
|
611246
|
-
this.chatHistory.set(sessionKey,
|
|
611814
|
+
this.chatHistory.set(sessionKey, loadedHistory.slice(-TELEGRAM_CHAT_HISTORY_LIMIT));
|
|
611247
611815
|
}
|
|
611248
611816
|
if (Array.isArray(parsed.participants)) {
|
|
611249
611817
|
const participants = /* @__PURE__ */ new Map();
|
|
@@ -611257,8 +611825,28 @@ ${mediaContext}` : ""
|
|
|
611257
611825
|
}
|
|
611258
611826
|
this.chatParticipants.set(sessionKey, participants);
|
|
611259
611827
|
}
|
|
611260
|
-
|
|
611828
|
+
const cardsLookTruncated = Array.isArray(parsed.memoryCards) && parsed.memoryCards.some((card) => Array.isArray(card.notes) && card.notes.some((note) => typeof note === "string" && note.endsWith("...")));
|
|
611829
|
+
if (Array.isArray(parsed.memoryCards) && !cardsLookTruncated) {
|
|
611261
611830
|
this.chatMemoryCards.set(sessionKey, parsed.memoryCards.slice(0, TELEGRAM_MEMORY_CARD_LIMIT));
|
|
611831
|
+
} else if (loadedHistory.length > 0) {
|
|
611832
|
+
this.chatMemoryCards.set(sessionKey, []);
|
|
611833
|
+
for (const entry of loadedHistory) {
|
|
611834
|
+
this.updateTelegramMemoryCards(sessionKey, entry);
|
|
611835
|
+
}
|
|
611836
|
+
}
|
|
611837
|
+
const associativeLooksTruncated = parsed.associativeMemory && Array.isArray(parsed.associativeMemory.facts) && parsed.associativeMemory.facts.some(
|
|
611838
|
+
(fact) => typeof fact?.text === "string" && fact.text.endsWith("...")
|
|
611839
|
+
);
|
|
611840
|
+
if (parsed.associativeMemory && !associativeLooksTruncated) {
|
|
611841
|
+
this.chatAssociativeMemory.set(
|
|
611842
|
+
sessionKey,
|
|
611843
|
+
this.normalizeTelegramAssociativeMemory(parsed.associativeMemory)
|
|
611844
|
+
);
|
|
611845
|
+
} else if (loadedHistory.length > 0) {
|
|
611846
|
+
this.hydrateTelegramAssociativeMemoryFromHistory(sessionKey, loadedHistory);
|
|
611847
|
+
}
|
|
611848
|
+
if (loadedHistory.length > 0) {
|
|
611849
|
+
this.backfillTelegramLoadedHistory(sessionKey, loadedHistory);
|
|
611262
611850
|
}
|
|
611263
611851
|
if (parsed.stimulation) {
|
|
611264
611852
|
this.stimulation.setState(sessionKey, parsed.stimulation);
|
|
@@ -611273,6 +611861,108 @@ ${mediaContext}` : ""
|
|
|
611273
611861
|
} catch {
|
|
611274
611862
|
}
|
|
611275
611863
|
}
|
|
611864
|
+
hydrateTelegramAssociativeMemoryFromHistory(sessionKey, history) {
|
|
611865
|
+
const memory = this.createTelegramAssociativeMemory();
|
|
611866
|
+
this.chatAssociativeMemory.set(sessionKey, memory);
|
|
611867
|
+
for (const entry of history) {
|
|
611868
|
+
this.updateTelegramAssociativeMemory(sessionKey, entry);
|
|
611869
|
+
}
|
|
611870
|
+
}
|
|
611871
|
+
telegramHistoryBackfillMessageId(sessionKey, entry, index) {
|
|
611872
|
+
if (typeof entry.messageId === "number" && Number.isFinite(entry.messageId)) return entry.messageId;
|
|
611873
|
+
const digest3 = createHash23("sha1").update(`${sessionKey}:${index}:${entry.role}:${entry.ts ?? ""}:${entry.text}`).digest("hex").slice(0, 8);
|
|
611874
|
+
return -Number.parseInt(digest3, 16);
|
|
611875
|
+
}
|
|
611876
|
+
backfillTelegramLoadedHistory(sessionKey, history) {
|
|
611877
|
+
if (!this.repoRoot || history.length === 0) return;
|
|
611878
|
+
const db = this.telegramDb();
|
|
611879
|
+
if (db) {
|
|
611880
|
+
try {
|
|
611881
|
+
const existing = db.prepare("SELECT COUNT(*) AS n FROM telegram_messages WHERE session_key = ?").get(sessionKey);
|
|
611882
|
+
if ((existing?.n ?? 0) < history.length) {
|
|
611883
|
+
const insert = db.prepare(`
|
|
611884
|
+
INSERT INTO telegram_messages (
|
|
611885
|
+
session_key, chat_id, chat_type, chat_title, message_id, message_thread_id,
|
|
611886
|
+
update_id, telegram_date, received_at, role, from_user_id, username, first_name,
|
|
611887
|
+
text, raw_json, normalized_json, reply_to_message_id, reply_to_username, media_json
|
|
611888
|
+
) VALUES (
|
|
611889
|
+
@session_key, @chat_id, @chat_type, @chat_title, @message_id, @message_thread_id,
|
|
611890
|
+
NULL, NULL, @received_at, @role, @from_user_id, @username, @first_name,
|
|
611891
|
+
@text, @raw_json, @normalized_json, @reply_to_message_id, @reply_to_username, @media_json
|
|
611892
|
+
)
|
|
611893
|
+
ON CONFLICT(chat_id, message_id, role) DO UPDATE SET
|
|
611894
|
+
session_key=excluded.session_key,
|
|
611895
|
+
chat_type=excluded.chat_type,
|
|
611896
|
+
chat_title=excluded.chat_title,
|
|
611897
|
+
message_thread_id=excluded.message_thread_id,
|
|
611898
|
+
received_at=excluded.received_at,
|
|
611899
|
+
from_user_id=excluded.from_user_id,
|
|
611900
|
+
username=excluded.username,
|
|
611901
|
+
first_name=excluded.first_name,
|
|
611902
|
+
text=excluded.text,
|
|
611903
|
+
raw_json=excluded.raw_json,
|
|
611904
|
+
normalized_json=excluded.normalized_json,
|
|
611905
|
+
reply_to_message_id=excluded.reply_to_message_id,
|
|
611906
|
+
reply_to_username=excluded.reply_to_username,
|
|
611907
|
+
media_json=excluded.media_json
|
|
611908
|
+
`);
|
|
611909
|
+
const transaction = db.transaction((entries) => {
|
|
611910
|
+
entries.forEach((entry, index) => {
|
|
611911
|
+
const messageId = this.telegramHistoryBackfillMessageId(sessionKey, entry, index);
|
|
611912
|
+
const chatId = entry.chatId !== void 0 ? String(entry.chatId) : sessionKey.replace(/^chat:/, "");
|
|
611913
|
+
insert.run({
|
|
611914
|
+
session_key: sessionKey,
|
|
611915
|
+
chat_id: chatId || "unknown",
|
|
611916
|
+
chat_type: entry.chatType || null,
|
|
611917
|
+
chat_title: entry.chatTitle || null,
|
|
611918
|
+
message_id: messageId,
|
|
611919
|
+
message_thread_id: entry.messageThreadId ?? null,
|
|
611920
|
+
received_at: entry.ts ?? Date.now(),
|
|
611921
|
+
role: entry.role === "assistant" ? "assistant" : "user",
|
|
611922
|
+
from_user_id: entry.fromUserId ?? null,
|
|
611923
|
+
username: entry.role === "assistant" ? this.state.botUsername || "omnius" : entry.username || null,
|
|
611924
|
+
first_name: entry.role === "assistant" ? "Omnius" : entry.firstName || null,
|
|
611925
|
+
text: entry.text || "",
|
|
611926
|
+
raw_json: JSON.stringify({ source: "telegram-conversations", sessionKey, entry }),
|
|
611927
|
+
normalized_json: JSON.stringify({ ...entry, messageId }),
|
|
611928
|
+
reply_to_message_id: entry.replyToMessageId ?? null,
|
|
611929
|
+
reply_to_username: entry.replyContext?.sender?.username || null,
|
|
611930
|
+
media_json: entry.mediaSummary ? JSON.stringify({ summary: entry.mediaSummary }) : null
|
|
611931
|
+
});
|
|
611932
|
+
});
|
|
611933
|
+
});
|
|
611934
|
+
transaction(history);
|
|
611935
|
+
try {
|
|
611936
|
+
db.exec("INSERT INTO telegram_messages_fts(telegram_messages_fts) VALUES('rebuild')");
|
|
611937
|
+
} catch {
|
|
611938
|
+
}
|
|
611939
|
+
}
|
|
611940
|
+
} catch {
|
|
611941
|
+
}
|
|
611942
|
+
}
|
|
611943
|
+
try {
|
|
611944
|
+
const paths = omniusMemoryDbPaths(this.repoRoot);
|
|
611945
|
+
const graph = new TemporalGraph(paths.knowledge);
|
|
611946
|
+
const store2 = new EpisodeStore(paths.episodes, graph);
|
|
611947
|
+
let existingTelegramEpisodes = 0;
|
|
611948
|
+
try {
|
|
611949
|
+
const row = store2.getDb().prepare(`
|
|
611950
|
+
SELECT COUNT(*) AS n FROM episodes
|
|
611951
|
+
WHERE session_id = ? AND metadata LIKE '%"sourceSurface":"telegram"%'
|
|
611952
|
+
`).get(sessionKey);
|
|
611953
|
+
existingTelegramEpisodes = row?.n ?? 0;
|
|
611954
|
+
} finally {
|
|
611955
|
+
store2.close();
|
|
611956
|
+
graph.close();
|
|
611957
|
+
}
|
|
611958
|
+
if (existingTelegramEpisodes < history.length) {
|
|
611959
|
+
for (const entry of history) {
|
|
611960
|
+
this.upsertTelegramReflectionHistoryEntry(sessionKey, entry);
|
|
611961
|
+
}
|
|
611962
|
+
}
|
|
611963
|
+
} catch {
|
|
611964
|
+
}
|
|
611965
|
+
}
|
|
611276
611966
|
ensureAllTelegramConversationsLoaded() {
|
|
611277
611967
|
if (this.loadedAllConversationState) return;
|
|
611278
611968
|
this.loadedAllConversationState = true;
|
|
@@ -611304,6 +611994,7 @@ ${mediaContext}` : ""
|
|
|
611304
611994
|
history: this.chatHistory.get(sessionKey) ?? [],
|
|
611305
611995
|
participants,
|
|
611306
611996
|
memoryCards: this.chatMemoryCards.get(sessionKey) ?? [],
|
|
611997
|
+
associativeMemory: this.telegramAssociativeMemoryForSession(sessionKey),
|
|
611307
611998
|
stimulation: this.stimulation.getState(sessionKey),
|
|
611308
611999
|
reflection: this.channelReflectionState.get(sessionKey) ?? { autoFollowup: false }
|
|
611309
612000
|
};
|
|
@@ -611331,7 +612022,8 @@ ${mediaContext}` : ""
|
|
|
611331
612022
|
};
|
|
611332
612023
|
}
|
|
611333
612024
|
buildTelegramChannelDaydreamInput(sessionKey, nowMs = Date.now()) {
|
|
611334
|
-
const
|
|
612025
|
+
const sqliteHistory = this.telegramSqliteHistoryForSession(sessionKey, 1500);
|
|
612026
|
+
const history = sqliteHistory.length > 0 ? sqliteHistory : this.chatHistory.get(sessionKey) ?? [];
|
|
611335
612027
|
if (history.length === 0) return null;
|
|
611336
612028
|
const last2 = [...history].reverse().find((entry) => entry.chatId !== void 0);
|
|
611337
612029
|
if (!last2) return null;
|
|
@@ -611636,6 +612328,7 @@ ${mediaContext}` : ""
|
|
|
611636
612328
|
this.upsertTelegramReflectionHistoryEntry(sessionKey, entry);
|
|
611637
612329
|
this.updateTelegramParticipantProfile(sessionKey, msg, text);
|
|
611638
612330
|
this.updateTelegramMemoryCards(sessionKey, entry);
|
|
612331
|
+
this.updateTelegramAssociativeMemory(sessionKey, entry);
|
|
611639
612332
|
try {
|
|
611640
612333
|
updateScopedPersonality(this.telegramPersonalityScope(sessionKey, msg), {
|
|
611641
612334
|
speaker: telegramSpeakerLabel(msg),
|
|
@@ -611669,9 +612362,16 @@ ${mediaContext}` : ""
|
|
|
611669
612362
|
chatTitle: msg.chatTitle
|
|
611670
612363
|
};
|
|
611671
612364
|
this.recordChatHistory(sessionKey, entry);
|
|
612365
|
+
this.persistTelegramAssistantMessage(
|
|
612366
|
+
msg,
|
|
612367
|
+
clean5,
|
|
612368
|
+
options2.messageId ?? void 0,
|
|
612369
|
+
options2.replyToMessageId
|
|
612370
|
+
);
|
|
611672
612371
|
this.upsertTelegramReflectionHistoryEntry(sessionKey, entry);
|
|
611673
612372
|
this.stimulation.recordAgentOutput(sessionKey);
|
|
611674
612373
|
this.updateTelegramMemoryCards(sessionKey, entry);
|
|
612374
|
+
this.updateTelegramAssociativeMemory(sessionKey, entry);
|
|
611675
612375
|
try {
|
|
611676
612376
|
updateScopedPersonality(this.telegramPersonalityScope(sessionKey, msg), {
|
|
611677
612377
|
speaker: entry.speaker || "Assistant",
|
|
@@ -611847,14 +612547,14 @@ ${mediaContext}` : ""
|
|
|
611847
612547
|
profile.fromUserId = msg.fromUserId || profile.fromUserId;
|
|
611848
612548
|
profile.messageCount += 1;
|
|
611849
612549
|
profile.lastSeenTs = Date.now();
|
|
611850
|
-
profile.lastMessage =
|
|
612550
|
+
profile.lastMessage = stripTelegramHiddenThinking(text).replace(/\s+/g, " ").trim();
|
|
611851
612551
|
if (msg.replyToMessageId) profile.replyCount += 1;
|
|
611852
612552
|
if (this.state.botUsername && text.toLowerCase().includes(`@${this.state.botUsername.toLowerCase()}`)) {
|
|
611853
612553
|
profile.directAddressCount += 1;
|
|
611854
612554
|
}
|
|
611855
612555
|
for (const tag of inferTelegramToneTags(text)) profile.toneTags.add(tag);
|
|
611856
612556
|
if (text.trim()) {
|
|
611857
|
-
profile.samples.push(
|
|
612557
|
+
profile.samples.push(stripTelegramHiddenThinking(text).replace(/\s+/g, " ").trim());
|
|
611858
612558
|
if (profile.samples.length > TELEGRAM_CONTEXT_SAMPLE_LIMIT) {
|
|
611859
612559
|
profile.samples.splice(0, profile.samples.length - TELEGRAM_CONTEXT_SAMPLE_LIMIT);
|
|
611860
612560
|
}
|
|
@@ -611862,8 +612562,162 @@ ${mediaContext}` : ""
|
|
|
611862
612562
|
participants.set(participantKey, profile);
|
|
611863
612563
|
this.chatParticipants.set(sessionKey, participants);
|
|
611864
612564
|
}
|
|
612565
|
+
updateTelegramAssociativeMemory(sessionKey, entry) {
|
|
612566
|
+
const memory = this.telegramAssociativeMemoryForSession(sessionKey);
|
|
612567
|
+
const now = entry.ts ?? Date.now();
|
|
612568
|
+
memory.updatedAt = now;
|
|
612569
|
+
const speaker = telegramHistorySpeaker(entry);
|
|
612570
|
+
const actionId = createHash23("sha1").update(`${sessionKey}:${entry.role}:${entry.messageId ?? ""}:${now}:${entry.text}`).digest("hex").slice(0, 16);
|
|
612571
|
+
if (!memory.actions.some((action) => action.id === actionId)) {
|
|
612572
|
+
memory.actions.push({
|
|
612573
|
+
id: actionId,
|
|
612574
|
+
ts: now,
|
|
612575
|
+
role: entry.role,
|
|
612576
|
+
speaker,
|
|
612577
|
+
mode: entry.mode,
|
|
612578
|
+
text: stripTelegramHiddenThinking(entry.text || "").replace(/\s+/g, " ").trim(),
|
|
612579
|
+
messageId: entry.messageId,
|
|
612580
|
+
replyToMessageId: entry.replyToMessageId,
|
|
612581
|
+
userId: entry.fromUserId,
|
|
612582
|
+
username: entry.username
|
|
612583
|
+
});
|
|
612584
|
+
if (memory.actions.length > TELEGRAM_ASSOCIATIVE_ACTION_LIMIT) {
|
|
612585
|
+
memory.actions.splice(
|
|
612586
|
+
0,
|
|
612587
|
+
memory.actions.length - TELEGRAM_ASSOCIATIVE_ACTION_LIMIT
|
|
612588
|
+
);
|
|
612589
|
+
}
|
|
612590
|
+
}
|
|
612591
|
+
if (entry.role === "user") {
|
|
612592
|
+
const userKey = String(entry.fromUserId || entry.username || entry.firstName || speaker);
|
|
612593
|
+
const existing = memory.users[userKey];
|
|
612594
|
+
const userMemory = existing ?? {
|
|
612595
|
+
userId: entry.fromUserId,
|
|
612596
|
+
username: entry.username || "unknown",
|
|
612597
|
+
displayName: entry.firstName || entry.username || speaker,
|
|
612598
|
+
aliases: [],
|
|
612599
|
+
firstSeenTs: now,
|
|
612600
|
+
lastSeenTs: now,
|
|
612601
|
+
messageCount: 0,
|
|
612602
|
+
directAddressCount: 0,
|
|
612603
|
+
replyCount: 0,
|
|
612604
|
+
toneTags: [],
|
|
612605
|
+
facts: [],
|
|
612606
|
+
relationshipHints: [],
|
|
612607
|
+
recentTopics: [],
|
|
612608
|
+
lastMessages: []
|
|
612609
|
+
};
|
|
612610
|
+
userMemory.userId = entry.fromUserId ?? userMemory.userId;
|
|
612611
|
+
userMemory.username = entry.username || userMemory.username;
|
|
612612
|
+
userMemory.displayName = entry.firstName || userMemory.displayName;
|
|
612613
|
+
for (const alias of [entry.username, entry.firstName, speaker].filter(Boolean)) {
|
|
612614
|
+
const clean5 = alias.replace(/^@/, "").trim();
|
|
612615
|
+
if (clean5 && !userMemory.aliases.includes(clean5)) userMemory.aliases.push(clean5);
|
|
612616
|
+
}
|
|
612617
|
+
userMemory.aliases = userMemory.aliases.slice(0, 20);
|
|
612618
|
+
userMemory.lastSeenTs = now;
|
|
612619
|
+
userMemory.messageCount += 1;
|
|
612620
|
+
if (entry.replyToMessageId) userMemory.replyCount += 1;
|
|
612621
|
+
if (this.state.botUsername && entry.text.toLowerCase().includes(`@${this.state.botUsername.toLowerCase()}`)) {
|
|
612622
|
+
userMemory.directAddressCount += 1;
|
|
612623
|
+
}
|
|
612624
|
+
for (const tag of inferTelegramToneTags(entry.text)) {
|
|
612625
|
+
if (!userMemory.toneTags.includes(tag)) userMemory.toneTags.push(tag);
|
|
612626
|
+
}
|
|
612627
|
+
userMemory.toneTags = userMemory.toneTags.slice(0, 20);
|
|
612628
|
+
const compact2 = stripTelegramHiddenThinking(entry.text || "").replace(/\s+/g, " ").trim();
|
|
612629
|
+
if (compact2) {
|
|
612630
|
+
userMemory.lastMessages.push(compact2);
|
|
612631
|
+
userMemory.lastMessages = userMemory.lastMessages.slice(-40);
|
|
612632
|
+
}
|
|
612633
|
+
for (const topic of telegramMemoryTags(entry.text, entry.mediaSummary).slice(0, 6)) {
|
|
612634
|
+
if (!userMemory.recentTopics.includes(topic)) userMemory.recentTopics.push(topic);
|
|
612635
|
+
}
|
|
612636
|
+
userMemory.recentTopics = userMemory.recentTopics.slice(-80);
|
|
612637
|
+
const facts = this.extractTelegramAssociativeFacts(entry, speaker);
|
|
612638
|
+
for (const text of facts) {
|
|
612639
|
+
const fact = this.upsertTelegramAssociativeFact(memory.facts, text, entry, speaker, 1.5);
|
|
612640
|
+
this.upsertTelegramAssociativeFact(userMemory.facts, fact.text, entry, speaker, fact.weight);
|
|
612641
|
+
}
|
|
612642
|
+
if (entry.replyContext?.sender || entry.replyToMessageId) {
|
|
612643
|
+
const target = entry.replyContext?.sender ? telegramReplySenderLabel(entry.replyContext.sender) : `message ${entry.replyToMessageId}`;
|
|
612644
|
+
const hint = `${speaker} replied to ${target}: ${stripTelegramHiddenThinking(entry.text || "").replace(/\s+/g, " ").trim()}`;
|
|
612645
|
+
if (!userMemory.relationshipHints.includes(hint)) {
|
|
612646
|
+
userMemory.relationshipHints.push(hint);
|
|
612647
|
+
userMemory.relationshipHints = userMemory.relationshipHints.slice(-80);
|
|
612648
|
+
}
|
|
612649
|
+
this.upsertTelegramAssociativeFact(memory.relationships, hint, entry, speaker, 1.2);
|
|
612650
|
+
}
|
|
612651
|
+
userMemory.facts.sort((a2, b) => b.weight - a2.weight || b.updatedAt - a2.updatedAt);
|
|
612652
|
+
userMemory.facts = userMemory.facts.slice(0, TELEGRAM_ASSOCIATIVE_USER_FACT_LIMIT);
|
|
612653
|
+
memory.users[userKey] = userMemory;
|
|
612654
|
+
}
|
|
612655
|
+
memory.facts.sort((a2, b) => b.weight - a2.weight || b.updatedAt - a2.updatedAt);
|
|
612656
|
+
memory.facts = memory.facts.slice(0, TELEGRAM_ASSOCIATIVE_FACT_LIMIT);
|
|
612657
|
+
memory.relationships.sort((a2, b) => b.updatedAt - a2.updatedAt);
|
|
612658
|
+
memory.relationships = memory.relationships.slice(0, TELEGRAM_ASSOCIATIVE_RELATION_LIMIT);
|
|
612659
|
+
}
|
|
612660
|
+
extractTelegramAssociativeFacts(entry, speaker) {
|
|
612661
|
+
if (entry.role !== "user") return [];
|
|
612662
|
+
const text = stripTelegramHiddenThinking(entry.text || "").replace(/\s+/g, " ").trim();
|
|
612663
|
+
const facts = /* @__PURE__ */ new Set();
|
|
612664
|
+
const patterns = [
|
|
612665
|
+
/\b(?:remember|note|keep in mind|for future reference)\s+(?:that\s+)?(.{4,260})/i,
|
|
612666
|
+
/\bmy name is\s+(.{2,120})/i,
|
|
612667
|
+
/\bcall me\s+(.{2,120})/i,
|
|
612668
|
+
/\bi\s+(?:am|work as|live in|like|love|prefer|use|run|have|need|want)\b.{0,220}/i,
|
|
612669
|
+
/\b(?:we|this group|our)\s+.{0,220}\b(?:is|are|uses|prefers|likes|works|has|needs)\b.{0,220}/i
|
|
612670
|
+
];
|
|
612671
|
+
for (const pattern of patterns) {
|
|
612672
|
+
const match = text.match(pattern);
|
|
612673
|
+
if (!match) continue;
|
|
612674
|
+
const body = (match[1] || match[0]).trim().replace(/[.!?]*$/, "");
|
|
612675
|
+
if (body.length >= 3) facts.add(`${speaker}: ${body}`);
|
|
612676
|
+
}
|
|
612677
|
+
if (entry.mediaSummary) {
|
|
612678
|
+
facts.add(`${speaker} shared media: ${entry.mediaSummary}`);
|
|
612679
|
+
}
|
|
612680
|
+
return [...facts].slice(0, 8);
|
|
612681
|
+
}
|
|
612682
|
+
upsertTelegramAssociativeFact(facts, text, entry, speaker, weight = 1) {
|
|
612683
|
+
const clean5 = stripTelegramHiddenThinking(text || "").replace(/\s+/g, " ").trim();
|
|
612684
|
+
const key = clean5.toLowerCase();
|
|
612685
|
+
const now = entry.ts ?? Date.now();
|
|
612686
|
+
let fact = facts.find((item) => item.text.toLowerCase() === key);
|
|
612687
|
+
if (!fact) {
|
|
612688
|
+
fact = {
|
|
612689
|
+
id: createHash23("sha1").update(`${entry.chatId ?? ""}:${key}`).digest("hex").slice(0, 12),
|
|
612690
|
+
text: clean5,
|
|
612691
|
+
tags: telegramMemoryTags(clean5, entry.mediaSummary),
|
|
612692
|
+
speakers: [],
|
|
612693
|
+
userIds: [],
|
|
612694
|
+
usernames: [],
|
|
612695
|
+
messageIds: [],
|
|
612696
|
+
createdAt: now,
|
|
612697
|
+
updatedAt: now,
|
|
612698
|
+
weight
|
|
612699
|
+
};
|
|
612700
|
+
facts.push(fact);
|
|
612701
|
+
}
|
|
612702
|
+
fact.updatedAt = now;
|
|
612703
|
+
fact.weight = Math.min(10, Math.max(fact.weight, weight) + 0.2);
|
|
612704
|
+
if (!fact.speakers.includes(speaker)) fact.speakers.push(speaker);
|
|
612705
|
+
fact.speakers = fact.speakers.slice(0, 16);
|
|
612706
|
+
if (entry.fromUserId && !fact.userIds.includes(entry.fromUserId)) fact.userIds.push(entry.fromUserId);
|
|
612707
|
+
fact.userIds = fact.userIds.slice(0, 32);
|
|
612708
|
+
const username = (entry.username || "").replace(/^@/, "").toLowerCase();
|
|
612709
|
+
if (username && !fact.usernames.includes(username)) fact.usernames.push(username);
|
|
612710
|
+
fact.usernames = fact.usernames.slice(0, 32);
|
|
612711
|
+
if (entry.messageId && !fact.messageIds.includes(entry.messageId)) fact.messageIds.push(entry.messageId);
|
|
612712
|
+
fact.messageIds = fact.messageIds.slice(-80);
|
|
612713
|
+
for (const tag of telegramMemoryTags(clean5, entry.mediaSummary)) {
|
|
612714
|
+
if (!fact.tags.includes(tag)) fact.tags.push(tag);
|
|
612715
|
+
}
|
|
612716
|
+
fact.tags = fact.tags.slice(0, 16);
|
|
612717
|
+
return fact;
|
|
612718
|
+
}
|
|
611865
612719
|
updateTelegramMemoryCards(sessionKey, entry) {
|
|
611866
|
-
const text =
|
|
612720
|
+
const text = stripTelegramHiddenThinking(entry.text || "").replace(/\s+/g, " ").trim();
|
|
611867
612721
|
if (!text || text.length < 3) return;
|
|
611868
612722
|
const speaker = telegramHistorySpeaker(entry);
|
|
611869
612723
|
const tags = telegramMemoryTags(text, entry.mediaSummary);
|
|
@@ -611955,6 +612809,304 @@ ${mediaContext}` : ""
|
|
|
611955
612809
|
)
|
|
611956
612810
|
})).sort((a2, b) => b.score - a2.score || b.card.updatedAt - a2.card.updatedAt).slice(0, limit);
|
|
611957
612811
|
}
|
|
612812
|
+
relevantTelegramAssociativeMemoryContext(sessionKey, msg, limit = 12) {
|
|
612813
|
+
const memory = this.chatAssociativeMemory.get(sessionKey);
|
|
612814
|
+
if (!memory) return "";
|
|
612815
|
+
const queryTokens = telegramMemoryTokens([
|
|
612816
|
+
telegramSpeakerLabel(msg),
|
|
612817
|
+
msg.username || "",
|
|
612818
|
+
msg.firstName || "",
|
|
612819
|
+
msg.text,
|
|
612820
|
+
summarizeTelegramMessageAttachments(msg)
|
|
612821
|
+
].join(" "));
|
|
612822
|
+
const currentUsername = (msg.username || "").replace(/^@/, "").toLowerCase();
|
|
612823
|
+
const userEntries = Object.entries(memory.users).filter(
|
|
612824
|
+
([, user]) => msg.fromUserId !== void 0 && user.userId === msg.fromUserId || !!currentUsername && user.username.replace(/^@/, "").toLowerCase() === currentUsername || user.aliases.some((alias) => alias.replace(/^@/, "").toLowerCase() === currentUsername)
|
|
612825
|
+
);
|
|
612826
|
+
const scoredFacts = memory.facts.map((fact) => ({
|
|
612827
|
+
fact,
|
|
612828
|
+
score: telegramMemorySimilarity(
|
|
612829
|
+
queryTokens,
|
|
612830
|
+
telegramMemoryTokens([fact.text, fact.tags.join(" "), fact.speakers.join(" ")].join(" "))
|
|
612831
|
+
) + (fact.userIds.includes(msg.fromUserId ?? -1) ? 0.35 : 0) + (currentUsername && fact.usernames.includes(currentUsername) ? 0.35 : 0)
|
|
612832
|
+
})).filter((item) => item.score > 0).sort((a2, b) => b.score - a2.score || b.fact.updatedAt - a2.fact.updatedAt).slice(0, limit);
|
|
612833
|
+
const relationshipFacts = memory.relationships.filter(
|
|
612834
|
+
(fact) => fact.userIds.includes(msg.fromUserId ?? -1) || !!currentUsername && fact.usernames.includes(currentUsername)
|
|
612835
|
+
).slice(0, 6);
|
|
612836
|
+
const recentActions = memory.actions.filter(
|
|
612837
|
+
(action) => action.userId === msg.fromUserId || !!currentUsername && action.username?.replace(/^@/, "").toLowerCase() === currentUsername || action.role === "assistant"
|
|
612838
|
+
).slice(-8);
|
|
612839
|
+
const sections = [];
|
|
612840
|
+
if (userEntries.length > 0) {
|
|
612841
|
+
const lines = userEntries.map(([, user]) => {
|
|
612842
|
+
const facts = user.facts.slice(0, 6).map((fact) => ` - ${telegramContextJsonString(fact.text, 220)}`).join("\n");
|
|
612843
|
+
const hints = user.relationshipHints.slice(-4).map((hint) => ` - relation=${telegramContextJsonString(hint, 200)}`).join("\n");
|
|
612844
|
+
const topics = user.recentTopics.slice(-8).join(", ") || "none";
|
|
612845
|
+
return [
|
|
612846
|
+
`- ${user.username && user.username !== "unknown" ? `@${user.username}` : user.displayName || "user"}${user.userId ? ` id:${user.userId}` : ""}: messages:${user.messageCount}, direct:${user.directAddressCount}, replies:${user.replyCount}, topics:${topics}`,
|
|
612847
|
+
facts,
|
|
612848
|
+
hints
|
|
612849
|
+
].filter(Boolean).join("\n");
|
|
612850
|
+
});
|
|
612851
|
+
sections.push(`### Durable Associative User Memory
|
|
612852
|
+
${lines.join("\n")}`);
|
|
612853
|
+
}
|
|
612854
|
+
if (scoredFacts.length > 0) {
|
|
612855
|
+
const lines = scoredFacts.map(
|
|
612856
|
+
({ fact, score }) => `- (${score.toFixed(2)}) ${telegramContextJsonString(fact.text, 260)}${fact.messageIds.length ? ` [messages:${fact.messageIds.slice(-4).join(",")}]` : ""}`
|
|
612857
|
+
);
|
|
612858
|
+
sections.push(`### Durable Associative Memory Recall
|
|
612859
|
+
${lines.join("\n")}`);
|
|
612860
|
+
}
|
|
612861
|
+
if (relationshipFacts.length > 0) {
|
|
612862
|
+
const lines = relationshipFacts.map((fact) => `- ${telegramContextJsonString(fact.text, 240)}`);
|
|
612863
|
+
sections.push(`### Durable Relationship Recall
|
|
612864
|
+
${lines.join("\n")}`);
|
|
612865
|
+
}
|
|
612866
|
+
if (recentActions.length > 0) {
|
|
612867
|
+
const lines = recentActions.map(
|
|
612868
|
+
(action) => `- ${telegramHistoryTime({ ts: action.ts, role: action.role, text: action.text })} ${action.speaker}/${action.role}${action.mode ? `/${action.mode}` : ""}: ${telegramContextJsonString(action.text, 220)}`
|
|
612869
|
+
);
|
|
612870
|
+
sections.push(`### Durable Recent Action Ledger Recall
|
|
612871
|
+
${lines.join("\n")}`);
|
|
612872
|
+
}
|
|
612873
|
+
return sections.join("\n\n");
|
|
612874
|
+
}
|
|
612875
|
+
relevantTelegramSqliteMirrorContext(sessionKey, msg, limit = 12) {
|
|
612876
|
+
const db = this.telegramDb();
|
|
612877
|
+
if (!db) return "";
|
|
612878
|
+
const rows = /* @__PURE__ */ new Map();
|
|
612879
|
+
const addRows = (items) => {
|
|
612880
|
+
for (const row of items) {
|
|
612881
|
+
const key = Number(row.rowid);
|
|
612882
|
+
if (Number.isFinite(key)) rows.set(key, row);
|
|
612883
|
+
}
|
|
612884
|
+
};
|
|
612885
|
+
try {
|
|
612886
|
+
if (msg.fromUserId !== void 0) {
|
|
612887
|
+
addRows(db.prepare(`
|
|
612888
|
+
SELECT rowid, * FROM telegram_messages
|
|
612889
|
+
WHERE session_key = ? AND from_user_id = ?
|
|
612890
|
+
ORDER BY received_at DESC
|
|
612891
|
+
LIMIT ?
|
|
612892
|
+
`).all(sessionKey, msg.fromUserId, limit));
|
|
612893
|
+
}
|
|
612894
|
+
const username = (msg.username || "").replace(/^@/, "").toLowerCase();
|
|
612895
|
+
if (username) {
|
|
612896
|
+
addRows(db.prepare(`
|
|
612897
|
+
SELECT rowid, * FROM telegram_messages
|
|
612898
|
+
WHERE session_key = ? AND lower(username) = ?
|
|
612899
|
+
ORDER BY received_at DESC
|
|
612900
|
+
LIMIT ?
|
|
612901
|
+
`).all(sessionKey, username, Math.max(4, Math.floor(limit / 2))));
|
|
612902
|
+
}
|
|
612903
|
+
const queryTokens = [...telegramMemoryTokens(msg.text)].slice(0, 6);
|
|
612904
|
+
for (const token of queryTokens) {
|
|
612905
|
+
addRows(db.prepare(`
|
|
612906
|
+
SELECT rowid, * FROM telegram_messages
|
|
612907
|
+
WHERE session_key = ? AND text LIKE ?
|
|
612908
|
+
ORDER BY received_at DESC
|
|
612909
|
+
LIMIT 4
|
|
612910
|
+
`).all(sessionKey, `%${token}%`));
|
|
612911
|
+
}
|
|
612912
|
+
} catch {
|
|
612913
|
+
return "";
|
|
612914
|
+
}
|
|
612915
|
+
const selected = [...rows.values()].sort((a2, b) => Number(b.received_at ?? 0) - Number(a2.received_at ?? 0)).slice(0, limit);
|
|
612916
|
+
if (selected.length === 0) return "";
|
|
612917
|
+
const lines = selected.map((row) => {
|
|
612918
|
+
const when = row.received_at ? new Date(Number(row.received_at)).toISOString() : "";
|
|
612919
|
+
const speaker = row.role === "assistant" ? `@${this.state.botUsername || "omnius"}` : row.username ? `@${row.username}` : row.from_user_id ? `user:${row.from_user_id}` : "unknown";
|
|
612920
|
+
const reply = row.reply_to_message_id ? ` reply_to:${row.reply_to_message_id}` : "";
|
|
612921
|
+
return `- ${when} ${speaker}/${row.role || "user"} msg:${row.message_id}${reply}: ${telegramContextJsonString(String(row.text || ""), 260)}`;
|
|
612922
|
+
});
|
|
612923
|
+
return [
|
|
612924
|
+
"### SQLite Telegram Raw Mirror Recall",
|
|
612925
|
+
"Durable local mirror rows for this scoped chat. Use as historical evidence when the rolling context omitted older turns.",
|
|
612926
|
+
lines.join("\n")
|
|
612927
|
+
].join("\n");
|
|
612928
|
+
}
|
|
612929
|
+
/**
|
|
612930
|
+
* Always-on episodic memory recall for the active Telegram session.
|
|
612931
|
+
* Pulls scored episodes from .omnius/episodes.db where session_id = sessionKey,
|
|
612932
|
+
* including social / tool_result / reflection modalities. This surfaces day-old
|
|
612933
|
+
* context the rolling 36-turn window has already shed.
|
|
612934
|
+
*/
|
|
612935
|
+
relevantTelegramEpisodicMemoryContext(sessionKey, msg, limit = 8) {
|
|
612936
|
+
if (!this.repoRoot) return "";
|
|
612937
|
+
const paths = omniusMemoryDbPaths(this.repoRoot);
|
|
612938
|
+
if (!existsSync112(paths.episodes)) return "";
|
|
612939
|
+
let episodes = [];
|
|
612940
|
+
const graph = new TemporalGraph(paths.knowledge);
|
|
612941
|
+
const store2 = new EpisodeStore(paths.episodes, graph);
|
|
612942
|
+
try {
|
|
612943
|
+
const recent = store2.search({ sessionId: sessionKey, limit: Math.max(limit * 3, 24) }) ?? [];
|
|
612944
|
+
const queryText = (msg.text || "").toLowerCase().trim();
|
|
612945
|
+
if (queryText.length > 3) {
|
|
612946
|
+
const qTokens = queryText.split(/\s+/).filter((t2) => t2.length >= 4);
|
|
612947
|
+
const scored = recent.map((ep) => {
|
|
612948
|
+
const content = String(ep.content || "").toLowerCase();
|
|
612949
|
+
const hits = qTokens.filter((t2) => content.includes(t2)).length;
|
|
612950
|
+
return { ep, score: hits };
|
|
612951
|
+
}).sort((a2, b) => b.score - a2.score || b.ep.timestamp - a2.ep.timestamp);
|
|
612952
|
+
episodes = scored.slice(0, limit).map((s2) => s2.ep);
|
|
612953
|
+
} else {
|
|
612954
|
+
episodes = recent.slice(0, limit);
|
|
612955
|
+
}
|
|
612956
|
+
} catch {
|
|
612957
|
+
return "";
|
|
612958
|
+
} finally {
|
|
612959
|
+
try {
|
|
612960
|
+
store2.close();
|
|
612961
|
+
} catch {
|
|
612962
|
+
}
|
|
612963
|
+
try {
|
|
612964
|
+
graph.close();
|
|
612965
|
+
} catch {
|
|
612966
|
+
}
|
|
612967
|
+
}
|
|
612968
|
+
if (episodes.length === 0) return "";
|
|
612969
|
+
const lines = episodes.map((ep) => {
|
|
612970
|
+
const when = ep.timestamp ? new Date(ep.timestamp).toISOString() : "";
|
|
612971
|
+
const meta = ep.metadata || {};
|
|
612972
|
+
const tg = meta.telegram && typeof meta.telegram === "object" ? meta.telegram : {};
|
|
612973
|
+
const speaker = tg.speaker || tg.username || (ep.modality === "tool_result" ? `tool:${ep.toolName || "?"}` : "agent");
|
|
612974
|
+
const mode = tg.mode ? `/${tg.mode}` : "";
|
|
612975
|
+
const text = ep.gist || ep.content;
|
|
612976
|
+
return `- ${when} ${speaker}${mode} [${ep.modality}]: ${telegramContextJsonString(String(text || ""), 260)}`;
|
|
612977
|
+
});
|
|
612978
|
+
return [
|
|
612979
|
+
"### Episodic Memory Recall (durable, day+ scope)",
|
|
612980
|
+
"Scored episodes for this Telegram session from episodes.db. These are PERSISTENT across restarts and survive the rolling-context window. Treat as canonical for older facts.",
|
|
612981
|
+
lines.join("\n")
|
|
612982
|
+
].join("\n");
|
|
612983
|
+
}
|
|
612984
|
+
telegramSqliteHistoryForSession(sessionKey, limit = 1e3) {
|
|
612985
|
+
const db = this.telegramDb();
|
|
612986
|
+
if (!db) return [];
|
|
612987
|
+
try {
|
|
612988
|
+
const rows = db.prepare(`
|
|
612989
|
+
SELECT * FROM telegram_messages
|
|
612990
|
+
WHERE session_key = ?
|
|
612991
|
+
ORDER BY received_at DESC
|
|
612992
|
+
LIMIT ?
|
|
612993
|
+
`).all(sessionKey, limit);
|
|
612994
|
+
return rows.reverse().map((row) => ({
|
|
612995
|
+
role: row.role === "assistant" ? "assistant" : "user",
|
|
612996
|
+
text: String(row.text || ""),
|
|
612997
|
+
ts: Number(row.received_at || 0) || void 0,
|
|
612998
|
+
chatId: row.chat_id,
|
|
612999
|
+
speaker: row.role === "assistant" ? `@${this.state.botUsername || "omnius"}` : row.username ? `@${row.username}` : row.from_user_id ? `user:${row.from_user_id}` : "unknown",
|
|
613000
|
+
username: row.username || void 0,
|
|
613001
|
+
firstName: row.first_name || void 0,
|
|
613002
|
+
fromUserId: typeof row.from_user_id === "number" ? row.from_user_id : void 0,
|
|
613003
|
+
messageId: typeof row.message_id === "number" ? row.message_id : void 0,
|
|
613004
|
+
messageThreadId: typeof row.message_thread_id === "number" ? row.message_thread_id : void 0,
|
|
613005
|
+
replyToMessageId: typeof row.reply_to_message_id === "number" ? row.reply_to_message_id : void 0,
|
|
613006
|
+
chatType: row.chat_type,
|
|
613007
|
+
chatTitle: row.chat_title || void 0,
|
|
613008
|
+
mediaSummary: row.media_json ? "media attached in raw Telegram SQLite mirror" : void 0
|
|
613009
|
+
}));
|
|
613010
|
+
} catch {
|
|
613011
|
+
return [];
|
|
613012
|
+
}
|
|
613013
|
+
}
|
|
613014
|
+
searchTelegramSqliteMirrorRows(sessionKey, query, options2 = {}) {
|
|
613015
|
+
const db = this.telegramDb();
|
|
613016
|
+
if (!db) return [];
|
|
613017
|
+
const limit = Math.max(1, Math.min(50, Math.floor(options2.limit ?? 12)));
|
|
613018
|
+
const username = (options2.username || "").replace(/^@/, "").trim().toLowerCase();
|
|
613019
|
+
const tokens = [...telegramMemoryTokens(query)].slice(0, 8);
|
|
613020
|
+
const rows = /* @__PURE__ */ new Map();
|
|
613021
|
+
const addRows = (items) => {
|
|
613022
|
+
for (const row of items) {
|
|
613023
|
+
const key = Number(row.rowid);
|
|
613024
|
+
if (Number.isFinite(key)) rows.set(key, row);
|
|
613025
|
+
}
|
|
613026
|
+
};
|
|
613027
|
+
try {
|
|
613028
|
+
if (options2.userId !== void 0) {
|
|
613029
|
+
addRows(db.prepare(`
|
|
613030
|
+
SELECT rowid, * FROM telegram_messages
|
|
613031
|
+
WHERE session_key = ? AND from_user_id = ?
|
|
613032
|
+
ORDER BY received_at DESC
|
|
613033
|
+
LIMIT ?
|
|
613034
|
+
`).all(sessionKey, options2.userId, limit));
|
|
613035
|
+
}
|
|
613036
|
+
if (username) {
|
|
613037
|
+
addRows(db.prepare(`
|
|
613038
|
+
SELECT rowid, * FROM telegram_messages
|
|
613039
|
+
WHERE session_key = ? AND lower(username) = ?
|
|
613040
|
+
ORDER BY received_at DESC
|
|
613041
|
+
LIMIT ?
|
|
613042
|
+
`).all(sessionKey, username, limit));
|
|
613043
|
+
}
|
|
613044
|
+
if (tokens.length > 0) {
|
|
613045
|
+
const clauses = tokens.map(() => "text LIKE ?").join(" OR ");
|
|
613046
|
+
addRows(db.prepare(`
|
|
613047
|
+
SELECT rowid, * FROM telegram_messages
|
|
613048
|
+
WHERE session_key = ? AND (${clauses})
|
|
613049
|
+
ORDER BY received_at DESC
|
|
613050
|
+
LIMIT ?
|
|
613051
|
+
`).all(sessionKey, ...tokens.map((token) => `%${token}%`), limit));
|
|
613052
|
+
}
|
|
613053
|
+
if (rows.size === 0) {
|
|
613054
|
+
addRows(db.prepare(`
|
|
613055
|
+
SELECT rowid, * FROM telegram_messages
|
|
613056
|
+
WHERE session_key = ?
|
|
613057
|
+
ORDER BY received_at DESC
|
|
613058
|
+
LIMIT ?
|
|
613059
|
+
`).all(sessionKey, Math.min(limit, 12)));
|
|
613060
|
+
}
|
|
613061
|
+
} catch {
|
|
613062
|
+
return [];
|
|
613063
|
+
}
|
|
613064
|
+
return [...rows.values()].sort((a2, b) => Number(b.received_at ?? 0) - Number(a2.received_at ?? 0)).slice(0, limit);
|
|
613065
|
+
}
|
|
613066
|
+
formatTelegramSqliteMirrorRows(rows, maxText = 260) {
|
|
613067
|
+
return rows.map((row) => {
|
|
613068
|
+
const when = row.received_at ? new Date(Number(row.received_at)).toISOString() : "";
|
|
613069
|
+
const speaker = row.role === "assistant" ? `@${this.state.botUsername || "omnius"}` : row.username ? `@${row.username}` : row.from_user_id ? `user:${row.from_user_id}` : "unknown";
|
|
613070
|
+
const reply = row.reply_to_message_id ? ` reply_to:${row.reply_to_message_id}` : "";
|
|
613071
|
+
const media = row.media_json ? " media:attached" : "";
|
|
613072
|
+
return `- ${when} ${speaker}/${row.role || "user"} msg:${row.message_id}${reply}${media}: ${telegramContextJsonString(String(row.text || ""), maxText)}`;
|
|
613073
|
+
}).join("\n");
|
|
613074
|
+
}
|
|
613075
|
+
searchTelegramEpisodeMemory(sessionKey, query, limit = 8) {
|
|
613076
|
+
if (!this.repoRoot || !query.trim()) return [];
|
|
613077
|
+
const paths = omniusMemoryDbPaths(this.repoRoot);
|
|
613078
|
+
if (!existsSync112(paths.episodes)) return [];
|
|
613079
|
+
const graph = new TemporalGraph(paths.knowledge);
|
|
613080
|
+
const store2 = new EpisodeStore(paths.episodes, graph);
|
|
613081
|
+
try {
|
|
613082
|
+
return store2.searchWithPPR(
|
|
613083
|
+
{
|
|
613084
|
+
sessionId: sessionKey,
|
|
613085
|
+
query,
|
|
613086
|
+
limit: Math.max(1, Math.min(20, Math.floor(limit))),
|
|
613087
|
+
metadataFilter: { sourceSurface: "telegram" }
|
|
613088
|
+
},
|
|
613089
|
+
{ lexicalWeight: 1.25, embeddingWeight: 0 }
|
|
613090
|
+
);
|
|
613091
|
+
} catch {
|
|
613092
|
+
return [];
|
|
613093
|
+
} finally {
|
|
613094
|
+
store2.close();
|
|
613095
|
+
graph.close();
|
|
613096
|
+
}
|
|
613097
|
+
}
|
|
613098
|
+
formatTelegramEpisodeMemoryResults(episodes, maxText = 320) {
|
|
613099
|
+
return episodes.map((episode) => {
|
|
613100
|
+
const meta = episode.metadata;
|
|
613101
|
+
const telegram = meta?.telegram && typeof meta.telegram === "object" ? meta.telegram : {};
|
|
613102
|
+
const when = episode.timestamp ? new Date(episode.timestamp).toISOString() : "";
|
|
613103
|
+
const speaker = telegram.speaker || telegram.username || "unknown";
|
|
613104
|
+
const messageId = telegram.messageId ? ` msg:${telegram.messageId}` : "";
|
|
613105
|
+
const mode = telegram.mode ? `/${telegram.mode}` : "";
|
|
613106
|
+
const text = episode.content.split("\n").filter((line) => !/^(Telegram|session:|chat:|message_id:|thread_id:|speaker:|mode:)/i.test(line.trim())).join(" ").replace(/\s+/g, " ").trim() || episode.content;
|
|
613107
|
+
return `- ${when} ${speaker}${mode}${messageId}: ${telegramContextJsonString(text, maxText)}`;
|
|
613108
|
+
}).join("\n");
|
|
613109
|
+
}
|
|
611958
613110
|
buildTelegramConversationContextStream(sessionKey, msg, maxRecent = TELEGRAM_CONTEXT_RECENT_DEFAULT) {
|
|
611959
613111
|
this.ensureTelegramConversationLoaded(sessionKey);
|
|
611960
613112
|
const history = this.chatHistory.get(sessionKey) ?? [];
|
|
@@ -611992,6 +613144,33 @@ ${mediaContext}` : ""
|
|
|
611992
613144
|
});
|
|
611993
613145
|
sections.push(`### Participants And Relationship Signals
|
|
611994
613146
|
${participantLines.join("\n")}`);
|
|
613147
|
+
}
|
|
613148
|
+
const associativeContext = this.relevantTelegramAssociativeMemoryContext(
|
|
613149
|
+
sessionKey,
|
|
613150
|
+
msg,
|
|
613151
|
+
isGroup ? 14 : 8
|
|
613152
|
+
);
|
|
613153
|
+
if (associativeContext) {
|
|
613154
|
+
sections.push(associativeContext);
|
|
613155
|
+
}
|
|
613156
|
+
const sqliteMirrorContext = this.relevantTelegramSqliteMirrorContext(
|
|
613157
|
+
sessionKey,
|
|
613158
|
+
msg,
|
|
613159
|
+
isGroup ? 14 : 8
|
|
613160
|
+
);
|
|
613161
|
+
if (sqliteMirrorContext) {
|
|
613162
|
+
sections.push(sqliteMirrorContext);
|
|
613163
|
+
}
|
|
613164
|
+
try {
|
|
613165
|
+
const episodicContext = this.relevantTelegramEpisodicMemoryContext(
|
|
613166
|
+
sessionKey,
|
|
613167
|
+
msg,
|
|
613168
|
+
isGroup ? 10 : 6
|
|
613169
|
+
);
|
|
613170
|
+
if (episodicContext) {
|
|
613171
|
+
sections.push(episodicContext);
|
|
613172
|
+
}
|
|
613173
|
+
} catch {
|
|
611995
613174
|
}
|
|
611996
613175
|
const memoryCards = this.relevantTelegramMemoryCards(sessionKey, msg, isGroup ? 10 : 6);
|
|
611997
613176
|
if (memoryCards.length > 0) {
|
|
@@ -612176,7 +613355,8 @@ ${lines.join("\n")}`);
|
|
|
612176
613355
|
``,
|
|
612177
613356
|
`Reply discretion: infer from the live thread, speaker relationships, direct platform signals, replies, tone, current message, and any private channel daydream artifact supplied in context. Do not use static keyword rules.`,
|
|
612178
613357
|
`Private chats: should_reply is normally true.`,
|
|
612179
|
-
`Group/public chats: default should_reply to false unless the current message clearly addresses the bot, replies to the bot, continues an active bot-involved exchange, assigns the bot work,
|
|
613358
|
+
`Group/public chats: default should_reply to false unless the current message clearly addresses the bot, replies to the bot, continues an active bot-involved exchange, assigns the bot work, asks for the bot's view, or is semantically connected to durable memory/current discussion in a way where a concise bot reply is socially useful. Ambient chatter, third-person discussion about the bot, commands meant for a human, or questions among other people are false. Do not set true just because the bot could help.`,
|
|
613359
|
+
`Memory discipline: use durable associative user memory, relationships, prior actions, and recent context to infer whether this speaker is continuing a bot-related thread. A mention is not required when the semantic target is clearly the bot or an ongoing bot-mediated discussion.`,
|
|
612180
613360
|
`Channel daydream discipline: a daydream artifact may highlight relationship signals, unresolved questions, or possible reply opportunities from idle reflection. It can justify analyzing this turn, but it does not force a reply. Reply only if the current user entry makes the intervention timely and socially appropriate.`,
|
|
612181
613361
|
`Stimulation discipline: also set attention_state, attention_delta, and optional next_check_after_messages/next_check_after_ms. These control future analysis cadence only; they do not force a reply. Use engaged for active back-and-forth, observing for likely relevant context, cooldown for recently irrelevant context, and idle for ambient chatter.`,
|
|
612182
613362
|
forcedLine,
|
|
@@ -612558,6 +613738,40 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
|
|
|
612558
613738
|
if (!me?.ok) {
|
|
612559
613739
|
throw new Error(`Invalid Telegram bot token: ${me?.description || "unknown error"}`);
|
|
612560
613740
|
}
|
|
613741
|
+
if (this.repoRoot && me.result?.id) {
|
|
613742
|
+
try {
|
|
613743
|
+
const lockDir = resolve43(this.repoRoot, ".omnius", "telegram-runner-state");
|
|
613744
|
+
mkdirSync65(lockDir, { recursive: true });
|
|
613745
|
+
const lockFile = join127(lockDir, `bot-${me.result.id}.owner.lock`);
|
|
613746
|
+
if (existsSync112(lockFile)) {
|
|
613747
|
+
try {
|
|
613748
|
+
const prior = JSON.parse(readFileSync92(lockFile, "utf8"));
|
|
613749
|
+
const priorAlive = typeof prior.pid === "number" && prior.pid !== process.pid ? this.processIsAlive(prior.pid) : false;
|
|
613750
|
+
if (priorAlive) {
|
|
613751
|
+
throw new Error(
|
|
613752
|
+
`Telegram bot @${prior.botUsername || me.result.username || "unknown"} is already being polled by pid ${prior.pid} (cwd ${prior.cwd || "?"}). Stop that instance or use a different bot token in this project.`
|
|
613753
|
+
);
|
|
613754
|
+
}
|
|
613755
|
+
} catch (e2) {
|
|
613756
|
+
if (e2 instanceof Error && e2.message.startsWith("Telegram bot @")) throw e2;
|
|
613757
|
+
}
|
|
613758
|
+
}
|
|
613759
|
+
writeFileSync59(
|
|
613760
|
+
lockFile,
|
|
613761
|
+
JSON.stringify({
|
|
613762
|
+
pid: process.pid,
|
|
613763
|
+
cwd: this.repoRoot,
|
|
613764
|
+
botUsername: me.result.username,
|
|
613765
|
+
botUserId: me.result.id,
|
|
613766
|
+
ts: Date.now()
|
|
613767
|
+
}, null, 2),
|
|
613768
|
+
{ encoding: "utf-8", mode: 384 }
|
|
613769
|
+
);
|
|
613770
|
+
this.telegramOwnerLockFile = lockFile;
|
|
613771
|
+
} catch (e2) {
|
|
613772
|
+
if (e2 instanceof Error && e2.message.startsWith("Telegram bot @")) throw e2;
|
|
613773
|
+
}
|
|
613774
|
+
}
|
|
612561
613775
|
this.state = {
|
|
612562
613776
|
active: true,
|
|
612563
613777
|
botUsername: me.result?.username ?? "unknown",
|
|
@@ -612601,10 +613815,40 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
|
|
|
612601
613815
|
agent.aborted = true;
|
|
612602
613816
|
if (agent.typingInterval) clearInterval(agent.typingInterval);
|
|
612603
613817
|
}
|
|
613818
|
+
if (this.telegramSqliteDb && this.telegramSqliteDb !== false) {
|
|
613819
|
+
try {
|
|
613820
|
+
this.telegramSqliteDb.close();
|
|
613821
|
+
} catch {
|
|
613822
|
+
}
|
|
613823
|
+
this.telegramSqliteDb = null;
|
|
613824
|
+
}
|
|
613825
|
+
if (this.telegramOwnerLockFile) {
|
|
613826
|
+
try {
|
|
613827
|
+
if (existsSync112(this.telegramOwnerLockFile)) {
|
|
613828
|
+
unlinkSync22(this.telegramOwnerLockFile);
|
|
613829
|
+
}
|
|
613830
|
+
} catch {
|
|
613831
|
+
}
|
|
613832
|
+
this.telegramOwnerLockFile = null;
|
|
613833
|
+
}
|
|
612604
613834
|
this.subAgents.clear();
|
|
612605
613835
|
this.activeChatViews.clear();
|
|
612606
613836
|
this.refreshActiveTelegramInteractionCount();
|
|
612607
613837
|
}
|
|
613838
|
+
/**
|
|
613839
|
+
* Cheap liveness probe: kill(pid, 0) throws ESRCH when the process is dead
|
|
613840
|
+
* and EPERM when it exists but we can't signal it (still alive from our POV).
|
|
613841
|
+
*/
|
|
613842
|
+
processIsAlive(pid) {
|
|
613843
|
+
if (!pid || !Number.isFinite(pid)) return false;
|
|
613844
|
+
try {
|
|
613845
|
+
process.kill(pid, 0);
|
|
613846
|
+
return true;
|
|
613847
|
+
} catch (e2) {
|
|
613848
|
+
if (e2 && e2.code === "EPERM") return true;
|
|
613849
|
+
return false;
|
|
613850
|
+
}
|
|
613851
|
+
}
|
|
612608
613852
|
// ── Typing indicator ──────────────────────────────────────────────────
|
|
612609
613853
|
/** Start sending "typing" indicator every 4 seconds */
|
|
612610
613854
|
startTypingIndicator(chatId) {
|
|
@@ -613529,7 +614773,7 @@ ${currentTelegramPrompt}`;
|
|
|
613529
614773
|
const toolHint = [
|
|
613530
614774
|
"You have access to isolated per-chat memory (memory_write, memory_read, memory_search) scoped to this conversation.",
|
|
613531
614775
|
"memory_search may use scope=group/current_chat for this group or scope=user with user_id/username for a participant in this same group. Other groups, admin chats, and private DMs are not accessible here.",
|
|
613532
|
-
"You can remember facts about users and retrieve them later. You also have web_search and web_fetch to look up information.",
|
|
614776
|
+
"You can remember facts about users and retrieve them later. Durable associative memory in the prompt includes participant profiles, relationships, scoped facts, and prior actions retained across days, sessions, and Omnius updates. You also have web_search and web_fetch to look up information.",
|
|
613533
614777
|
"You have the full scoped Telegram media-analysis stack by default: telegram_media_recent, image_read, ocr, ocr_image_advanced, vision, pdf_to_text, ocr_pdf, transcribe_file, video_understand, audio_analyze, and identity_memory. For complex textual imagery, screenshots, forms, scans, or dense labels, prefer ocr_image_advanced after resolving media with path='reply' or path='latest'.",
|
|
613534
614778
|
formatIdentityMemoryContext(chatLabel || "Telegram private chat"),
|
|
613535
614779
|
reminderToolContract,
|
|
@@ -613695,7 +614939,7 @@ ${result.llmContent ?? result.output}` };
|
|
|
613695
614939
|
if (tool.name === "memory_search") {
|
|
613696
614940
|
return {
|
|
613697
614941
|
...tool,
|
|
613698
|
-
description: "Search only this Telegram chat's isolated
|
|
614942
|
+
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, but never crosses into admin/private/global memory.",
|
|
613699
614943
|
parameters: (() => {
|
|
613700
614944
|
const base3 = tool.parameters ?? {};
|
|
613701
614945
|
const props = base3["properties"] && typeof base3["properties"] === "object" && !Array.isArray(base3["properties"]) ? base3["properties"] : {};
|
|
@@ -613713,6 +614957,8 @@ ${result.llmContent ?? result.output}` };
|
|
|
613713
614957
|
execute: async (args) => {
|
|
613714
614958
|
const query = String(args["query"] || "").trim();
|
|
613715
614959
|
const maxResults = typeof args["max_results"] === "number" && Number.isFinite(args["max_results"]) ? Math.max(1, Math.min(20, Math.floor(args["max_results"]))) : 8;
|
|
614960
|
+
if (!query) return { success: true, output: "Search query is required." };
|
|
614961
|
+
this.ensureTelegramConversationLoaded(msgSessionKey);
|
|
613716
614962
|
const currentGroupId = chatId === void 0 ? "" : String(chatId);
|
|
613717
614963
|
const requestedGroupId = String(args["group_id"] ?? args["chat_id"] ?? "").trim();
|
|
613718
614964
|
if (requestedGroupId && currentGroupId && requestedGroupId !== currentGroupId) {
|
|
@@ -613740,32 +614986,70 @@ ${result.llmContent ?? result.output}` };
|
|
|
613740
614986
|
if (effectiveUsername && haystack.includes(`@${effectiveUsername}`)) return true;
|
|
613741
614987
|
return false;
|
|
613742
614988
|
});
|
|
613743
|
-
if (!query || cards.length === 0) {
|
|
613744
|
-
const scopeLabel2 = wantsUserScope ? `user ${effectiveUsername ? `@${effectiveUsername}` : `id ${effectiveUserId}`}` : `chat ${currentGroupId || msgSessionKey}`;
|
|
613745
|
-
return { success: true, output: cards.length === 0 ? `No scoped memories found for ${scopeLabel2}.` : "Search query is required." };
|
|
613746
|
-
}
|
|
613747
614989
|
const queryTokens = telegramMemoryTokens(query);
|
|
613748
|
-
const
|
|
614990
|
+
const cardResults = cards.map((card) => ({
|
|
613749
614991
|
card,
|
|
613750
614992
|
score: telegramMemorySimilarity(
|
|
613751
614993
|
queryTokens,
|
|
613752
614994
|
telegramMemoryTokens([card.title, card.tags.join(" "), card.speakers.join(" "), card.notes.join(" ")].join(" "))
|
|
613753
614995
|
)
|
|
613754
614996
|
})).filter((item) => item.score > 0).sort((a2, b) => b.score - a2.score || b.card.updatedAt - a2.card.updatedAt).slice(0, maxResults);
|
|
613755
|
-
|
|
613756
|
-
return { success: true, output: `No scoped memories matched "${query}" in this Telegram chat.` };
|
|
613757
|
-
}
|
|
613758
|
-
const lines = results.map(({ card, score }) => {
|
|
614997
|
+
const cardLines = cardResults.map(({ card, score }) => {
|
|
613759
614998
|
const notes2 = card.notes.slice(-4).map((note) => ` - ${truncateTelegramContextLine(note, 220)}`).join("\n");
|
|
613760
614999
|
const tags = card.tags.length ? ` tags:${card.tags.slice(0, 8).join(",")}` : "";
|
|
613761
615000
|
const users = Array.isArray(card.userIds) && card.userIds.length ? ` users:${card.userIds.slice(0, 6).join(",")}` : "";
|
|
613762
615001
|
return `[${card.id}] ${card.title} (relevance ${score.toFixed(2)}${users}${tags})
|
|
613763
615002
|
${notes2}`;
|
|
613764
615003
|
});
|
|
615004
|
+
const memory = this.chatAssociativeMemory.get(msgSessionKey);
|
|
615005
|
+
const associativeFacts = memory ? [...memory.facts, ...memory.relationships].filter((fact) => {
|
|
615006
|
+
if (!wantsUserScope) return true;
|
|
615007
|
+
if (effectiveUserId !== void 0 && fact.userIds.includes(effectiveUserId)) return true;
|
|
615008
|
+
return !!effectiveUsername && fact.usernames.includes(effectiveUsername);
|
|
615009
|
+
}).map((fact) => ({
|
|
615010
|
+
fact,
|
|
615011
|
+
score: telegramMemorySimilarity(
|
|
615012
|
+
queryTokens,
|
|
615013
|
+
telegramMemoryTokens([fact.text, fact.tags.join(" "), fact.speakers.join(" ")].join(" "))
|
|
615014
|
+
) + (effectiveUserId !== void 0 && fact.userIds.includes(effectiveUserId) ? 0.3 : 0) + (effectiveUsername && fact.usernames.includes(effectiveUsername) ? 0.3 : 0)
|
|
615015
|
+
})).filter((item) => item.score > 0).sort((a2, b) => b.score - a2.score || b.fact.updatedAt - a2.fact.updatedAt).slice(0, maxResults) : [];
|
|
615016
|
+
const rawRows = this.searchTelegramSqliteMirrorRows(msgSessionKey, query, {
|
|
615017
|
+
limit: maxResults,
|
|
615018
|
+
userId: effectiveUserId,
|
|
615019
|
+
username: effectiveUsername
|
|
615020
|
+
});
|
|
615021
|
+
const episodeResults = this.searchTelegramEpisodeMemory(msgSessionKey, query, maxResults);
|
|
613765
615022
|
const scopeLabel = wantsUserScope ? `user ${effectiveUsername ? `@${effectiveUsername}` : `id ${effectiveUserId}`}` : `chat ${currentGroupId || msgSessionKey}`;
|
|
613766
|
-
|
|
613767
|
-
|
|
613768
|
-
|
|
615023
|
+
const sections = [];
|
|
615024
|
+
if (cardLines.length > 0) {
|
|
615025
|
+
sections.push(`### Scoped Memory Cards
|
|
615026
|
+
${cardLines.join("\n\n")}`);
|
|
615027
|
+
}
|
|
615028
|
+
if (associativeFacts.length > 0) {
|
|
615029
|
+
const lines = associativeFacts.map(
|
|
615030
|
+
({ fact, score }) => `- (${score.toFixed(2)}) ${telegramContextJsonString(fact.text, 300)}${fact.messageIds.length ? ` [messages:${fact.messageIds.slice(-6).join(",")}]` : ""}`
|
|
615031
|
+
);
|
|
615032
|
+
sections.push(`### Durable Associative Facts
|
|
615033
|
+
${lines.join("\n")}`);
|
|
615034
|
+
}
|
|
615035
|
+
if (episodeResults.length > 0) {
|
|
615036
|
+
sections.push(`### Episode/Knowledge Graph Recall
|
|
615037
|
+
${this.formatTelegramEpisodeMemoryResults(episodeResults)}`);
|
|
615038
|
+
}
|
|
615039
|
+
if (rawRows.length > 0) {
|
|
615040
|
+
sections.push(`### Raw SQLite Message Mirror
|
|
615041
|
+
${this.formatTelegramSqliteMirrorRows(rawRows)}`);
|
|
615042
|
+
}
|
|
615043
|
+
if (sections.length === 0) {
|
|
615044
|
+
return { success: true, output: `No scoped Telegram memories matched "${query}" in ${scopeLabel}.` };
|
|
615045
|
+
}
|
|
615046
|
+
const output = [
|
|
615047
|
+
`Scoped Telegram memory search for "${query}" in ${scopeLabel}.`,
|
|
615048
|
+
"Results are scoped to this Telegram chat and may include raw message evidence, graph episodes, associative facts, and memory cards.",
|
|
615049
|
+
"",
|
|
615050
|
+
sections.join("\n\n")
|
|
615051
|
+
].join("\n");
|
|
615052
|
+
return { success: true, output, llmContent: output };
|
|
613769
615053
|
}
|
|
613770
615054
|
};
|
|
613771
615055
|
}
|
|
@@ -616320,6 +617604,7 @@ ${caption}\r
|
|
|
616320
617604
|
}
|
|
616321
617605
|
const msg = normalizeTelegramUpdate(update2);
|
|
616322
617606
|
if (!msg) continue;
|
|
617607
|
+
this.persistTelegramRawMessage(update2, msg);
|
|
616323
617608
|
const isAdmin = this.adminUserId ? String(msg.fromUserId) === this.adminUserId || msg.username === this.adminUserId : false;
|
|
616324
617609
|
if (this.adminUserId && !this.agentConfig) {
|
|
616325
617610
|
if (!isAdmin) continue;
|
|
@@ -616665,8 +617950,8 @@ function appendCheckin(sessionId, steering) {
|
|
|
616665
617950
|
mkdirSync66(sessionsDir(), { recursive: true });
|
|
616666
617951
|
const fp = checkinPath(sessionId);
|
|
616667
617952
|
const entry = JSON.stringify({ ts: Date.now(), steering }) + "\n";
|
|
616668
|
-
const { appendFileSync:
|
|
616669
|
-
|
|
617953
|
+
const { appendFileSync: appendFileSync12 } = __require("node:fs");
|
|
617954
|
+
appendFileSync12(fp, entry, "utf-8");
|
|
616670
617955
|
} catch {
|
|
616671
617956
|
}
|
|
616672
617957
|
}
|
|
@@ -618865,7 +620150,7 @@ __export(audit_log_exports, {
|
|
|
618865
620150
|
recordAudit: () => recordAudit,
|
|
618866
620151
|
sanitizeBody: () => sanitizeBody
|
|
618867
620152
|
});
|
|
618868
|
-
import { mkdirSync as mkdirSync69, appendFileSync as
|
|
620153
|
+
import { mkdirSync as mkdirSync69, appendFileSync as appendFileSync10, readFileSync as readFileSync96, existsSync as existsSync116 } from "node:fs";
|
|
618869
620154
|
import { join as join131 } from "node:path";
|
|
618870
620155
|
function initAuditLog(omniusDir) {
|
|
618871
620156
|
auditDir = join131(omniusDir, "audit");
|
|
@@ -618880,7 +620165,7 @@ function recordAudit(record) {
|
|
|
618880
620165
|
if (!initialized) return;
|
|
618881
620166
|
try {
|
|
618882
620167
|
const line = JSON.stringify(record) + "\n";
|
|
618883
|
-
|
|
620168
|
+
appendFileSync10(auditFile, line, "utf-8");
|
|
618884
620169
|
} catch {
|
|
618885
620170
|
}
|
|
618886
620171
|
}
|
|
@@ -641179,7 +642464,7 @@ import { fileURLToPath as fileURLToPath18 } from "node:url";
|
|
|
641179
642464
|
import {
|
|
641180
642465
|
readFileSync as readFileSync105,
|
|
641181
642466
|
writeFileSync as writeFileSync70,
|
|
641182
|
-
appendFileSync as
|
|
642467
|
+
appendFileSync as appendFileSync11,
|
|
641183
642468
|
rmSync as rmSync7,
|
|
641184
642469
|
readdirSync as readdirSync46,
|
|
641185
642470
|
mkdirSync as mkdirSync78
|
|
@@ -645673,7 +646958,7 @@ This is an independent background session started from /background.`
|
|
|
645673
646958
|
if (!line.trim()) return;
|
|
645674
646959
|
try {
|
|
645675
646960
|
mkdirSync78(HISTORY_DIR, { recursive: true });
|
|
645676
|
-
|
|
646961
|
+
appendFileSync11(HISTORY_FILE, line + "\n", "utf8");
|
|
645677
646962
|
if (Math.random() < 0.02) {
|
|
645678
646963
|
const all2 = readFileSync105(HISTORY_FILE, "utf8").trim().split("\n");
|
|
645679
646964
|
if (all2.length > MAX_HISTORY_LINES) {
|
|
@@ -647127,7 +648412,9 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
647127
648412
|
...settings.admin !== void 0 ? { telegramAdmin: settings.admin } : {},
|
|
647128
648413
|
...settings.mode !== void 0 ? { telegramMode: settings.mode } : {}
|
|
647129
648414
|
};
|
|
647130
|
-
|
|
648415
|
+
const projectHasOmnius = existsSync127(join143(repoRoot, ".omnius"));
|
|
648416
|
+
const useProject = settings.local === true || settings.local === void 0 && projectHasOmnius;
|
|
648417
|
+
if (useProject) {
|
|
647131
648418
|
saveProjectSettings(repoRoot, payload);
|
|
647132
648419
|
} else {
|
|
647133
648420
|
saveGlobalSettings(payload);
|
|
@@ -651460,12 +652747,12 @@ function crashLog(label, err) {
|
|
|
651460
652747
|
const logLine = `[${timestamp}] ${label}: ${msg}
|
|
651461
652748
|
`;
|
|
651462
652749
|
try {
|
|
651463
|
-
const { appendFileSync:
|
|
652750
|
+
const { appendFileSync: appendFileSync12, mkdirSync: mkdirSync81 } = __require("node:fs");
|
|
651464
652751
|
const { join: join148 } = __require("node:path");
|
|
651465
652752
|
const { homedir: homedir52 } = __require("node:os");
|
|
651466
652753
|
const logDir = join148(homedir52(), ".omnius");
|
|
651467
652754
|
mkdirSync81(logDir, { recursive: true });
|
|
651468
|
-
|
|
652755
|
+
appendFileSync12(join148(logDir, "crash.log"), logLine);
|
|
651469
652756
|
} catch {
|
|
651470
652757
|
}
|
|
651471
652758
|
try {
|