omnius 1.0.59 → 1.0.60
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 +1163 -60
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -561982,8 +561982,7 @@ function wrapTaskCompleteSummary(summary) {
|
|
|
561982
561982
|
}
|
|
561983
561983
|
function wrapPlainLine(line, width) {
|
|
561984
561984
|
const out = [];
|
|
561985
|
-
const
|
|
561986
|
-
const continuationIndent = indent.length > 0 ? indent : "";
|
|
561985
|
+
const continuationIndent = hangingIndentForPlainLine(line, width);
|
|
561987
561986
|
let remaining = line;
|
|
561988
561987
|
while (remaining.length > width) {
|
|
561989
561988
|
let breakAt = remaining.lastIndexOf(" ", width);
|
|
@@ -561994,6 +561993,20 @@ function wrapPlainLine(line, width) {
|
|
|
561994
561993
|
out.push(remaining);
|
|
561995
561994
|
return out;
|
|
561996
561995
|
}
|
|
561996
|
+
function hangingIndentForPlainLine(line, width) {
|
|
561997
|
+
const capped = (n2) => " ".repeat(Math.max(0, Math.min(n2, width - 4)));
|
|
561998
|
+
const patterns = [
|
|
561999
|
+
/^(\s*(?:[-*+○•]\s+))/,
|
|
562000
|
+
/^(\s*(?:\d+[.)]\s+))/,
|
|
562001
|
+
/^(\s*(?:>\s*))/,
|
|
562002
|
+
/^(\s*(?:[A-Za-z][\w.-]{0,28}:\s+))/
|
|
562003
|
+
];
|
|
562004
|
+
for (const pattern of patterns) {
|
|
562005
|
+
const match = line.match(pattern);
|
|
562006
|
+
if (match?.[1]) return capped(match[1].length);
|
|
562007
|
+
}
|
|
562008
|
+
return capped(line.match(/^\s*/)?.[0].length ?? 0);
|
|
562009
|
+
}
|
|
561997
562010
|
function renderTaskIncomplete(turns, toolCalls, durationMs, tokens) {
|
|
561998
562011
|
const duration = formatDuration2(durationMs);
|
|
561999
562012
|
const tokenStr = tokens ? ` ${formatTokenCount(tokens)}` : "";
|
|
@@ -563699,7 +563712,7 @@ var init_voice_session = __esm({
|
|
|
563699
563712
|
|
|
563700
563713
|
// packages/cli/src/tui/scoped-personality.ts
|
|
563701
563714
|
import { createHash as createHash17 } from "node:crypto";
|
|
563702
|
-
import { existsSync as existsSync82, mkdirSync as mkdirSync46, readFileSync as readFileSync65, writeFileSync as writeFileSync41 } from "node:fs";
|
|
563715
|
+
import { appendFileSync as appendFileSync4, existsSync as existsSync82, mkdirSync as mkdirSync46, readFileSync as readFileSync65, writeFileSync as writeFileSync41 } from "node:fs";
|
|
563703
563716
|
import { join as join98, resolve as resolve37 } from "node:path";
|
|
563704
563717
|
function safeName(input) {
|
|
563705
563718
|
return input.replace(/[^A-Za-z0-9_.-]/g, "-").slice(0, 80) || "default";
|
|
@@ -563715,7 +563728,8 @@ function scopedPersonalityPaths(scope) {
|
|
|
563715
563728
|
const prefix = `${safeName(scope.label)}-${scopeHash(scope)}`;
|
|
563716
563729
|
return {
|
|
563717
563730
|
json: join98(dir, `${prefix}.json`),
|
|
563718
|
-
markdown: join98(dir, `${prefix}.md`)
|
|
563731
|
+
markdown: join98(dir, `${prefix}.md`),
|
|
563732
|
+
events: join98(dir, `${prefix}.events.jsonl`)
|
|
563719
563733
|
};
|
|
563720
563734
|
}
|
|
563721
563735
|
function inferToneTags(text) {
|
|
@@ -563776,16 +563790,49 @@ function newDocument(scope) {
|
|
|
563776
563790
|
"Do not expose private local files, secrets, credentials, or hidden reasoning in public or group scopes.",
|
|
563777
563791
|
"Do not claim memory is unavailable when scoped context is present."
|
|
563778
563792
|
],
|
|
563793
|
+
topicCounts: {},
|
|
563794
|
+
durableObservations: [],
|
|
563795
|
+
relationshipEvents: [],
|
|
563779
563796
|
recentObservations: []
|
|
563780
563797
|
};
|
|
563781
563798
|
}
|
|
563799
|
+
function normalizeDocument(scope, parsed) {
|
|
563800
|
+
const fresh = newDocument(scope);
|
|
563801
|
+
const doc = {
|
|
563802
|
+
...fresh,
|
|
563803
|
+
...parsed,
|
|
563804
|
+
scope: {
|
|
563805
|
+
kind: scope.kind,
|
|
563806
|
+
idHash: scopeHash(scope),
|
|
563807
|
+
label: scope.label || parsed.scope?.label || fresh.scope.label
|
|
563808
|
+
},
|
|
563809
|
+
participants: parsed.participants && typeof parsed.participants === "object" ? parsed.participants : {},
|
|
563810
|
+
dominantTone: Array.isArray(parsed.dominantTone) ? parsed.dominantTone : [],
|
|
563811
|
+
responseStyle: Array.isArray(parsed.responseStyle) && parsed.responseStyle.length > 0 ? parsed.responseStyle : fresh.responseStyle,
|
|
563812
|
+
relationshipModel: Array.isArray(parsed.relationshipModel) && parsed.relationshipModel.length > 0 ? parsed.relationshipModel : fresh.relationshipModel,
|
|
563813
|
+
behavioralGuidance: Array.isArray(parsed.behavioralGuidance) && parsed.behavioralGuidance.length > 0 ? parsed.behavioralGuidance : fresh.behavioralGuidance,
|
|
563814
|
+
boundaries: Array.isArray(parsed.boundaries) && parsed.boundaries.length > 0 ? parsed.boundaries : fresh.boundaries,
|
|
563815
|
+
topicCounts: parsed.topicCounts && typeof parsed.topicCounts === "object" ? parsed.topicCounts : {},
|
|
563816
|
+
durableObservations: Array.isArray(parsed.durableObservations) ? parsed.durableObservations.slice(-MAX_PROFILE_DURABLE_OBSERVATIONS) : [],
|
|
563817
|
+
relationshipEvents: Array.isArray(parsed.relationshipEvents) ? parsed.relationshipEvents.slice(-MAX_PROFILE_RELATIONSHIP_EVENTS) : [],
|
|
563818
|
+
recentObservations: Array.isArray(parsed.recentObservations) ? parsed.recentObservations.slice(-MAX_PROFILE_OBSERVATIONS) : []
|
|
563819
|
+
};
|
|
563820
|
+
doc.messageCount = Number.isFinite(doc.messageCount) ? doc.messageCount : 0;
|
|
563821
|
+
for (const profile of Object.values(doc.participants)) {
|
|
563822
|
+
if (!Array.isArray(profile.toneTags)) profile.toneTags = [];
|
|
563823
|
+
if (!Array.isArray(profile.samples)) profile.samples = [];
|
|
563824
|
+
profile.toneTags = profile.toneTags.slice(0, MAX_PROFILE_TAGS);
|
|
563825
|
+
profile.samples = profile.samples.slice(-MAX_PROFILE_SAMPLES);
|
|
563826
|
+
}
|
|
563827
|
+
return doc;
|
|
563828
|
+
}
|
|
563782
563829
|
function loadScopedPersonality(scope) {
|
|
563783
563830
|
const paths = scopedPersonalityPaths(scope);
|
|
563784
563831
|
if (!existsSync82(paths.json)) return newDocument(scope);
|
|
563785
563832
|
try {
|
|
563786
563833
|
const parsed = JSON.parse(readFileSync65(paths.json, "utf8"));
|
|
563787
563834
|
if (parsed.version !== PROFILE_VERSION) return newDocument(scope);
|
|
563788
|
-
return parsed;
|
|
563835
|
+
return normalizeDocument(scope, parsed);
|
|
563789
563836
|
} catch {
|
|
563790
563837
|
return newDocument(scope);
|
|
563791
563838
|
}
|
|
@@ -563796,6 +563843,14 @@ function saveScopedPersonality(scope, doc) {
|
|
|
563796
563843
|
writeFileSync41(paths.json, JSON.stringify(doc, null, 2) + "\n", "utf8");
|
|
563797
563844
|
writeFileSync41(paths.markdown, renderScopedPersonalityMarkdown(doc) + "\n", "utf8");
|
|
563798
563845
|
}
|
|
563846
|
+
function appendScopedPersonalityEvent(scope, event) {
|
|
563847
|
+
try {
|
|
563848
|
+
const paths = scopedPersonalityPaths(scope);
|
|
563849
|
+
mkdirSync46(scopedPersonalityDir(scope.repoRoot, scope.kind), { recursive: true });
|
|
563850
|
+
appendFileSync4(paths.events, JSON.stringify(event) + "\n", "utf8");
|
|
563851
|
+
} catch {
|
|
563852
|
+
}
|
|
563853
|
+
}
|
|
563799
563854
|
function updateScopedPersonality(scope, observation) {
|
|
563800
563855
|
const doc = loadScopedPersonality(scope);
|
|
563801
563856
|
const now = new Date(observation.ts ?? Date.now()).toISOString();
|
|
@@ -563806,6 +563861,7 @@ function updateScopedPersonality(scope, observation) {
|
|
|
563806
563861
|
...inferToneTags(observation.text),
|
|
563807
563862
|
...keywords(observation.text)
|
|
563808
563863
|
];
|
|
563864
|
+
const topicTags = keywords(observation.text);
|
|
563809
563865
|
doc.updatedAt = now;
|
|
563810
563866
|
doc.messageCount += 1;
|
|
563811
563867
|
doc.scope.label = scope.label;
|
|
@@ -563831,6 +563887,9 @@ function updateScopedPersonality(scope, observation) {
|
|
|
563831
563887
|
for (const tag of profile.toneTags) allTags.set(tag, (allTags.get(tag) ?? 0) + profile.count);
|
|
563832
563888
|
}
|
|
563833
563889
|
doc.dominantTone = [...allTags.entries()].sort((a2, b) => b[1] - a2[1]).map(([tag]) => tag).slice(0, MAX_PROFILE_TAGS);
|
|
563890
|
+
for (const tag of topicTags) {
|
|
563891
|
+
doc.topicCounts[tag] = (doc.topicCounts[tag] ?? 0) + 1;
|
|
563892
|
+
}
|
|
563834
563893
|
if (doc.dominantTone.includes("frustrated")) {
|
|
563835
563894
|
doc.responseStyle = [
|
|
563836
563895
|
"Start with the concrete fix or answer; avoid soothing filler.",
|
|
@@ -563852,15 +563911,31 @@ function updateScopedPersonality(scope, observation) {
|
|
|
563852
563911
|
for (const hint of relationshipHints) {
|
|
563853
563912
|
const clean5 = compactLine(hint, 180);
|
|
563854
563913
|
if (clean5 && !doc.relationshipModel.includes(clean5)) doc.relationshipModel.push(clean5);
|
|
563914
|
+
if (clean5 && !doc.relationshipEvents.includes(`${now} ${clean5}`)) doc.relationshipEvents.push(`${now} ${clean5}`);
|
|
563855
563915
|
}
|
|
563856
563916
|
doc.relationshipModel = doc.relationshipModel.slice(-12);
|
|
563917
|
+
doc.relationshipEvents = doc.relationshipEvents.slice(-MAX_PROFILE_RELATIONSHIP_EVENTS);
|
|
563857
563918
|
if (text) {
|
|
563858
563919
|
const line = `${now} ${speaker}${observation.mode ? `/${observation.mode}` : ""}: ${text}`;
|
|
563859
563920
|
doc.recentObservations.push(line);
|
|
563860
563921
|
if (doc.recentObservations.length > MAX_PROFILE_OBSERVATIONS) {
|
|
563861
563922
|
doc.recentObservations.splice(0, doc.recentObservations.length - MAX_PROFILE_OBSERVATIONS);
|
|
563862
563923
|
}
|
|
563924
|
+
doc.durableObservations.push(line);
|
|
563925
|
+
if (doc.durableObservations.length > MAX_PROFILE_DURABLE_OBSERVATIONS) {
|
|
563926
|
+
doc.durableObservations.splice(0, doc.durableObservations.length - MAX_PROFILE_DURABLE_OBSERVATIONS);
|
|
563927
|
+
}
|
|
563863
563928
|
}
|
|
563929
|
+
appendScopedPersonalityEvent(scope, {
|
|
563930
|
+
ts: observation.ts ?? Date.now(),
|
|
563931
|
+
iso: now,
|
|
563932
|
+
speaker,
|
|
563933
|
+
role: observation.role,
|
|
563934
|
+
mode: observation.mode,
|
|
563935
|
+
text,
|
|
563936
|
+
toneTags: [...new Set(toneTags)].slice(0, 32),
|
|
563937
|
+
relationshipHints
|
|
563938
|
+
});
|
|
563864
563939
|
saveScopedPersonality(scope, doc);
|
|
563865
563940
|
return doc;
|
|
563866
563941
|
}
|
|
@@ -563871,6 +563946,9 @@ function renderScopedPersonalityMarkdown(doc) {
|
|
|
563871
563946
|
return `- ${name10}: messages=${profile.count}; last=${profile.lastSeenAt};${tags}${samples ? `
|
|
563872
563947
|
${samples}` : ""}`;
|
|
563873
563948
|
});
|
|
563949
|
+
const topTopics = Object.entries(doc.topicCounts ?? {}).sort((a2, b) => b[1] - a2[1]).slice(0, 18).map(([topic, count]) => `- ${topic}: ${count}`);
|
|
563950
|
+
const durable = doc.durableObservations.slice(-18).map((line) => `- ${line}`);
|
|
563951
|
+
const relationships = doc.relationshipEvents.slice(-10).map((line) => `- ${line}`);
|
|
563874
563952
|
return [
|
|
563875
563953
|
`# Scoped Personality Profile`,
|
|
563876
563954
|
``,
|
|
@@ -563887,6 +563965,11 @@ ${samples}` : ""}`;
|
|
|
563887
563965
|
`## Relationship Model`,
|
|
563888
563966
|
doc.relationshipModel.map((line) => `- ${line}`).join("\n"),
|
|
563889
563967
|
``,
|
|
563968
|
+
`## Durable Profile Memory`,
|
|
563969
|
+
topTopics.length ? [`Top recurring topics:`, ...topTopics].join("\n") : "Top recurring topics:\n- none",
|
|
563970
|
+
durable.length ? [`Longer-term observations:`, ...durable].join("\n") : "Longer-term observations:\n- none",
|
|
563971
|
+
relationships.length ? [`Relationship events:`, ...relationships].join("\n") : "Relationship events:\n- none",
|
|
563972
|
+
``,
|
|
563890
563973
|
`## Behavioral Guidance`,
|
|
563891
563974
|
doc.behavioralGuidance.map((line) => `- ${line}`).join("\n"),
|
|
563892
563975
|
``,
|
|
@@ -563910,13 +563993,15 @@ function renderScopedPersonalityContext(doc) {
|
|
|
563910
563993
|
function buildScopedPersonalityContext(scope) {
|
|
563911
563994
|
return renderScopedPersonalityContext(loadScopedPersonality(scope));
|
|
563912
563995
|
}
|
|
563913
|
-
var PROFILE_VERSION, MAX_PROFILE_OBSERVATIONS, MAX_PROFILE_SAMPLES, MAX_PROFILE_TAGS, STOPWORDS2;
|
|
563996
|
+
var PROFILE_VERSION, MAX_PROFILE_OBSERVATIONS, MAX_PROFILE_DURABLE_OBSERVATIONS, MAX_PROFILE_RELATIONSHIP_EVENTS, MAX_PROFILE_SAMPLES, MAX_PROFILE_TAGS, STOPWORDS2;
|
|
563914
563997
|
var init_scoped_personality = __esm({
|
|
563915
563998
|
"packages/cli/src/tui/scoped-personality.ts"() {
|
|
563916
563999
|
"use strict";
|
|
563917
564000
|
PROFILE_VERSION = 1;
|
|
563918
|
-
MAX_PROFILE_OBSERVATIONS =
|
|
563919
|
-
|
|
564001
|
+
MAX_PROFILE_OBSERVATIONS = 40;
|
|
564002
|
+
MAX_PROFILE_DURABLE_OBSERVATIONS = 240;
|
|
564003
|
+
MAX_PROFILE_RELATIONSHIP_EVENTS = 120;
|
|
564004
|
+
MAX_PROFILE_SAMPLES = 16;
|
|
563920
564005
|
MAX_PROFILE_TAGS = 12;
|
|
563921
564006
|
STOPWORDS2 = /* @__PURE__ */ new Set([
|
|
563922
564007
|
"about",
|
|
@@ -567727,7 +567812,7 @@ __export(omnius_directory_exports, {
|
|
|
567727
567812
|
writeIndexMeta: () => writeIndexMeta,
|
|
567728
567813
|
writeTaskHandoff: () => writeTaskHandoff2
|
|
567729
567814
|
});
|
|
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";
|
|
567815
|
+
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
567816
|
import { join as join104, relative as relative9, basename as basename17, dirname as dirname29 } from "node:path";
|
|
567732
567817
|
import { homedir as homedir31 } from "node:os";
|
|
567733
567818
|
import { createHash as createHash20 } from "node:crypto";
|
|
@@ -568248,6 +568333,7 @@ function saveSessionContext(repoRoot, entry) {
|
|
|
568248
568333
|
const contextDir = join104(repoRoot, OMNIUS_DIR, "context");
|
|
568249
568334
|
mkdirSync50(contextDir, { recursive: true });
|
|
568250
568335
|
const filePath = join104(contextDir, CONTEXT_SAVE_FILE);
|
|
568336
|
+
const ledgerPath = join104(contextDir, CONTEXT_LEDGER_FILE);
|
|
568251
568337
|
const lockPath = join104(contextDir, CONTEXT_SAVE_FILE + ".lock");
|
|
568252
568338
|
const locked = acquireLock(lockPath);
|
|
568253
568339
|
if (!locked) {
|
|
@@ -568264,7 +568350,23 @@ function saveSessionContext(repoRoot, entry) {
|
|
|
568264
568350
|
} catch {
|
|
568265
568351
|
ctx3 = { entries: [], maxEntries: MAX_CONTEXT_ENTRIES, updatedAt: "" };
|
|
568266
568352
|
}
|
|
568353
|
+
ctx3.maxEntries = Math.max(
|
|
568354
|
+
Number.isFinite(ctx3.maxEntries) ? ctx3.maxEntries : 0,
|
|
568355
|
+
MAX_CONTEXT_ENTRIES
|
|
568356
|
+
);
|
|
568267
568357
|
const normalizedEntry = normalizeSessionContextEntry(entry);
|
|
568358
|
+
try {
|
|
568359
|
+
appendFileSync5(
|
|
568360
|
+
ledgerPath,
|
|
568361
|
+
JSON.stringify({
|
|
568362
|
+
...normalizedEntry,
|
|
568363
|
+
ledgerSavedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
568364
|
+
ledgerVersion: 1
|
|
568365
|
+
}) + "\n",
|
|
568366
|
+
"utf-8"
|
|
568367
|
+
);
|
|
568368
|
+
} catch {
|
|
568369
|
+
}
|
|
568268
568370
|
const hashToIndex = /* @__PURE__ */ new Map();
|
|
568269
568371
|
for (let i2 = 0; i2 < ctx3.entries.length; i2++) {
|
|
568270
568372
|
const existing = ctx3.entries[i2];
|
|
@@ -568316,7 +568418,7 @@ function saveSessionContext(repoRoot, entry) {
|
|
|
568316
568418
|
try {
|
|
568317
568419
|
writeFileSync45(
|
|
568318
568420
|
join104(contextDir, "session-diary.md"),
|
|
568319
|
-
renderSessionDiary(ctx3.entries.slice(-
|
|
568421
|
+
renderSessionDiary(ctx3.entries.slice(-MAX_SESSION_DIARY_ENTRIES)),
|
|
568320
568422
|
"utf-8"
|
|
568321
568423
|
);
|
|
568322
568424
|
} catch {
|
|
@@ -568744,7 +568846,7 @@ function deleteUsageRecord(kind, value2, repoRoot) {
|
|
|
568744
568846
|
remove(join104(repoRoot, OMNIUS_DIR, USAGE_HISTORY_FILE));
|
|
568745
568847
|
}
|
|
568746
568848
|
}
|
|
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;
|
|
568849
|
+
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
568850
|
var init_omnius_directory = __esm({
|
|
568749
568851
|
"packages/cli/src/tui/omnius-directory.ts"() {
|
|
568750
568852
|
"use strict";
|
|
@@ -568764,7 +568866,9 @@ var init_omnius_directory = __esm({
|
|
|
568764
568866
|
PENDING_TASK_FILE = "pending-task.json";
|
|
568765
568867
|
HANDOFF_FILE = "task-handoff.json";
|
|
568766
568868
|
CONTEXT_SAVE_FILE = "session-context.json";
|
|
568767
|
-
|
|
568869
|
+
CONTEXT_LEDGER_FILE = "session-context.events.jsonl";
|
|
568870
|
+
MAX_CONTEXT_ENTRIES = 200;
|
|
568871
|
+
MAX_SESSION_DIARY_ENTRIES = 80;
|
|
568768
568872
|
SAME_TASK_REPLACE_WINDOW_MS = 12 * 60 * 60 * 1e3;
|
|
568769
568873
|
LOCK_TIMEOUT_MS = 5e3;
|
|
568770
568874
|
LOCK_RETRY_MS = 50;
|
|
@@ -572786,6 +572890,106 @@ ${CONTENT_BG_SEQ}`);
|
|
|
572786
572890
|
(seq) => seq.endsWith("m") ? seq : ""
|
|
572787
572891
|
);
|
|
572788
572892
|
}
|
|
572893
|
+
reflowContentLines(livePartialLine, width) {
|
|
572894
|
+
const maxWidth = Math.max(16, width);
|
|
572895
|
+
const source = livePartialLine ? [...this._contentLines, livePartialLine] : this._contentLines;
|
|
572896
|
+
return source.flatMap(
|
|
572897
|
+
(line, idx) => this.reflowContentLine(line, maxWidth).map((segment) => ({
|
|
572898
|
+
line: segment,
|
|
572899
|
+
bufferIdx: idx
|
|
572900
|
+
}))
|
|
572901
|
+
);
|
|
572902
|
+
}
|
|
572903
|
+
reflowContentLine(line, width) {
|
|
572904
|
+
const visible = stripAnsi(line);
|
|
572905
|
+
if (visible.length <= width) return [line];
|
|
572906
|
+
const continuationIndent = this.hangingIndentForVisibleLine(
|
|
572907
|
+
visible,
|
|
572908
|
+
width
|
|
572909
|
+
);
|
|
572910
|
+
const ranges = this.visibleWrapRanges(
|
|
572911
|
+
visible,
|
|
572912
|
+
width,
|
|
572913
|
+
continuationIndent.length
|
|
572914
|
+
);
|
|
572915
|
+
if (ranges.length <= 1) return [line];
|
|
572916
|
+
return ranges.map((range, idx) => {
|
|
572917
|
+
const raw = this.rawSliceForVisibleRange(line, range.start, range.end);
|
|
572918
|
+
return idx === 0 ? raw : continuationIndent + raw.trimStart();
|
|
572919
|
+
});
|
|
572920
|
+
}
|
|
572921
|
+
hangingIndentForVisibleLine(visible, width) {
|
|
572922
|
+
const capped = (n2) => " ".repeat(Math.max(0, Math.min(n2, width - 4)));
|
|
572923
|
+
const patterns = [
|
|
572924
|
+
/^(\s*(?:[│├└]\s*)?(?:[-*+○•]\s+))/,
|
|
572925
|
+
/^(\s*(?:[│├└]\s*)?(?:\d+[.)]\s+))/,
|
|
572926
|
+
/^(\s*(?:[│├└]\s*)?(?:>\s*))/,
|
|
572927
|
+
/^(\s*(?:[│├└]\s*)?(?:[A-Za-z][\w.-]{0,28}:\s+))/
|
|
572928
|
+
];
|
|
572929
|
+
for (const pattern of patterns) {
|
|
572930
|
+
const match = visible.match(pattern);
|
|
572931
|
+
if (match?.[1]) return capped(match[1].length);
|
|
572932
|
+
}
|
|
572933
|
+
return capped(visible.match(/^\s*/)?.[0].length ?? 0);
|
|
572934
|
+
}
|
|
572935
|
+
visibleWrapRanges(visible, width, continuationIndent) {
|
|
572936
|
+
const ranges = [];
|
|
572937
|
+
let start2 = 0;
|
|
572938
|
+
let available = width;
|
|
572939
|
+
while (start2 < visible.length) {
|
|
572940
|
+
if (visible.length - start2 <= available) {
|
|
572941
|
+
ranges.push({ start: start2, end: visible.length });
|
|
572942
|
+
break;
|
|
572943
|
+
}
|
|
572944
|
+
const limit = start2 + available;
|
|
572945
|
+
let breakAt = visible.lastIndexOf(" ", limit);
|
|
572946
|
+
if (breakAt <= start2 + 2) breakAt = limit;
|
|
572947
|
+
let end = breakAt;
|
|
572948
|
+
while (end > start2 && /\s/.test(visible[end - 1] ?? "")) end--;
|
|
572949
|
+
ranges.push({ start: start2, end: Math.max(start2 + 1, end) });
|
|
572950
|
+
start2 = breakAt;
|
|
572951
|
+
while (start2 < visible.length && /\s/.test(visible[start2] ?? "")) start2++;
|
|
572952
|
+
available = Math.max(8, width - continuationIndent);
|
|
572953
|
+
}
|
|
572954
|
+
return ranges;
|
|
572955
|
+
}
|
|
572956
|
+
rawSliceForVisibleRange(line, start2, end) {
|
|
572957
|
+
const startRaw = this.rawIndexForVisibleColumn(line, start2);
|
|
572958
|
+
const endRaw = this.rawIndexForVisibleColumn(line, end);
|
|
572959
|
+
const activeStyle = this.activeSgrAt(line, startRaw);
|
|
572960
|
+
const raw = line.slice(startRaw, endRaw);
|
|
572961
|
+
return activeStyle ? `${activeStyle}${raw}${RESET2}` : raw;
|
|
572962
|
+
}
|
|
572963
|
+
rawIndexForVisibleColumn(line, target) {
|
|
572964
|
+
if (target <= 0) return 0;
|
|
572965
|
+
let visible = 0;
|
|
572966
|
+
for (let i2 = 0; i2 < line.length; i2++) {
|
|
572967
|
+
if (line.charCodeAt(i2) === 27) {
|
|
572968
|
+
const match = line.slice(i2).match(/^\x1B\[[0-?]*[ -/]*[@-~]/);
|
|
572969
|
+
if (match) {
|
|
572970
|
+
i2 += match[0].length - 1;
|
|
572971
|
+
continue;
|
|
572972
|
+
}
|
|
572973
|
+
}
|
|
572974
|
+
if (visible >= target) return i2;
|
|
572975
|
+
visible++;
|
|
572976
|
+
}
|
|
572977
|
+
return line.length;
|
|
572978
|
+
}
|
|
572979
|
+
activeSgrAt(line, rawIndex) {
|
|
572980
|
+
let active = "";
|
|
572981
|
+
const sgr = /\x1B\[[0-9;]*m/g;
|
|
572982
|
+
let match;
|
|
572983
|
+
while ((match = sgr.exec(line)) && match.index < rawIndex) {
|
|
572984
|
+
const seq = match[0];
|
|
572985
|
+
if (seq === RESET2 || /\x1B\[(?:0|39|49)(?:;0)?m/.test(seq)) {
|
|
572986
|
+
active = "";
|
|
572987
|
+
} else {
|
|
572988
|
+
active += seq;
|
|
572989
|
+
}
|
|
572990
|
+
}
|
|
572991
|
+
return active;
|
|
572992
|
+
}
|
|
572789
572993
|
/**
|
|
572790
572994
|
* Remove the last N lines from the content scrollback buffer and repaint.
|
|
572791
572995
|
* Used by Esc-to-recall to erase the just-rendered user prompt.
|
|
@@ -572876,9 +573080,10 @@ ${CONTENT_BG_SEQ}`);
|
|
|
572876
573080
|
repaintContent() {
|
|
572877
573081
|
const h = this.contentHeight;
|
|
572878
573082
|
const livePartialLine = this.getLiveBufferedLine();
|
|
572879
|
-
const totalLines = this._contentLines.length + (livePartialLine ? 1 : 0);
|
|
572880
|
-
const startIdx = Math.max(0, totalLines - h - this._contentScrollOffset);
|
|
572881
573083
|
const w = termCols();
|
|
573084
|
+
const reflowedLines = this.reflowContentLines(livePartialLine, w);
|
|
573085
|
+
const totalLines = reflowedLines.length;
|
|
573086
|
+
const startIdx = Math.max(0, totalLines - h - this._contentScrollOffset);
|
|
572882
573087
|
const headerSafeFloor = layout().headerBottom + 1;
|
|
572883
573088
|
let buf = "\x1B[?2026h";
|
|
572884
573089
|
buf += "\x1B7";
|
|
@@ -572886,16 +573091,12 @@ ${CONTENT_BG_SEQ}`);
|
|
|
572886
573091
|
const selRanges = this._textSelection.getSelectedRanges();
|
|
572887
573092
|
for (let row = 0; row < h; row++) {
|
|
572888
573093
|
const lineIdx = startIdx + row;
|
|
572889
|
-
|
|
572890
|
-
|
|
572891
|
-
line = this._contentLines[lineIdx];
|
|
572892
|
-
} else if (livePartialLine && lineIdx === this._contentLines.length) {
|
|
572893
|
-
line = livePartialLine;
|
|
572894
|
-
}
|
|
573094
|
+
const reflowed = reflowedLines[lineIdx];
|
|
573095
|
+
let line = reflowed?.line ?? "";
|
|
572895
573096
|
const screenRow = this.scrollRegionTop + row;
|
|
572896
573097
|
if (screenRow < headerSafeFloor) continue;
|
|
572897
573098
|
line = line.replace(/\x1B\[0m/g, `\x1B[0m${CONTENT_BG_SEQ}`);
|
|
572898
|
-
const sel = selRanges.find((r2) => r2.bufferIdx ===
|
|
573099
|
+
const sel = selRanges.find((r2) => r2.bufferIdx === reflowed?.bufferIdx);
|
|
572899
573100
|
if (sel) {
|
|
572900
573101
|
line = TextSelection.applyHighlight(line, sel.startCol, sel.endCol);
|
|
572901
573102
|
}
|
|
@@ -575667,7 +575868,7 @@ __export(setup_exports, {
|
|
|
575667
575868
|
import * as readline from "node:readline";
|
|
575668
575869
|
import { execSync as execSync50, spawn as spawn26, exec as exec4 } from "node:child_process";
|
|
575669
575870
|
import { promisify as promisify6 } from "node:util";
|
|
575670
|
-
import { existsSync as existsSync90, writeFileSync as writeFileSync47, readFileSync as readFileSync74, appendFileSync as
|
|
575871
|
+
import { existsSync as existsSync90, writeFileSync as writeFileSync47, readFileSync as readFileSync74, appendFileSync as appendFileSync6, mkdirSync as mkdirSync52 } from "node:fs";
|
|
575671
575872
|
import { join as join107 } from "node:path";
|
|
575672
575873
|
import { homedir as homedir34, platform as platform5 } from "node:os";
|
|
575673
575874
|
function wrapText(value2, width) {
|
|
@@ -578034,7 +578235,7 @@ function ensurePathInShellRc(binDir) {
|
|
|
578034
578235
|
const exportLine = `
|
|
578035
578236
|
export PATH="${binDir}:$PATH" # Added by omnius for nvim
|
|
578036
578237
|
`;
|
|
578037
|
-
|
|
578238
|
+
appendFileSync6(rcFile, exportLine, "utf8");
|
|
578038
578239
|
console.log(` Added ${binDir} to ${rcFile}`);
|
|
578039
578240
|
} catch {
|
|
578040
578241
|
}
|
|
@@ -587604,7 +587805,7 @@ import {
|
|
|
587604
587805
|
lstatSync,
|
|
587605
587806
|
statSync as statSync37,
|
|
587606
587807
|
rmSync as rmSync4,
|
|
587607
|
-
appendFileSync as
|
|
587808
|
+
appendFileSync as appendFileSync7
|
|
587608
587809
|
} from "node:fs";
|
|
587609
587810
|
import { relative as relative11, join as join114 } from "node:path";
|
|
587610
587811
|
async function _immediateReregister(newUrl) {
|
|
@@ -592004,7 +592205,7 @@ sleep 1
|
|
|
592004
592205
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
592005
592206
|
`;
|
|
592006
592207
|
try {
|
|
592007
|
-
|
|
592208
|
+
appendFileSync7(_spLogFile, line);
|
|
592008
592209
|
} catch {
|
|
592009
592210
|
}
|
|
592010
592211
|
};
|
|
@@ -602934,10 +603135,13 @@ var init_stream_renderer = __esm({
|
|
|
602934
603135
|
const words = text.split(/(\s+)/);
|
|
602935
603136
|
const lines = [];
|
|
602936
603137
|
let currentLine = "";
|
|
603138
|
+
const continuationIndent = this.hangingIndentForText(text, maxWidth);
|
|
603139
|
+
let available = maxWidth;
|
|
602937
603140
|
for (const segment of words) {
|
|
602938
|
-
if (currentLine.length + segment.length >
|
|
603141
|
+
if (currentLine.length + segment.length > available && currentLine.trim()) {
|
|
602939
603142
|
lines.push(currentLine.trimEnd());
|
|
602940
|
-
currentLine = segment.replace(/^\s+/, "");
|
|
603143
|
+
currentLine = continuationIndent + segment.replace(/^\s+/, "");
|
|
603144
|
+
available = maxWidth;
|
|
602941
603145
|
} else {
|
|
602942
603146
|
currentLine += segment;
|
|
602943
603147
|
}
|
|
@@ -602947,12 +603151,26 @@ var init_stream_renderer = __esm({
|
|
|
602947
603151
|
}
|
|
602948
603152
|
return lines.length > 0 ? lines : [text];
|
|
602949
603153
|
}
|
|
603154
|
+
hangingIndentForText(text, maxWidth) {
|
|
603155
|
+
const capped = (n2) => " ".repeat(Math.max(0, Math.min(n2, maxWidth - 4)));
|
|
603156
|
+
const patterns = [
|
|
603157
|
+
/^(\s*(?:[-*+○•]\s+))/,
|
|
603158
|
+
/^(\s*(?:\d+[.)]\s+))/,
|
|
603159
|
+
/^(\s*(?:>\s*))/,
|
|
603160
|
+
/^(\s*(?:[A-Za-z][\w.-]{0,28}:\s+))/
|
|
603161
|
+
];
|
|
603162
|
+
for (const pattern of patterns) {
|
|
603163
|
+
const match = text.match(pattern);
|
|
603164
|
+
if (match?.[1]) return capped(match[1].length);
|
|
603165
|
+
}
|
|
603166
|
+
return capped(text.match(/^\s*/)?.[0].length ?? 0);
|
|
603167
|
+
}
|
|
602950
603168
|
};
|
|
602951
603169
|
}
|
|
602952
603170
|
});
|
|
602953
603171
|
|
|
602954
603172
|
// packages/cli/src/tui/edit-history.ts
|
|
602955
|
-
import { appendFileSync as
|
|
603173
|
+
import { appendFileSync as appendFileSync8, mkdirSync as mkdirSync60 } from "node:fs";
|
|
602956
603174
|
import { join as join119 } from "node:path";
|
|
602957
603175
|
function createEditHistoryLogger(repoRoot, sessionId) {
|
|
602958
603176
|
const historyDir = join119(repoRoot, ".omnius", "history");
|
|
@@ -602973,7 +603191,7 @@ function createEditHistoryLogger(repoRoot, sessionId) {
|
|
|
602973
603191
|
args: sanitizeArgs(toolName, toolArgs)
|
|
602974
603192
|
};
|
|
602975
603193
|
try {
|
|
602976
|
-
|
|
603194
|
+
appendFileSync8(logPath3, JSON.stringify(entry) + "\n", "utf-8");
|
|
602977
603195
|
} catch {
|
|
602978
603196
|
}
|
|
602979
603197
|
}
|
|
@@ -608356,6 +608574,9 @@ function upsertTelegramReflectionMessage(repoRoot, sessionKey, entry, options2)
|
|
|
608356
608574
|
content,
|
|
608357
608575
|
labels: [entry.mode, entry.mediaSummary, senderLabel(entry)].filter((value2) => Boolean(value2))
|
|
608358
608576
|
});
|
|
608577
|
+
if (typeof entry.ts === "number" && Number.isFinite(entry.ts) && result.episodeId) {
|
|
608578
|
+
store2.getDb().prepare("UPDATE episodes SET timestamp = ? WHERE id = ?").run(entry.ts, result.episodeId);
|
|
608579
|
+
}
|
|
608359
608580
|
addTextEdges(graph, result, entry);
|
|
608360
608581
|
const after = store2.count(sessionKey);
|
|
608361
608582
|
return { episodeId: result.episodeId, reused: after === before };
|
|
@@ -608418,6 +608639,9 @@ async function buildTelegramReflectionCorpus(options2) {
|
|
|
608418
608639
|
content,
|
|
608419
608640
|
labels: [entry.mode, entry.mediaSummary, senderLabel(entry)].filter((value2) => Boolean(value2))
|
|
608420
608641
|
});
|
|
608642
|
+
if (typeof entry.ts === "number" && Number.isFinite(entry.ts) && result.episodeId) {
|
|
608643
|
+
store2.getDb().prepare("UPDATE episodes SET timestamp = ? WHERE id = ?").run(entry.ts, result.episodeId);
|
|
608644
|
+
}
|
|
608421
608645
|
addTextEdges(graph, result, entry);
|
|
608422
608646
|
const after = store2.count(options2.sessionKey);
|
|
608423
608647
|
if (after === before) reusedEpisodes++;
|
|
@@ -609061,7 +609285,7 @@ var init_vision_ingress = __esm({
|
|
|
609061
609285
|
});
|
|
609062
609286
|
|
|
609063
609287
|
// 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";
|
|
609288
|
+
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
609289
|
import { join as join127, resolve as resolve43, basename as basename27, relative as relative13, isAbsolute as isAbsolute8, extname as extname16 } from "node:path";
|
|
609066
609290
|
import { writeFile as writeFileAsync } from "node:fs/promises";
|
|
609067
609291
|
import { createHash as createHash23, randomBytes as randomBytes22, randomInt } from "node:crypto";
|
|
@@ -610277,7 +610501,7 @@ function renderTelegramSubAgentError(username, error) {
|
|
|
610277
610501
|
process.stdout.write(` ${c3.dim("│")} ${c3.red("✘")} @${username}: ${c3.dim(preview)}
|
|
610278
610502
|
`);
|
|
610279
610503
|
}
|
|
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;
|
|
610504
|
+
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
610505
|
var init_telegram_bridge = __esm({
|
|
610282
610506
|
"packages/cli/src/tui/telegram-bridge.ts"() {
|
|
610283
610507
|
"use strict";
|
|
@@ -610296,7 +610520,9 @@ var init_telegram_bridge = __esm({
|
|
|
610296
610520
|
init_visual_identity_association();
|
|
610297
610521
|
init_telegram_channel_dmn();
|
|
610298
610522
|
init_telegram_reflection_corpus();
|
|
610523
|
+
init_memory_paths();
|
|
610299
610524
|
init_telegram_reflection_extraction();
|
|
610525
|
+
init_dist7();
|
|
610300
610526
|
TELEGRAM_TOOL_ACTION_GROUPS = [
|
|
610301
610527
|
"read",
|
|
610302
610528
|
"message",
|
|
@@ -610458,6 +610684,7 @@ PUBLIC TELEGRAM MEMORY SCOPE
|
|
|
610458
610684
|
|
|
610459
610685
|
This turn may use memory and conversation history for the current Telegram group/private chat scope only.
|
|
610460
610686
|
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.
|
|
610687
|
+
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
610688
|
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
610689
|
`.trim();
|
|
610463
610690
|
TELEGRAM_PUBLIC_VISION_STACK_CONTRACT = `
|
|
@@ -610538,12 +610765,16 @@ External acquisition contract:
|
|
|
610538
610765
|
/^hmm,?\s+(let me|maybe|wait)\b/i,
|
|
610539
610766
|
/^ok(ay)?,?\s+let me try\b/i
|
|
610540
610767
|
];
|
|
610541
|
-
TELEGRAM_CHAT_HISTORY_LIMIT =
|
|
610768
|
+
TELEGRAM_CHAT_HISTORY_LIMIT = 5e3;
|
|
610542
610769
|
TELEGRAM_CONTEXT_RECENT_DEFAULT = 36;
|
|
610543
610770
|
TELEGRAM_CONTEXT_LINE_LIMIT = 320;
|
|
610544
610771
|
TELEGRAM_CONTEXT_SAMPLE_LIMIT = 10;
|
|
610545
|
-
TELEGRAM_MEMORY_CARD_LIMIT =
|
|
610546
|
-
TELEGRAM_MEMORY_NOTE_LIMIT =
|
|
610772
|
+
TELEGRAM_MEMORY_CARD_LIMIT = 240;
|
|
610773
|
+
TELEGRAM_MEMORY_NOTE_LIMIT = 24;
|
|
610774
|
+
TELEGRAM_ASSOCIATIVE_FACT_LIMIT = 600;
|
|
610775
|
+
TELEGRAM_ASSOCIATIVE_USER_FACT_LIMIT = 80;
|
|
610776
|
+
TELEGRAM_ASSOCIATIVE_ACTION_LIMIT = 5e3;
|
|
610777
|
+
TELEGRAM_ASSOCIATIVE_RELATION_LIMIT = 300;
|
|
610547
610778
|
TELEGRAM_MEMORY_STOPWORDS = /* @__PURE__ */ new Set([
|
|
610548
610779
|
"about",
|
|
610549
610780
|
"after",
|
|
@@ -610627,6 +610858,7 @@ External acquisition contract:
|
|
|
610627
610858
|
this.telegramToolPolicy = resolveSettings(repoRoot || ".").telegramToolPolicy ?? {};
|
|
610628
610859
|
this.mediaCacheDir = resolve43(repoRoot || ".", ".omnius", "telegram-media-cache");
|
|
610629
610860
|
this.telegramConversationDir = resolve43(repoRoot || ".", ".omnius", "telegram-conversations");
|
|
610861
|
+
this.telegramSqlitePath = resolve43(repoRoot || ".", ".omnius", "telegram.sqlite");
|
|
610630
610862
|
this.telegramToolButtonDir = resolve43(repoRoot || ".", ".omnius", "telegram-tool-buttons");
|
|
610631
610863
|
}
|
|
610632
610864
|
botToken;
|
|
@@ -610658,6 +610890,8 @@ External acquisition contract:
|
|
|
610658
610890
|
chatParticipants = /* @__PURE__ */ new Map();
|
|
610659
610891
|
/** Lightweight Zettelkasten-style memory cards by chat/guest session key */
|
|
610660
610892
|
chatMemoryCards = /* @__PURE__ */ new Map();
|
|
610893
|
+
/** Durable associative memory by scoped Telegram chat key. */
|
|
610894
|
+
chatAssociativeMemory = /* @__PURE__ */ new Map();
|
|
610661
610895
|
/** Generic chronological attention cadence shared by live surfaces. */
|
|
610662
610896
|
stimulation = new StimulationController();
|
|
610663
610897
|
/** Throttles noisy "skipped group chatter" waterfall logs */
|
|
@@ -610707,6 +610941,9 @@ External acquisition contract:
|
|
|
610707
610941
|
mediaCacheDir;
|
|
610708
610942
|
/** Persistent conversation memory directory */
|
|
610709
610943
|
telegramConversationDir;
|
|
610944
|
+
/** Durable SQLite mirror for raw Telegram messages and metadata. */
|
|
610945
|
+
telegramSqlitePath;
|
|
610946
|
+
telegramSqliteDb = null;
|
|
610710
610947
|
/** Session keys loaded from persistent conversation memory */
|
|
610711
610948
|
loadedConversationState = /* @__PURE__ */ new Set();
|
|
610712
610949
|
/** True once persisted Telegram conversation scopes have been bulk-loaded. */
|
|
@@ -610993,12 +611230,26 @@ No scoped reflection artifact exists yet for this chat. Use <code>/reflect</code
|
|
|
610993
611230
|
}
|
|
610994
611231
|
recordChatHistory(sessionKey, entry) {
|
|
610995
611232
|
this.ensureTelegramConversationLoaded(sessionKey);
|
|
611233
|
+
const stamped = { ...entry, ts: entry.ts ?? Date.now() };
|
|
610996
611234
|
const existing = this.chatHistory.get(sessionKey) ?? [];
|
|
610997
|
-
existing.push(
|
|
611235
|
+
existing.push(stamped);
|
|
610998
611236
|
if (existing.length > TELEGRAM_CHAT_HISTORY_LIMIT) {
|
|
610999
611237
|
existing.splice(0, existing.length - TELEGRAM_CHAT_HISTORY_LIMIT);
|
|
611000
611238
|
}
|
|
611001
611239
|
this.chatHistory.set(sessionKey, existing);
|
|
611240
|
+
this.appendTelegramConversationLedger(sessionKey, stamped);
|
|
611241
|
+
}
|
|
611242
|
+
appendTelegramConversationLedger(sessionKey, entry) {
|
|
611243
|
+
if (!this.repoRoot) return;
|
|
611244
|
+
try {
|
|
611245
|
+
mkdirSync65(this.telegramConversationDir, { recursive: true });
|
|
611246
|
+
appendFileSync9(
|
|
611247
|
+
this.telegramConversationLedgerPath(sessionKey),
|
|
611248
|
+
JSON.stringify({ sessionKey, ...entry }) + "\n",
|
|
611249
|
+
"utf8"
|
|
611250
|
+
);
|
|
611251
|
+
} catch {
|
|
611252
|
+
}
|
|
611002
611253
|
}
|
|
611003
611254
|
telegramReplySenderWithSelfFlag(sender) {
|
|
611004
611255
|
if (!sender) return void 0;
|
|
@@ -611226,6 +611477,272 @@ ${mediaContext}` : ""
|
|
|
611226
611477
|
const safe = createHash23("sha1").update(sessionKey).digest("hex").slice(0, 20);
|
|
611227
611478
|
return join127(this.telegramConversationDir, `${safe}.json`);
|
|
611228
611479
|
}
|
|
611480
|
+
telegramConversationLedgerPath(sessionKey) {
|
|
611481
|
+
const safe = createHash23("sha1").update(sessionKey).digest("hex").slice(0, 20);
|
|
611482
|
+
return join127(this.telegramConversationDir, `${safe}.events.jsonl`);
|
|
611483
|
+
}
|
|
611484
|
+
telegramDb() {
|
|
611485
|
+
if (this.telegramSqliteDb === false) return null;
|
|
611486
|
+
if (this.telegramSqliteDb) return this.telegramSqliteDb;
|
|
611487
|
+
if (!this.repoRoot) {
|
|
611488
|
+
this.telegramSqliteDb = false;
|
|
611489
|
+
return null;
|
|
611490
|
+
}
|
|
611491
|
+
try {
|
|
611492
|
+
mkdirSync65(resolve43(this.repoRoot, ".omnius"), { recursive: true });
|
|
611493
|
+
const db = initDb(this.telegramSqlitePath);
|
|
611494
|
+
db.exec(`
|
|
611495
|
+
CREATE TABLE IF NOT EXISTS telegram_messages (
|
|
611496
|
+
session_key TEXT NOT NULL,
|
|
611497
|
+
chat_id TEXT NOT NULL,
|
|
611498
|
+
chat_type TEXT,
|
|
611499
|
+
chat_title TEXT,
|
|
611500
|
+
message_id INTEGER NOT NULL,
|
|
611501
|
+
message_thread_id INTEGER,
|
|
611502
|
+
update_id INTEGER,
|
|
611503
|
+
telegram_date INTEGER,
|
|
611504
|
+
received_at INTEGER NOT NULL,
|
|
611505
|
+
role TEXT NOT NULL DEFAULT 'user',
|
|
611506
|
+
from_user_id INTEGER,
|
|
611507
|
+
username TEXT,
|
|
611508
|
+
first_name TEXT,
|
|
611509
|
+
text TEXT,
|
|
611510
|
+
raw_json TEXT NOT NULL,
|
|
611511
|
+
normalized_json TEXT NOT NULL,
|
|
611512
|
+
reply_to_message_id INTEGER,
|
|
611513
|
+
reply_to_username TEXT,
|
|
611514
|
+
media_json TEXT,
|
|
611515
|
+
PRIMARY KEY (chat_id, message_id, role)
|
|
611516
|
+
);
|
|
611517
|
+
CREATE INDEX IF NOT EXISTS idx_telegram_messages_session_time ON telegram_messages(session_key, received_at);
|
|
611518
|
+
CREATE INDEX IF NOT EXISTS idx_telegram_messages_user_time ON telegram_messages(session_key, from_user_id, received_at);
|
|
611519
|
+
CREATE INDEX IF NOT EXISTS idx_telegram_messages_username_time ON telegram_messages(session_key, username, received_at);
|
|
611520
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS telegram_messages_fts USING fts5(
|
|
611521
|
+
session_key UNINDEXED,
|
|
611522
|
+
chat_id UNINDEXED,
|
|
611523
|
+
message_id UNINDEXED,
|
|
611524
|
+
username,
|
|
611525
|
+
first_name,
|
|
611526
|
+
text,
|
|
611527
|
+
content='telegram_messages',
|
|
611528
|
+
content_rowid='rowid'
|
|
611529
|
+
);
|
|
611530
|
+
CREATE TRIGGER IF NOT EXISTS telegram_messages_ai AFTER INSERT ON telegram_messages BEGIN
|
|
611531
|
+
INSERT INTO telegram_messages_fts(rowid, session_key, chat_id, message_id, username, first_name, text)
|
|
611532
|
+
VALUES (new.rowid, new.session_key, new.chat_id, new.message_id, new.username, new.first_name, new.text);
|
|
611533
|
+
END;
|
|
611534
|
+
CREATE TRIGGER IF NOT EXISTS telegram_messages_ad AFTER DELETE ON telegram_messages BEGIN
|
|
611535
|
+
INSERT INTO telegram_messages_fts(telegram_messages_fts, rowid, session_key, chat_id, message_id, username, first_name, text)
|
|
611536
|
+
VALUES('delete', old.rowid, old.session_key, old.chat_id, old.message_id, old.username, old.first_name, old.text);
|
|
611537
|
+
END;
|
|
611538
|
+
CREATE TRIGGER IF NOT EXISTS telegram_messages_au AFTER UPDATE ON telegram_messages BEGIN
|
|
611539
|
+
INSERT INTO telegram_messages_fts(telegram_messages_fts, rowid, session_key, chat_id, message_id, username, first_name, text)
|
|
611540
|
+
VALUES('delete', old.rowid, old.session_key, old.chat_id, old.message_id, old.username, old.first_name, old.text);
|
|
611541
|
+
INSERT INTO telegram_messages_fts(rowid, session_key, chat_id, message_id, username, first_name, text)
|
|
611542
|
+
VALUES (new.rowid, new.session_key, new.chat_id, new.message_id, new.username, new.first_name, new.text);
|
|
611543
|
+
END;
|
|
611544
|
+
`);
|
|
611545
|
+
this.telegramSqliteDb = db;
|
|
611546
|
+
return db;
|
|
611547
|
+
} catch {
|
|
611548
|
+
this.telegramSqliteDb = false;
|
|
611549
|
+
return null;
|
|
611550
|
+
}
|
|
611551
|
+
}
|
|
611552
|
+
persistTelegramRawMessage(update2, msg) {
|
|
611553
|
+
const db = this.telegramDb();
|
|
611554
|
+
if (!db) return;
|
|
611555
|
+
const rawMessage = update2.message ?? update2.guest_message ?? update2.edited_message ?? update2.channel_post ?? {};
|
|
611556
|
+
const sessionKey = this.sessionKeyForMessage(msg);
|
|
611557
|
+
const media = msg.media || msg.replyToMedia || msg.livePhoto;
|
|
611558
|
+
try {
|
|
611559
|
+
db.prepare(`
|
|
611560
|
+
INSERT INTO telegram_messages (
|
|
611561
|
+
session_key, chat_id, chat_type, chat_title, message_id, message_thread_id,
|
|
611562
|
+
update_id, telegram_date, received_at, role, from_user_id, username, first_name,
|
|
611563
|
+
text, raw_json, normalized_json, reply_to_message_id, reply_to_username, media_json
|
|
611564
|
+
) VALUES (
|
|
611565
|
+
@session_key, @chat_id, @chat_type, @chat_title, @message_id, @message_thread_id,
|
|
611566
|
+
@update_id, @telegram_date, @received_at, 'user', @from_user_id, @username, @first_name,
|
|
611567
|
+
@text, @raw_json, @normalized_json, @reply_to_message_id, @reply_to_username, @media_json
|
|
611568
|
+
)
|
|
611569
|
+
ON CONFLICT(chat_id, message_id, role) DO UPDATE SET
|
|
611570
|
+
session_key=excluded.session_key,
|
|
611571
|
+
chat_type=excluded.chat_type,
|
|
611572
|
+
chat_title=excluded.chat_title,
|
|
611573
|
+
message_thread_id=excluded.message_thread_id,
|
|
611574
|
+
update_id=excluded.update_id,
|
|
611575
|
+
telegram_date=excluded.telegram_date,
|
|
611576
|
+
received_at=excluded.received_at,
|
|
611577
|
+
from_user_id=excluded.from_user_id,
|
|
611578
|
+
username=excluded.username,
|
|
611579
|
+
first_name=excluded.first_name,
|
|
611580
|
+
text=excluded.text,
|
|
611581
|
+
raw_json=excluded.raw_json,
|
|
611582
|
+
normalized_json=excluded.normalized_json,
|
|
611583
|
+
reply_to_message_id=excluded.reply_to_message_id,
|
|
611584
|
+
reply_to_username=excluded.reply_to_username,
|
|
611585
|
+
media_json=excluded.media_json
|
|
611586
|
+
`).run({
|
|
611587
|
+
session_key: sessionKey,
|
|
611588
|
+
chat_id: String(msg.chatId),
|
|
611589
|
+
chat_type: msg.chatType,
|
|
611590
|
+
chat_title: msg.chatTitle ?? null,
|
|
611591
|
+
message_id: msg.messageId,
|
|
611592
|
+
message_thread_id: msg.messageThreadId ?? null,
|
|
611593
|
+
update_id: typeof update2.update_id === "number" ? update2.update_id : null,
|
|
611594
|
+
telegram_date: typeof rawMessage.date === "number" ? rawMessage.date : null,
|
|
611595
|
+
received_at: Date.now(),
|
|
611596
|
+
from_user_id: msg.fromUserId ?? null,
|
|
611597
|
+
username: msg.username || null,
|
|
611598
|
+
first_name: msg.firstName || null,
|
|
611599
|
+
text: msg.text || "",
|
|
611600
|
+
raw_json: JSON.stringify(rawMessage),
|
|
611601
|
+
normalized_json: JSON.stringify(msg),
|
|
611602
|
+
reply_to_message_id: msg.replyToMessageId ?? null,
|
|
611603
|
+
reply_to_username: msg.replyToUsername || null,
|
|
611604
|
+
media_json: media ? JSON.stringify(media) : null
|
|
611605
|
+
});
|
|
611606
|
+
} catch {
|
|
611607
|
+
}
|
|
611608
|
+
}
|
|
611609
|
+
persistTelegramAssistantMessage(msg, text, messageId, replyToMessageId) {
|
|
611610
|
+
if (!messageId) return;
|
|
611611
|
+
const db = this.telegramDb();
|
|
611612
|
+
if (!db) return;
|
|
611613
|
+
const sessionKey = this.sessionKeyForMessage(msg);
|
|
611614
|
+
const now = Date.now();
|
|
611615
|
+
const normalized = {
|
|
611616
|
+
role: "assistant",
|
|
611617
|
+
chatId: msg.chatId,
|
|
611618
|
+
chatType: msg.chatType,
|
|
611619
|
+
chatTitle: msg.chatTitle,
|
|
611620
|
+
messageId,
|
|
611621
|
+
messageThreadId: msg.messageThreadId,
|
|
611622
|
+
replyToMessageId,
|
|
611623
|
+
username: this.state.botUsername || "omnius",
|
|
611624
|
+
text
|
|
611625
|
+
};
|
|
611626
|
+
try {
|
|
611627
|
+
db.prepare(`
|
|
611628
|
+
INSERT INTO telegram_messages (
|
|
611629
|
+
session_key, chat_id, chat_type, chat_title, message_id, message_thread_id,
|
|
611630
|
+
update_id, telegram_date, received_at, role, from_user_id, username, first_name,
|
|
611631
|
+
text, raw_json, normalized_json, reply_to_message_id, reply_to_username, media_json
|
|
611632
|
+
) VALUES (
|
|
611633
|
+
@session_key, @chat_id, @chat_type, @chat_title, @message_id, @message_thread_id,
|
|
611634
|
+
NULL, NULL, @received_at, 'assistant', NULL, @username, 'Omnius',
|
|
611635
|
+
@text, @raw_json, @normalized_json, @reply_to_message_id, NULL, NULL
|
|
611636
|
+
)
|
|
611637
|
+
ON CONFLICT(chat_id, message_id, role) DO UPDATE SET
|
|
611638
|
+
session_key=excluded.session_key,
|
|
611639
|
+
chat_type=excluded.chat_type,
|
|
611640
|
+
chat_title=excluded.chat_title,
|
|
611641
|
+
message_thread_id=excluded.message_thread_id,
|
|
611642
|
+
received_at=excluded.received_at,
|
|
611643
|
+
username=excluded.username,
|
|
611644
|
+
text=excluded.text,
|
|
611645
|
+
raw_json=excluded.raw_json,
|
|
611646
|
+
normalized_json=excluded.normalized_json,
|
|
611647
|
+
reply_to_message_id=excluded.reply_to_message_id
|
|
611648
|
+
`).run({
|
|
611649
|
+
session_key: sessionKey,
|
|
611650
|
+
chat_id: String(msg.chatId),
|
|
611651
|
+
chat_type: msg.chatType,
|
|
611652
|
+
chat_title: msg.chatTitle ?? null,
|
|
611653
|
+
message_id: messageId,
|
|
611654
|
+
message_thread_id: msg.messageThreadId ?? null,
|
|
611655
|
+
received_at: now,
|
|
611656
|
+
username: this.state.botUsername || "omnius",
|
|
611657
|
+
text,
|
|
611658
|
+
raw_json: JSON.stringify(normalized),
|
|
611659
|
+
normalized_json: JSON.stringify(normalized),
|
|
611660
|
+
reply_to_message_id: replyToMessageId ?? null
|
|
611661
|
+
});
|
|
611662
|
+
} catch {
|
|
611663
|
+
}
|
|
611664
|
+
}
|
|
611665
|
+
createTelegramAssociativeMemory() {
|
|
611666
|
+
const now = Date.now();
|
|
611667
|
+
return {
|
|
611668
|
+
version: 1,
|
|
611669
|
+
createdAt: now,
|
|
611670
|
+
updatedAt: now,
|
|
611671
|
+
facts: [],
|
|
611672
|
+
users: {},
|
|
611673
|
+
relationships: [],
|
|
611674
|
+
actions: []
|
|
611675
|
+
};
|
|
611676
|
+
}
|
|
611677
|
+
normalizeTelegramAssociativeMemory(raw) {
|
|
611678
|
+
const created = this.createTelegramAssociativeMemory();
|
|
611679
|
+
const users = {};
|
|
611680
|
+
const rawUsers = raw.users && typeof raw.users === "object" ? raw.users : {};
|
|
611681
|
+
for (const [key, user] of Object.entries(rawUsers)) {
|
|
611682
|
+
if (!user || typeof user !== "object") continue;
|
|
611683
|
+
users[key] = {
|
|
611684
|
+
username: String(user.username || "unknown"),
|
|
611685
|
+
displayName: user.displayName,
|
|
611686
|
+
userId: typeof user.userId === "number" ? user.userId : void 0,
|
|
611687
|
+
aliases: Array.isArray(user.aliases) ? user.aliases.map(String).slice(0, 20) : [],
|
|
611688
|
+
firstSeenTs: typeof user.firstSeenTs === "number" ? user.firstSeenTs : created.createdAt,
|
|
611689
|
+
lastSeenTs: typeof user.lastSeenTs === "number" ? user.lastSeenTs : created.updatedAt,
|
|
611690
|
+
messageCount: typeof user.messageCount === "number" ? user.messageCount : 0,
|
|
611691
|
+
directAddressCount: typeof user.directAddressCount === "number" ? user.directAddressCount : 0,
|
|
611692
|
+
replyCount: typeof user.replyCount === "number" ? user.replyCount : 0,
|
|
611693
|
+
toneTags: Array.isArray(user.toneTags) ? user.toneTags.map(String).slice(0, 20) : [],
|
|
611694
|
+
facts: Array.isArray(user.facts) ? user.facts.slice(0, TELEGRAM_ASSOCIATIVE_USER_FACT_LIMIT).map((fact) => this.normalizeTelegramAssociativeFact(fact)) : [],
|
|
611695
|
+
relationshipHints: Array.isArray(user.relationshipHints) ? user.relationshipHints.map(String).slice(0, 80) : [],
|
|
611696
|
+
recentTopics: Array.isArray(user.recentTopics) ? user.recentTopics.map(String).slice(0, 80) : [],
|
|
611697
|
+
lastMessages: Array.isArray(user.lastMessages) ? user.lastMessages.map(String).slice(-40) : []
|
|
611698
|
+
};
|
|
611699
|
+
}
|
|
611700
|
+
return {
|
|
611701
|
+
version: typeof raw.version === "number" ? raw.version : 1,
|
|
611702
|
+
createdAt: typeof raw.createdAt === "number" ? raw.createdAt : created.createdAt,
|
|
611703
|
+
updatedAt: typeof raw.updatedAt === "number" ? raw.updatedAt : created.updatedAt,
|
|
611704
|
+
facts: Array.isArray(raw.facts) ? raw.facts.slice(0, TELEGRAM_ASSOCIATIVE_FACT_LIMIT).map((fact) => this.normalizeTelegramAssociativeFact(fact)) : [],
|
|
611705
|
+
users,
|
|
611706
|
+
relationships: Array.isArray(raw.relationships) ? raw.relationships.slice(0, TELEGRAM_ASSOCIATIVE_RELATION_LIMIT).map((fact) => this.normalizeTelegramAssociativeFact(fact)) : [],
|
|
611707
|
+
actions: Array.isArray(raw.actions) ? raw.actions.slice(-TELEGRAM_ASSOCIATIVE_ACTION_LIMIT).map((action) => ({
|
|
611708
|
+
id: String(action.id || createHash23("sha1").update(JSON.stringify(action)).digest("hex").slice(0, 12)),
|
|
611709
|
+
ts: typeof action.ts === "number" ? action.ts : Date.now(),
|
|
611710
|
+
role: action.role === "assistant" ? "assistant" : "user",
|
|
611711
|
+
speaker: String(action.speaker || "unknown"),
|
|
611712
|
+
mode: action.mode,
|
|
611713
|
+
text: String(action.text || ""),
|
|
611714
|
+
messageId: typeof action.messageId === "number" ? action.messageId : void 0,
|
|
611715
|
+
replyToMessageId: typeof action.replyToMessageId === "number" ? action.replyToMessageId : void 0,
|
|
611716
|
+
userId: typeof action.userId === "number" ? action.userId : void 0,
|
|
611717
|
+
username: typeof action.username === "string" ? action.username : void 0
|
|
611718
|
+
})) : []
|
|
611719
|
+
};
|
|
611720
|
+
}
|
|
611721
|
+
normalizeTelegramAssociativeFact(raw) {
|
|
611722
|
+
const text = String(raw.text || "").trim();
|
|
611723
|
+
const now = Date.now();
|
|
611724
|
+
return {
|
|
611725
|
+
id: String(raw.id || createHash23("sha1").update(text || String(now)).digest("hex").slice(0, 12)),
|
|
611726
|
+
text,
|
|
611727
|
+
tags: Array.isArray(raw.tags) ? raw.tags.map(String).slice(0, 16) : [],
|
|
611728
|
+
speakers: Array.isArray(raw.speakers) ? raw.speakers.map(String).slice(0, 16) : [],
|
|
611729
|
+
userIds: Array.isArray(raw.userIds) ? raw.userIds.filter((id) => typeof id === "number").slice(0, 32) : [],
|
|
611730
|
+
usernames: Array.isArray(raw.usernames) ? raw.usernames.map(String).slice(0, 32) : [],
|
|
611731
|
+
messageIds: Array.isArray(raw.messageIds) ? raw.messageIds.filter((id) => typeof id === "number").slice(0, 80) : [],
|
|
611732
|
+
createdAt: typeof raw.createdAt === "number" ? raw.createdAt : now,
|
|
611733
|
+
updatedAt: typeof raw.updatedAt === "number" ? raw.updatedAt : now,
|
|
611734
|
+
weight: typeof raw.weight === "number" ? raw.weight : 1
|
|
611735
|
+
};
|
|
611736
|
+
}
|
|
611737
|
+
telegramAssociativeMemoryForSession(sessionKey) {
|
|
611738
|
+
this.ensureTelegramConversationLoaded(sessionKey);
|
|
611739
|
+
let memory = this.chatAssociativeMemory.get(sessionKey);
|
|
611740
|
+
if (!memory) {
|
|
611741
|
+
memory = this.createTelegramAssociativeMemory();
|
|
611742
|
+
this.chatAssociativeMemory.set(sessionKey, memory);
|
|
611743
|
+
}
|
|
611744
|
+
return memory;
|
|
611745
|
+
}
|
|
611229
611746
|
telegramPersonalityScope(sessionKey, msg) {
|
|
611230
611747
|
const label = msg.chatType !== "private" ? `${msg.chatTitle || msg.chatType}-${String(msg.chatId)}` : `private-${msg.username || msg.fromUserId || msg.chatId}`;
|
|
611231
611748
|
return {
|
|
@@ -611242,8 +611759,9 @@ ${mediaContext}` : ""
|
|
|
611242
611759
|
if (!existsSync112(path12)) return;
|
|
611243
611760
|
try {
|
|
611244
611761
|
const parsed = JSON.parse(readFileSync92(path12, "utf8"));
|
|
611762
|
+
const loadedHistory = Array.isArray(parsed.history) ? parsed.history : [];
|
|
611245
611763
|
if (Array.isArray(parsed.history)) {
|
|
611246
|
-
this.chatHistory.set(sessionKey,
|
|
611764
|
+
this.chatHistory.set(sessionKey, loadedHistory.slice(-TELEGRAM_CHAT_HISTORY_LIMIT));
|
|
611247
611765
|
}
|
|
611248
611766
|
if (Array.isArray(parsed.participants)) {
|
|
611249
611767
|
const participants = /* @__PURE__ */ new Map();
|
|
@@ -611260,6 +611778,17 @@ ${mediaContext}` : ""
|
|
|
611260
611778
|
if (Array.isArray(parsed.memoryCards)) {
|
|
611261
611779
|
this.chatMemoryCards.set(sessionKey, parsed.memoryCards.slice(0, TELEGRAM_MEMORY_CARD_LIMIT));
|
|
611262
611780
|
}
|
|
611781
|
+
if (parsed.associativeMemory) {
|
|
611782
|
+
this.chatAssociativeMemory.set(
|
|
611783
|
+
sessionKey,
|
|
611784
|
+
this.normalizeTelegramAssociativeMemory(parsed.associativeMemory)
|
|
611785
|
+
);
|
|
611786
|
+
} else if (loadedHistory.length > 0) {
|
|
611787
|
+
this.hydrateTelegramAssociativeMemoryFromHistory(sessionKey, loadedHistory);
|
|
611788
|
+
}
|
|
611789
|
+
if (loadedHistory.length > 0) {
|
|
611790
|
+
this.backfillTelegramLoadedHistory(sessionKey, loadedHistory);
|
|
611791
|
+
}
|
|
611263
611792
|
if (parsed.stimulation) {
|
|
611264
611793
|
this.stimulation.setState(sessionKey, parsed.stimulation);
|
|
611265
611794
|
}
|
|
@@ -611273,6 +611802,108 @@ ${mediaContext}` : ""
|
|
|
611273
611802
|
} catch {
|
|
611274
611803
|
}
|
|
611275
611804
|
}
|
|
611805
|
+
hydrateTelegramAssociativeMemoryFromHistory(sessionKey, history) {
|
|
611806
|
+
const memory = this.createTelegramAssociativeMemory();
|
|
611807
|
+
this.chatAssociativeMemory.set(sessionKey, memory);
|
|
611808
|
+
for (const entry of history) {
|
|
611809
|
+
this.updateTelegramAssociativeMemory(sessionKey, entry);
|
|
611810
|
+
}
|
|
611811
|
+
}
|
|
611812
|
+
telegramHistoryBackfillMessageId(sessionKey, entry, index) {
|
|
611813
|
+
if (typeof entry.messageId === "number" && Number.isFinite(entry.messageId)) return entry.messageId;
|
|
611814
|
+
const digest3 = createHash23("sha1").update(`${sessionKey}:${index}:${entry.role}:${entry.ts ?? ""}:${entry.text}`).digest("hex").slice(0, 8);
|
|
611815
|
+
return -Number.parseInt(digest3, 16);
|
|
611816
|
+
}
|
|
611817
|
+
backfillTelegramLoadedHistory(sessionKey, history) {
|
|
611818
|
+
if (!this.repoRoot || history.length === 0) return;
|
|
611819
|
+
const db = this.telegramDb();
|
|
611820
|
+
if (db) {
|
|
611821
|
+
try {
|
|
611822
|
+
const existing = db.prepare("SELECT COUNT(*) AS n FROM telegram_messages WHERE session_key = ?").get(sessionKey);
|
|
611823
|
+
if ((existing?.n ?? 0) < history.length) {
|
|
611824
|
+
const insert = db.prepare(`
|
|
611825
|
+
INSERT INTO telegram_messages (
|
|
611826
|
+
session_key, chat_id, chat_type, chat_title, message_id, message_thread_id,
|
|
611827
|
+
update_id, telegram_date, received_at, role, from_user_id, username, first_name,
|
|
611828
|
+
text, raw_json, normalized_json, reply_to_message_id, reply_to_username, media_json
|
|
611829
|
+
) VALUES (
|
|
611830
|
+
@session_key, @chat_id, @chat_type, @chat_title, @message_id, @message_thread_id,
|
|
611831
|
+
NULL, NULL, @received_at, @role, @from_user_id, @username, @first_name,
|
|
611832
|
+
@text, @raw_json, @normalized_json, @reply_to_message_id, @reply_to_username, @media_json
|
|
611833
|
+
)
|
|
611834
|
+
ON CONFLICT(chat_id, message_id, role) DO UPDATE SET
|
|
611835
|
+
session_key=excluded.session_key,
|
|
611836
|
+
chat_type=excluded.chat_type,
|
|
611837
|
+
chat_title=excluded.chat_title,
|
|
611838
|
+
message_thread_id=excluded.message_thread_id,
|
|
611839
|
+
received_at=excluded.received_at,
|
|
611840
|
+
from_user_id=excluded.from_user_id,
|
|
611841
|
+
username=excluded.username,
|
|
611842
|
+
first_name=excluded.first_name,
|
|
611843
|
+
text=excluded.text,
|
|
611844
|
+
raw_json=excluded.raw_json,
|
|
611845
|
+
normalized_json=excluded.normalized_json,
|
|
611846
|
+
reply_to_message_id=excluded.reply_to_message_id,
|
|
611847
|
+
reply_to_username=excluded.reply_to_username,
|
|
611848
|
+
media_json=excluded.media_json
|
|
611849
|
+
`);
|
|
611850
|
+
const transaction = db.transaction((entries) => {
|
|
611851
|
+
entries.forEach((entry, index) => {
|
|
611852
|
+
const messageId = this.telegramHistoryBackfillMessageId(sessionKey, entry, index);
|
|
611853
|
+
const chatId = entry.chatId !== void 0 ? String(entry.chatId) : sessionKey.replace(/^chat:/, "");
|
|
611854
|
+
insert.run({
|
|
611855
|
+
session_key: sessionKey,
|
|
611856
|
+
chat_id: chatId || "unknown",
|
|
611857
|
+
chat_type: entry.chatType || null,
|
|
611858
|
+
chat_title: entry.chatTitle || null,
|
|
611859
|
+
message_id: messageId,
|
|
611860
|
+
message_thread_id: entry.messageThreadId ?? null,
|
|
611861
|
+
received_at: entry.ts ?? Date.now(),
|
|
611862
|
+
role: entry.role === "assistant" ? "assistant" : "user",
|
|
611863
|
+
from_user_id: entry.fromUserId ?? null,
|
|
611864
|
+
username: entry.role === "assistant" ? this.state.botUsername || "omnius" : entry.username || null,
|
|
611865
|
+
first_name: entry.role === "assistant" ? "Omnius" : entry.firstName || null,
|
|
611866
|
+
text: entry.text || "",
|
|
611867
|
+
raw_json: JSON.stringify({ source: "telegram-conversations", sessionKey, entry }),
|
|
611868
|
+
normalized_json: JSON.stringify({ ...entry, messageId }),
|
|
611869
|
+
reply_to_message_id: entry.replyToMessageId ?? null,
|
|
611870
|
+
reply_to_username: entry.replyContext?.sender?.username || null,
|
|
611871
|
+
media_json: entry.mediaSummary ? JSON.stringify({ summary: entry.mediaSummary }) : null
|
|
611872
|
+
});
|
|
611873
|
+
});
|
|
611874
|
+
});
|
|
611875
|
+
transaction(history);
|
|
611876
|
+
try {
|
|
611877
|
+
db.exec("INSERT INTO telegram_messages_fts(telegram_messages_fts) VALUES('rebuild')");
|
|
611878
|
+
} catch {
|
|
611879
|
+
}
|
|
611880
|
+
}
|
|
611881
|
+
} catch {
|
|
611882
|
+
}
|
|
611883
|
+
}
|
|
611884
|
+
try {
|
|
611885
|
+
const paths = omniusMemoryDbPaths(this.repoRoot);
|
|
611886
|
+
const graph = new TemporalGraph(paths.knowledge);
|
|
611887
|
+
const store2 = new EpisodeStore(paths.episodes, graph);
|
|
611888
|
+
let existingTelegramEpisodes = 0;
|
|
611889
|
+
try {
|
|
611890
|
+
const row = store2.getDb().prepare(`
|
|
611891
|
+
SELECT COUNT(*) AS n FROM episodes
|
|
611892
|
+
WHERE session_id = ? AND metadata LIKE '%"sourceSurface":"telegram"%'
|
|
611893
|
+
`).get(sessionKey);
|
|
611894
|
+
existingTelegramEpisodes = row?.n ?? 0;
|
|
611895
|
+
} finally {
|
|
611896
|
+
store2.close();
|
|
611897
|
+
graph.close();
|
|
611898
|
+
}
|
|
611899
|
+
if (existingTelegramEpisodes < history.length) {
|
|
611900
|
+
for (const entry of history) {
|
|
611901
|
+
this.upsertTelegramReflectionHistoryEntry(sessionKey, entry);
|
|
611902
|
+
}
|
|
611903
|
+
}
|
|
611904
|
+
} catch {
|
|
611905
|
+
}
|
|
611906
|
+
}
|
|
611276
611907
|
ensureAllTelegramConversationsLoaded() {
|
|
611277
611908
|
if (this.loadedAllConversationState) return;
|
|
611278
611909
|
this.loadedAllConversationState = true;
|
|
@@ -611304,6 +611935,7 @@ ${mediaContext}` : ""
|
|
|
611304
611935
|
history: this.chatHistory.get(sessionKey) ?? [],
|
|
611305
611936
|
participants,
|
|
611306
611937
|
memoryCards: this.chatMemoryCards.get(sessionKey) ?? [],
|
|
611938
|
+
associativeMemory: this.telegramAssociativeMemoryForSession(sessionKey),
|
|
611307
611939
|
stimulation: this.stimulation.getState(sessionKey),
|
|
611308
611940
|
reflection: this.channelReflectionState.get(sessionKey) ?? { autoFollowup: false }
|
|
611309
611941
|
};
|
|
@@ -611331,7 +611963,8 @@ ${mediaContext}` : ""
|
|
|
611331
611963
|
};
|
|
611332
611964
|
}
|
|
611333
611965
|
buildTelegramChannelDaydreamInput(sessionKey, nowMs = Date.now()) {
|
|
611334
|
-
const
|
|
611966
|
+
const sqliteHistory = this.telegramSqliteHistoryForSession(sessionKey, 1500);
|
|
611967
|
+
const history = sqliteHistory.length > 0 ? sqliteHistory : this.chatHistory.get(sessionKey) ?? [];
|
|
611335
611968
|
if (history.length === 0) return null;
|
|
611336
611969
|
const last2 = [...history].reverse().find((entry) => entry.chatId !== void 0);
|
|
611337
611970
|
if (!last2) return null;
|
|
@@ -611636,6 +612269,7 @@ ${mediaContext}` : ""
|
|
|
611636
612269
|
this.upsertTelegramReflectionHistoryEntry(sessionKey, entry);
|
|
611637
612270
|
this.updateTelegramParticipantProfile(sessionKey, msg, text);
|
|
611638
612271
|
this.updateTelegramMemoryCards(sessionKey, entry);
|
|
612272
|
+
this.updateTelegramAssociativeMemory(sessionKey, entry);
|
|
611639
612273
|
try {
|
|
611640
612274
|
updateScopedPersonality(this.telegramPersonalityScope(sessionKey, msg), {
|
|
611641
612275
|
speaker: telegramSpeakerLabel(msg),
|
|
@@ -611669,9 +612303,16 @@ ${mediaContext}` : ""
|
|
|
611669
612303
|
chatTitle: msg.chatTitle
|
|
611670
612304
|
};
|
|
611671
612305
|
this.recordChatHistory(sessionKey, entry);
|
|
612306
|
+
this.persistTelegramAssistantMessage(
|
|
612307
|
+
msg,
|
|
612308
|
+
clean5,
|
|
612309
|
+
options2.messageId ?? void 0,
|
|
612310
|
+
options2.replyToMessageId
|
|
612311
|
+
);
|
|
611672
612312
|
this.upsertTelegramReflectionHistoryEntry(sessionKey, entry);
|
|
611673
612313
|
this.stimulation.recordAgentOutput(sessionKey);
|
|
611674
612314
|
this.updateTelegramMemoryCards(sessionKey, entry);
|
|
612315
|
+
this.updateTelegramAssociativeMemory(sessionKey, entry);
|
|
611675
612316
|
try {
|
|
611676
612317
|
updateScopedPersonality(this.telegramPersonalityScope(sessionKey, msg), {
|
|
611677
612318
|
speaker: entry.speaker || "Assistant",
|
|
@@ -611862,6 +612503,160 @@ ${mediaContext}` : ""
|
|
|
611862
612503
|
participants.set(participantKey, profile);
|
|
611863
612504
|
this.chatParticipants.set(sessionKey, participants);
|
|
611864
612505
|
}
|
|
612506
|
+
updateTelegramAssociativeMemory(sessionKey, entry) {
|
|
612507
|
+
const memory = this.telegramAssociativeMemoryForSession(sessionKey);
|
|
612508
|
+
const now = entry.ts ?? Date.now();
|
|
612509
|
+
memory.updatedAt = now;
|
|
612510
|
+
const speaker = telegramHistorySpeaker(entry);
|
|
612511
|
+
const actionId = createHash23("sha1").update(`${sessionKey}:${entry.role}:${entry.messageId ?? ""}:${now}:${entry.text}`).digest("hex").slice(0, 16);
|
|
612512
|
+
if (!memory.actions.some((action) => action.id === actionId)) {
|
|
612513
|
+
memory.actions.push({
|
|
612514
|
+
id: actionId,
|
|
612515
|
+
ts: now,
|
|
612516
|
+
role: entry.role,
|
|
612517
|
+
speaker,
|
|
612518
|
+
mode: entry.mode,
|
|
612519
|
+
text: truncateTelegramContextLine(entry.text, 900),
|
|
612520
|
+
messageId: entry.messageId,
|
|
612521
|
+
replyToMessageId: entry.replyToMessageId,
|
|
612522
|
+
userId: entry.fromUserId,
|
|
612523
|
+
username: entry.username
|
|
612524
|
+
});
|
|
612525
|
+
if (memory.actions.length > TELEGRAM_ASSOCIATIVE_ACTION_LIMIT) {
|
|
612526
|
+
memory.actions.splice(
|
|
612527
|
+
0,
|
|
612528
|
+
memory.actions.length - TELEGRAM_ASSOCIATIVE_ACTION_LIMIT
|
|
612529
|
+
);
|
|
612530
|
+
}
|
|
612531
|
+
}
|
|
612532
|
+
if (entry.role === "user") {
|
|
612533
|
+
const userKey = String(entry.fromUserId || entry.username || entry.firstName || speaker);
|
|
612534
|
+
const existing = memory.users[userKey];
|
|
612535
|
+
const userMemory = existing ?? {
|
|
612536
|
+
userId: entry.fromUserId,
|
|
612537
|
+
username: entry.username || "unknown",
|
|
612538
|
+
displayName: entry.firstName || entry.username || speaker,
|
|
612539
|
+
aliases: [],
|
|
612540
|
+
firstSeenTs: now,
|
|
612541
|
+
lastSeenTs: now,
|
|
612542
|
+
messageCount: 0,
|
|
612543
|
+
directAddressCount: 0,
|
|
612544
|
+
replyCount: 0,
|
|
612545
|
+
toneTags: [],
|
|
612546
|
+
facts: [],
|
|
612547
|
+
relationshipHints: [],
|
|
612548
|
+
recentTopics: [],
|
|
612549
|
+
lastMessages: []
|
|
612550
|
+
};
|
|
612551
|
+
userMemory.userId = entry.fromUserId ?? userMemory.userId;
|
|
612552
|
+
userMemory.username = entry.username || userMemory.username;
|
|
612553
|
+
userMemory.displayName = entry.firstName || userMemory.displayName;
|
|
612554
|
+
for (const alias of [entry.username, entry.firstName, speaker].filter(Boolean)) {
|
|
612555
|
+
const clean5 = alias.replace(/^@/, "").trim();
|
|
612556
|
+
if (clean5 && !userMemory.aliases.includes(clean5)) userMemory.aliases.push(clean5);
|
|
612557
|
+
}
|
|
612558
|
+
userMemory.aliases = userMemory.aliases.slice(0, 20);
|
|
612559
|
+
userMemory.lastSeenTs = now;
|
|
612560
|
+
userMemory.messageCount += 1;
|
|
612561
|
+
if (entry.replyToMessageId) userMemory.replyCount += 1;
|
|
612562
|
+
if (this.state.botUsername && entry.text.toLowerCase().includes(`@${this.state.botUsername.toLowerCase()}`)) {
|
|
612563
|
+
userMemory.directAddressCount += 1;
|
|
612564
|
+
}
|
|
612565
|
+
for (const tag of inferTelegramToneTags(entry.text)) {
|
|
612566
|
+
if (!userMemory.toneTags.includes(tag)) userMemory.toneTags.push(tag);
|
|
612567
|
+
}
|
|
612568
|
+
userMemory.toneTags = userMemory.toneTags.slice(0, 20);
|
|
612569
|
+
const compact2 = truncateTelegramContextLine(entry.text, 240);
|
|
612570
|
+
if (compact2) {
|
|
612571
|
+
userMemory.lastMessages.push(compact2);
|
|
612572
|
+
userMemory.lastMessages = userMemory.lastMessages.slice(-40);
|
|
612573
|
+
}
|
|
612574
|
+
for (const topic of telegramMemoryTags(entry.text, entry.mediaSummary).slice(0, 6)) {
|
|
612575
|
+
if (!userMemory.recentTopics.includes(topic)) userMemory.recentTopics.push(topic);
|
|
612576
|
+
}
|
|
612577
|
+
userMemory.recentTopics = userMemory.recentTopics.slice(-80);
|
|
612578
|
+
const facts = this.extractTelegramAssociativeFacts(entry, speaker);
|
|
612579
|
+
for (const text of facts) {
|
|
612580
|
+
const fact = this.upsertTelegramAssociativeFact(memory.facts, text, entry, speaker, 1.5);
|
|
612581
|
+
this.upsertTelegramAssociativeFact(userMemory.facts, fact.text, entry, speaker, fact.weight);
|
|
612582
|
+
}
|
|
612583
|
+
if (entry.replyContext?.sender || entry.replyToMessageId) {
|
|
612584
|
+
const target = entry.replyContext?.sender ? telegramReplySenderLabel(entry.replyContext.sender) : `message ${entry.replyToMessageId}`;
|
|
612585
|
+
const hint = `${speaker} replied to ${target}: ${truncateTelegramContextLine(entry.text, 180)}`;
|
|
612586
|
+
if (!userMemory.relationshipHints.includes(hint)) {
|
|
612587
|
+
userMemory.relationshipHints.push(hint);
|
|
612588
|
+
userMemory.relationshipHints = userMemory.relationshipHints.slice(-80);
|
|
612589
|
+
}
|
|
612590
|
+
this.upsertTelegramAssociativeFact(memory.relationships, hint, entry, speaker, 1.2);
|
|
612591
|
+
}
|
|
612592
|
+
userMemory.facts.sort((a2, b) => b.weight - a2.weight || b.updatedAt - a2.updatedAt);
|
|
612593
|
+
userMemory.facts = userMemory.facts.slice(0, TELEGRAM_ASSOCIATIVE_USER_FACT_LIMIT);
|
|
612594
|
+
memory.users[userKey] = userMemory;
|
|
612595
|
+
}
|
|
612596
|
+
memory.facts.sort((a2, b) => b.weight - a2.weight || b.updatedAt - a2.updatedAt);
|
|
612597
|
+
memory.facts = memory.facts.slice(0, TELEGRAM_ASSOCIATIVE_FACT_LIMIT);
|
|
612598
|
+
memory.relationships.sort((a2, b) => b.updatedAt - a2.updatedAt);
|
|
612599
|
+
memory.relationships = memory.relationships.slice(0, TELEGRAM_ASSOCIATIVE_RELATION_LIMIT);
|
|
612600
|
+
}
|
|
612601
|
+
extractTelegramAssociativeFacts(entry, speaker) {
|
|
612602
|
+
if (entry.role !== "user") return [];
|
|
612603
|
+
const text = truncateTelegramContextLine(entry.text, 500);
|
|
612604
|
+
const facts = /* @__PURE__ */ new Set();
|
|
612605
|
+
const patterns = [
|
|
612606
|
+
/\b(?:remember|note|keep in mind|for future reference)\s+(?:that\s+)?(.{4,260})/i,
|
|
612607
|
+
/\bmy name is\s+(.{2,120})/i,
|
|
612608
|
+
/\bcall me\s+(.{2,120})/i,
|
|
612609
|
+
/\bi\s+(?:am|work as|live in|like|love|prefer|use|run|have|need|want)\b.{0,220}/i,
|
|
612610
|
+
/\b(?:we|this group|our)\s+.{0,220}\b(?:is|are|uses|prefers|likes|works|has|needs)\b.{0,220}/i
|
|
612611
|
+
];
|
|
612612
|
+
for (const pattern of patterns) {
|
|
612613
|
+
const match = text.match(pattern);
|
|
612614
|
+
if (!match) continue;
|
|
612615
|
+
const body = (match[1] || match[0]).trim().replace(/[.!?]*$/, "");
|
|
612616
|
+
if (body.length >= 3) facts.add(`${speaker}: ${body}`);
|
|
612617
|
+
}
|
|
612618
|
+
if (entry.mediaSummary) {
|
|
612619
|
+
facts.add(`${speaker} shared media: ${entry.mediaSummary}`);
|
|
612620
|
+
}
|
|
612621
|
+
return [...facts].slice(0, 8);
|
|
612622
|
+
}
|
|
612623
|
+
upsertTelegramAssociativeFact(facts, text, entry, speaker, weight = 1) {
|
|
612624
|
+
const clean5 = truncateTelegramContextLine(text, 500);
|
|
612625
|
+
const key = clean5.toLowerCase();
|
|
612626
|
+
const now = entry.ts ?? Date.now();
|
|
612627
|
+
let fact = facts.find((item) => item.text.toLowerCase() === key);
|
|
612628
|
+
if (!fact) {
|
|
612629
|
+
fact = {
|
|
612630
|
+
id: createHash23("sha1").update(`${entry.chatId ?? ""}:${key}`).digest("hex").slice(0, 12),
|
|
612631
|
+
text: clean5,
|
|
612632
|
+
tags: telegramMemoryTags(clean5, entry.mediaSummary),
|
|
612633
|
+
speakers: [],
|
|
612634
|
+
userIds: [],
|
|
612635
|
+
usernames: [],
|
|
612636
|
+
messageIds: [],
|
|
612637
|
+
createdAt: now,
|
|
612638
|
+
updatedAt: now,
|
|
612639
|
+
weight
|
|
612640
|
+
};
|
|
612641
|
+
facts.push(fact);
|
|
612642
|
+
}
|
|
612643
|
+
fact.updatedAt = now;
|
|
612644
|
+
fact.weight = Math.min(10, Math.max(fact.weight, weight) + 0.2);
|
|
612645
|
+
if (!fact.speakers.includes(speaker)) fact.speakers.push(speaker);
|
|
612646
|
+
fact.speakers = fact.speakers.slice(0, 16);
|
|
612647
|
+
if (entry.fromUserId && !fact.userIds.includes(entry.fromUserId)) fact.userIds.push(entry.fromUserId);
|
|
612648
|
+
fact.userIds = fact.userIds.slice(0, 32);
|
|
612649
|
+
const username = (entry.username || "").replace(/^@/, "").toLowerCase();
|
|
612650
|
+
if (username && !fact.usernames.includes(username)) fact.usernames.push(username);
|
|
612651
|
+
fact.usernames = fact.usernames.slice(0, 32);
|
|
612652
|
+
if (entry.messageId && !fact.messageIds.includes(entry.messageId)) fact.messageIds.push(entry.messageId);
|
|
612653
|
+
fact.messageIds = fact.messageIds.slice(-80);
|
|
612654
|
+
for (const tag of telegramMemoryTags(clean5, entry.mediaSummary)) {
|
|
612655
|
+
if (!fact.tags.includes(tag)) fact.tags.push(tag);
|
|
612656
|
+
}
|
|
612657
|
+
fact.tags = fact.tags.slice(0, 16);
|
|
612658
|
+
return fact;
|
|
612659
|
+
}
|
|
611865
612660
|
updateTelegramMemoryCards(sessionKey, entry) {
|
|
611866
612661
|
const text = truncateTelegramContextLine(entry.text, 500);
|
|
611867
612662
|
if (!text || text.length < 3) return;
|
|
@@ -611955,6 +612750,249 @@ ${mediaContext}` : ""
|
|
|
611955
612750
|
)
|
|
611956
612751
|
})).sort((a2, b) => b.score - a2.score || b.card.updatedAt - a2.card.updatedAt).slice(0, limit);
|
|
611957
612752
|
}
|
|
612753
|
+
relevantTelegramAssociativeMemoryContext(sessionKey, msg, limit = 12) {
|
|
612754
|
+
const memory = this.chatAssociativeMemory.get(sessionKey);
|
|
612755
|
+
if (!memory) return "";
|
|
612756
|
+
const queryTokens = telegramMemoryTokens([
|
|
612757
|
+
telegramSpeakerLabel(msg),
|
|
612758
|
+
msg.username || "",
|
|
612759
|
+
msg.firstName || "",
|
|
612760
|
+
msg.text,
|
|
612761
|
+
summarizeTelegramMessageAttachments(msg)
|
|
612762
|
+
].join(" "));
|
|
612763
|
+
const currentUsername = (msg.username || "").replace(/^@/, "").toLowerCase();
|
|
612764
|
+
const userEntries = Object.entries(memory.users).filter(
|
|
612765
|
+
([, user]) => msg.fromUserId !== void 0 && user.userId === msg.fromUserId || !!currentUsername && user.username.replace(/^@/, "").toLowerCase() === currentUsername || user.aliases.some((alias) => alias.replace(/^@/, "").toLowerCase() === currentUsername)
|
|
612766
|
+
);
|
|
612767
|
+
const scoredFacts = memory.facts.map((fact) => ({
|
|
612768
|
+
fact,
|
|
612769
|
+
score: telegramMemorySimilarity(
|
|
612770
|
+
queryTokens,
|
|
612771
|
+
telegramMemoryTokens([fact.text, fact.tags.join(" "), fact.speakers.join(" ")].join(" "))
|
|
612772
|
+
) + (fact.userIds.includes(msg.fromUserId ?? -1) ? 0.35 : 0) + (currentUsername && fact.usernames.includes(currentUsername) ? 0.35 : 0)
|
|
612773
|
+
})).filter((item) => item.score > 0).sort((a2, b) => b.score - a2.score || b.fact.updatedAt - a2.fact.updatedAt).slice(0, limit);
|
|
612774
|
+
const relationshipFacts = memory.relationships.filter(
|
|
612775
|
+
(fact) => fact.userIds.includes(msg.fromUserId ?? -1) || !!currentUsername && fact.usernames.includes(currentUsername)
|
|
612776
|
+
).slice(0, 6);
|
|
612777
|
+
const recentActions = memory.actions.filter(
|
|
612778
|
+
(action) => action.userId === msg.fromUserId || !!currentUsername && action.username?.replace(/^@/, "").toLowerCase() === currentUsername || action.role === "assistant"
|
|
612779
|
+
).slice(-8);
|
|
612780
|
+
const sections = [];
|
|
612781
|
+
if (userEntries.length > 0) {
|
|
612782
|
+
const lines = userEntries.map(([, user]) => {
|
|
612783
|
+
const facts = user.facts.slice(0, 6).map((fact) => ` - ${telegramContextJsonString(fact.text, 220)}`).join("\n");
|
|
612784
|
+
const hints = user.relationshipHints.slice(-4).map((hint) => ` - relation=${telegramContextJsonString(hint, 200)}`).join("\n");
|
|
612785
|
+
const topics = user.recentTopics.slice(-8).join(", ") || "none";
|
|
612786
|
+
return [
|
|
612787
|
+
`- ${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}`,
|
|
612788
|
+
facts,
|
|
612789
|
+
hints
|
|
612790
|
+
].filter(Boolean).join("\n");
|
|
612791
|
+
});
|
|
612792
|
+
sections.push(`### Durable Associative User Memory
|
|
612793
|
+
${lines.join("\n")}`);
|
|
612794
|
+
}
|
|
612795
|
+
if (scoredFacts.length > 0) {
|
|
612796
|
+
const lines = scoredFacts.map(
|
|
612797
|
+
({ fact, score }) => `- (${score.toFixed(2)}) ${telegramContextJsonString(fact.text, 260)}${fact.messageIds.length ? ` [messages:${fact.messageIds.slice(-4).join(",")}]` : ""}`
|
|
612798
|
+
);
|
|
612799
|
+
sections.push(`### Durable Associative Memory Recall
|
|
612800
|
+
${lines.join("\n")}`);
|
|
612801
|
+
}
|
|
612802
|
+
if (relationshipFacts.length > 0) {
|
|
612803
|
+
const lines = relationshipFacts.map((fact) => `- ${telegramContextJsonString(fact.text, 240)}`);
|
|
612804
|
+
sections.push(`### Durable Relationship Recall
|
|
612805
|
+
${lines.join("\n")}`);
|
|
612806
|
+
}
|
|
612807
|
+
if (recentActions.length > 0) {
|
|
612808
|
+
const lines = recentActions.map(
|
|
612809
|
+
(action) => `- ${telegramHistoryTime({ ts: action.ts, role: action.role, text: action.text })} ${action.speaker}/${action.role}${action.mode ? `/${action.mode}` : ""}: ${telegramContextJsonString(action.text, 220)}`
|
|
612810
|
+
);
|
|
612811
|
+
sections.push(`### Durable Recent Action Ledger Recall
|
|
612812
|
+
${lines.join("\n")}`);
|
|
612813
|
+
}
|
|
612814
|
+
return sections.join("\n\n");
|
|
612815
|
+
}
|
|
612816
|
+
relevantTelegramSqliteMirrorContext(sessionKey, msg, limit = 12) {
|
|
612817
|
+
const db = this.telegramDb();
|
|
612818
|
+
if (!db) return "";
|
|
612819
|
+
const rows = /* @__PURE__ */ new Map();
|
|
612820
|
+
const addRows = (items) => {
|
|
612821
|
+
for (const row of items) {
|
|
612822
|
+
const key = Number(row.rowid);
|
|
612823
|
+
if (Number.isFinite(key)) rows.set(key, row);
|
|
612824
|
+
}
|
|
612825
|
+
};
|
|
612826
|
+
try {
|
|
612827
|
+
if (msg.fromUserId !== void 0) {
|
|
612828
|
+
addRows(db.prepare(`
|
|
612829
|
+
SELECT rowid, * FROM telegram_messages
|
|
612830
|
+
WHERE session_key = ? AND from_user_id = ?
|
|
612831
|
+
ORDER BY received_at DESC
|
|
612832
|
+
LIMIT ?
|
|
612833
|
+
`).all(sessionKey, msg.fromUserId, limit));
|
|
612834
|
+
}
|
|
612835
|
+
const username = (msg.username || "").replace(/^@/, "").toLowerCase();
|
|
612836
|
+
if (username) {
|
|
612837
|
+
addRows(db.prepare(`
|
|
612838
|
+
SELECT rowid, * FROM telegram_messages
|
|
612839
|
+
WHERE session_key = ? AND lower(username) = ?
|
|
612840
|
+
ORDER BY received_at DESC
|
|
612841
|
+
LIMIT ?
|
|
612842
|
+
`).all(sessionKey, username, Math.max(4, Math.floor(limit / 2))));
|
|
612843
|
+
}
|
|
612844
|
+
const queryTokens = [...telegramMemoryTokens(msg.text)].slice(0, 6);
|
|
612845
|
+
for (const token of queryTokens) {
|
|
612846
|
+
addRows(db.prepare(`
|
|
612847
|
+
SELECT rowid, * FROM telegram_messages
|
|
612848
|
+
WHERE session_key = ? AND text LIKE ?
|
|
612849
|
+
ORDER BY received_at DESC
|
|
612850
|
+
LIMIT 4
|
|
612851
|
+
`).all(sessionKey, `%${token}%`));
|
|
612852
|
+
}
|
|
612853
|
+
} catch {
|
|
612854
|
+
return "";
|
|
612855
|
+
}
|
|
612856
|
+
const selected = [...rows.values()].sort((a2, b) => Number(b.received_at ?? 0) - Number(a2.received_at ?? 0)).slice(0, limit);
|
|
612857
|
+
if (selected.length === 0) return "";
|
|
612858
|
+
const lines = selected.map((row) => {
|
|
612859
|
+
const when = row.received_at ? new Date(Number(row.received_at)).toISOString() : "";
|
|
612860
|
+
const speaker = row.role === "assistant" ? `@${this.state.botUsername || "omnius"}` : row.username ? `@${row.username}` : row.from_user_id ? `user:${row.from_user_id}` : "unknown";
|
|
612861
|
+
const reply = row.reply_to_message_id ? ` reply_to:${row.reply_to_message_id}` : "";
|
|
612862
|
+
return `- ${when} ${speaker}/${row.role || "user"} msg:${row.message_id}${reply}: ${telegramContextJsonString(String(row.text || ""), 260)}`;
|
|
612863
|
+
});
|
|
612864
|
+
return [
|
|
612865
|
+
"### SQLite Telegram Raw Mirror Recall",
|
|
612866
|
+
"Durable local mirror rows for this scoped chat. Use as historical evidence when the rolling context omitted older turns.",
|
|
612867
|
+
lines.join("\n")
|
|
612868
|
+
].join("\n");
|
|
612869
|
+
}
|
|
612870
|
+
telegramSqliteHistoryForSession(sessionKey, limit = 1e3) {
|
|
612871
|
+
const db = this.telegramDb();
|
|
612872
|
+
if (!db) return [];
|
|
612873
|
+
try {
|
|
612874
|
+
const rows = db.prepare(`
|
|
612875
|
+
SELECT * FROM telegram_messages
|
|
612876
|
+
WHERE session_key = ?
|
|
612877
|
+
ORDER BY received_at DESC
|
|
612878
|
+
LIMIT ?
|
|
612879
|
+
`).all(sessionKey, limit);
|
|
612880
|
+
return rows.reverse().map((row) => ({
|
|
612881
|
+
role: row.role === "assistant" ? "assistant" : "user",
|
|
612882
|
+
text: String(row.text || ""),
|
|
612883
|
+
ts: Number(row.received_at || 0) || void 0,
|
|
612884
|
+
chatId: row.chat_id,
|
|
612885
|
+
speaker: row.role === "assistant" ? `@${this.state.botUsername || "omnius"}` : row.username ? `@${row.username}` : row.from_user_id ? `user:${row.from_user_id}` : "unknown",
|
|
612886
|
+
username: row.username || void 0,
|
|
612887
|
+
firstName: row.first_name || void 0,
|
|
612888
|
+
fromUserId: typeof row.from_user_id === "number" ? row.from_user_id : void 0,
|
|
612889
|
+
messageId: typeof row.message_id === "number" ? row.message_id : void 0,
|
|
612890
|
+
messageThreadId: typeof row.message_thread_id === "number" ? row.message_thread_id : void 0,
|
|
612891
|
+
replyToMessageId: typeof row.reply_to_message_id === "number" ? row.reply_to_message_id : void 0,
|
|
612892
|
+
chatType: row.chat_type,
|
|
612893
|
+
chatTitle: row.chat_title || void 0,
|
|
612894
|
+
mediaSummary: row.media_json ? "media attached in raw Telegram SQLite mirror" : void 0
|
|
612895
|
+
}));
|
|
612896
|
+
} catch {
|
|
612897
|
+
return [];
|
|
612898
|
+
}
|
|
612899
|
+
}
|
|
612900
|
+
searchTelegramSqliteMirrorRows(sessionKey, query, options2 = {}) {
|
|
612901
|
+
const db = this.telegramDb();
|
|
612902
|
+
if (!db) return [];
|
|
612903
|
+
const limit = Math.max(1, Math.min(50, Math.floor(options2.limit ?? 12)));
|
|
612904
|
+
const username = (options2.username || "").replace(/^@/, "").trim().toLowerCase();
|
|
612905
|
+
const tokens = [...telegramMemoryTokens(query)].slice(0, 8);
|
|
612906
|
+
const rows = /* @__PURE__ */ new Map();
|
|
612907
|
+
const addRows = (items) => {
|
|
612908
|
+
for (const row of items) {
|
|
612909
|
+
const key = Number(row.rowid);
|
|
612910
|
+
if (Number.isFinite(key)) rows.set(key, row);
|
|
612911
|
+
}
|
|
612912
|
+
};
|
|
612913
|
+
try {
|
|
612914
|
+
if (options2.userId !== void 0) {
|
|
612915
|
+
addRows(db.prepare(`
|
|
612916
|
+
SELECT rowid, * FROM telegram_messages
|
|
612917
|
+
WHERE session_key = ? AND from_user_id = ?
|
|
612918
|
+
ORDER BY received_at DESC
|
|
612919
|
+
LIMIT ?
|
|
612920
|
+
`).all(sessionKey, options2.userId, limit));
|
|
612921
|
+
}
|
|
612922
|
+
if (username) {
|
|
612923
|
+
addRows(db.prepare(`
|
|
612924
|
+
SELECT rowid, * FROM telegram_messages
|
|
612925
|
+
WHERE session_key = ? AND lower(username) = ?
|
|
612926
|
+
ORDER BY received_at DESC
|
|
612927
|
+
LIMIT ?
|
|
612928
|
+
`).all(sessionKey, username, limit));
|
|
612929
|
+
}
|
|
612930
|
+
if (tokens.length > 0) {
|
|
612931
|
+
const clauses = tokens.map(() => "text LIKE ?").join(" OR ");
|
|
612932
|
+
addRows(db.prepare(`
|
|
612933
|
+
SELECT rowid, * FROM telegram_messages
|
|
612934
|
+
WHERE session_key = ? AND (${clauses})
|
|
612935
|
+
ORDER BY received_at DESC
|
|
612936
|
+
LIMIT ?
|
|
612937
|
+
`).all(sessionKey, ...tokens.map((token) => `%${token}%`), limit));
|
|
612938
|
+
}
|
|
612939
|
+
if (rows.size === 0) {
|
|
612940
|
+
addRows(db.prepare(`
|
|
612941
|
+
SELECT rowid, * FROM telegram_messages
|
|
612942
|
+
WHERE session_key = ?
|
|
612943
|
+
ORDER BY received_at DESC
|
|
612944
|
+
LIMIT ?
|
|
612945
|
+
`).all(sessionKey, Math.min(limit, 12)));
|
|
612946
|
+
}
|
|
612947
|
+
} catch {
|
|
612948
|
+
return [];
|
|
612949
|
+
}
|
|
612950
|
+
return [...rows.values()].sort((a2, b) => Number(b.received_at ?? 0) - Number(a2.received_at ?? 0)).slice(0, limit);
|
|
612951
|
+
}
|
|
612952
|
+
formatTelegramSqliteMirrorRows(rows, maxText = 260) {
|
|
612953
|
+
return rows.map((row) => {
|
|
612954
|
+
const when = row.received_at ? new Date(Number(row.received_at)).toISOString() : "";
|
|
612955
|
+
const speaker = row.role === "assistant" ? `@${this.state.botUsername || "omnius"}` : row.username ? `@${row.username}` : row.from_user_id ? `user:${row.from_user_id}` : "unknown";
|
|
612956
|
+
const reply = row.reply_to_message_id ? ` reply_to:${row.reply_to_message_id}` : "";
|
|
612957
|
+
const media = row.media_json ? " media:attached" : "";
|
|
612958
|
+
return `- ${when} ${speaker}/${row.role || "user"} msg:${row.message_id}${reply}${media}: ${telegramContextJsonString(String(row.text || ""), maxText)}`;
|
|
612959
|
+
}).join("\n");
|
|
612960
|
+
}
|
|
612961
|
+
searchTelegramEpisodeMemory(sessionKey, query, limit = 8) {
|
|
612962
|
+
if (!this.repoRoot || !query.trim()) return [];
|
|
612963
|
+
const paths = omniusMemoryDbPaths(this.repoRoot);
|
|
612964
|
+
if (!existsSync112(paths.episodes)) return [];
|
|
612965
|
+
const graph = new TemporalGraph(paths.knowledge);
|
|
612966
|
+
const store2 = new EpisodeStore(paths.episodes, graph);
|
|
612967
|
+
try {
|
|
612968
|
+
return store2.searchWithPPR(
|
|
612969
|
+
{
|
|
612970
|
+
sessionId: sessionKey,
|
|
612971
|
+
query,
|
|
612972
|
+
limit: Math.max(1, Math.min(20, Math.floor(limit))),
|
|
612973
|
+
metadataFilter: { sourceSurface: "telegram" }
|
|
612974
|
+
},
|
|
612975
|
+
{ lexicalWeight: 1.25, embeddingWeight: 0 }
|
|
612976
|
+
);
|
|
612977
|
+
} catch {
|
|
612978
|
+
return [];
|
|
612979
|
+
} finally {
|
|
612980
|
+
store2.close();
|
|
612981
|
+
graph.close();
|
|
612982
|
+
}
|
|
612983
|
+
}
|
|
612984
|
+
formatTelegramEpisodeMemoryResults(episodes, maxText = 320) {
|
|
612985
|
+
return episodes.map((episode) => {
|
|
612986
|
+
const meta = episode.metadata;
|
|
612987
|
+
const telegram = meta?.telegram && typeof meta.telegram === "object" ? meta.telegram : {};
|
|
612988
|
+
const when = episode.timestamp ? new Date(episode.timestamp).toISOString() : "";
|
|
612989
|
+
const speaker = telegram.speaker || telegram.username || "unknown";
|
|
612990
|
+
const messageId = telegram.messageId ? ` msg:${telegram.messageId}` : "";
|
|
612991
|
+
const mode = telegram.mode ? `/${telegram.mode}` : "";
|
|
612992
|
+
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;
|
|
612993
|
+
return `- ${when} ${speaker}${mode}${messageId}: ${telegramContextJsonString(text, maxText)}`;
|
|
612994
|
+
}).join("\n");
|
|
612995
|
+
}
|
|
611958
612996
|
buildTelegramConversationContextStream(sessionKey, msg, maxRecent = TELEGRAM_CONTEXT_RECENT_DEFAULT) {
|
|
611959
612997
|
this.ensureTelegramConversationLoaded(sessionKey);
|
|
611960
612998
|
const history = this.chatHistory.get(sessionKey) ?? [];
|
|
@@ -611993,6 +613031,22 @@ ${mediaContext}` : ""
|
|
|
611993
613031
|
sections.push(`### Participants And Relationship Signals
|
|
611994
613032
|
${participantLines.join("\n")}`);
|
|
611995
613033
|
}
|
|
613034
|
+
const associativeContext = this.relevantTelegramAssociativeMemoryContext(
|
|
613035
|
+
sessionKey,
|
|
613036
|
+
msg,
|
|
613037
|
+
isGroup ? 14 : 8
|
|
613038
|
+
);
|
|
613039
|
+
if (associativeContext) {
|
|
613040
|
+
sections.push(associativeContext);
|
|
613041
|
+
}
|
|
613042
|
+
const sqliteMirrorContext = this.relevantTelegramSqliteMirrorContext(
|
|
613043
|
+
sessionKey,
|
|
613044
|
+
msg,
|
|
613045
|
+
isGroup ? 14 : 8
|
|
613046
|
+
);
|
|
613047
|
+
if (sqliteMirrorContext) {
|
|
613048
|
+
sections.push(sqliteMirrorContext);
|
|
613049
|
+
}
|
|
611996
613050
|
const memoryCards = this.relevantTelegramMemoryCards(sessionKey, msg, isGroup ? 10 : 6);
|
|
611997
613051
|
if (memoryCards.length > 0) {
|
|
611998
613052
|
const cardLines = memoryCards.map(({ card, score }) => {
|
|
@@ -612176,7 +613230,8 @@ ${lines.join("\n")}`);
|
|
|
612176
613230
|
``,
|
|
612177
613231
|
`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
613232
|
`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,
|
|
613233
|
+
`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.`,
|
|
613234
|
+
`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
613235
|
`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
613236
|
`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
613237
|
forcedLine,
|
|
@@ -612601,6 +613656,13 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
|
|
|
612601
613656
|
agent.aborted = true;
|
|
612602
613657
|
if (agent.typingInterval) clearInterval(agent.typingInterval);
|
|
612603
613658
|
}
|
|
613659
|
+
if (this.telegramSqliteDb && this.telegramSqliteDb !== false) {
|
|
613660
|
+
try {
|
|
613661
|
+
this.telegramSqliteDb.close();
|
|
613662
|
+
} catch {
|
|
613663
|
+
}
|
|
613664
|
+
this.telegramSqliteDb = null;
|
|
613665
|
+
}
|
|
612604
613666
|
this.subAgents.clear();
|
|
612605
613667
|
this.activeChatViews.clear();
|
|
612606
613668
|
this.refreshActiveTelegramInteractionCount();
|
|
@@ -613529,7 +614591,7 @@ ${currentTelegramPrompt}`;
|
|
|
613529
614591
|
const toolHint = [
|
|
613530
614592
|
"You have access to isolated per-chat memory (memory_write, memory_read, memory_search) scoped to this conversation.",
|
|
613531
614593
|
"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.",
|
|
614594
|
+
"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
614595
|
"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
614596
|
formatIdentityMemoryContext(chatLabel || "Telegram private chat"),
|
|
613535
614597
|
reminderToolContract,
|
|
@@ -613695,7 +614757,7 @@ ${result.llmContent ?? result.output}` };
|
|
|
613695
614757
|
if (tool.name === "memory_search") {
|
|
613696
614758
|
return {
|
|
613697
614759
|
...tool,
|
|
613698
|
-
description: "Search only this Telegram chat's isolated
|
|
614760
|
+
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
614761
|
parameters: (() => {
|
|
613700
614762
|
const base3 = tool.parameters ?? {};
|
|
613701
614763
|
const props = base3["properties"] && typeof base3["properties"] === "object" && !Array.isArray(base3["properties"]) ? base3["properties"] : {};
|
|
@@ -613713,6 +614775,8 @@ ${result.llmContent ?? result.output}` };
|
|
|
613713
614775
|
execute: async (args) => {
|
|
613714
614776
|
const query = String(args["query"] || "").trim();
|
|
613715
614777
|
const maxResults = typeof args["max_results"] === "number" && Number.isFinite(args["max_results"]) ? Math.max(1, Math.min(20, Math.floor(args["max_results"]))) : 8;
|
|
614778
|
+
if (!query) return { success: true, output: "Search query is required." };
|
|
614779
|
+
this.ensureTelegramConversationLoaded(msgSessionKey);
|
|
613716
614780
|
const currentGroupId = chatId === void 0 ? "" : String(chatId);
|
|
613717
614781
|
const requestedGroupId = String(args["group_id"] ?? args["chat_id"] ?? "").trim();
|
|
613718
614782
|
if (requestedGroupId && currentGroupId && requestedGroupId !== currentGroupId) {
|
|
@@ -613740,32 +614804,70 @@ ${result.llmContent ?? result.output}` };
|
|
|
613740
614804
|
if (effectiveUsername && haystack.includes(`@${effectiveUsername}`)) return true;
|
|
613741
614805
|
return false;
|
|
613742
614806
|
});
|
|
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
614807
|
const queryTokens = telegramMemoryTokens(query);
|
|
613748
|
-
const
|
|
614808
|
+
const cardResults = cards.map((card) => ({
|
|
613749
614809
|
card,
|
|
613750
614810
|
score: telegramMemorySimilarity(
|
|
613751
614811
|
queryTokens,
|
|
613752
614812
|
telegramMemoryTokens([card.title, card.tags.join(" "), card.speakers.join(" "), card.notes.join(" ")].join(" "))
|
|
613753
614813
|
)
|
|
613754
614814
|
})).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 }) => {
|
|
614815
|
+
const cardLines = cardResults.map(({ card, score }) => {
|
|
613759
614816
|
const notes2 = card.notes.slice(-4).map((note) => ` - ${truncateTelegramContextLine(note, 220)}`).join("\n");
|
|
613760
614817
|
const tags = card.tags.length ? ` tags:${card.tags.slice(0, 8).join(",")}` : "";
|
|
613761
614818
|
const users = Array.isArray(card.userIds) && card.userIds.length ? ` users:${card.userIds.slice(0, 6).join(",")}` : "";
|
|
613762
614819
|
return `[${card.id}] ${card.title} (relevance ${score.toFixed(2)}${users}${tags})
|
|
613763
614820
|
${notes2}`;
|
|
613764
614821
|
});
|
|
614822
|
+
const memory = this.chatAssociativeMemory.get(msgSessionKey);
|
|
614823
|
+
const associativeFacts = memory ? [...memory.facts, ...memory.relationships].filter((fact) => {
|
|
614824
|
+
if (!wantsUserScope) return true;
|
|
614825
|
+
if (effectiveUserId !== void 0 && fact.userIds.includes(effectiveUserId)) return true;
|
|
614826
|
+
return !!effectiveUsername && fact.usernames.includes(effectiveUsername);
|
|
614827
|
+
}).map((fact) => ({
|
|
614828
|
+
fact,
|
|
614829
|
+
score: telegramMemorySimilarity(
|
|
614830
|
+
queryTokens,
|
|
614831
|
+
telegramMemoryTokens([fact.text, fact.tags.join(" "), fact.speakers.join(" ")].join(" "))
|
|
614832
|
+
) + (effectiveUserId !== void 0 && fact.userIds.includes(effectiveUserId) ? 0.3 : 0) + (effectiveUsername && fact.usernames.includes(effectiveUsername) ? 0.3 : 0)
|
|
614833
|
+
})).filter((item) => item.score > 0).sort((a2, b) => b.score - a2.score || b.fact.updatedAt - a2.fact.updatedAt).slice(0, maxResults) : [];
|
|
614834
|
+
const rawRows = this.searchTelegramSqliteMirrorRows(msgSessionKey, query, {
|
|
614835
|
+
limit: maxResults,
|
|
614836
|
+
userId: effectiveUserId,
|
|
614837
|
+
username: effectiveUsername
|
|
614838
|
+
});
|
|
614839
|
+
const episodeResults = this.searchTelegramEpisodeMemory(msgSessionKey, query, maxResults);
|
|
613765
614840
|
const scopeLabel = wantsUserScope ? `user ${effectiveUsername ? `@${effectiveUsername}` : `id ${effectiveUserId}`}` : `chat ${currentGroupId || msgSessionKey}`;
|
|
613766
|
-
|
|
613767
|
-
|
|
613768
|
-
|
|
614841
|
+
const sections = [];
|
|
614842
|
+
if (cardLines.length > 0) {
|
|
614843
|
+
sections.push(`### Scoped Memory Cards
|
|
614844
|
+
${cardLines.join("\n\n")}`);
|
|
614845
|
+
}
|
|
614846
|
+
if (associativeFacts.length > 0) {
|
|
614847
|
+
const lines = associativeFacts.map(
|
|
614848
|
+
({ fact, score }) => `- (${score.toFixed(2)}) ${telegramContextJsonString(fact.text, 300)}${fact.messageIds.length ? ` [messages:${fact.messageIds.slice(-6).join(",")}]` : ""}`
|
|
614849
|
+
);
|
|
614850
|
+
sections.push(`### Durable Associative Facts
|
|
614851
|
+
${lines.join("\n")}`);
|
|
614852
|
+
}
|
|
614853
|
+
if (episodeResults.length > 0) {
|
|
614854
|
+
sections.push(`### Episode/Knowledge Graph Recall
|
|
614855
|
+
${this.formatTelegramEpisodeMemoryResults(episodeResults)}`);
|
|
614856
|
+
}
|
|
614857
|
+
if (rawRows.length > 0) {
|
|
614858
|
+
sections.push(`### Raw SQLite Message Mirror
|
|
614859
|
+
${this.formatTelegramSqliteMirrorRows(rawRows)}`);
|
|
614860
|
+
}
|
|
614861
|
+
if (sections.length === 0) {
|
|
614862
|
+
return { success: true, output: `No scoped Telegram memories matched "${query}" in ${scopeLabel}.` };
|
|
614863
|
+
}
|
|
614864
|
+
const output = [
|
|
614865
|
+
`Scoped Telegram memory search for "${query}" in ${scopeLabel}.`,
|
|
614866
|
+
"Results are scoped to this Telegram chat and may include raw message evidence, graph episodes, associative facts, and memory cards.",
|
|
614867
|
+
"",
|
|
614868
|
+
sections.join("\n\n")
|
|
614869
|
+
].join("\n");
|
|
614870
|
+
return { success: true, output, llmContent: output };
|
|
613769
614871
|
}
|
|
613770
614872
|
};
|
|
613771
614873
|
}
|
|
@@ -616320,6 +617422,7 @@ ${caption}\r
|
|
|
616320
617422
|
}
|
|
616321
617423
|
const msg = normalizeTelegramUpdate(update2);
|
|
616322
617424
|
if (!msg) continue;
|
|
617425
|
+
this.persistTelegramRawMessage(update2, msg);
|
|
616323
617426
|
const isAdmin = this.adminUserId ? String(msg.fromUserId) === this.adminUserId || msg.username === this.adminUserId : false;
|
|
616324
617427
|
if (this.adminUserId && !this.agentConfig) {
|
|
616325
617428
|
if (!isAdmin) continue;
|
|
@@ -616665,8 +617768,8 @@ function appendCheckin(sessionId, steering) {
|
|
|
616665
617768
|
mkdirSync66(sessionsDir(), { recursive: true });
|
|
616666
617769
|
const fp = checkinPath(sessionId);
|
|
616667
617770
|
const entry = JSON.stringify({ ts: Date.now(), steering }) + "\n";
|
|
616668
|
-
const { appendFileSync:
|
|
616669
|
-
|
|
617771
|
+
const { appendFileSync: appendFileSync12 } = __require("node:fs");
|
|
617772
|
+
appendFileSync12(fp, entry, "utf-8");
|
|
616670
617773
|
} catch {
|
|
616671
617774
|
}
|
|
616672
617775
|
}
|
|
@@ -618865,7 +619968,7 @@ __export(audit_log_exports, {
|
|
|
618865
619968
|
recordAudit: () => recordAudit,
|
|
618866
619969
|
sanitizeBody: () => sanitizeBody
|
|
618867
619970
|
});
|
|
618868
|
-
import { mkdirSync as mkdirSync69, appendFileSync as
|
|
619971
|
+
import { mkdirSync as mkdirSync69, appendFileSync as appendFileSync10, readFileSync as readFileSync96, existsSync as existsSync116 } from "node:fs";
|
|
618869
619972
|
import { join as join131 } from "node:path";
|
|
618870
619973
|
function initAuditLog(omniusDir) {
|
|
618871
619974
|
auditDir = join131(omniusDir, "audit");
|
|
@@ -618880,7 +619983,7 @@ function recordAudit(record) {
|
|
|
618880
619983
|
if (!initialized) return;
|
|
618881
619984
|
try {
|
|
618882
619985
|
const line = JSON.stringify(record) + "\n";
|
|
618883
|
-
|
|
619986
|
+
appendFileSync10(auditFile, line, "utf-8");
|
|
618884
619987
|
} catch {
|
|
618885
619988
|
}
|
|
618886
619989
|
}
|
|
@@ -641179,7 +642282,7 @@ import { fileURLToPath as fileURLToPath18 } from "node:url";
|
|
|
641179
642282
|
import {
|
|
641180
642283
|
readFileSync as readFileSync105,
|
|
641181
642284
|
writeFileSync as writeFileSync70,
|
|
641182
|
-
appendFileSync as
|
|
642285
|
+
appendFileSync as appendFileSync11,
|
|
641183
642286
|
rmSync as rmSync7,
|
|
641184
642287
|
readdirSync as readdirSync46,
|
|
641185
642288
|
mkdirSync as mkdirSync78
|
|
@@ -645673,7 +646776,7 @@ This is an independent background session started from /background.`
|
|
|
645673
646776
|
if (!line.trim()) return;
|
|
645674
646777
|
try {
|
|
645675
646778
|
mkdirSync78(HISTORY_DIR, { recursive: true });
|
|
645676
|
-
|
|
646779
|
+
appendFileSync11(HISTORY_FILE, line + "\n", "utf8");
|
|
645677
646780
|
if (Math.random() < 0.02) {
|
|
645678
646781
|
const all2 = readFileSync105(HISTORY_FILE, "utf8").trim().split("\n");
|
|
645679
646782
|
if (all2.length > MAX_HISTORY_LINES) {
|
|
@@ -651460,12 +652563,12 @@ function crashLog(label, err) {
|
|
|
651460
652563
|
const logLine = `[${timestamp}] ${label}: ${msg}
|
|
651461
652564
|
`;
|
|
651462
652565
|
try {
|
|
651463
|
-
const { appendFileSync:
|
|
652566
|
+
const { appendFileSync: appendFileSync12, mkdirSync: mkdirSync81 } = __require("node:fs");
|
|
651464
652567
|
const { join: join148 } = __require("node:path");
|
|
651465
652568
|
const { homedir: homedir52 } = __require("node:os");
|
|
651466
652569
|
const logDir = join148(homedir52(), ".omnius");
|
|
651467
652570
|
mkdirSync81(logDir, { recursive: true });
|
|
651468
|
-
|
|
652571
|
+
appendFileSync12(join148(logDir, "crash.log"), logLine);
|
|
651469
652572
|
} catch {
|
|
651470
652573
|
}
|
|
651471
652574
|
try {
|