@blunking/codexlink 0.1.19 → 0.1.20

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.
@@ -3,134 +3,134 @@ param(
3
3
  [switch]$Json,
4
4
  [switch]$Fix
5
5
  )
6
-
7
- $ErrorActionPreference = "Stop"
8
-
6
+
7
+ $ErrorActionPreference = "Stop"
8
+
9
9
  function Read-DotEnvFile {
10
- param([string]$Path)
11
- $values = @{}
12
- if (-not (Test-Path $Path)) { return $values }
13
- foreach ($line in (Get-Content -Path $Path)) {
14
- if (-not $line) { continue }
15
- if ($line.Trim().StartsWith("#")) { continue }
16
- $parts = $line -split "=", 2
17
- if ($parts.Count -ne 2) { continue }
18
- $values[$parts[0].Trim()] = $parts[1]
19
- }
20
- return $values
21
- }
22
-
23
- function Add-Check {
24
- param(
25
- [System.Collections.Generic.List[object]]$List,
26
- [string]$Name,
27
- [string]$Status,
28
- [string]$Detail
29
- )
30
- $List.Add([pscustomobject]@{
31
- name = $Name
32
- status = $Status
33
- detail = $Detail
34
- }) | Out-Null
35
- }
36
-
37
- function Test-TelegramTokenFormat {
38
- param([string]$Value)
39
- if (-not $Value) { return $false }
40
- return $Value -match '^\d{6,}:[A-Za-z0-9_-]{20,}$'
41
- }
42
-
43
- function Test-AllowedChatIdsFormat {
44
- param([string]$Value)
45
- if (-not $Value) { return $false }
46
- $parts = @($Value -split "," | ForEach-Object { $_.Trim() } | Where-Object { $_ })
47
- if ($parts.Count -eq 0) { return $false }
48
- foreach ($part in $parts) {
49
- if ($part -notmatch '^-?\d+$') {
50
- return $false
51
- }
52
- }
53
- return $true
54
- }
55
-
56
- function Get-OverallStatus {
57
- param([System.Collections.Generic.List[object]]$Checks)
58
- if (@($Checks | Where-Object { $_.status -eq "fail" }).Count -gt 0) {
59
- return "fail"
60
- }
61
- if (@($Checks | Where-Object { $_.status -eq "warn" }).Count -gt 0) {
62
- return "warn"
63
- }
64
- return "ok"
65
- }
66
-
10
+ param([string]$Path)
11
+ $values = @{}
12
+ if (-not (Test-Path $Path)) { return $values }
13
+ foreach ($line in (Get-Content -Path $Path)) {
14
+ if (-not $line) { continue }
15
+ if ($line.Trim().StartsWith("#")) { continue }
16
+ $parts = $line -split "=", 2
17
+ if ($parts.Count -ne 2) { continue }
18
+ $values[$parts[0].Trim()] = $parts[1]
19
+ }
20
+ return $values
21
+ }
22
+
23
+ function Add-Check {
24
+ param(
25
+ [System.Collections.Generic.List[object]]$List,
26
+ [string]$Name,
27
+ [string]$Status,
28
+ [string]$Detail
29
+ )
30
+ $List.Add([pscustomobject]@{
31
+ name = $Name
32
+ status = $Status
33
+ detail = $Detail
34
+ }) | Out-Null
35
+ }
36
+
37
+ function Test-TelegramTokenFormat {
38
+ param([string]$Value)
39
+ if (-not $Value) { return $false }
40
+ return $Value -match '^\d{6,}:[A-Za-z0-9_-]{20,}$'
41
+ }
42
+
43
+ function Test-AllowedChatIdsFormat {
44
+ param([string]$Value)
45
+ if (-not $Value) { return $false }
46
+ $parts = @($Value -split "," | ForEach-Object { $_.Trim() } | Where-Object { $_ })
47
+ if ($parts.Count -eq 0) { return $false }
48
+ foreach ($part in $parts) {
49
+ if ($part -notmatch '^-?\d+$') {
50
+ return $false
51
+ }
52
+ }
53
+ return $true
54
+ }
55
+
56
+ function Get-OverallStatus {
57
+ param([System.Collections.Generic.List[object]]$Checks)
58
+ if (@($Checks | Where-Object { $_.status -eq "fail" }).Count -gt 0) {
59
+ return "fail"
60
+ }
61
+ if (@($Checks | Where-Object { $_.status -eq "warn" }).Count -gt 0) {
62
+ return "warn"
63
+ }
64
+ return "ok"
65
+ }
66
+
67
67
  function Write-DoctorReport {
68
- param(
69
- [object]$Result,
70
- [string]$TokenSource,
71
- [string]$AllowedChatSource
72
- )
73
-
74
- $emoji = switch ($Result.overall) {
75
- "ok" { "[OK]" }
76
- "warn" { "[WARN]" }
77
- default { "[FAIL]" }
78
- }
79
-
80
- Write-Host ""
81
- Write-Host "CodexLink Telegram Doctor $emoji" -ForegroundColor Cyan
82
- Write-Host "Profil: $($Result.profile)"
83
- Write-Host "State-Ordner: $($Result.state_dir)"
84
- Write-Host ""
85
-
86
- foreach ($check in $Result.checks) {
87
- $prefix = switch ($check.status) {
88
- "ok" { "[OK]" }
89
- "warn" { "[WARN]" }
90
- default { "[FAIL]" }
91
- }
92
- $color = switch ($check.status) {
93
- "ok" { "Green" }
94
- "warn" { "Yellow" }
95
- default { "Red" }
96
- }
97
- Write-Host "$prefix $($check.name): $($check.detail)" -ForegroundColor $color
98
- }
99
-
100
- Write-Host ""
101
- if ($TokenSource) {
102
- Write-Host "Bot-Token gefunden aus: $TokenSource" -ForegroundColor DarkGray
103
- }
104
- if ($AllowedChatSource) {
105
- Write-Host "Erlaubte Chat-ID(s) gefunden aus: $AllowedChatSource" -ForegroundColor DarkGray
106
- }
107
-
108
- $failed = @($Result.checks | Where-Object { $_.status -eq "fail" })
109
- if ($failed.Count -gt 0) {
110
- Write-Host ""
111
- Write-Host "Was jetzt fehlt:" -ForegroundColor Yellow
112
- foreach ($item in $failed) {
113
- switch ($item.name) {
114
- "bot_token" { Write-Host " - Telegram Bot Token fehlt. Starte: blun-codex --profile $($Result.profile) telegram-setup" }
68
+ param(
69
+ [object]$Result,
70
+ [string]$TokenSource,
71
+ [string]$AllowedChatSource
72
+ )
73
+
74
+ $emoji = switch ($Result.overall) {
75
+ "ok" { "[OK]" }
76
+ "warn" { "[WARN]" }
77
+ default { "[FAIL]" }
78
+ }
79
+
80
+ Write-Host ""
81
+ Write-Host "CodexLink Telegram Doctor $emoji" -ForegroundColor Cyan
82
+ Write-Host "Profil: $($Result.profile)"
83
+ Write-Host "State-Ordner: $($Result.state_dir)"
84
+ Write-Host ""
85
+
86
+ foreach ($check in $Result.checks) {
87
+ $prefix = switch ($check.status) {
88
+ "ok" { "[OK]" }
89
+ "warn" { "[WARN]" }
90
+ default { "[FAIL]" }
91
+ }
92
+ $color = switch ($check.status) {
93
+ "ok" { "Green" }
94
+ "warn" { "Yellow" }
95
+ default { "Red" }
96
+ }
97
+ Write-Host "$prefix $($check.name): $($check.detail)" -ForegroundColor $color
98
+ }
99
+
100
+ Write-Host ""
101
+ if ($TokenSource) {
102
+ Write-Host "Bot-Token gefunden aus: $TokenSource" -ForegroundColor DarkGray
103
+ }
104
+ if ($AllowedChatSource) {
105
+ Write-Host "Erlaubte Chat-ID(s) gefunden aus: $AllowedChatSource" -ForegroundColor DarkGray
106
+ }
107
+
108
+ $failed = @($Result.checks | Where-Object { $_.status -eq "fail" })
109
+ if ($failed.Count -gt 0) {
110
+ Write-Host ""
111
+ Write-Host "Was jetzt fehlt:" -ForegroundColor Yellow
112
+ foreach ($item in $failed) {
113
+ switch ($item.name) {
114
+ "bot_token" { Write-Host " - Telegram Bot Token fehlt. Starte: blun-codex --profile $($Result.profile) telegram-setup" }
115
115
  "allowed_chat_ids" { Write-Host " - Chat-ID ist optional. Zum automatischen Koppeln: blun-codex --profile $($Result.profile) telegram-setup" }
116
- "state_dir" { Write-Host " - Der lokale Telegram-State-Ordner fehlt noch. Ein Setup-Lauf legt ihn automatisch an." }
117
- "profile_file" { Write-Host " - Das angegebene Profil existiert nicht." }
118
- "node" { Write-Host " - Node.js fehlt in PATH." }
119
- "codex" { Write-Host " - Der lokale codex-Befehl fehlt in PATH." }
120
- "telegram_plugin_root" { Write-Host " - Der Telegram-Plugin-Ordner konnte nicht gefunden werden." }
121
- default { Write-Host " - $($item.detail)" }
122
- }
123
- }
124
- }
125
-
126
- if ($Result.overall -eq "ok") {
127
- Write-Host ""
128
- Write-Host "Telegram ist sauber eingerichtet." -ForegroundColor Green
129
- Write-Host "Starten: blun-codex --profile $($Result.profile) telegram-plugin"
130
- } elseif ($Result.overall -eq "warn") {
131
- Write-Host ""
132
- Write-Host "Die Grundkonfiguration steht, aber es gibt noch Laufzeit-Hinweise." -ForegroundColor Yellow
133
- Write-Host "Das ist oft normal, wenn Telegram noch nicht aktiv gestartet wurde oder noch keine Nachricht durchlief."
116
+ "state_dir" { Write-Host " - Der lokale Telegram-State-Ordner fehlt noch. Ein Setup-Lauf legt ihn automatisch an." }
117
+ "profile_file" { Write-Host " - Das angegebene Profil existiert nicht." }
118
+ "node" { Write-Host " - Node.js fehlt in PATH." }
119
+ "codex" { Write-Host " - Der lokale codex-Befehl fehlt in PATH." }
120
+ "telegram_plugin_root" { Write-Host " - Der Telegram-Plugin-Ordner konnte nicht gefunden werden." }
121
+ default { Write-Host " - $($item.detail)" }
122
+ }
123
+ }
124
+ }
125
+
126
+ if ($Result.overall -eq "ok") {
127
+ Write-Host ""
128
+ Write-Host "Telegram ist sauber eingerichtet." -ForegroundColor Green
129
+ Write-Host "Starten: blun-codex --profile $($Result.profile) telegram-plugin"
130
+ } elseif ($Result.overall -eq "warn") {
131
+ Write-Host ""
132
+ Write-Host "Die Grundkonfiguration steht, aber es gibt noch Laufzeit-Hinweise." -ForegroundColor Yellow
133
+ Write-Host "Das ist oft normal, wenn Telegram noch nicht aktiv gestartet wurde oder noch keine Nachricht durchlief."
134
134
  }
135
135
  }
136
136
 
@@ -251,40 +251,40 @@ function Get-ProfilePath {
251
251
  $runtimeRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
252
252
  $profilePath = Get-ProfilePath -RuntimeRoot $runtimeRoot -ProfileName $Profile
253
253
  $checks = New-Object 'System.Collections.Generic.List[object]'
254
-
255
- if (Test-Path $profilePath) {
256
- Add-Check -List $checks -Name "profile_file" -Status "ok" -Detail $profilePath
257
- } else {
258
- Add-Check -List $checks -Name "profile_file" -Status "fail" -Detail ("Missing profile: " + $profilePath)
259
- }
260
-
261
- $statusRaw = & powershell -ExecutionPolicy Bypass -File (Join-Path $runtimeRoot "telegram-status.ps1") -Profile $Profile
262
- $status = $statusRaw | ConvertFrom-Json
263
-
264
- $nodeCommand = Get-Command node -ErrorAction SilentlyContinue
265
- $codexCommand = Get-Command codex -ErrorAction SilentlyContinue
266
- Add-Check -List $checks -Name "node" -Status $(if ($nodeCommand) { "ok" } else { "fail" }) -Detail $(if ($nodeCommand) { $nodeCommand.Source } else { "node not found in PATH" })
267
- Add-Check -List $checks -Name "codex" -Status $(if ($codexCommand) { "ok" } else { "fail" }) -Detail $(if ($codexCommand) { $codexCommand.Source } else { "codex not found in PATH" })
268
-
269
- if ($status.plugin_root) {
270
- Add-Check -List $checks -Name "telegram_plugin_root" -Status "ok" -Detail $status.plugin_root
271
- } else {
272
- Add-Check -List $checks -Name "telegram_plugin_root" -Status "fail" -Detail "Telegram plugin root could not be resolved."
273
- }
274
-
275
- if ($status.state_dir -and (Test-Path $status.state_dir)) {
276
- Add-Check -List $checks -Name "state_dir" -Status "ok" -Detail $status.state_dir
277
- } else {
278
- Add-Check -List $checks -Name "state_dir" -Status "fail" -Detail ("Missing state dir: " + $status.state_dir)
279
- }
280
-
281
- $activeEnvPath = Join-Path $status.state_dir ".env"
282
- $activeEnv = Read-DotEnvFile -Path $activeEnvPath
283
- $legacyEnvPath = Join-Path $env:USERPROFILE ".codex\channels\codexlink-telegram\.env"
284
- $legacyEnv = Read-DotEnvFile -Path $legacyEnvPath
285
-
286
- $tokenValue = ""
287
- $tokenSource = ""
254
+
255
+ if (Test-Path $profilePath) {
256
+ Add-Check -List $checks -Name "profile_file" -Status "ok" -Detail $profilePath
257
+ } else {
258
+ Add-Check -List $checks -Name "profile_file" -Status "fail" -Detail ("Missing profile: " + $profilePath)
259
+ }
260
+
261
+ $statusRaw = & powershell -ExecutionPolicy Bypass -File (Join-Path $runtimeRoot "telegram-status.ps1") -Profile $Profile
262
+ $status = $statusRaw | ConvertFrom-Json
263
+
264
+ $nodeCommand = Get-Command node -ErrorAction SilentlyContinue
265
+ $codexCommand = Get-Command codex -ErrorAction SilentlyContinue
266
+ Add-Check -List $checks -Name "node" -Status $(if ($nodeCommand) { "ok" } else { "fail" }) -Detail $(if ($nodeCommand) { $nodeCommand.Source } else { "node not found in PATH" })
267
+ Add-Check -List $checks -Name "codex" -Status $(if ($codexCommand) { "ok" } else { "fail" }) -Detail $(if ($codexCommand) { $codexCommand.Source } else { "codex not found in PATH" })
268
+
269
+ if ($status.plugin_root) {
270
+ Add-Check -List $checks -Name "telegram_plugin_root" -Status "ok" -Detail $status.plugin_root
271
+ } else {
272
+ Add-Check -List $checks -Name "telegram_plugin_root" -Status "fail" -Detail "Telegram plugin root could not be resolved."
273
+ }
274
+
275
+ if ($status.state_dir -and (Test-Path $status.state_dir)) {
276
+ Add-Check -List $checks -Name "state_dir" -Status "ok" -Detail $status.state_dir
277
+ } else {
278
+ Add-Check -List $checks -Name "state_dir" -Status "fail" -Detail ("Missing state dir: " + $status.state_dir)
279
+ }
280
+
281
+ $activeEnvPath = Join-Path $status.state_dir ".env"
282
+ $activeEnv = Read-DotEnvFile -Path $activeEnvPath
283
+ $legacyEnvPath = Join-Path $env:USERPROFILE ".codex\channels\codexlink-telegram\.env"
284
+ $legacyEnv = Read-DotEnvFile -Path $legacyEnvPath
285
+
286
+ $tokenValue = ""
287
+ $tokenSource = ""
288
288
  if (Test-TelegramTokenFormat -Value $activeEnv["BLUN_TELEGRAM_BOT_TOKEN"]) {
289
289
  $tokenValue = [string]$activeEnv["BLUN_TELEGRAM_BOT_TOKEN"]
290
290
  $tokenSource = "state env"
@@ -298,10 +298,10 @@ if (Test-TelegramTokenFormat -Value $activeEnv["BLUN_TELEGRAM_BOT_TOKEN"]) {
298
298
  $tokenValue = [string]$legacyEnv["TELEGRAM_BOT_TOKEN"]
299
299
  $tokenSource = "legacy env fallback legacy key"
300
300
  }
301
- Add-Check -List $checks -Name "bot_token" -Status $(if ($tokenValue) { "ok" } else { "fail" }) -Detail $(if ($tokenValue) { $tokenSource } else { "No valid BLUN_TELEGRAM_BOT_TOKEN found." })
302
-
303
- $allowedChatIds = ""
304
- $allowedChatSource = ""
301
+ Add-Check -List $checks -Name "bot_token" -Status $(if ($tokenValue) { "ok" } else { "fail" }) -Detail $(if ($tokenValue) { $tokenSource } else { "No valid BLUN_TELEGRAM_BOT_TOKEN found." })
302
+
303
+ $allowedChatIds = ""
304
+ $allowedChatSource = ""
305
305
  if (Test-AllowedChatIdsFormat -Value $activeEnv["BLUN_TELEGRAM_ALLOWED_CHAT_ID"]) {
306
306
  $allowedChatIds = [string]$activeEnv["BLUN_TELEGRAM_ALLOWED_CHAT_ID"]
307
307
  $allowedChatSource = "state env"
@@ -316,7 +316,7 @@ if (Test-AllowedChatIdsFormat -Value $activeEnv["BLUN_TELEGRAM_ALLOWED_CHAT_ID"]
316
316
  $allowedChatSource = "legacy env fallback legacy key"
317
317
  }
318
318
  Add-Check -List $checks -Name "allowed_chat_ids" -Status $(if ($allowedChatIds) { "ok" } else { "warn" }) -Detail $(if ($allowedChatIds) { $allowedChatIds } else { "No allowlist set. Telegram currently accepts any chat the bot can see." })
319
-
319
+
320
320
  Add-Check -List $checks -Name "app_server_ws" -Status $(if ($status.active_ws) { "ok" } else { "warn" }) -Detail $(if ($status.active_ws) { $status.active_ws } else { "No active websocket recorded." })
321
321
  Add-Check -List $checks -Name "dispatch_mode" -Status $(if ($status.dispatch_mode -eq "deferred") { "ok" } else { "warn" }) -Detail ("mode=" + [string]$status.dispatch_mode + " cooldown_ms=" + [string]$status.idle_cooldown_ms + " pending_reply_timeout_ms=" + [string]$status.pending_reply_timeout_ms)
322
322
  Add-Check -List $checks -Name "bound_thread" -Status $(if ($status.active_thread_id) { "ok" } else { "warn" }) -Detail $(if ($status.active_thread_id) { $status.active_thread_id } else { "No active thread bound yet." })
@@ -336,42 +336,42 @@ Add-Check -List $checks -Name "queue_notifier" -Status $(if (($null -eq $status.
336
336
  Add-Check -List $checks -Name "poller" -Status $(if ($status.poller_alive) { "ok" } else { "warn" }) -Detail ("pid=" + [string]$status.poller_pid + " alive=" + [string]$status.poller_alive)
337
337
  Add-Check -List $checks -Name "dispatcher" -Status $(if ($status.dispatcher_alive) { "ok" } else { "warn" }) -Detail ("pid=" + [string]$status.dispatcher_pid + " alive=" + [string]$status.dispatcher_alive)
338
338
  Add-Check -List $checks -Name "responder" -Status $(if ($status.responder_alive) { "ok" } else { "warn" }) -Detail ("pid=" + [string]$status.responder_pid + " alive=" + [string]$status.responder_alive)
339
-
340
- if ($status.last_inbound) {
341
- $lastInboundSummary = [string]::Format(
342
- "chat={0} message={1} type={2} thread={3}",
343
- $status.last_inbound.chatId,
344
- $status.last_inbound.messageId,
345
- $status.last_inbound.chatType,
346
- $(if ($status.last_inbound.telegramThreadId) { $status.last_inbound.telegramThreadId } else { "-" })
347
- )
348
- Add-Check -List $checks -Name "last_inbound" -Status "ok" -Detail $lastInboundSummary
349
- } else {
350
- Add-Check -List $checks -Name "last_inbound" -Status "warn" -Detail "No inbound Telegram message recorded yet."
351
- }
352
-
353
- if ($status.last_outbound) {
354
- $lastOutboundSummary = [string]::Format(
355
- "chat={0} message={1} reply_to={2} thread={3}",
356
- $status.last_outbound.chatId,
357
- $status.last_outbound.messageId,
358
- $(if ($status.last_outbound.replyToMessageId) { $status.last_outbound.replyToMessageId } else { "-" }),
359
- $(if ($status.last_outbound.telegramThreadId) { $status.last_outbound.telegramThreadId } else { "-" })
360
- )
361
- Add-Check -List $checks -Name "last_outbound" -Status "ok" -Detail $lastOutboundSummary
362
- } else {
363
- Add-Check -List $checks -Name "last_outbound" -Status "warn" -Detail "No outbound Telegram message recorded yet."
364
- }
365
-
339
+
340
+ if ($status.last_inbound) {
341
+ $lastInboundSummary = [string]::Format(
342
+ "chat={0} message={1} type={2} thread={3}",
343
+ $status.last_inbound.chatId,
344
+ $status.last_inbound.messageId,
345
+ $status.last_inbound.chatType,
346
+ $(if ($status.last_inbound.telegramThreadId) { $status.last_inbound.telegramThreadId } else { "-" })
347
+ )
348
+ Add-Check -List $checks -Name "last_inbound" -Status "ok" -Detail $lastInboundSummary
349
+ } else {
350
+ Add-Check -List $checks -Name "last_inbound" -Status "warn" -Detail "No inbound Telegram message recorded yet."
351
+ }
352
+
353
+ if ($status.last_outbound) {
354
+ $lastOutboundSummary = [string]::Format(
355
+ "chat={0} message={1} reply_to={2} thread={3}",
356
+ $status.last_outbound.chatId,
357
+ $status.last_outbound.messageId,
358
+ $(if ($status.last_outbound.replyToMessageId) { $status.last_outbound.replyToMessageId } else { "-" }),
359
+ $(if ($status.last_outbound.telegramThreadId) { $status.last_outbound.telegramThreadId } else { "-" })
360
+ )
361
+ Add-Check -List $checks -Name "last_outbound" -Status "ok" -Detail $lastOutboundSummary
362
+ } else {
363
+ Add-Check -List $checks -Name "last_outbound" -Status "warn" -Detail "No outbound Telegram message recorded yet."
364
+ }
365
+
366
366
  Add-Check -List $checks -Name "queue" -Status $(if (([int]$status.queue_depth -eq 0) -and ([int]$status.pending_reply_depth -eq 0)) { "ok" } else { "warn" }) -Detail ("queued=" + $status.queue_depth + " ambient=" + $status.ambient_queue_depth + " parked=" + $status.parked_queue_depth + " submitted=" + $status.submitted_depth + " pending_replies=" + $status.pending_reply_depth + " expired_pending_replies=" + $status.expired_pending_reply_depth)
367
-
367
+
368
368
  $result = [ordered]@{
369
- profile = $status.profile
370
- overall = Get-OverallStatus -Checks $checks
371
- runtime_root = $runtimeRoot
372
- state_dir = $status.state_dir
373
- plugin_root = $status.plugin_root
374
- checks = $checks
369
+ profile = $status.profile
370
+ overall = Get-OverallStatus -Checks $checks
371
+ runtime_root = $runtimeRoot
372
+ state_dir = $status.state_dir
373
+ plugin_root = $status.plugin_root
374
+ checks = $checks
375
375
  status = $status
376
376
  }
377
377
 
@@ -390,10 +390,10 @@ if ($Fix) {
390
390
  }
391
391
  exit 0
392
392
  }
393
-
394
- if ($Json) {
395
- $result | ConvertTo-Json -Depth 8
396
- exit 0
397
- }
398
-
399
- Write-DoctorReport -Result $result -TokenSource $tokenSource -AllowedChatSource $allowedChatSource
393
+
394
+ if ($Json) {
395
+ $result | ConvertTo-Json -Depth 8
396
+ exit 0
397
+ }
398
+
399
+ Write-DoctorReport -Result $result -TokenSource $tokenSource -AllowedChatSource $allowedChatSource
@@ -79,6 +79,10 @@ function containsToken(text, token) {
79
79
  return new RegExp(`(^|[^a-z0-9_])${escaped}([^a-z0-9_]|$)`, "i").test(String(text || ""));
80
80
  }
81
81
 
82
+ function shouldAckOnlyAddressPing() {
83
+ return String(process.env.BLUN_TELEGRAM_PING_ACK_ONLY || "").trim() === "1";
84
+ }
85
+
82
86
  function looksLikeEscalation(text) {
83
87
  const value = foldTriggerText(text);
84
88
  if (!value) {
@@ -445,6 +449,10 @@ function isAgentAddressed(config, text) {
445
449
  if (workDirective) {
446
450
  return true;
447
451
  }
452
+
453
+ if (containsToken(normalized, name)) {
454
+ return true;
455
+ }
448
456
  }
449
457
 
450
458
  return false;
@@ -2270,6 +2278,11 @@ function isRealtimeAppServerEntry(entry) {
2270
2278
  return chatType === "private" || relevance === "direct" || relevance === "lane";
2271
2279
  }
2272
2280
 
2281
+ function isGroupChatEntry(entry) {
2282
+ const chatType = String(entry?.chatType || "").trim().toLowerCase();
2283
+ return chatType === "group" || chatType === "supergroup";
2284
+ }
2285
+
2273
2286
  export async function injectNext(threadId, options = {}) {
2274
2287
  const config = loadConfig();
2275
2288
  const state = loadState(config);
@@ -2313,7 +2326,7 @@ export async function injectNext(threadId, options = {}) {
2313
2326
  };
2314
2327
  }
2315
2328
 
2316
- if (isAddressOnlyPing(config, next.text) && !looksLikeBotSender(next)) {
2329
+ if (shouldAckOnlyAddressPing() && isAddressOnlyPing(config, next.text) && !looksLikeBotSender(next) && !isGroupChatEntry(next)) {
2317
2330
  next.status = "delivered";
2318
2331
  next.deliveredAt = nowIso();
2319
2332
  next.threadId = null;
@@ -55,31 +55,31 @@ export function saveJson(path, value) {
55
55
 
56
56
  throw lastError || new Error(`Failed to save JSON file: ${path}`);
57
57
  }
58
-
59
- export function appendJsonl(path, value) {
60
- appendFileSync(path, `${JSON.stringify(value)}\n`, "utf8");
61
- }
62
-
63
- export function appendLog(path, message) {
64
- appendFileSync(path, `${nowIso()} ${message}\n`, "utf8");
65
- }
66
-
67
- export function readTail(path, lines = 20) {
68
- if (!existsSync(path)) {
69
- return [];
70
- }
71
- const text = readFileSync(path, "utf8");
72
- return text.split(/\r?\n/).filter(Boolean).slice(-lines);
73
- }
74
-
75
- export function defaultState() {
76
- return {
77
- offset: 0,
78
- currentThreadId: "",
79
- queue: [],
80
- pendingReplies: [],
81
- replyOffsets: {},
82
- replyBuffers: {},
58
+
59
+ export function appendJsonl(path, value) {
60
+ appendFileSync(path, `${JSON.stringify(value)}\n`, "utf8");
61
+ }
62
+
63
+ export function appendLog(path, message) {
64
+ appendFileSync(path, `${nowIso()} ${message}\n`, "utf8");
65
+ }
66
+
67
+ export function readTail(path, lines = 20) {
68
+ if (!existsSync(path)) {
69
+ return [];
70
+ }
71
+ const text = readFileSync(path, "utf8");
72
+ return text.split(/\r?\n/).filter(Boolean).slice(-lines);
73
+ }
74
+
75
+ export function defaultState() {
76
+ return {
77
+ offset: 0,
78
+ currentThreadId: "",
79
+ queue: [],
80
+ pendingReplies: [],
81
+ replyOffsets: {},
82
+ replyBuffers: {},
83
83
  lastInbound: null,
84
84
  lastOutbound: null,
85
85
  lastUiNotice: null,