blun-king-cli 9.1.404 → 9.1.406
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/LIESMICH.txt +22 -5
- package/README.md +21 -5
- package/bin/relationship-continuity-policy.cjs +29 -10
- package/bin/relationship-learning-policy.cjs +1 -0
- package/bin/telegram-console-status-policy.cjs +102 -0
- package/bin/telegram-remote-status-policy.cjs +47 -4
- package/blun.mjs +24 -0
- package/package.json +1 -1
- package/telegram-plugin/dist/bridge.mjs +30 -5
package/LIESMICH.txt
CHANGED
|
@@ -307,11 +307,15 @@ Rechner aus Telegram prüfen
|
|
|
307
307
|
----------------------------
|
|
308
308
|
Der Telegram-Befehl /status prüft die lokale Verbindung ohne Modellaufruf. Er
|
|
309
309
|
zeigt den Rechnernamen, die installierte BLUN-Version, die Laufzeit der
|
|
310
|
-
Telegram-Brücke, die Verbindung
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
310
|
+
Telegram-Brücke, die Verbindung und Laufzeit der Konsole, den aktuellen
|
|
311
|
+
Zustellweg, den Konsolen-Herzschlag, den letzten Warteschlangen-Fortschritt sowie
|
|
312
|
+
den aktuellen Arbeitsschritt. Die Warteschlange wird als tatsächlich ungelesener
|
|
313
|
+
Anteil und Gesamtgröße gemessen; ein Prüfpunkt wird nur für exakt dieselbe
|
|
314
|
+
Warteschlangendatei akzeptiert. Damit bleibt der Zustand auch dann abfragbar,
|
|
315
|
+
wenn die Konsole hängt oder der Modellanbieter ausgelastet ist. Die Ausgabe
|
|
316
|
+
enthält keine Chat-IDs, Dateipfade, Zugangsdaten, Nachrichteninhalte oder
|
|
317
|
+
vollständigen Aufgabenlisten. Nicht verbundene Absender erhalten weiterhin nur
|
|
318
|
+
die Kopplungsanweisung.
|
|
315
319
|
|
|
316
320
|
Nachweisbare Arbeitsabläufe
|
|
317
321
|
---------------------------
|
|
@@ -400,6 +404,19 @@ Bei identischem Inhalt bleiben Dateidentität und Änderungszeit erhalten; die
|
|
|
400
404
|
Prüfung und Härtung der privaten Dateirechte läuft trotzdem weiter. Echte
|
|
401
405
|
Änderungen verwenden unverändert den crash-sicheren atomaren Schreibweg.
|
|
402
406
|
|
|
407
|
+
Verlässliche Beziehungskontinuität
|
|
408
|
+
----------------------------------
|
|
409
|
+
|
|
410
|
+
Ab BLUN King 9.1.406 wird ein erneut zugestelltes privates Telegram-Ereignis
|
|
411
|
+
mit demselben Zeitstempel und Text nur einmal gespeichert. Eine tatsächlich
|
|
412
|
+
später wiederholte Aussage bleibt dagegen Teil des Verlaufs.
|
|
413
|
+
|
|
414
|
+
Natürliche ausdrückliche Hinweise wie „Dieter ist dein Boss, bitte merken“
|
|
415
|
+
werden auch dann erkannt, wenn die Bitte um Erinnerung am Ende steht. Sie
|
|
416
|
+
gelangen als Referenzdaten in einen begrenzten, nur ergänzbaren
|
|
417
|
+
Beziehungskontext. Sie erteilen oder ändern niemals Berechtigungen;
|
|
418
|
+
Gruppeninhalte, Geheimnisse und Zugriffsaussagen bleiben ausgeschlossen.
|
|
419
|
+
|
|
403
420
|
Der eigentliche Anhang und die vollständige Nutzernachricht bleiben für den Zug
|
|
404
421
|
unverändert verfügbar. Die Begrenzung betrifft ausschließlich die kleine
|
|
405
422
|
lexikalische Vorauswahl von höchstens zwei Werkzeugschemas; ToolSearch und alle
|
package/README.md
CHANGED
|
@@ -313,11 +313,15 @@ Sicherheitsgründen als normale Chatnachrichten behandelt.
|
|
|
313
313
|
|
|
314
314
|
Der Telegram-Befehl `/status` prüft die lokale Verbindung ohne Modellaufruf.
|
|
315
315
|
Er zeigt den Rechnernamen, die installierte BLUN-Version, die Laufzeit der
|
|
316
|
-
Telegram-Brücke, die Verbindung
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
316
|
+
Telegram-Brücke, die Verbindung und Laufzeit der Konsole, den aktuellen
|
|
317
|
+
Zustellweg, den Konsolen-Herzschlag, den letzten Warteschlangen-Fortschritt sowie
|
|
318
|
+
den aktuellen Arbeitsschritt. Die Warteschlange wird als tatsächlich ungelesener
|
|
319
|
+
Anteil und Gesamtgröße gemessen; ein Prüfpunkt wird nur für exakt dieselbe
|
|
320
|
+
Warteschlangendatei akzeptiert. Damit bleibt der Zustand auch dann abfragbar,
|
|
321
|
+
wenn die Konsole hängt oder der Modellanbieter ausgelastet ist. Die Ausgabe
|
|
322
|
+
enthält keine Chat-IDs, Dateipfade, Zugangsdaten, Nachrichteninhalte oder
|
|
323
|
+
vollständigen Aufgabenlisten. Nicht verbundene Absender erhalten weiterhin nur
|
|
324
|
+
die Kopplungsanweisung.
|
|
321
325
|
|
|
322
326
|
Beim ersten Start werden das Telegram-Plugin und die mitgelieferten Skills
|
|
323
327
|
eingerichtet. Die Anmeldung erfolgt anschließend in der
|
|
@@ -408,6 +412,18 @@ Bei identischem Inhalt bleiben Dateidentität und Änderungszeit erhalten; die
|
|
|
408
412
|
Prüfung und Härtung der privaten Dateirechte läuft trotzdem weiter. Echte
|
|
409
413
|
Änderungen verwenden unverändert den crash-sicheren atomaren Schreibweg.
|
|
410
414
|
|
|
415
|
+
## Verlässliche Beziehungskontinuität
|
|
416
|
+
|
|
417
|
+
Ab BLUN King 9.1.406 wird ein erneut zugestelltes privates Telegram-Ereignis
|
|
418
|
+
mit demselben Zeitstempel und Text nur einmal gespeichert. Eine tatsächlich
|
|
419
|
+
später wiederholte Aussage bleibt dagegen Teil des Verlaufs.
|
|
420
|
+
|
|
421
|
+
Natürliche ausdrückliche Hinweise wie „Dieter ist dein Boss, bitte merken“
|
|
422
|
+
werden auch dann erkannt, wenn die Bitte um Erinnerung am Ende steht. Sie
|
|
423
|
+
gelangen als Referenzdaten in einen begrenzten, nur ergänzbaren
|
|
424
|
+
Beziehungskontext. Sie erteilen oder ändern niemals Berechtigungen;
|
|
425
|
+
Gruppeninhalte, Geheimnisse und Zugriffsaussagen bleiben ausgeschlossen.
|
|
426
|
+
|
|
411
427
|
Der eigentliche Anhang und die vollständige Nutzernachricht bleiben für den Zug
|
|
412
428
|
unverändert verfügbar. Die Begrenzung betrifft ausschließlich die kleine
|
|
413
429
|
lexikalische Vorauswahl von höchstens zwei Werkzeugschemas; ToolSearch und alle
|
|
@@ -86,6 +86,22 @@ function attentionForReturn({ root, agentId, actorId, previousAt, currentAt }) {
|
|
|
86
86
|
return writeJsonAtomic(root, target, attention) ? attention : undefined;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
function normalizedJournalEntries(value) {
|
|
90
|
+
if (!Array.isArray(value)) return [];
|
|
91
|
+
const entries = [];
|
|
92
|
+
const seen = new Set();
|
|
93
|
+
for (const entry of value) {
|
|
94
|
+
const occurredAt = trustedTimestamp(entry?.occurred_at);
|
|
95
|
+
const text = redactSecrets(entry?.text);
|
|
96
|
+
if (!occurredAt || !text) continue;
|
|
97
|
+
const key = `${occurredAt}\0${text}`;
|
|
98
|
+
if (seen.has(key)) continue;
|
|
99
|
+
seen.add(key);
|
|
100
|
+
entries.push({ occurred_at: occurredAt, text });
|
|
101
|
+
}
|
|
102
|
+
return entries.slice(-MAX_ENTRIES);
|
|
103
|
+
}
|
|
104
|
+
|
|
89
105
|
function prepareRelationshipTurn(input = {}) {
|
|
90
106
|
const root = path.resolve(String(input.root ?? ''));
|
|
91
107
|
const agentId = safeId(input.agentId);
|
|
@@ -98,14 +114,14 @@ function prepareRelationshipTurn(input = {}) {
|
|
|
98
114
|
const text = redactSecrets(input.text);
|
|
99
115
|
const target = path.resolve(root, 'agents', agentId, 'journal', 'recent', `${actorId}.json`);
|
|
100
116
|
const existing = readJson(root, target);
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
117
|
+
const rawEntries = Array.isArray(existing?.entries) ? existing.entries.slice(-MAX_ENTRIES) : [];
|
|
118
|
+
const entries = normalizedJournalEntries(rawEntries);
|
|
119
|
+
const currentKey = text ? `${occurredAt}\0${text}` : '';
|
|
120
|
+
const historical = currentKey
|
|
121
|
+
? entries.filter((entry) => `${entry.occurred_at}\0${entry.text}` !== currentKey)
|
|
122
|
+
: entries;
|
|
123
|
+
const previous = historical.at(-1);
|
|
124
|
+
const recent = historical.slice(-MAX_RECENT_CONTEXT);
|
|
109
125
|
const attention = attentionForReturn({
|
|
110
126
|
root,
|
|
111
127
|
agentId,
|
|
@@ -115,8 +131,11 @@ function prepareRelationshipTurn(input = {}) {
|
|
|
115
131
|
});
|
|
116
132
|
|
|
117
133
|
if (text) {
|
|
118
|
-
const
|
|
119
|
-
|
|
134
|
+
const alreadyStored = entries.some((entry) => `${entry.occurred_at}\0${entry.text}` === currentKey);
|
|
135
|
+
const next = alreadyStored ? entries : [...entries, { occurred_at: occurredAt, text }].slice(-MAX_ENTRIES);
|
|
136
|
+
if (JSON.stringify(next) !== JSON.stringify(rawEntries)) {
|
|
137
|
+
writeJsonAtomic(root, target, { version: 1, actor_id: actorId, entries: next });
|
|
138
|
+
}
|
|
120
139
|
}
|
|
121
140
|
return { recent, attention };
|
|
122
141
|
}
|
|
@@ -30,6 +30,7 @@ const CANDIDATE_PATTERNS = [
|
|
|
30
30
|
kind: 'explicit_relationship_note',
|
|
31
31
|
patterns: [
|
|
32
32
|
/^(?:bitte\s+)?merk(?:e)?\s+dir\s*(?::|,|\s+dass\s+)?\s*(.+)$/iu,
|
|
33
|
+
/^(.+?)(?:\s*[,;:\u2014-]\s*|\s+)bitte\s+merken[.!?]*$/iu,
|
|
33
34
|
/^(?:please\s+)?remember(?:\s+that)?\s*(?::|,)?\s*(.+)$/iu,
|
|
34
35
|
],
|
|
35
36
|
},
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } = require('node:fs');
|
|
4
|
+
const { join } = require('node:path');
|
|
5
|
+
|
|
6
|
+
const STATUS_FILE = 'telegram-console-status.json';
|
|
7
|
+
const MAX_STATUS_BYTES = 16_384;
|
|
8
|
+
const MAX_TASK_CHARS = 240;
|
|
9
|
+
|
|
10
|
+
function validInstant(value) {
|
|
11
|
+
if (typeof value !== 'string' || value.length === 0) return undefined;
|
|
12
|
+
return Number.isFinite(Date.parse(value)) ? value : undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function boundedTask(value) {
|
|
16
|
+
if (typeof value !== 'string') return undefined;
|
|
17
|
+
const title = value.replace(/\s+/gu, ' ').trim();
|
|
18
|
+
return title.length > 0 ? title.slice(0, MAX_TASK_CHARS) : undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function buildTelegramConsoleStatus(input = {}) {
|
|
22
|
+
const capturedAt = validInstant(input.capturedAt);
|
|
23
|
+
const processStartedAt = validInstant(input.processStartedAt);
|
|
24
|
+
const pid = Number.isSafeInteger(input.pid) && input.pid > 1 ? input.pid : undefined;
|
|
25
|
+
const step = Number.isSafeInteger(input.step) && input.step >= 0 ? input.step : 0;
|
|
26
|
+
if (capturedAt === undefined || processStartedAt === undefined || pid === undefined
|
|
27
|
+
|| typeof input.turnActive !== 'boolean') {
|
|
28
|
+
throw new TypeError('invalid Telegram console status');
|
|
29
|
+
}
|
|
30
|
+
const activeTask = input.turnActive
|
|
31
|
+
? boundedTask(input.todos?.find((todo) => todo?.status === 'in_progress')?.title)
|
|
32
|
+
: undefined;
|
|
33
|
+
return {
|
|
34
|
+
version: 1,
|
|
35
|
+
pid,
|
|
36
|
+
capturedAt,
|
|
37
|
+
processStartedAt,
|
|
38
|
+
turnActive: input.turnActive,
|
|
39
|
+
step,
|
|
40
|
+
...(activeTask === undefined ? {} : { activeTask }),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function parseTelegramConsoleStatus(value, options = {}) {
|
|
45
|
+
try {
|
|
46
|
+
const text = typeof value === 'string' ? value : JSON.stringify(value);
|
|
47
|
+
if (text.length === 0 || Buffer.byteLength(text, 'utf8') > MAX_STATUS_BYTES) return undefined;
|
|
48
|
+
const parsed = typeof value === 'string' ? JSON.parse(value) : value;
|
|
49
|
+
if (parsed?.version !== 1 || !Number.isSafeInteger(parsed.pid) || parsed.pid <= 1
|
|
50
|
+
|| (options.expectedPid !== undefined && parsed.pid !== options.expectedPid)
|
|
51
|
+
|| validInstant(parsed.capturedAt) === undefined
|
|
52
|
+
|| validInstant(parsed.processStartedAt) === undefined
|
|
53
|
+
|| typeof parsed.turnActive !== 'boolean'
|
|
54
|
+
|| !Number.isSafeInteger(parsed.step) || parsed.step < 0) return undefined;
|
|
55
|
+
const activeTask = boundedTask(parsed.activeTask);
|
|
56
|
+
return {
|
|
57
|
+
version: 1,
|
|
58
|
+
pid: parsed.pid,
|
|
59
|
+
capturedAt: parsed.capturedAt,
|
|
60
|
+
processStartedAt: parsed.processStartedAt,
|
|
61
|
+
turnActive: parsed.turnActive,
|
|
62
|
+
step: parsed.step,
|
|
63
|
+
...(parsed.turnActive && activeTask !== undefined ? { activeTask } : {}),
|
|
64
|
+
};
|
|
65
|
+
} catch {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function writeTelegramConsoleStatus(input = {}) {
|
|
71
|
+
const status = buildTelegramConsoleStatus(input);
|
|
72
|
+
const directory = input.directory;
|
|
73
|
+
if (typeof directory !== 'string' || directory.length === 0) {
|
|
74
|
+
throw new TypeError('invalid Telegram console status directory');
|
|
75
|
+
}
|
|
76
|
+
mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
77
|
+
const target = join(directory, STATUS_FILE);
|
|
78
|
+
const temporary = join(directory, `.telegram-console-status.${status.pid}.${Date.now()}.tmp`);
|
|
79
|
+
try {
|
|
80
|
+
writeFileSync(temporary, `${JSON.stringify(status)}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
81
|
+
renameSync(temporary, target);
|
|
82
|
+
} finally {
|
|
83
|
+
rmSync(temporary, { force: true });
|
|
84
|
+
}
|
|
85
|
+
return status;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function readTelegramConsoleStatus(directory, options = {}) {
|
|
89
|
+
try {
|
|
90
|
+
return parseTelegramConsoleStatus(readFileSync(join(directory, STATUS_FILE), 'utf8'), options);
|
|
91
|
+
} catch {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = {
|
|
97
|
+
STATUS_FILE,
|
|
98
|
+
buildTelegramConsoleStatus,
|
|
99
|
+
parseTelegramConsoleStatus,
|
|
100
|
+
readTelegramConsoleStatus,
|
|
101
|
+
writeTelegramConsoleStatus,
|
|
102
|
+
};
|
|
@@ -23,8 +23,35 @@ function formatDuration(seconds) {
|
|
|
23
23
|
function formatBytes(bytes) {
|
|
24
24
|
const value = Math.max(0, Number(bytes) || 0);
|
|
25
25
|
if (value < 1024) return `${Math.round(value)} B`;
|
|
26
|
-
if (value < 1024 * 1024) return `${(value / 1024).toFixed(1)} KB`;
|
|
27
|
-
return `${(value / (1024 * 1024)).toFixed(1)} MB`;
|
|
26
|
+
if (value < 1024 * 1024) return `${(value / 1024).toFixed(1).replace('.', ',')} KB`;
|
|
27
|
+
return `${(value / (1024 * 1024)).toFixed(1).replace('.', ',')} MB`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function formatAge(value, capturedAt) {
|
|
31
|
+
const then = Date.parse(value);
|
|
32
|
+
const now = Date.parse(capturedAt);
|
|
33
|
+
if (!Number.isFinite(then) || !Number.isFinite(now) || then > now) return undefined;
|
|
34
|
+
const seconds = Math.floor((now - then) / 1000);
|
|
35
|
+
return seconds < 60 ? `vor ${seconds} s` : `vor ${formatDuration(seconds)}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function resolveTelegramQueueSnapshot(input = {}) {
|
|
39
|
+
const totalBytes = Number.isSafeInteger(input.queueBytes) && input.queueBytes >= 0
|
|
40
|
+
? input.queueBytes
|
|
41
|
+
: 0;
|
|
42
|
+
const checkpoint = input.checkpoint;
|
|
43
|
+
const checkpointValid = checkpoint?.version === 1
|
|
44
|
+
&& typeof input.queueFileId === 'string'
|
|
45
|
+
&& input.queueFileId.length > 0
|
|
46
|
+
&& checkpoint.fileId === input.queueFileId
|
|
47
|
+
&& Number.isSafeInteger(checkpoint.offset)
|
|
48
|
+
&& checkpoint.offset >= 0
|
|
49
|
+
&& checkpoint.offset <= totalBytes;
|
|
50
|
+
return {
|
|
51
|
+
totalBytes,
|
|
52
|
+
unreadBytes: checkpointValid ? totalBytes - checkpoint.offset : undefined,
|
|
53
|
+
checkpointValid,
|
|
54
|
+
};
|
|
28
55
|
}
|
|
29
56
|
|
|
30
57
|
function buildTelegramRemoteStatus(input) {
|
|
@@ -34,11 +61,23 @@ function buildTelegramRemoteStatus(input) {
|
|
|
34
61
|
|
|
35
62
|
const username = input.username ? `@${String(input.username).replace(/^@/u, '')}` : 'verbundener Nutzer';
|
|
36
63
|
const tui = input.tuiFresh
|
|
37
|
-
? `verbunden${Number.isInteger(input.tuiPid) ? ` (PID ${input.tuiPid}
|
|
64
|
+
? `verbunden${Number.isInteger(input.tuiPid) ? ` (PID ${input.tuiPid}${input.consoleStatus?.processStartedAt
|
|
65
|
+
? `, ${formatDuration((Date.parse(input.capturedAt) - Date.parse(input.consoleStatus.processStartedAt)) / 1000)}`
|
|
66
|
+
: ''})` : ''}`
|
|
38
67
|
: 'nicht verbunden';
|
|
39
68
|
const delivery = input.deliveryTarget === 'tui'
|
|
40
69
|
? 'Konsolen-Warteschlange'
|
|
41
70
|
: 'Brücke ohne Konsole';
|
|
71
|
+
const heartbeatAge = formatAge(input.tuiHeartbeatAt, input.capturedAt);
|
|
72
|
+
const checkpointAge = formatAge(input.queueCheckpointAt, input.capturedAt);
|
|
73
|
+
const workLines = input.consoleStatus === undefined
|
|
74
|
+
? ['Arbeitsstatus: nicht verfügbar']
|
|
75
|
+
: input.consoleStatus.turnActive
|
|
76
|
+
? [
|
|
77
|
+
`Arbeitsstatus: aktiv, Schritt ${input.consoleStatus.step}`,
|
|
78
|
+
...(input.consoleStatus.activeTask === undefined ? [] : [`Aktive Aufgabe: ${input.consoleStatus.activeTask}`]),
|
|
79
|
+
]
|
|
80
|
+
: ['Arbeitsstatus: Leerlauf'];
|
|
42
81
|
return [
|
|
43
82
|
`BLUN-Fernstatus für ${username}`,
|
|
44
83
|
`Rechner: ${input.machineName || 'unbekannt'}`,
|
|
@@ -46,7 +85,10 @@ function buildTelegramRemoteStatus(input) {
|
|
|
46
85
|
`Brücke: aktiv (PID ${input.bridgePid}, ${formatDuration(input.bridgeUptimeSeconds)})`,
|
|
47
86
|
`Konsole: ${tui}`,
|
|
48
87
|
`Zustellung: ${delivery}`,
|
|
49
|
-
`Warteschlange: ${formatBytes(input.queueBytes)}`,
|
|
88
|
+
`Warteschlange: ${input.queueUnreadBytes === undefined ? 'unbekannt' : `${formatBytes(input.queueUnreadBytes)} ungelesen`} (${formatBytes(input.queueBytes)} gesamt)`,
|
|
89
|
+
...(heartbeatAge === undefined ? [] : [`Konsolen-Herzschlag: ${heartbeatAge}`]),
|
|
90
|
+
...(checkpointAge === undefined ? [] : [`Warteschlangen-Fortschritt: ${checkpointAge}`]),
|
|
91
|
+
...workLines,
|
|
50
92
|
`Gemessen: ${input.capturedAt}`,
|
|
51
93
|
].join('\n');
|
|
52
94
|
}
|
|
@@ -55,5 +97,6 @@ module.exports = {
|
|
|
55
97
|
buildTelegramRemoteStatus,
|
|
56
98
|
formatBytes,
|
|
57
99
|
formatDuration,
|
|
100
|
+
resolveTelegramQueueSnapshot,
|
|
58
101
|
resolveTelegramRemoteVersion,
|
|
59
102
|
};
|
package/blun.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import { dirname as __cjsShimDirname } from 'node:path';
|
|
|
5
5
|
const __filename = __cjsShimFileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = __cjsShimDirname(__filename);
|
|
7
7
|
import { createRequire } from "node:module";
|
|
8
|
+
import telegramConsoleStatusPolicy from "./bin/telegram-console-status-policy.cjs";
|
|
8
9
|
import crypto$1, { createHash, randomBytes, randomInt, randomUUID, timingSafeEqual } from "node:crypto";
|
|
9
10
|
import * as fs$16 from "node:fs";
|
|
10
11
|
import Kt, { accessSync, appendFileSync, chmodSync, closeSync, constants, copyFileSync, createReadStream, createWriteStream, existsSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, promises, readFileSync, readSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
@@ -34,6 +35,7 @@ import * as posixPath from "node:path/posix";
|
|
|
34
35
|
import * as win32Path from "node:path/win32";
|
|
35
36
|
import { createServer } from "node:http";
|
|
36
37
|
import { pipeline as pipeline$1 } from "node:stream/promises";
|
|
38
|
+
const { writeTelegramConsoleStatus } = telegramConsoleStatusPolicy;
|
|
37
39
|
import { EventEmitter as EventEmitter$1 } from "node:events";
|
|
38
40
|
import { StringDecoder } from "node:string_decoder";
|
|
39
41
|
import co from "node:assert";
|
|
@@ -511029,6 +511031,8 @@ var StreamingUIController = class {
|
|
|
511029
511031
|
pendingToolCallFlushIds = /* @__PURE__ */ new Set();
|
|
511030
511032
|
_currentTurnId = void 0;
|
|
511031
511033
|
_currentStep = 0;
|
|
511034
|
+
_remoteTodos = [];
|
|
511035
|
+
_remoteProcessStartedAt = new Date(Date.now() - process.uptime() * 1e3).toISOString();
|
|
511032
511036
|
_liveTurnStartedAtMs = void 0;
|
|
511033
511037
|
_liveOutputTokens = new LiveOutputTokenCounter();
|
|
511034
511038
|
_countedToolCallIds = /* @__PURE__ */ new Set();
|
|
@@ -511054,9 +511058,25 @@ var StreamingUIController = class {
|
|
|
511054
511058
|
}
|
|
511055
511059
|
setTurnId(turnId) {
|
|
511056
511060
|
this._currentTurnId = turnId;
|
|
511061
|
+
this.publishTelegramConsoleStatus();
|
|
511057
511062
|
}
|
|
511058
511063
|
setStep(step) {
|
|
511059
511064
|
this._currentStep = step;
|
|
511065
|
+
this.publishTelegramConsoleStatus();
|
|
511066
|
+
}
|
|
511067
|
+
publishTelegramConsoleStatus() {
|
|
511068
|
+
if (!telegramChannelAttachEnabled()) return;
|
|
511069
|
+
try {
|
|
511070
|
+
writeTelegramConsoleStatus({
|
|
511071
|
+
directory: telegramStateDir(),
|
|
511072
|
+
capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
511073
|
+
pid: process.pid,
|
|
511074
|
+
processStartedAt: this._remoteProcessStartedAt,
|
|
511075
|
+
step: this._currentStep,
|
|
511076
|
+
todos: this._remoteTodos,
|
|
511077
|
+
turnActive: this._currentTurnId !== void 0
|
|
511078
|
+
});
|
|
511079
|
+
} catch {}
|
|
511060
511080
|
}
|
|
511061
511081
|
hasActiveTurn() {
|
|
511062
511082
|
return this._currentTurnId !== void 0;
|
|
@@ -511364,8 +511384,10 @@ var StreamingUIController = class {
|
|
|
511364
511384
|
this._pendingReadGroup = null;
|
|
511365
511385
|
this._currentTurnId = void 0;
|
|
511366
511386
|
this._currentStep = 0;
|
|
511387
|
+
this._remoteTodos = [];
|
|
511367
511388
|
this._streamingToolCallArguments.clear();
|
|
511368
511389
|
this.pendingToolCallFlushIds.clear();
|
|
511390
|
+
this.publishTelegramConsoleStatus();
|
|
511369
511391
|
this.host.state.ui.requestRender();
|
|
511370
511392
|
}
|
|
511371
511393
|
disposeActiveThinkingComponent() {
|
|
@@ -511627,9 +511649,11 @@ var StreamingUIController = class {
|
|
|
511627
511649
|
}
|
|
511628
511650
|
setTodoList(todos) {
|
|
511629
511651
|
const { state } = this.host;
|
|
511652
|
+
this._remoteTodos = todos;
|
|
511630
511653
|
state.todoPanel.setTodos(todos);
|
|
511631
511654
|
state.todoPanelContainer.clear();
|
|
511632
511655
|
if (!state.todoPanel.isEmpty()) state.todoPanelContainer.addChild(state.todoPanel);
|
|
511656
|
+
this.publishTelegramConsoleStatus();
|
|
511633
511657
|
state.ui.requestRender();
|
|
511634
511658
|
}
|
|
511635
511659
|
beginCompaction(instruction) {
|
package/package.json
CHANGED
|
@@ -7,9 +7,11 @@ import { Readable, Writable } from "node:stream";
|
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { hostname } from "node:os";
|
|
9
9
|
import remoteStatusPolicy from "../../bin/telegram-remote-status-policy.cjs";
|
|
10
|
+
import consoleStatusPolicy from "../../bin/telegram-console-status-policy.cjs";
|
|
10
11
|
import telegramApprovalRelay from "../../bin/telegram-approval-relay.cjs";
|
|
11
12
|
import privateConversationPolicy from "../bin/telegram-private-conversation-policy.cjs";
|
|
12
|
-
const { buildTelegramRemoteStatus, resolveTelegramRemoteVersion } = remoteStatusPolicy;
|
|
13
|
+
const { buildTelegramRemoteStatus, resolveTelegramQueueSnapshot, resolveTelegramRemoteVersion } = remoteStatusPolicy;
|
|
14
|
+
const { parseTelegramConsoleStatus } = consoleStatusPolicy;
|
|
13
15
|
const { buildTelegramApprovalCard, isAuthorizedTelegramApprovalCallback, listPendingTelegramApprovals, markTelegramApprovalSent, parseTelegramApprovalCallback, resolveTelegramApprovalTarget, wasTelegramApprovalSent, writeTelegramApprovalResponse } = telegramApprovalRelay;
|
|
14
16
|
const { findDuplicateReplyWithoutNewInbound, isPrivateInternalStatusReply, sanitizePrivateConversationReply } = privateConversationPolicy;
|
|
15
17
|
//#region ../../node_modules/pathe/dist/shared/pathe.M-eThtNZ.mjs
|
|
@@ -4759,26 +4761,49 @@ bot.command("status", async (ctx) => {
|
|
|
4759
4761
|
if (access.allowFrom.includes(senderId)) {
|
|
4760
4762
|
let version = "unbekannt";
|
|
4761
4763
|
let tuiPid;
|
|
4762
|
-
let
|
|
4764
|
+
let tuiHeartbeatAt;
|
|
4765
|
+
let queueStats;
|
|
4766
|
+
let checkpoint;
|
|
4767
|
+
let queueCheckpointAt;
|
|
4768
|
+
let consoleStatus;
|
|
4769
|
+
const capturedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4763
4770
|
try {
|
|
4764
4771
|
version = JSON.parse(readFileSync(join(dirname(KING_ENTRY), "package.json"), "utf8")).version ?? version;
|
|
4765
4772
|
} catch {}
|
|
4766
4773
|
try {
|
|
4767
4774
|
tuiPid = parseInt(readFileSync(tuiPidFile(), "utf8"), 10);
|
|
4775
|
+
tuiHeartbeatAt = statSync(tuiPidFile()).mtime.toISOString();
|
|
4768
4776
|
} catch {}
|
|
4769
4777
|
try {
|
|
4770
|
-
|
|
4778
|
+
queueStats = statSync(tuiInboundQueueFile());
|
|
4771
4779
|
} catch {}
|
|
4780
|
+
try {
|
|
4781
|
+
const checkpointFile = join(stateDir(), "inbound-queue.checkpoint.json");
|
|
4782
|
+
checkpoint = JSON.parse(readFileSync(checkpointFile, "utf8"));
|
|
4783
|
+
queueCheckpointAt = statSync(checkpointFile).mtime.toISOString();
|
|
4784
|
+
} catch {}
|
|
4785
|
+
if (Number.isInteger(tuiPid)) try {
|
|
4786
|
+
consoleStatus = parseTelegramConsoleStatus(readFileSync(join(stateDir(), "telegram-console-status.json"), "utf8"), { expectedPid: tuiPid });
|
|
4787
|
+
} catch {}
|
|
4788
|
+
const queueSnapshot = resolveTelegramQueueSnapshot({
|
|
4789
|
+
checkpoint,
|
|
4790
|
+
queueBytes: queueStats?.size,
|
|
4791
|
+
queueFileId: queueStats === void 0 ? void 0 : `${queueStats.dev}:${queueStats.ino}:${queueStats.birthtimeMs}`
|
|
4792
|
+
});
|
|
4772
4793
|
await ctx.reply(buildTelegramRemoteStatus({
|
|
4773
4794
|
access,
|
|
4774
4795
|
bridgePid: process.pid,
|
|
4775
4796
|
bridgeUptimeSeconds: process.uptime(),
|
|
4776
|
-
capturedAt
|
|
4797
|
+
capturedAt,
|
|
4798
|
+
consoleStatus,
|
|
4777
4799
|
deliveryTarget: deliveryTarget(),
|
|
4778
4800
|
machineName: hostname(),
|
|
4779
|
-
queueBytes,
|
|
4801
|
+
queueBytes: queueSnapshot.totalBytes,
|
|
4802
|
+
queueCheckpointAt,
|
|
4803
|
+
queueUnreadBytes: queueSnapshot.unreadBytes,
|
|
4780
4804
|
senderId,
|
|
4781
4805
|
tuiFresh: isTuiLeaseFresh(),
|
|
4806
|
+
tuiHeartbeatAt,
|
|
4782
4807
|
tuiPid: Number.isInteger(tuiPid) ? tuiPid : void 0,
|
|
4783
4808
|
username: ctx.from?.username,
|
|
4784
4809
|
version: resolveTelegramRemoteVersion({
|