@blunking/codexlink 0.1.10 → 0.1.14
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/README.md +6 -4
- package/package.json +1 -1
- package/telegram-doctor.ps1 +1 -1
- package/telegram-plugin/dispatcher.js +1 -1
- package/telegram-plugin/lib/bridge.js +49 -19
- package/telegram-plugin/lib/telegram.js +2 -1
- package/telegram-plugin/poller.js +1 -1
- package/telegram-setup.ps1 +2 -1
- package/telegram-title-embed.ps1 +9 -2
- package/telegram-title-watcher.ps1 +6 -0
package/README.md
CHANGED
|
@@ -105,13 +105,15 @@ blun-codex --telegram plugin
|
|
|
105
105
|
Status:
|
|
106
106
|
|
|
107
107
|
```powershell
|
|
108
|
-
blun-codex telegram-status
|
|
109
|
-
```
|
|
110
|
-
|
|
108
|
+
blun-codex telegram-status
|
|
109
|
+
```
|
|
110
|
+
|
|
111
111
|
Wenn waehrend einer laufenden Arbeit Telegram-Nachrichten gepuffert werden, bleibt die sichtbare CLI-Eingabe unberuehrt. Pending-Nachrichten bleiben im Fenstertitel/Status sichtbar, bis die Antwort raus ist oder sie wirklich ablaufen. Den Queue-Stand kannst du jederzeit mit `blun-codex telegram-status` pruefen.
|
|
112
112
|
|
|
113
113
|
Der automatische Progress-Hinweis ist bewusst defensiv: standardmaessig sendet Telegram nur finale Antworten plus bei laengeren echten Arbeitslaeufen einen neutralen Status. Interne Commentary-Texte werden nicht als zweite fachliche Antwort gespiegelt. Wer das alte Verhalten will, kann `BLUN_TELEGRAM_PROGRESS_RELAY=commentary` setzen; mit `off` werden Progress-Hinweise ganz deaktiviert.
|
|
114
114
|
|
|
115
|
+
Wichtig bei mehreren Profilen: ein Telegram-Bot-Token darf nicht gleichzeitig von alten oder fremden Pollern abgefragt werden. Sonst meldet Telegram `Conflict: terminated by other getUpdates request`, und Nachrichten koennen verspaetet oder gar nicht in der sichtbaren CLI landen. Aktuelle Versionen pollen non-blocking und schneller; wenn trotzdem Conflicts auftauchen, alle alten `blun-codex telegram-plugin` Fenster fuer denselben Bot schliessen und genau eine aktuelle Session starten.
|
|
116
|
+
|
|
115
117
|
Wenn mehrere Agents denselben Gruppenchat nutzen, kann ein Agent andere Agent-Namen als Fremdroute markieren. Dann werden Owner-Nachrichten wie `Frida mach weiter` nicht in Ottos Session gezogen:
|
|
116
118
|
|
|
117
119
|
```text
|
|
@@ -244,7 +246,7 @@ The bundled plugin lives under `telegram-plugin/` and contains:
|
|
|
244
246
|
|
|
245
247
|
1. check whether Telegram is already configured
|
|
246
248
|
2. ask only for a missing Bot Token
|
|
247
|
-
3. wait for one Telegram message to the bot
|
|
249
|
+
3. wait for one Telegram message to the bot when no chat is paired yet
|
|
248
250
|
4. detect and store the chat/group ID automatically
|
|
249
251
|
5. continue into Telegram mode
|
|
250
252
|
|
package/package.json
CHANGED
package/telegram-doctor.ps1
CHANGED
|
@@ -111,7 +111,7 @@ function Write-DoctorReport {
|
|
|
111
111
|
foreach ($item in $failed) {
|
|
112
112
|
switch ($item.name) {
|
|
113
113
|
"bot_token" { Write-Host " - Telegram Bot Token fehlt. Starte: blun-codex --profile $($Result.profile) telegram-setup" }
|
|
114
|
-
"allowed_chat_ids" { Write-Host " -
|
|
114
|
+
"allowed_chat_ids" { Write-Host " - Chat-ID ist optional. Zum automatischen Koppeln: blun-codex --profile $($Result.profile) telegram-setup" }
|
|
115
115
|
"state_dir" { Write-Host " - Der lokale Telegram-State-Ordner fehlt noch. Ein Setup-Lauf legt ihn automatisch an." }
|
|
116
116
|
"profile_file" { Write-Host " - Das angegebene Profil existiert nicht." }
|
|
117
117
|
"node" { Write-Host " - Node.js fehlt in PATH." }
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { injectNext } from "./lib/bridge.js";
|
|
3
3
|
import { isCurrentSidecarPid } from "./lib/singleton.js";
|
|
4
4
|
|
|
5
|
-
const intervalMs = Number.parseInt(process.env.BLUN_TELEGRAM_INJECT_INTERVAL_MS || "
|
|
5
|
+
const intervalMs = Number.parseInt(process.env.BLUN_TELEGRAM_INJECT_INTERVAL_MS || "700", 10) || 700;
|
|
6
6
|
let stopping = false;
|
|
7
7
|
|
|
8
8
|
function sleep(ms) {
|
|
@@ -389,7 +389,10 @@ function looksLikeStatusBroadcast(text) {
|
|
|
389
389
|
if (/^status(?:\s|$|[~:.-])/u.test(normalized)) {
|
|
390
390
|
return true;
|
|
391
391
|
}
|
|
392
|
-
|
|
392
|
+
if (/^status\s+~?\s*\d{1,2}\s+\d{2}\b/u.test(normalized)) {
|
|
393
|
+
return true;
|
|
394
|
+
}
|
|
395
|
+
return /^[a-z][a-z0-9_-]{1,24}\s+~?\s*\d{1,2}\s+\d{2}\b/u.test(normalized);
|
|
393
396
|
}
|
|
394
397
|
|
|
395
398
|
function isAgentAddressed(config, text) {
|
|
@@ -415,7 +418,7 @@ function isAgentAddressed(config, text) {
|
|
|
415
418
|
return true;
|
|
416
419
|
}
|
|
417
420
|
|
|
418
|
-
const briefDirective = new RegExp(`\\bbrief\\b
|
|
421
|
+
const briefDirective = new RegExp(`\\bbrief\\b(?:\\s+#?\\d+)?\\s+(?:fuer|fur|for|an|to)\\s+@?${mention}\\b|\\b@?${mention}\\b\\s*[-:,]?\\s*brief\\b`, "u").test(normalized);
|
|
419
422
|
if (briefDirective) {
|
|
420
423
|
return true;
|
|
421
424
|
}
|
|
@@ -452,7 +455,7 @@ function isOtherAgentAddressed(config, text) {
|
|
|
452
455
|
return true;
|
|
453
456
|
}
|
|
454
457
|
|
|
455
|
-
const briefDirective = new RegExp(`\\bbrief\\b
|
|
458
|
+
const briefDirective = new RegExp(`\\bbrief\\b(?:\\s+#?\\d+)?\\s+(?:fuer|fur|for|an|to)\\s+@?${mention}\\b|\\b@?${mention}\\b\\s*[-:,]?\\s*brief\\b`, "u").test(normalized);
|
|
456
459
|
if (briefDirective) {
|
|
457
460
|
return true;
|
|
458
461
|
}
|
|
@@ -467,31 +470,27 @@ function isOtherAgentAddressed(config, text) {
|
|
|
467
470
|
}
|
|
468
471
|
|
|
469
472
|
function classifyInboundRelevance(config, inbound) {
|
|
470
|
-
if (looksLikeEscalation(inbound.text)) {
|
|
471
|
-
return "escalation";
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
if (String(inbound.chatType || "") === "private") {
|
|
475
|
-
return "direct";
|
|
476
|
-
}
|
|
477
|
-
|
|
478
473
|
const text = String(inbound.text || "");
|
|
479
|
-
|
|
474
|
+
const isStatusBroadcast = looksLikeStatusBroadcast(text);
|
|
475
|
+
|
|
476
|
+
if (!isStatusBroadcast && isAgentAddressed(config, text)) {
|
|
480
477
|
return "direct";
|
|
481
478
|
}
|
|
482
479
|
|
|
483
|
-
if (!
|
|
480
|
+
if (!isStatusBroadcast && isOtherAgentAddressed(config, text)) {
|
|
484
481
|
return "ambient";
|
|
485
482
|
}
|
|
486
483
|
|
|
487
|
-
|
|
488
|
-
if (!inbound.senderIsBot && allowedUserIds.includes(String(inbound.userId || ""))) {
|
|
484
|
+
if (String(inbound.chatType || "") === "private") {
|
|
489
485
|
return "direct";
|
|
490
486
|
}
|
|
491
487
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
488
|
+
if (inbound.senderIsBot || isStatusBroadcast) {
|
|
489
|
+
return "ambient";
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
if (looksLikeEscalation(text)) {
|
|
493
|
+
return "escalation";
|
|
495
494
|
}
|
|
496
495
|
|
|
497
496
|
const lane = String(config.lane || "").trim();
|
|
@@ -759,6 +758,33 @@ function parkExpiredAmbientQueueEntriesInPlace(config, queue) {
|
|
|
759
758
|
return parked;
|
|
760
759
|
}
|
|
761
760
|
|
|
761
|
+
function reclassifyQueuedEntriesInPlace(config, queue) {
|
|
762
|
+
const entries = Array.isArray(queue) ? queue : [];
|
|
763
|
+
let changed = 0;
|
|
764
|
+
let parked = 0;
|
|
765
|
+
for (const entry of entries) {
|
|
766
|
+
if (!entry || entry.status !== "queued") {
|
|
767
|
+
continue;
|
|
768
|
+
}
|
|
769
|
+
const previous = String(entry.relevance || "").trim().toLowerCase();
|
|
770
|
+
const next = classifyInboundRelevance(config, entry);
|
|
771
|
+
if (next === previous) {
|
|
772
|
+
continue;
|
|
773
|
+
}
|
|
774
|
+
const reclassifiedAt = nowIso();
|
|
775
|
+
entry.relevance = next;
|
|
776
|
+
entry.reclassifiedAt = reclassifiedAt;
|
|
777
|
+
if (next === "ambient" && String(entry.chatType || "").trim().toLowerCase() !== "private") {
|
|
778
|
+
entry.status = "parked";
|
|
779
|
+
entry.parkedAt = entry.parkedAt || reclassifiedAt;
|
|
780
|
+
entry.responsePreview = entry.responsePreview || "[reclassified ambient]";
|
|
781
|
+
parked += 1;
|
|
782
|
+
}
|
|
783
|
+
changed += 1;
|
|
784
|
+
}
|
|
785
|
+
return { changed, parked };
|
|
786
|
+
}
|
|
787
|
+
|
|
762
788
|
function mergeQueueEntry(current, incoming) {
|
|
763
789
|
if (!current && !incoming) {
|
|
764
790
|
return null;
|
|
@@ -2068,11 +2094,15 @@ function isFastDispatchEntry(entry) {
|
|
|
2068
2094
|
export async function injectNext(threadId, options = {}) {
|
|
2069
2095
|
const config = loadConfig();
|
|
2070
2096
|
const state = loadState(config);
|
|
2097
|
+
const reclassified = reclassifyQueuedEntriesInPlace(config, state.queue || []);
|
|
2071
2098
|
const parkedAmbient = parkExpiredAmbientQueueEntriesInPlace(config, state.queue || []);
|
|
2072
2099
|
const runtimeOwner = getRuntimeOwner(config);
|
|
2073
2100
|
state.pendingReplies = reconcilePendingRepliesInPlace(state.pendingReplies || []);
|
|
2074
2101
|
const expiredPendingReplies = closeExpiredPendingRepliesInPlace(config, state.pendingReplies || []);
|
|
2075
|
-
if (expiredPendingReplies > 0 || parkedAmbient > 0) {
|
|
2102
|
+
if (expiredPendingReplies > 0 || parkedAmbient > 0 || reclassified.changed > 0) {
|
|
2103
|
+
if (reclassified.changed > 0) {
|
|
2104
|
+
appendLog(config.paths.activityFile, `QUEUE_RECLASSIFIED changed=${reclassified.changed} parked=${reclassified.parked}`);
|
|
2105
|
+
}
|
|
2076
2106
|
if (parkedAmbient > 0) {
|
|
2077
2107
|
appendLog(config.paths.activityFile, `AMBIENT_PARKED count=${parkedAmbient}`);
|
|
2078
2108
|
}
|
|
@@ -19,9 +19,10 @@ async function telegramRequest(config, method, body) {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export async function getUpdates(config, offset) {
|
|
22
|
+
const timeout = Math.max(Number.parseInt(process.env.BLUN_TELEGRAM_GETUPDATES_TIMEOUT || "0", 10) || 0, 0);
|
|
22
23
|
return telegramRequest(config, "getUpdates", {
|
|
23
24
|
offset,
|
|
24
|
-
timeout
|
|
25
|
+
timeout,
|
|
25
26
|
allowed_updates: ["message"]
|
|
26
27
|
});
|
|
27
28
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { pollOnce } from "./lib/bridge.js";
|
|
3
3
|
import { isCurrentSidecarPid } from "./lib/singleton.js";
|
|
4
4
|
|
|
5
|
-
const intervalMs = Number.parseInt(process.env.BLUN_TELEGRAM_POLL_INTERVAL_MS || "
|
|
5
|
+
const intervalMs = Number.parseInt(process.env.BLUN_TELEGRAM_POLL_INTERVAL_MS || "700", 10) || 700;
|
|
6
6
|
let stopping = false;
|
|
7
7
|
|
|
8
8
|
function sleep(ms) {
|
package/telegram-setup.ps1
CHANGED
|
@@ -349,7 +349,8 @@ if ($needsToken) {
|
|
|
349
349
|
if ($needsChatIds -and -not $Json) {
|
|
350
350
|
try {
|
|
351
351
|
$botInfo = Get-TelegramBotInfo -Token $currentToken
|
|
352
|
-
$
|
|
352
|
+
$pairingDone = [string]$envValues["BLUN_TELEGRAM_PAIRING_DONE"]
|
|
353
|
+
$shouldPair = $tokenWasPrompted -or (-not $EnsureConfigured) -or ($pairingDone -ne "1")
|
|
353
354
|
if ($shouldPair) {
|
|
354
355
|
$pairedChat = Wait-TelegramPairingChat -Token $currentToken -BotInfo $botInfo -TimeoutSeconds 90
|
|
355
356
|
if ($pairedChat -and $pairedChat.chat_id) {
|
package/telegram-title-embed.ps1
CHANGED
|
@@ -131,8 +131,15 @@ public static class BlunEmbeddedQueueTitleWatcher
|
|
|
131
131
|
|
|
132
132
|
try
|
|
133
133
|
{
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
var enabled = Environment.GetEnvironmentVariable("BLUN_TELEGRAM_CONSOLE_UI_NOTICES");
|
|
135
|
+
if (string.Equals(enabled, "1", StringComparison.OrdinalIgnoreCase)
|
|
136
|
+
|| string.Equals(enabled, "true", StringComparison.OrdinalIgnoreCase)
|
|
137
|
+
|| string.Equals(enabled, "yes", StringComparison.OrdinalIgnoreCase)
|
|
138
|
+
|| string.Equals(enabled, "on", StringComparison.OrdinalIgnoreCase))
|
|
139
|
+
{
|
|
140
|
+
Console.WriteLine("");
|
|
141
|
+
Console.WriteLine("[Telegram] " + normalized);
|
|
142
|
+
}
|
|
136
143
|
}
|
|
137
144
|
catch
|
|
138
145
|
{
|
|
@@ -273,6 +273,12 @@ function Write-ConsoleUiNotice {
|
|
|
273
273
|
[string]$Kind,
|
|
274
274
|
[string]$Notice
|
|
275
275
|
)
|
|
276
|
+
if ($env:BLUN_TELEGRAM_CONSOLE_UI_NOTICES -ne "1" -and
|
|
277
|
+
$env:BLUN_TELEGRAM_CONSOLE_UI_NOTICES -ine "true" -and
|
|
278
|
+
$env:BLUN_TELEGRAM_CONSOLE_UI_NOTICES -ine "yes" -and
|
|
279
|
+
$env:BLUN_TELEGRAM_CONSOLE_UI_NOTICES -ine "on") {
|
|
280
|
+
return $false
|
|
281
|
+
}
|
|
276
282
|
try {
|
|
277
283
|
[void][CodexLink.NativeMethods]::FreeConsole()
|
|
278
284
|
$targetPid = Get-EffectiveAttachPid
|