alvin-bot 5.6.2 β 5.8.0
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/CHANGELOG.md +29 -0
- package/README.md +1 -1
- package/dist/claude.js +1 -102
- package/dist/config.js +1 -96
- package/dist/engine.js +1 -90
- package/dist/find-claude-binary.js +1 -98
- package/dist/handlers/async-agent-chunk-handler.js +1 -50
- package/dist/handlers/background-bypass.js +1 -75
- package/dist/handlers/commands.js +1 -2336
- package/dist/handlers/cron-progress.js +1 -52
- package/dist/handlers/document.js +1 -194
- package/dist/handlers/message.js +1 -959
- package/dist/handlers/photo.js +1 -154
- package/dist/handlers/platform-message.js +1 -360
- package/dist/handlers/stuck-timer.js +1 -54
- package/dist/handlers/video.js +1 -237
- package/dist/handlers/voice.js +1 -148
- package/dist/i18n.js +1 -805
- package/dist/index.js +1 -697
- package/dist/init-data-dir.js +1 -98
- package/dist/middleware/auth.js +1 -233
- package/dist/migrate.js +1 -162
- package/dist/paths.js +1 -146
- package/dist/platforms/discord.js +1 -175
- package/dist/platforms/index.js +1 -130
- package/dist/platforms/signal.js +1 -205
- package/dist/platforms/slack-slash-parser.js +1 -32
- package/dist/platforms/slack.js +1 -501
- package/dist/platforms/telegram.js +1 -111
- package/dist/platforms/types.js +1 -8
- package/dist/platforms/whatsapp-auth-helpers.js +1 -53
- package/dist/platforms/whatsapp.js +1 -707
- package/dist/providers/claude-sdk-provider.js +1 -565
- package/dist/providers/codex-cli-provider.js +1 -134
- package/dist/providers/index.js +1 -7
- package/dist/providers/ollama-provider.js +1 -32
- package/dist/providers/openai-compatible.js +1 -406
- package/dist/providers/registry.js +1 -352
- package/dist/providers/runtime-header.js +1 -45
- package/dist/providers/tool-executor.js +1 -475
- package/dist/providers/types.js +1 -227
- package/dist/services/access.js +1 -144
- package/dist/services/allowed-users-gate.js +1 -56
- package/dist/services/alvin-dispatch.js +1 -130
- package/dist/services/alvin-mcp-tools.js +1 -104
- package/dist/services/asset-index.js +1 -224
- package/dist/services/async-agent-parser.js +1 -418
- package/dist/services/async-agent-watcher.js +1 -443
- package/dist/services/auto-diagnostic.js +1 -228
- package/dist/services/broadcast.js +1 -52
- package/dist/services/browser-manager.js +1 -562
- package/dist/services/browser-webfetch.js +1 -127
- package/dist/services/browser.js +1 -121
- package/dist/services/cdp-bootstrap.js +1 -357
- package/dist/services/compaction.js +1 -144
- package/dist/services/critical-notify.js +1 -203
- package/dist/services/cron-resolver.js +1 -58
- package/dist/services/cron-scheduling.js +1 -310
- package/dist/services/cron.js +1 -861
- package/dist/services/custom-tools.js +1 -317
- package/dist/services/delivery-queue.js +1 -173
- package/dist/services/delivery-registry.js +1 -21
- package/dist/services/disk-cleanup.js +1 -203
- package/dist/services/elevenlabs.js +1 -58
- package/dist/services/embeddings/auto-detect.js +1 -74
- package/dist/services/embeddings/fts5.js +1 -108
- package/dist/services/embeddings/gemini.js +1 -65
- package/dist/services/embeddings/index.js +1 -496
- package/dist/services/embeddings/ollama.js +1 -78
- package/dist/services/embeddings/openai.js +1 -49
- package/dist/services/embeddings/provider.js +1 -22
- package/dist/services/embeddings/vector-base.js +1 -113
- package/dist/services/embeddings-migration.js +1 -193
- package/dist/services/embeddings.js +1 -9
- package/dist/services/env-file.js +1 -50
- package/dist/services/exec-guard.js +1 -71
- package/dist/services/fallback-order.js +1 -154
- package/dist/services/file-permissions.js +1 -93
- package/dist/services/heartbeat-file.js +1 -65
- package/dist/services/heartbeat.js +1 -313
- package/dist/services/hooks.js +1 -44
- package/dist/services/imagegen.js +1 -72
- package/dist/services/language-detect.js +1 -154
- package/dist/services/markdown.js +1 -63
- package/dist/services/mcp.js +1 -263
- package/dist/services/memory-extractor.js +1 -178
- package/dist/services/memory-inject-mode.js +1 -43
- package/dist/services/memory-layers.js +1 -156
- package/dist/services/memory.js +1 -146
- package/dist/services/ollama-manager.js +1 -339
- package/dist/services/permissions-wizard.js +1 -291
- package/dist/services/personality.js +1 -376
- package/dist/services/plugins.js +1 -171
- package/dist/services/preflight.js +1 -292
- package/dist/services/process-manager.js +1 -291
- package/dist/services/release-highlights.js +1 -79
- package/dist/services/reminders.js +1 -97
- package/dist/services/restart.js +1 -48
- package/dist/services/security-audit.js +1 -74
- package/dist/services/self-diagnosis.js +1 -272
- package/dist/services/self-search.js +1 -129
- package/dist/services/session-persistence.js +1 -237
- package/dist/services/session.js +1 -282
- package/dist/services/skills.js +1 -290
- package/dist/services/ssrf-guard.js +1 -162
- package/dist/services/standing-orders.js +1 -29
- package/dist/services/steer-channel.js +1 -46
- package/dist/services/stop-controller.js +1 -52
- package/dist/services/subagent-dedup.js +1 -0
- package/dist/services/subagent-delivery.js +1 -452
- package/dist/services/subagent-stats.js +1 -123
- package/dist/services/subagents.js +1 -814
- package/dist/services/sudo.js +1 -329
- package/dist/services/telegram.js +1 -158
- package/dist/services/timing-safe-bearer.js +1 -51
- package/dist/services/tool-discovery.js +1 -214
- package/dist/services/trends.js +1 -580
- package/dist/services/updater.js +1 -291
- package/dist/services/usage-tracker.js +1 -144
- package/dist/services/users.js +1 -271
- package/dist/services/voice.js +1 -104
- package/dist/services/watchdog-brake.js +1 -154
- package/dist/services/watchdog.js +1 -311
- package/dist/services/workspaces.js +1 -276
- package/dist/tui/index.js +1 -667
- package/dist/util/console-formatter.js +1 -109
- package/dist/util/debounce.js +1 -24
- package/dist/util/telegram-error-filter.js +1 -62
- package/dist/version.js +1 -24
- package/dist/web/bind-strategy.js +1 -42
- package/dist/web/canvas.js +1 -30
- package/dist/web/doctor-api.js +1 -604
- package/dist/web/openai-compat.js +1 -252
- package/dist/web/server.js +1 -1831
- package/dist/web/setup-api.js +1 -1101
- package/package.json +5 -2
- package/dist/.metadata_never_index +0 -0
package/dist/handlers/message.js
CHANGED
|
@@ -1,959 +1 @@
|
|
|
1
|
-
import { InputFile, InlineKeyboard } from "grammy";
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
import crypto from "crypto";
|
|
4
|
-
import { getSession, addToHistory, trackProviderUsage, buildSessionKey, getTelegramWorkspace, markSessionDirty } from "../services/session.js";
|
|
5
|
-
import { resolveWorkspaceOrDefault, getWorkspace } from "../services/workspaces.js";
|
|
6
|
-
import { TelegramStreamer } from "../services/telegram.js";
|
|
7
|
-
import { getRegistry } from "../engine.js";
|
|
8
|
-
import { textToSpeech } from "../services/voice.js";
|
|
9
|
-
import { buildSmartSystemPrompt } from "../services/personality.js";
|
|
10
|
-
import { buildSkillContext } from "../services/skills.js";
|
|
11
|
-
import { isForwardingAllowed } from "../services/access.js";
|
|
12
|
-
import { touchProfile } from "../services/users.js";
|
|
13
|
-
import { trackAndAdapt } from "../services/language-detect.js";
|
|
14
|
-
import { shouldCompact, compactSession } from "../services/compaction.js";
|
|
15
|
-
import { emit } from "../services/hooks.js";
|
|
16
|
-
import { trackUsage } from "../services/usage-tracker.js";
|
|
17
|
-
import { emitUserMessage as broadcastUserMessage, emitResponseStart as broadcastResponseStart, emitResponseDelta as broadcastResponseDelta, emitResponseDone as broadcastResponseDone, } from "../services/broadcast.js";
|
|
18
|
-
import { t } from "../i18n.js";
|
|
19
|
-
import { isHarmlessTelegramError } from "../util/telegram-error-filter.js";
|
|
20
|
-
import { handleToolResultChunk } from "./async-agent-chunk-handler.js";
|
|
21
|
-
import { createStuckTimer } from "./stuck-timer.js";
|
|
22
|
-
import { shouldBypassQueue, shouldBypassSdkResume, waitUntilProcessingFalse, } from "./background-bypass.js";
|
|
23
|
-
import { SteerChannel } from "../services/steer-channel.js";
|
|
24
|
-
import { isSteeringEnabled } from "../config.js";
|
|
25
|
-
/**
|
|
26
|
-
* Stuck-only timeout β NO absolute cap.
|
|
27
|
-
*
|
|
28
|
-
* Alvin is designed to work as long as it needs to, including overnight
|
|
29
|
-
* on multi-hour tasks. The ONLY condition under which we abort a running
|
|
30
|
-
* query is when Claude produces no chunks at all for STUCK_TIMEOUT_MINUTES
|
|
31
|
-
* β that's a genuine hang, not legitimate work. Every text chunk and
|
|
32
|
-
* tool_use chunk resets this timer, so an actively-progressing task will
|
|
33
|
-
* never be cut off regardless of total duration.
|
|
34
|
-
*
|
|
35
|
-
* Previous design had an additional 30-minute absolute cap that violated
|
|
36
|
-
* this "work as long as needed" character. Removed entirely β only the
|
|
37
|
-
* stuck detector remains.
|
|
38
|
-
*
|
|
39
|
-
* Configurable via ALVIN_STUCK_TIMEOUT_MINUTES env var. Default 10 minutes,
|
|
40
|
-
* which is generous for normal work (Claude typically streams chunks every
|
|
41
|
-
* few seconds) but still catches real deadlocks quickly.
|
|
42
|
-
*/
|
|
43
|
-
const STUCK_TIMEOUT_MINUTES = Number(process.env.ALVIN_STUCK_TIMEOUT_MINUTES) || 10;
|
|
44
|
-
const STUCK_TIMEOUT_MS = STUCK_TIMEOUT_MINUTES * 60 * 1000;
|
|
45
|
-
/**
|
|
46
|
-
* v4.12.1 β Task-aware stuck timeout for sync Task/Agent tool calls.
|
|
47
|
-
*
|
|
48
|
-
* When Claude calls the Task/Agent tool WITHOUT run_in_background: true,
|
|
49
|
-
* the Claude Agent SDK runs the sub-agent synchronously inside the tool
|
|
50
|
-
* call. The parent stream emits NO intermediate chunks during that time
|
|
51
|
-
* β it's silent until the sub-agent finishes and the final tool_result
|
|
52
|
-
* arrives. With the normal STUCK_TIMEOUT_MS (10 min), this triggered a
|
|
53
|
-
* false abort on legitimate long-running sub-agents.
|
|
54
|
-
*
|
|
55
|
-
* The new approach: track pending sync Task/Agent tool calls by their
|
|
56
|
-
* toolUseId, and while any are active, escalate the idle timeout to
|
|
57
|
-
* SYNC_AGENT_IDLE_TIMEOUT_MS (default 120 min, env-configurable). After
|
|
58
|
-
* the matching tool_result arrives, revert to the normal timeout.
|
|
59
|
-
*
|
|
60
|
-
* The normal 10-min timeout still applies for genuine SDK hangs (no
|
|
61
|
-
* sync tool call active, no chunks arriving).
|
|
62
|
-
*/
|
|
63
|
-
const SYNC_AGENT_IDLE_TIMEOUT_MINUTES = Number(process.env.ALVIN_SYNC_AGENT_IDLE_TIMEOUT_MINUTES) || 120;
|
|
64
|
-
const SYNC_AGENT_IDLE_TIMEOUT_MS = SYNC_AGENT_IDLE_TIMEOUT_MINUTES * 60 * 1000;
|
|
65
|
-
/** Checkpoint reminder thresholds β kept in sync with
|
|
66
|
-
* src/providers/claude-sdk-provider.ts (where the actual hint injection
|
|
67
|
-
* happens). We mirror the check here so the session telemetry knows
|
|
68
|
-
* when the SDK provider would have injected a reminder. */
|
|
69
|
-
const CHECKPOINT_TOOL_THRESHOLD = 15;
|
|
70
|
-
const CHECKPOINT_MSG_THRESHOLD = 10;
|
|
71
|
-
/** Maximum characters in the bridge-message preamble that gets prepended
|
|
72
|
-
* to the first post-recovery SDK query. Oldest gap-turns get truncated. */
|
|
73
|
-
const BRIDGE_MAX_CHARS = 2500;
|
|
74
|
-
/** Maximum characters per individual message in the bridge preamble. */
|
|
75
|
-
const BRIDGE_MSG_MAX_CHARS = 500;
|
|
76
|
-
/**
|
|
77
|
-
* Build a "catch-up" preamble summarising turns that happened while the
|
|
78
|
-
* SDK was not the active provider (i.e., during a failover to Ollama or
|
|
79
|
-
* a manual /model switch). This gets prepended to the first post-recovery
|
|
80
|
-
* prompt so the SDK sees what its alter-ego did.
|
|
81
|
-
*/
|
|
82
|
-
function buildBridgeMessage(fallbackTurns) {
|
|
83
|
-
if (fallbackTurns.length === 0)
|
|
84
|
-
return "";
|
|
85
|
-
const renderTurn = (m) => {
|
|
86
|
-
const label = m.role === "user" ? "User" : "Assistant (Fallback)";
|
|
87
|
-
const content = m.content.length > BRIDGE_MSG_MAX_CHARS
|
|
88
|
-
? m.content.slice(0, BRIDGE_MSG_MAX_CHARS) + "β¦"
|
|
89
|
-
: m.content;
|
|
90
|
-
return `${label}: ${content}`;
|
|
91
|
-
};
|
|
92
|
-
// Start with all turns rendered, then trim from the oldest if we exceed budget.
|
|
93
|
-
let lines = fallbackTurns.map(renderTurn);
|
|
94
|
-
let body = lines.join("\n\n");
|
|
95
|
-
let truncatedOldest = 0;
|
|
96
|
-
while (body.length > BRIDGE_MAX_CHARS && lines.length > 2) {
|
|
97
|
-
lines.shift();
|
|
98
|
-
truncatedOldest++;
|
|
99
|
-
body = lines.join("\n\n");
|
|
100
|
-
}
|
|
101
|
-
const omittedNote = truncatedOldest > 0
|
|
102
|
-
? `[β¦${truncatedOldest} older turn(s) omittedβ¦]\n\n`
|
|
103
|
-
: "";
|
|
104
|
-
const count = fallbackTurns.length;
|
|
105
|
-
return (`[Context: While you (Claude) were briefly not the active provider, ` +
|
|
106
|
-
`the following ${count} message(s) were exchanged with a fallback model. ` +
|
|
107
|
-
`Catching you up:\n\n` +
|
|
108
|
-
omittedNote +
|
|
109
|
-
body +
|
|
110
|
-
`\n\n--- New message from user: ---]\n\n`);
|
|
111
|
-
}
|
|
112
|
-
/** Tool name β emoji. Used to render a status line while Alvin is running
|
|
113
|
-
* tools, so users see real progress instead of an endless typing indicator. */
|
|
114
|
-
const TOOL_ICONS = {
|
|
115
|
-
Read: "π",
|
|
116
|
-
Write: "π",
|
|
117
|
-
Edit: "βοΈ",
|
|
118
|
-
Bash: "β‘",
|
|
119
|
-
Glob: "π",
|
|
120
|
-
Grep: "π",
|
|
121
|
-
WebSearch: "π",
|
|
122
|
-
WebFetch: "π‘",
|
|
123
|
-
Task: "π€",
|
|
124
|
-
};
|
|
125
|
-
// ββ A3 β stop-suppress-undelivered pure predicate ββββββββββββββββββββββββββββ
|
|
126
|
-
/**
|
|
127
|
-
* Determine whether the final answer send should be suppressed because a stop
|
|
128
|
-
* was requested and no visible text has yet been delivered to the user.
|
|
129
|
-
*
|
|
130
|
-
* This closes the gap behind "I clicked Stop but it answered anyway": the
|
|
131
|
-
* Claude SDK delivers short answers atomically, so the for-await loop parks
|
|
132
|
-
* on IPC the whole time, and the complete answer arrives as one block. By the
|
|
133
|
-
* time the consumer bail fires at the top of the loop, the answer is computed
|
|
134
|
-
* and about to be sent. This guard is the only stoppable moment for atomic
|
|
135
|
-
* answers.
|
|
136
|
-
*
|
|
137
|
-
* HARD CONSTRAINT β no-retract invariant: if ANY visible text has already
|
|
138
|
-
* been streamed/committed to the user (visibleTextAlreadySent=true), the
|
|
139
|
-
* predicate returns false regardless of stop state. Partial output that
|
|
140
|
-
* already reached the user is NEVER retracted. The consumer bail in the
|
|
141
|
-
* for-await loop already handles mid-stream stops; this guard only acts on
|
|
142
|
-
* the final commit step.
|
|
143
|
-
*
|
|
144
|
-
* Truth table:
|
|
145
|
-
* stopRequested=truthy + visibleTextAlreadySent=false β true (suppress)
|
|
146
|
-
* stopRequested=truthy + visibleTextAlreadySent=true β false (no-retract)
|
|
147
|
-
* stopRequested=falsy + * β false (normal)
|
|
148
|
-
*/
|
|
149
|
-
export function shouldSuppressFinalSend(args) {
|
|
150
|
-
if (!args.stopRequested)
|
|
151
|
-
return false;
|
|
152
|
-
if (args.visibleTextAlreadySent)
|
|
153
|
-
return false;
|
|
154
|
-
return true;
|
|
155
|
-
}
|
|
156
|
-
// ββ v5.2 live steering β pure routing helper βββββββββββββββββββββββββββββββββ
|
|
157
|
-
/**
|
|
158
|
-
* Decide how a mid-task message (arriving while `session.isProcessing`) should
|
|
159
|
-
* be handled. Evaluated in the `if (session.isProcessing)` guard before any
|
|
160
|
-
* side-effects, so the caller can branch cleanly.
|
|
161
|
-
*
|
|
162
|
-
* Decision priority:
|
|
163
|
-
* 1. "bypass" β background-agent bypass path (pre-existing Cycle-1 logic)
|
|
164
|
-
* 2. "steer" β push into live SteerChannel (claude-sdk + steering on + channel open)
|
|
165
|
-
* 3. "queue" β normal queue behavior (all other cases)
|
|
166
|
-
*
|
|
167
|
-
* Defensive: if `isProcessing` is false the helper is being called incorrectly;
|
|
168
|
-
* it returns "queue" so the caller falls through to existing behavior.
|
|
169
|
-
*/
|
|
170
|
-
export function decideMidTaskRouting(args) {
|
|
171
|
-
if (!args.isProcessing)
|
|
172
|
-
return "queue";
|
|
173
|
-
if (args.shouldBypass)
|
|
174
|
-
return "bypass";
|
|
175
|
-
if (args.providerIsClaudeSdk && args.steeringEnabled && args.hasSteerChannel && args.hasLiveSdkQuery)
|
|
176
|
-
return "steer";
|
|
177
|
-
return "queue";
|
|
178
|
-
}
|
|
179
|
-
// ββ Cycle-3 P0 β background honesty guard ββββββββββββββββββββββββββββββββββββ
|
|
180
|
-
/**
|
|
181
|
-
* Detect when the bot falsely promised "running in the background β you can
|
|
182
|
-
* keep chatting" but actually ran a sync Task/Agent that blocked the session.
|
|
183
|
-
*
|
|
184
|
-
* Returns true when all of the following hold:
|
|
185
|
-
* 1. A Task/Agent chunk arrived WITHOUT `run_in_background: true` (i.e. the
|
|
186
|
-
* stuck-timer entered sync mode β `taskChunkSeenWithoutRunInBackground`).
|
|
187
|
-
* 2. No real background detach happened this turn:
|
|
188
|
-
* β’ `mcp__alvin__dispatch_agent` was NOT called (`dispatchAgentFired=false`)
|
|
189
|
-
* β’ `pendingBackgroundCount` did NOT increase (`pendingBackgroundDelta=0`)
|
|
190
|
-
*
|
|
191
|
-
* Exported so it can be unit-tested without a grammy Context mock.
|
|
192
|
-
*/
|
|
193
|
-
export function detectUndetachedBackgroundClaim(args) {
|
|
194
|
-
if (!args.taskChunkSeenWithoutRunInBackground)
|
|
195
|
-
return false;
|
|
196
|
-
// Dead in production wiring (always false there β PATH A is detected via pendingBackgroundDelta); kept for explicit unit-test truth-table coverage.
|
|
197
|
-
if (args.dispatchAgentFired)
|
|
198
|
-
return false;
|
|
199
|
-
if (args.pendingBackgroundDelta > 0)
|
|
200
|
-
return false;
|
|
201
|
-
return true;
|
|
202
|
-
}
|
|
203
|
-
/** React to a message with an emoji. Silently fails if reactions aren't supported. */
|
|
204
|
-
async function react(ctx, emoji) {
|
|
205
|
-
try {
|
|
206
|
-
await ctx.react(emoji);
|
|
207
|
-
}
|
|
208
|
-
catch {
|
|
209
|
-
// Reactions not supported in this chat β silently ignore
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
export async function handleMessage(ctx) {
|
|
213
|
-
const rawText = ctx.message?.text;
|
|
214
|
-
if (!rawText || rawText.startsWith("/"))
|
|
215
|
-
return;
|
|
216
|
-
let text = rawText;
|
|
217
|
-
// Forwarded message β add forward context (if allowed)
|
|
218
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
219
|
-
const msgAny = ctx.message;
|
|
220
|
-
if (msgAny?.forward_origin || msgAny?.forward_date) {
|
|
221
|
-
if (!isForwardingAllowed()) {
|
|
222
|
-
await ctx.reply("β οΈ Weitergeleitete Nachrichten sind deaktiviert. Aktiviere mit `/security forwards on`", { parse_mode: "Markdown" });
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
const forwardFrom = msgAny.forward_sender_name || "unbekannt";
|
|
226
|
-
text = `[Weitergeleitete Nachricht von ${forwardFrom}]\n\n${rawText}`;
|
|
227
|
-
}
|
|
228
|
-
// Reply context β include quoted message
|
|
229
|
-
const replyTo = ctx.message?.reply_to_message;
|
|
230
|
-
if (replyTo?.text) {
|
|
231
|
-
const quotedText = replyTo.text.length > 500
|
|
232
|
-
? replyTo.text.slice(0, 500) + "..."
|
|
233
|
-
: replyTo.text;
|
|
234
|
-
text = `[Replying to previous message: "${quotedText}"]\n\n${text}`;
|
|
235
|
-
}
|
|
236
|
-
const userId = ctx.from.id;
|
|
237
|
-
const sessionKey = buildSessionKey("telegram", ctx.chat.id, userId);
|
|
238
|
-
const session = getSession(sessionKey);
|
|
239
|
-
// Track user profile
|
|
240
|
-
touchProfile(userId, ctx.from?.first_name, ctx.from?.username, "telegram", text);
|
|
241
|
-
// Sync session language from persistent profile (on first message)
|
|
242
|
-
if (session.messageCount === 0) {
|
|
243
|
-
const { loadProfile } = await import("../services/users.js");
|
|
244
|
-
const profile = loadProfile(userId);
|
|
245
|
-
if (profile?.language)
|
|
246
|
-
session.language = profile.language;
|
|
247
|
-
}
|
|
248
|
-
if (session.isProcessing) {
|
|
249
|
-
// v4.12.3 β If a background agent is pending, the running query is
|
|
250
|
-
// almost certainly just the SDK's CLI subprocess sitting idle waiting
|
|
251
|
-
// for the task-notification to be ready (can take 5+ minutes for long
|
|
252
|
-
// audits). Don't queue β abort the blocked query and fall through so
|
|
253
|
-
// the new message gets processed immediately. The background task
|
|
254
|
-
// itself continues in its detached subprocess; the async-agent watcher
|
|
255
|
-
// delivers the result via subagent-delivery.ts when ready.
|
|
256
|
-
//
|
|
257
|
-
// v5.2 β decideMidTaskRouting unifies bypass / steer / queue in one place.
|
|
258
|
-
const _midTaskBypass = shouldBypassQueue({
|
|
259
|
-
isProcessing: session.isProcessing,
|
|
260
|
-
pendingBackgroundCount: session.pendingBackgroundCount,
|
|
261
|
-
abortController: session.abortController,
|
|
262
|
-
});
|
|
263
|
-
const _midTaskProviderIsSdk = getRegistry().getActive().config.type === "claude-sdk";
|
|
264
|
-
const _midTaskRoute = decideMidTaskRouting({
|
|
265
|
-
isProcessing: true,
|
|
266
|
-
providerIsClaudeSdk: _midTaskProviderIsSdk,
|
|
267
|
-
steeringEnabled: isSteeringEnabled(),
|
|
268
|
-
hasSteerChannel: !!session._steerChannel,
|
|
269
|
-
hasLiveSdkQuery: !!session._qHandle, // C-H3: require a live SDK query handle
|
|
270
|
-
shouldBypass: _midTaskBypass,
|
|
271
|
-
});
|
|
272
|
-
if (_midTaskRoute === "bypass") {
|
|
273
|
-
console.log(`[v4.12.3 bypass] aborting blocked query for ${sessionKey} β ` +
|
|
274
|
-
`${session.pendingBackgroundCount} background agent(s) pending`);
|
|
275
|
-
// Mark the abort as a bypass so the old handler's error branch
|
|
276
|
-
// doesn't surface a "request cancelled" reply to the user.
|
|
277
|
-
session._bypassAbortFired = true;
|
|
278
|
-
try {
|
|
279
|
-
session.abortController.abort();
|
|
280
|
-
}
|
|
281
|
-
catch {
|
|
282
|
-
/* ignore */
|
|
283
|
-
}
|
|
284
|
-
// Wait briefly for the old handler's finally to run. If it hangs
|
|
285
|
-
// (>5s, shouldn't happen), we fall through anyway β worst case is
|
|
286
|
-
// a brief overlap where both handlers run.
|
|
287
|
-
await waitUntilProcessingFalse(session, 5000);
|
|
288
|
-
// Fall through to start a fresh query below.
|
|
289
|
-
}
|
|
290
|
-
else if (_midTaskRoute === "steer") {
|
|
291
|
-
// v5.2 β btw live steering: push mid-task message into the open
|
|
292
|
-
// SteerChannel so the running claude-sdk query picks it up as a
|
|
293
|
-
// streaming-input user message. No abort, no queue.
|
|
294
|
-
// C-L2: push() returns boolean β only π¨/ack when accepted; reply bufferFull otherwise.
|
|
295
|
-
const steerAccepted = session._steerChannel.push(text);
|
|
296
|
-
if (steerAccepted) {
|
|
297
|
-
await react(ctx, "π¨");
|
|
298
|
-
if (!session._steerAckSentThisTurn) {
|
|
299
|
-
try {
|
|
300
|
-
await ctx.reply(t("bot.steer.ack", session.language));
|
|
301
|
-
}
|
|
302
|
-
catch {
|
|
303
|
-
/* harmless grammy race */
|
|
304
|
-
}
|
|
305
|
-
session._steerAckSentThisTurn = true;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
else {
|
|
309
|
-
// Buffer full or channel closed β tell the user honestly
|
|
310
|
-
try {
|
|
311
|
-
await ctx.reply(t("bot.steer.bufferFull", session.language));
|
|
312
|
-
}
|
|
313
|
-
catch {
|
|
314
|
-
/* harmless grammy race */
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
else {
|
|
320
|
-
// Normal queue behavior. v4.12.3 β emit a text reply in addition
|
|
321
|
-
// to the reaction so the user actually sees that their message
|
|
322
|
-
// was received and is waiting. Reactions alone are too subtle.
|
|
323
|
-
if (session.messageQueue.length < 3) {
|
|
324
|
-
session.messageQueue.push(text);
|
|
325
|
-
await react(ctx, "π");
|
|
326
|
-
try {
|
|
327
|
-
await ctx.reply("β³ Eine Anfrage lΓ€uft gerade. Deine Nachricht ist in der Warteschlange und wird als NΓ€chstes bearbeitet.");
|
|
328
|
-
}
|
|
329
|
-
catch {
|
|
330
|
-
/* harmless grammy race */
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
else {
|
|
334
|
-
await ctx.reply("β³ Warteschlange voll (3 Nachrichten). Bitte warten oder /cancel.");
|
|
335
|
-
}
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
// Consume queued messages (sent while previous query was processing)
|
|
340
|
-
if (session.messageQueue.length > 0) {
|
|
341
|
-
const queued = session.messageQueue.splice(0);
|
|
342
|
-
text = [...queued, text].join("\n\n");
|
|
343
|
-
}
|
|
344
|
-
session.isProcessing = true;
|
|
345
|
-
session.abortController = new AbortController();
|
|
346
|
-
// C-H2 β Stamp a per-turn identity token so the finally block can detect
|
|
347
|
-
// whether a NEW turn has already started before it runs. If requestStop
|
|
348
|
-
// fires mid-turn and allows a new message to start a fresh turn (with its
|
|
349
|
-
// own new abortController + _steerChannel), the old turn's finally sees the
|
|
350
|
-
// token mismatch and skips the clobber β preserving the new turn's state.
|
|
351
|
-
const _thisTurnId = crypto.randomUUID();
|
|
352
|
-
session._turnId = _thisTurnId;
|
|
353
|
-
// v4.12.3 β Clear any stale bypass flag from a previous aborted turn.
|
|
354
|
-
// The flag is set by the bypass path right before it calls abort(),
|
|
355
|
-
// read by the OLD handler's error path, and cleared here by the NEW
|
|
356
|
-
// handler so it doesn't misclassify future non-bypass aborts. Use
|
|
357
|
-
// `delete` so TypeScript doesn't narrow the flag to literal `false`
|
|
358
|
-
// for the rest of this function (it's mutated from the bypass path in
|
|
359
|
-
// another handler invocation, so the type stays `boolean | undefined`).
|
|
360
|
-
delete session._bypassAbortFired;
|
|
361
|
-
// v5.1 β Send a lightweight control message with an inline β Stop button.
|
|
362
|
-
// Payload "stop:<sessionKey>" matches the callbackQuery handler in commands.ts.
|
|
363
|
-
// Cleaned up (deleted) in the finally block regardless of how the turn ends.
|
|
364
|
-
// One message only β do NOT send if the chat can't receive replies.
|
|
365
|
-
let stopMsgId = null;
|
|
366
|
-
try {
|
|
367
|
-
const stopMsg = await ctx.reply("β³", {
|
|
368
|
-
reply_markup: new InlineKeyboard().text("β Stop", `stop:${sessionKey}`),
|
|
369
|
-
});
|
|
370
|
-
stopMsgId = stopMsg.message_id;
|
|
371
|
-
}
|
|
372
|
-
catch { /* harmless β typing indicator remains, button is best-effort */ }
|
|
373
|
-
const streamer = new TelegramStreamer(ctx.chat.id, ctx.api, ctx.message?.message_id);
|
|
374
|
-
let finalText = "";
|
|
375
|
-
let timedOut = false;
|
|
376
|
-
// v4.12.3 β Tracks whether the current turn ended because the bypass
|
|
377
|
-
// path aborted us. When true, skip the finalize/broadcast/π reaction
|
|
378
|
-
// flow at the bottom of the handler since the user isn't waiting on
|
|
379
|
-
// this turn anymore. Explicit `boolean` type so TS doesn't narrow to
|
|
380
|
-
// the literal `false` and reject the later comparison.
|
|
381
|
-
let bypassAborted = false;
|
|
382
|
-
const typingInterval = setInterval(() => {
|
|
383
|
-
ctx.api.sendChatAction(ctx.chat.id, "typing").catch(() => { });
|
|
384
|
-
}, 4000);
|
|
385
|
-
// v4.12.1 β Task-aware stuck timer. Normal mode (STUCK_TIMEOUT_MS)
|
|
386
|
-
// fires after 10 min of silence. When a sync Task/Agent tool call is
|
|
387
|
-
// active (tracked by toolUseId in the for-await loop below), the
|
|
388
|
-
// timeout escalates to SYNC_AGENT_IDLE_TIMEOUT_MS (120 min) so
|
|
389
|
-
// legitimate long-running sub-agents that emit no intermediate chunks
|
|
390
|
-
// don't get falsely aborted. See src/handlers/stuck-timer.ts.
|
|
391
|
-
const stuckTimer = createStuckTimer({
|
|
392
|
-
normalMs: STUCK_TIMEOUT_MS,
|
|
393
|
-
extendedMs: SYNC_AGENT_IDLE_TIMEOUT_MS,
|
|
394
|
-
onTimeout: () => {
|
|
395
|
-
if (session.abortController && !session.abortController.signal.aborted) {
|
|
396
|
-
timedOut = true;
|
|
397
|
-
session.abortController.abort();
|
|
398
|
-
}
|
|
399
|
-
},
|
|
400
|
-
});
|
|
401
|
-
stuckTimer.reset();
|
|
402
|
-
try {
|
|
403
|
-
// React with π€ to show we're thinking
|
|
404
|
-
await react(ctx, "π€");
|
|
405
|
-
await ctx.api.sendChatAction(ctx.chat.id, "typing");
|
|
406
|
-
session.messageCount++;
|
|
407
|
-
emit("message:received", { userId, text, platform: "telegram" });
|
|
408
|
-
// v4.5.0: broadcast the user message so TUI/WebUI observers can mirror it.
|
|
409
|
-
// The broadcast bus is fire-and-forget β never affects the Telegram flow.
|
|
410
|
-
broadcastUserMessage({
|
|
411
|
-
platform: "telegram",
|
|
412
|
-
userId,
|
|
413
|
-
userName: ctx.from?.first_name || ctx.from?.username,
|
|
414
|
-
chatId: ctx.chat.id,
|
|
415
|
-
text,
|
|
416
|
-
ts: Date.now(),
|
|
417
|
-
});
|
|
418
|
-
broadcastResponseStart({
|
|
419
|
-
platform: "telegram",
|
|
420
|
-
userId,
|
|
421
|
-
chatId: ctx.chat.id,
|
|
422
|
-
ts: Date.now(),
|
|
423
|
-
});
|
|
424
|
-
// Determine provider type early for compaction check
|
|
425
|
-
const registry = getRegistry();
|
|
426
|
-
const activeProvider = registry.getActive();
|
|
427
|
-
const isSDK = activeProvider.config.type === "claude-sdk";
|
|
428
|
-
// Auto-compact if needed (non-SDK only)
|
|
429
|
-
if (!isSDK) {
|
|
430
|
-
if (shouldCompact(session)) {
|
|
431
|
-
const result = await compactSession(session);
|
|
432
|
-
if (result.removedEntries > 0) {
|
|
433
|
-
console.log(`Compacted session: removed ${result.removedEntries} entries, flushed=${result.flushedToMemory}`);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
// Auto-detect and adapt language from user's message
|
|
438
|
-
const adaptedLang = trackAndAdapt(userId, text, session.language);
|
|
439
|
-
if (adaptedLang !== session.language) {
|
|
440
|
-
session.language = adaptedLang;
|
|
441
|
-
}
|
|
442
|
-
// Build query options (with semantic memory search for non-SDK + skill injection).
|
|
443
|
-
// v4.11.0 P0 #3: SDK now also gets semantic recall on first-turn. The signal
|
|
444
|
-
// is `session.sessionId === null` β meaning Claude SDK hasn't given us a
|
|
445
|
-
// resume token yet for this session. True for: brand-new users, post-/new,
|
|
446
|
-
// and rehydrated sessions where the persisted snapshot lacked a sessionId.
|
|
447
|
-
// After the first SDK turn, Claude resumes via SDK session_id and already
|
|
448
|
-
// carries the recalled context β no need for another search per turn.
|
|
449
|
-
//
|
|
450
|
-
// v4.12.0 β Resolve the user's active Telegram workspace (if any) and
|
|
451
|
-
// forward the persona to buildSmartSystemPrompt. If the workspace
|
|
452
|
-
// changed since last turn, update session's workingDir + workspaceName.
|
|
453
|
-
const activeWsName = getTelegramWorkspace(userId);
|
|
454
|
-
const workspace = activeWsName
|
|
455
|
-
? (getWorkspace(activeWsName) ?? resolveWorkspaceOrDefault("telegram", String(userId), undefined))
|
|
456
|
-
: resolveWorkspaceOrDefault("telegram", String(userId), undefined);
|
|
457
|
-
// v4.19.1 β Workspace switch detection. Claude Agent SDK's `resume` is
|
|
458
|
-
// bound to the cwd (session files live under
|
|
459
|
-
// ~/.claude/projects/<cwd-hash>/<session-id>.jsonl). If cwd changes as
|
|
460
|
-
// part of this switch, the stored sessionId points at a file the CLI
|
|
461
|
-
// cannot find in the new project folder β silent empty stream. Guard
|
|
462
|
-
// with a workspaceName change (not cwd comparison) so /dir-initiated
|
|
463
|
-
// custom cwds are preserved across turns where no workspace actually
|
|
464
|
-
// switched.
|
|
465
|
-
if (session.workspaceName !== workspace.name) {
|
|
466
|
-
const cwdChanged = session.workingDir !== workspace.cwd;
|
|
467
|
-
session.workspaceName = workspace.name;
|
|
468
|
-
session.workingDir = workspace.cwd;
|
|
469
|
-
if (cwdChanged) {
|
|
470
|
-
console.log(`[session] workspace switch changed cwd (β ${workspace.cwd}) β ` +
|
|
471
|
-
`invalidating SDK resume anchor and skipping bridge`);
|
|
472
|
-
session.sessionId = null;
|
|
473
|
-
// v4.19.2 β Anchor at the last turn BEFORE the new user message so
|
|
474
|
-
// buildBridgeMessage() produces no catch-up preamble. A workspace
|
|
475
|
-
// switch means "new persona, new task" β the previous conversation
|
|
476
|
-
// (often from a different workspace) should NOT be reframed as
|
|
477
|
-
// "Fallback model turns" and fed back into Claude. That framing
|
|
478
|
-
// was producing format-kaskaden where Claude imitated Telegram
|
|
479
|
-
// "(Keine Antwort)" fallback artifacts from history.
|
|
480
|
-
session.lastSdkHistoryIndex = session.history.length - 1;
|
|
481
|
-
markSessionDirty(userId);
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
const chatIdStr = String(ctx.chat.id);
|
|
485
|
-
const skillContext = buildSkillContext(text);
|
|
486
|
-
const isFirstSDKTurn = isSDK && session.sessionId === null;
|
|
487
|
-
const systemPrompt = (await buildSmartSystemPrompt(isSDK, session.language, text, chatIdStr, isFirstSDKTurn, workspace.systemPromptOverride)) + skillContext;
|
|
488
|
-
// Track the user turn in history regardless of provider type. This keeps
|
|
489
|
-
// the fallback path (Ollama etc.) aware of what was said on SDK turns.
|
|
490
|
-
addToHistory(userId, { role: "user", content: text });
|
|
491
|
-
// Checkpoint telemetry: mirror the SDK provider's threshold check here
|
|
492
|
-
// so session.checkpointHintsInjected reflects reality. The provider
|
|
493
|
-
// evaluates the exact same condition at query time β if it's true,
|
|
494
|
-
// it prepends a [CHECKPOINT] reminder to the prompt.
|
|
495
|
-
if (isSDK) {
|
|
496
|
-
const wouldInjectCheckpoint = session.toolUseCount >= CHECKPOINT_TOOL_THRESHOLD ||
|
|
497
|
-
session.messageCount >= CHECKPOINT_MSG_THRESHOLD;
|
|
498
|
-
if (wouldInjectCheckpoint) {
|
|
499
|
-
session.checkpointHintsInjected++;
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
// v4.12.3 β If a background agent is still pending, skip SDK resume.
|
|
503
|
-
// The OLD SDK session is blocked waiting to deliver the
|
|
504
|
-
// task-notification inline; resuming it would inherit that block.
|
|
505
|
-
// Start a fresh SDK session and rely on the bridge preamble below
|
|
506
|
-
// to carry recent history so Claude has context.
|
|
507
|
-
const bypassResume = isSDK && shouldBypassSdkResume({
|
|
508
|
-
pendingBackgroundCount: session.pendingBackgroundCount,
|
|
509
|
-
});
|
|
510
|
-
if (bypassResume) {
|
|
511
|
-
console.log(`[v4.12.3 bypass] starting fresh SDK session for ${sessionKey} β ` +
|
|
512
|
-
`${session.pendingBackgroundCount} background agent(s) still pending`);
|
|
513
|
-
}
|
|
514
|
-
// B2 Bridge-Message: if SDK is active but there are non-SDK turns since
|
|
515
|
-
// the last SDK turn, prepend a catch-up preamble so the SDK sees what
|
|
516
|
-
// happened during the failover. We defensively clamp the index against
|
|
517
|
-
// history bounds in case compaction shrank the array under our feet.
|
|
518
|
-
//
|
|
519
|
-
// v4.12.3 β Bypass-resume path also gets a bridge: since we're starting
|
|
520
|
-
// a fresh SDK session, Claude has no prior context from this chat.
|
|
521
|
-
// Bridge the last BYPASS_BRIDGE_TURNS entries so it knows what we were
|
|
522
|
-
// just talking about.
|
|
523
|
-
const BYPASS_BRIDGE_TURNS = 10;
|
|
524
|
-
let bridgedPrompt = text;
|
|
525
|
-
if (isSDK) {
|
|
526
|
-
let gapStart;
|
|
527
|
-
let gapEnd;
|
|
528
|
-
if (bypassResume) {
|
|
529
|
-
gapEnd = session.history.length - 1;
|
|
530
|
-
gapStart = Math.max(0, gapEnd - BYPASS_BRIDGE_TURNS);
|
|
531
|
-
}
|
|
532
|
-
else {
|
|
533
|
-
const anchor = Math.min(session.lastSdkHistoryIndex, session.history.length - 1);
|
|
534
|
-
gapStart = Math.max(0, anchor + 1);
|
|
535
|
-
// gapEnd excludes the user message we just added (history.length - 1).
|
|
536
|
-
gapEnd = session.history.length - 1;
|
|
537
|
-
}
|
|
538
|
-
if (gapEnd > gapStart) {
|
|
539
|
-
const gapTurns = session.history.slice(gapStart, gapEnd);
|
|
540
|
-
const bridge = buildBridgeMessage(gapTurns);
|
|
541
|
-
if (bridge) {
|
|
542
|
-
bridgedPrompt = bridge + text;
|
|
543
|
-
console.log(`[bridge] ${bypassResume ? "bypass" : "SDK recovery"}: ` +
|
|
544
|
-
`injecting ${gapTurns.length} turn(s) into prompt`);
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
// v4.19.0 β Per-workspace runtime overrides. Each is only applied when
|
|
549
|
-
// the workspace explicitly set it; otherwise the session/provider default
|
|
550
|
-
// wins. Toolset is mapped to a concrete allowedTools list via
|
|
551
|
-
// toolsetToAllowedTools(); providers that ignore allowedTools (Ollama etc.)
|
|
552
|
-
// just drop it.
|
|
553
|
-
const { toolsetToAllowedTools } = await import("../services/workspaces.js");
|
|
554
|
-
const wsAllowed = toolsetToAllowedTools(workspace.toolset);
|
|
555
|
-
const queryOpts = {
|
|
556
|
-
prompt: bridgedPrompt,
|
|
557
|
-
systemPrompt,
|
|
558
|
-
workingDir: session.workingDir,
|
|
559
|
-
effort: workspace.effort ?? session.effort,
|
|
560
|
-
// v4.15 β Per-workspace model override (optional YAML `model:` field).
|
|
561
|
-
// v4.19 β ditto for temperature and toolset-derived allowedTools.
|
|
562
|
-
...(workspace.model ? { model: workspace.model } : {}),
|
|
563
|
-
...(workspace.temperature !== undefined ? { temperature: workspace.temperature } : {}),
|
|
564
|
-
...(wsAllowed ? { allowedTools: wsAllowed } : {}),
|
|
565
|
-
abortSignal: session.abortController.signal,
|
|
566
|
-
// User's UI locale β registry uses it to localize failure messages.
|
|
567
|
-
locale: session.language,
|
|
568
|
-
// SDK-specific. v4.12.3 β bypass resume when background pending.
|
|
569
|
-
sessionId: isSDK && !bypassResume ? session.sessionId : null,
|
|
570
|
-
// Unified history: SDK ignores it (uses filesystem-resume instead),
|
|
571
|
-
// non-SDK providers use it for context. Keeping it populated for both
|
|
572
|
-
// means a failover from SDK β Ollama keeps the conversation context.
|
|
573
|
-
history: session.history,
|
|
574
|
-
// SDK checkpoint tracking
|
|
575
|
-
_sessionState: isSDK ? {
|
|
576
|
-
messageCount: session.messageCount,
|
|
577
|
-
toolUseCount: session.toolUseCount,
|
|
578
|
-
} : undefined,
|
|
579
|
-
// v4.13 β Expose alvin_dispatch_agent MCP tool so Claude can spawn
|
|
580
|
-
// truly detached background sub-agents (independent of this SDK
|
|
581
|
-
// subprocess's lifecycle). Only for SDK provider + Telegram here β
|
|
582
|
-
// non-SDK providers ignore this field.
|
|
583
|
-
alvinDispatchContext: isSDK ? {
|
|
584
|
-
chatId: ctx.chat.id,
|
|
585
|
-
userId,
|
|
586
|
-
sessionKey,
|
|
587
|
-
} : undefined,
|
|
588
|
-
// v5.1 β Store the SDK query handle so requestStop() can interrupt it.
|
|
589
|
-
onQueryHandle: (q) => { session._qHandle = q; },
|
|
590
|
-
};
|
|
591
|
-
// v5.2 β btw live steering: seed SteerChannel at turn start so mid-task
|
|
592
|
-
// user messages can be pushed in while this query is running. Only for
|
|
593
|
-
// claude-sdk (the only provider that supports streaming-input prompts).
|
|
594
|
-
// The initial bridged prompt is pushed first so the channel sequence is:
|
|
595
|
-
// [bridgedPrompt, <any mid-task messages>, <close on finally>]
|
|
596
|
-
// queryOpts.steerChannel is set so the provider uses the channel as the
|
|
597
|
-
// prompt source. queryOpts.prompt is kept as-is for non-SDK fallback paths
|
|
598
|
-
// (providers that don't support steerChannel ignore it and use prompt).
|
|
599
|
-
if (isSDK && isSteeringEnabled()) {
|
|
600
|
-
session._steerChannel = new SteerChannel();
|
|
601
|
-
session._steerChannel.push(bridgedPrompt);
|
|
602
|
-
queryOpts.steerChannel = session._steerChannel;
|
|
603
|
-
}
|
|
604
|
-
// Stream response from provider (with fallback)
|
|
605
|
-
let lastBroadcastLen = 0;
|
|
606
|
-
// Captured during tool_use chunks; consumed by tool_result chunks so
|
|
607
|
-
// the async-agent watcher can label pending agents with their human-
|
|
608
|
-
// readable description (which only appears in the tool_use input,
|
|
609
|
-
// not in the tool_result text). See Fix #17 Stage 2.
|
|
610
|
-
let lastAgentToolUseInput;
|
|
611
|
-
// v4.19.1 β Track whether the provider requested a session reset during
|
|
612
|
-
// this stream. If it did, the trailing `done` chunk's sessionId MUST be
|
|
613
|
-
// ignored β otherwise it restores the exact sessionId we just cleared
|
|
614
|
-
// (the empty-stream capturedSessionId) and the next turn loops again.
|
|
615
|
-
// This is the second half of the empty-stream-loop fix.
|
|
616
|
-
let sessionResetInStream = false;
|
|
617
|
-
// Cycle-3 P0 β background honesty guard tracking.
|
|
618
|
-
// `syncTaskSeenWithoutRunInBackground`: lifted from the stuckTimer.enterSync
|
|
619
|
-
// site below β true once a Task/Agent chunk arrives with no runInBackground.
|
|
620
|
-
// `pendingBackgroundCountAtTurnStart`: snapshot before the stream so we can
|
|
621
|
-
// compute the delta at turn end (dispatch_agent increments this counter).
|
|
622
|
-
let syncTaskSeenWithoutRunInBackground = false;
|
|
623
|
-
const pendingBackgroundCountAtTurnStart = session.pendingBackgroundCount ?? 0;
|
|
624
|
-
for await (const chunk of registry.queryWithFallback(queryOpts, workspace.provider)) {
|
|
625
|
-
// v5.1 β Bail as soon as requestStop() marks the session. The registry's
|
|
626
|
-
// outer loop already guards against new provider attempts; this guard
|
|
627
|
-
// drains the current generator's remaining chunks immediately.
|
|
628
|
-
if (session._stopRequested)
|
|
629
|
-
break;
|
|
630
|
-
// v4.12.1 β Update pending-sync-task state FIRST so the timer's
|
|
631
|
-
// next reset picks up the new state. This ordering is load-bearing:
|
|
632
|
-
// reversing it means the timer rearms with stale state. A sync
|
|
633
|
-
// Task/Agent tool call switches the stuck timer to extended mode
|
|
634
|
-
// (120 min) to tolerate the silent gap until tool_result arrives.
|
|
635
|
-
if (chunk.type === "tool_use" &&
|
|
636
|
-
(chunk.toolName === "Task" || chunk.toolName === "Agent") &&
|
|
637
|
-
chunk.toolUseId &&
|
|
638
|
-
chunk.runInBackground !== true) {
|
|
639
|
-
stuckTimer.enterSync(chunk.toolUseId);
|
|
640
|
-
// Cycle-3 P0 β lift the signal for honesty guard (same condition)
|
|
641
|
-
syncTaskSeenWithoutRunInBackground = true;
|
|
642
|
-
}
|
|
643
|
-
else if (chunk.type === "tool_result" && chunk.toolUseId) {
|
|
644
|
-
// Any tool_result may match a pending sync entry. Set.delete is
|
|
645
|
-
// a no-op if the id isn't in the set β safe for async results.
|
|
646
|
-
stuckTimer.exitSync(chunk.toolUseId);
|
|
647
|
-
}
|
|
648
|
-
// Any chunk is progress β reset the stuck timer (now with
|
|
649
|
-
// updated pending-sync state so the correct timeout is armed).
|
|
650
|
-
stuckTimer.reset();
|
|
651
|
-
switch (chunk.type) {
|
|
652
|
-
case "text":
|
|
653
|
-
finalText = chunk.text || "";
|
|
654
|
-
// Clear any tool-use status line β real content is flowing now.
|
|
655
|
-
streamer.setStatus(null);
|
|
656
|
-
await streamer.update(finalText);
|
|
657
|
-
// v4.18.5 β Provider requested a session reset (empty-stream / stale
|
|
658
|
-
// sessionId recovery). Clear the session's sessionId + SDK anchor so
|
|
659
|
-
// the next query starts a fresh Claude session instead of resuming
|
|
660
|
-
// the broken one. Without this, the bot would loop empty-stream
|
|
661
|
-
// replies and burn credits until the user manually runs /new.
|
|
662
|
-
if (chunk.sessionResetRequested) {
|
|
663
|
-
console.warn(`[session] provider requested reset for ${sessionKey} β clearing sessionId + SDK anchor`);
|
|
664
|
-
session.sessionId = null;
|
|
665
|
-
session.lastSdkHistoryIndex = -1;
|
|
666
|
-
sessionResetInStream = true;
|
|
667
|
-
markSessionDirty(userId);
|
|
668
|
-
}
|
|
669
|
-
// Emit the new delta for observers β accumulated text minus what
|
|
670
|
-
// we already broadcast.
|
|
671
|
-
if (finalText.length > lastBroadcastLen) {
|
|
672
|
-
const delta = finalText.slice(lastBroadcastLen);
|
|
673
|
-
broadcastResponseDelta({
|
|
674
|
-
platform: "telegram",
|
|
675
|
-
userId,
|
|
676
|
-
chatId: ctx.chat.id,
|
|
677
|
-
delta,
|
|
678
|
-
ts: Date.now(),
|
|
679
|
-
});
|
|
680
|
-
lastBroadcastLen = finalText.length;
|
|
681
|
-
}
|
|
682
|
-
break;
|
|
683
|
-
case "tool_use":
|
|
684
|
-
// Surface the active tool so users see real progress instead of
|
|
685
|
-
// an endless typing indicator. The streamer renders this as a
|
|
686
|
-
// dim italic footer under any accumulated text.
|
|
687
|
-
if (chunk.toolName) {
|
|
688
|
-
session.toolUseCount++;
|
|
689
|
-
const icon = TOOL_ICONS[chunk.toolName] || "π§";
|
|
690
|
-
// Special treatment for Claude's SDK-internal Task/Agent tool:
|
|
691
|
-
// track how many sub-tasks Claude delegated and surface the
|
|
692
|
-
// task description in the status line so the user sees WHAT
|
|
693
|
-
// is being delegated, not just "Taskβ¦". The tool was renamed
|
|
694
|
-
// from "Task" to "Agent" in Claude Code v2.1.63 β match both.
|
|
695
|
-
if (chunk.toolName === "Task" || chunk.toolName === "Agent") {
|
|
696
|
-
session.sdkSubTaskCount++;
|
|
697
|
-
let label = chunk.toolName;
|
|
698
|
-
if (chunk.toolInput) {
|
|
699
|
-
try {
|
|
700
|
-
const parsed = JSON.parse(chunk.toolInput);
|
|
701
|
-
if (parsed.description) {
|
|
702
|
-
// Trim long descriptions so the status line stays readable
|
|
703
|
-
const desc = parsed.description.length > 80
|
|
704
|
-
? parsed.description.slice(0, 80) + "β¦"
|
|
705
|
-
: parsed.description;
|
|
706
|
-
label = `${chunk.toolName}: ${desc}`;
|
|
707
|
-
}
|
|
708
|
-
else if (parsed.subagent_type) {
|
|
709
|
-
label = `${chunk.toolName} (${parsed.subagent_type})`;
|
|
710
|
-
}
|
|
711
|
-
// Capture the description+prompt for the upcoming
|
|
712
|
-
// tool_result. Used by Fix #17 Stage 2 to label
|
|
713
|
-
// background agents in the watcher's delivery banner.
|
|
714
|
-
lastAgentToolUseInput = {
|
|
715
|
-
description: parsed.description,
|
|
716
|
-
prompt: parsed.prompt,
|
|
717
|
-
};
|
|
718
|
-
}
|
|
719
|
-
catch {
|
|
720
|
-
// not JSON β keep generic label
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
streamer.setStatus(`${icon} ${label}β¦`);
|
|
724
|
-
}
|
|
725
|
-
else {
|
|
726
|
-
streamer.setStatus(`${icon} ${chunk.toolName}β¦`);
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
break;
|
|
730
|
-
case "tool_result":
|
|
731
|
-
// Fix #17 Stage 2: detect Agent async_launched payloads and
|
|
732
|
-
// hand them off to the async-agent watcher. The watcher will
|
|
733
|
-
// poll the outputFile and deliver the result as a separate
|
|
734
|
-
// Telegram message when the background agent finishes.
|
|
735
|
-
// v4.12.3 β Forward sessionKey so the watcher can route the
|
|
736
|
-
// delivery-complete decrement back to the right session.
|
|
737
|
-
handleToolResultChunk(chunk, {
|
|
738
|
-
chatId: ctx.chat.id,
|
|
739
|
-
userId,
|
|
740
|
-
sessionKey,
|
|
741
|
-
lastToolUseInput: lastAgentToolUseInput,
|
|
742
|
-
});
|
|
743
|
-
// Reset the captured input β only the immediately following
|
|
744
|
-
// tool_result should consume it.
|
|
745
|
-
lastAgentToolUseInput = undefined;
|
|
746
|
-
break;
|
|
747
|
-
case "done":
|
|
748
|
-
// v4.19.1 β Respect the in-stream session reset. If the provider
|
|
749
|
-
// already signalled `sessionResetRequested` on the preceding text
|
|
750
|
-
// chunk (empty-stream detection), do NOT let the trailing done
|
|
751
|
-
// chunk restore the sessionId we just nulled β that was the
|
|
752
|
-
// silent bug behind the empty-stream loop across workspace
|
|
753
|
-
// switches. The `done` chunk's sessionId on an empty stream is
|
|
754
|
-
// either the stale resume token we tried to use or a brand-new
|
|
755
|
-
// session file the CLI created in the wrong project folder;
|
|
756
|
-
// neither is safe to resume from.
|
|
757
|
-
if (chunk.sessionId && !sessionResetInStream)
|
|
758
|
-
session.sessionId = chunk.sessionId;
|
|
759
|
-
if (chunk.costUsd)
|
|
760
|
-
session.totalCost += chunk.costUsd;
|
|
761
|
-
// Track the input tokens this turn used β this approximates the
|
|
762
|
-
// current context window usage since the model receives the full
|
|
763
|
-
// conversation context on every turn. Used for the Context:X/Y
|
|
764
|
-
// progress meter in /status.
|
|
765
|
-
if (typeof chunk.inputTokens === "number" && chunk.inputTokens > 0) {
|
|
766
|
-
session.lastTurnInputTokens = chunk.inputTokens;
|
|
767
|
-
}
|
|
768
|
-
trackProviderUsage(userId, registry.getActiveKey(), chunk.costUsd || 0, chunk.inputTokens, chunk.outputTokens);
|
|
769
|
-
trackUsage(registry.getActiveKey(), chunk.inputTokens || 0, chunk.outputTokens || 0, chunk.costUsd || 0);
|
|
770
|
-
session.lastActivity = Date.now();
|
|
771
|
-
break;
|
|
772
|
-
case "fallback":
|
|
773
|
-
await ctx.reply(`β‘ _${chunk.failedProvider} unavailable β switching to ${chunk.providerName}_`, { parse_mode: "Markdown" });
|
|
774
|
-
break;
|
|
775
|
-
case "error":
|
|
776
|
-
// v4.12.3 β If the bypass path aborted us, swallow the error
|
|
777
|
-
// silently. The new handler is already preparing to process
|
|
778
|
-
// the user's next message; showing a cancellation notice here
|
|
779
|
-
// would be misleading.
|
|
780
|
-
if (session._bypassAbortFired === true &&
|
|
781
|
-
chunk.error?.toLowerCase().includes("abort")) {
|
|
782
|
-
bypassAborted = true;
|
|
783
|
-
break;
|
|
784
|
-
}
|
|
785
|
-
// If our stuck-timer fired, the abort travels up as a registry
|
|
786
|
-
// mid-stream error chunk. Prefer the explicit stuck message over
|
|
787
|
-
// the generic one so the user understands this was a real hang,
|
|
788
|
-
// not a random error.
|
|
789
|
-
if (timedOut) {
|
|
790
|
-
await ctx.reply(t("bot.error.timeoutStuck", session.language, { min: STUCK_TIMEOUT_MINUTES }));
|
|
791
|
-
}
|
|
792
|
-
else if (!isHarmlessTelegramError(chunk.error)) {
|
|
793
|
-
await ctx.reply(`${t("bot.error.prefix", session.language)} ${chunk.error}`);
|
|
794
|
-
}
|
|
795
|
-
break;
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
// Cycle-3 P0 β background honesty guard.
|
|
799
|
-
// If the turn ran a sync Task/Agent (blocking) and no real detach happened
|
|
800
|
-
// (no dispatch_agent, no pendingBackgroundCount increase), append one
|
|
801
|
-
// truthful notice so the user is never left with a false async promise.
|
|
802
|
-
// This fires only on "normal" turn endings β bypass-abort and user-stop
|
|
803
|
-
// are handled below and don't need the notice (neither promises async).
|
|
804
|
-
if (!bypassAborted &&
|
|
805
|
-
!timedOut &&
|
|
806
|
-
!session._stopRequested &&
|
|
807
|
-
detectUndetachedBackgroundClaim({
|
|
808
|
-
taskChunkSeenWithoutRunInBackground: syncTaskSeenWithoutRunInBackground,
|
|
809
|
-
dispatchAgentFired: false, // used purely via pendingBackgroundDelta below
|
|
810
|
-
pendingBackgroundDelta: (session.pendingBackgroundCount ?? 0) - pendingBackgroundCountAtTurnStart,
|
|
811
|
-
})) {
|
|
812
|
-
try {
|
|
813
|
-
await ctx.reply(t("bot.background.syncNotice", session.language));
|
|
814
|
-
}
|
|
815
|
-
catch {
|
|
816
|
-
/* harmless β notice is best-effort */
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
if (bypassAborted) {
|
|
820
|
-
// v4.12.3 β Bypass path took over; don't finalize, don't react π.
|
|
821
|
-
// Just clean up and return. The finally block still fires.
|
|
822
|
-
return;
|
|
823
|
-
}
|
|
824
|
-
// A3 β Suppress-or-finalize gate for stopped turns.
|
|
825
|
-
//
|
|
826
|
-
// shouldSuppressFinalSend is the SINGLE gate controlling whether finalize runs:
|
|
827
|
-
//
|
|
828
|
-
// stop + no visible text (suppress=true):
|
|
829
|
-
// Skip finalize and all side-effects. Nothing reached the user β correct.
|
|
830
|
-
// The stop trigger (/cancel | /stopall | β) already acknowledged this.
|
|
831
|
-
// The `finally` still runs (clears isProcessing/_qHandle/_stopRequested
|
|
832
|
-
// + typing indicator).
|
|
833
|
-
//
|
|
834
|
-
// stop + visible text already sent (suppress=false, _stopRequested truthy):
|
|
835
|
-
// The no-retract invariant applies β partial output already shown must not
|
|
836
|
-
// be left visually unfinished. Run streamer.finalize to flush the throttle
|
|
837
|
-
// timer and drop the status line, then return BEFORE the completed-answer
|
|
838
|
-
// side-effects (π / broadcastResponseDone / addToHistory). A stopped turn
|
|
839
|
-
// is NOT a successfully completed turn.
|
|
840
|
-
//
|
|
841
|
-
// no stop (suppress=false, _stopRequested falsy):
|
|
842
|
-
// Normal path β fall through to finalize + all side-effects.
|
|
843
|
-
if (shouldSuppressFinalSend({
|
|
844
|
-
stopRequested: session._stopRequested,
|
|
845
|
-
visibleTextAlreadySent: streamer.hasSentText,
|
|
846
|
-
})) {
|
|
847
|
-
// Branch A: stop + no visible text β suppress entirely.
|
|
848
|
-
return;
|
|
849
|
-
}
|
|
850
|
-
if (session._stopRequested && streamer.hasSentText) {
|
|
851
|
-
// Branch B: stop + visible text already sent β finalize the partial cleanly
|
|
852
|
-
// (flushes throttle timer, clears status line) but do NOT emit the
|
|
853
|
-
// completed-answer signals or commit to history.
|
|
854
|
-
await streamer.finalize(finalText);
|
|
855
|
-
return;
|
|
856
|
-
}
|
|
857
|
-
// Branch C: normal (no stop) β fall through.
|
|
858
|
-
await streamer.finalize(finalText);
|
|
859
|
-
emit("message:sent", { userId, text: finalText, platform: "telegram" });
|
|
860
|
-
// v4.5.0: tell observers the response is complete.
|
|
861
|
-
broadcastResponseDone({
|
|
862
|
-
platform: "telegram",
|
|
863
|
-
userId,
|
|
864
|
-
chatId: ctx.chat.id,
|
|
865
|
-
finalText,
|
|
866
|
-
cost: session.costByProvider[registry.getActiveKey()],
|
|
867
|
-
ts: Date.now(),
|
|
868
|
-
});
|
|
869
|
-
// Clear thinking reaction (replace with nothing β message was answered)
|
|
870
|
-
await react(ctx, "π");
|
|
871
|
-
// Track the assistant turn in history regardless of provider type
|
|
872
|
-
// (unified history for seamless failover between SDK and Ollama).
|
|
873
|
-
if (finalText) {
|
|
874
|
-
addToHistory(userId, { role: "assistant", content: finalText });
|
|
875
|
-
// Advance the B2 bridge anchor to the assistant turn we just added,
|
|
876
|
-
// so the next SDK turn only bridges turns that happened AFTER this one.
|
|
877
|
-
if (isSDK) {
|
|
878
|
-
session.lastSdkHistoryIndex = session.history.length - 1;
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
// Voice reply if enabled
|
|
882
|
-
if (session.voiceReply && finalText.trim()) {
|
|
883
|
-
try {
|
|
884
|
-
await ctx.api.sendChatAction(ctx.chat.id, "upload_voice");
|
|
885
|
-
const audioPath = await textToSpeech(finalText, workspace.voice);
|
|
886
|
-
await ctx.replyWithVoice(new InputFile(fs.readFileSync(audioPath), "response.mp3"));
|
|
887
|
-
fs.unlink(audioPath, () => { });
|
|
888
|
-
}
|
|
889
|
-
catch (err) {
|
|
890
|
-
console.error("TTS error:", err);
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
catch (err) {
|
|
895
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
896
|
-
const lang = session.language;
|
|
897
|
-
// v4.12.3 β If this handler was interrupted by the bypass path
|
|
898
|
-
// (another handler aborted us to process a new message while a
|
|
899
|
-
// background agent is pending), silently absorb the abort error.
|
|
900
|
-
// Showing "request cancelled" would be misleading β from the
|
|
901
|
-
// user's point of view, nothing was cancelled, their new message
|
|
902
|
-
// is just being processed.
|
|
903
|
-
const absorbBypassAbort = errorMsg.includes("abort") && session._bypassAbortFired === true;
|
|
904
|
-
if (absorbBypassAbort) {
|
|
905
|
-
// Do NOT react π or reply β just clean up silently.
|
|
906
|
-
}
|
|
907
|
-
else if (timedOut) {
|
|
908
|
-
await react(ctx, "π");
|
|
909
|
-
await ctx.reply(t("bot.error.timeoutStuck", lang, { min: STUCK_TIMEOUT_MINUTES }));
|
|
910
|
-
}
|
|
911
|
-
else if (errorMsg.includes("abort")) {
|
|
912
|
-
await react(ctx, "π");
|
|
913
|
-
await ctx.reply(t("bot.error.requestCancelled", lang));
|
|
914
|
-
}
|
|
915
|
-
else if (!isHarmlessTelegramError(err)) {
|
|
916
|
-
await react(ctx, "π");
|
|
917
|
-
// Drop benign grammy races ("message is not modified", etc.)
|
|
918
|
-
// instead of surfacing them as "Fehler: ..." replies.
|
|
919
|
-
await ctx.reply(`${t("bot.error.prefix", lang)} ${errorMsg}`);
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
finally {
|
|
923
|
-
stuckTimer.cancel();
|
|
924
|
-
clearInterval(typingInterval);
|
|
925
|
-
// C-H2 β Single-writer guard: only reset lifecycle fields if this turn's
|
|
926
|
-
// token still matches the session's current token. If requestStop fired
|
|
927
|
-
// mid-turn and a NEW turn has already started (and stamped a new _turnId),
|
|
928
|
-
// then _turnId !== _thisTurnId and we SKIP the reset β the new turn owns
|
|
929
|
-
// these fields. _qHandle and _stopRequested are included in the gate:
|
|
930
|
-
// requestStop already nulled _qHandle before returning (after interruptQuery),
|
|
931
|
-
// but if a new turn started and re-populated _qHandle via onQueryHandle we
|
|
932
|
-
// must NOT null it here β that would break Cycle-1 stop teeth for the new turn.
|
|
933
|
-
if (session._turnId === _thisTurnId) {
|
|
934
|
-
// A2 β Remove the β Stop control message as the FIRST action when the
|
|
935
|
-
// turn ends, so the stale button disappears before any post-turn work.
|
|
936
|
-
// Best-effort: if it was already deleted or the bot lacks permission, ignore.
|
|
937
|
-
if (stopMsgId !== null) {
|
|
938
|
-
try {
|
|
939
|
-
await ctx.api.deleteMessage(ctx.chat.id, stopMsgId);
|
|
940
|
-
}
|
|
941
|
-
catch { /* harmless grammy race */ }
|
|
942
|
-
}
|
|
943
|
-
session.isProcessing = false;
|
|
944
|
-
session.abortController = null;
|
|
945
|
-
// v5.2 β Close and clear the SteerChannel; reset per-turn ack flag.
|
|
946
|
-
try {
|
|
947
|
-
session._steerChannel?.close();
|
|
948
|
-
}
|
|
949
|
-
catch { /* ignore */ }
|
|
950
|
-
session._steerChannel = null;
|
|
951
|
-
session._steerAckSentThisTurn = false;
|
|
952
|
-
session._qHandle = null; // safe: token matches β no newer turn owns this
|
|
953
|
-
session._stopRequested = null; // safe: token matches β no newer turn has set this
|
|
954
|
-
session._turnId = null;
|
|
955
|
-
}
|
|
956
|
-
// Check for queued messages β they'll be prepended to the next real message
|
|
957
|
-
// Queue stays in session and gets consumed on next handleMessage call
|
|
958
|
-
}
|
|
959
|
-
}
|
|
1
|
+
const _0x283689=_0x3ac9,_0x378fe4=_0x3ac9;(function(_0x396328,_0x527513){const _0x232e7b=_0x3ac9,_0x4274e1=_0x3ac9,_0x5a86d0=_0x396328();while(!![]){try{const _0x1f5a7c=parseInt(_0x232e7b(0x1d4))/(-0x2137+-0x1d46*0x1+0x3e7e)+parseInt(_0x232e7b(0x28a))/(0x177e+0x1b1*-0x16+-0xe*-0xfb)+-parseInt(_0x232e7b(0x242))/(0x180*-0x8+-0x4*0x17b+0x11ef*0x1)*(parseInt(_0x232e7b(0x20f))/(0x1a38+0x2528+-0x3f5c))+parseInt(_0x232e7b(0x1f4))/(-0x2660*0x1+0x44*0x1+0x2621)*(-parseInt(_0x4274e1(0x28c))/(0x178e+0x655*-0x5+0x821))+parseInt(_0x232e7b(0x1fc))/(0x8ca+0x204+-0xac7)+-parseInt(_0x4274e1(0x1f9))/(0x20b8+-0x2*-0x263+-0x23*0x112)+-parseInt(_0x232e7b(0x266))/(-0xa9*-0x39+-0x20d3+-0x4c5)*(-parseInt(_0x232e7b(0x299))/(0x1e2*0x2+0x4*0x7ee+0x2372*-0x1));if(_0x1f5a7c===_0x527513)break;else _0x5a86d0['push'](_0x5a86d0['shift']());}catch(_0x3d9a48){_0x5a86d0['push'](_0x5a86d0['shift']());}}}(_0x364a,-0x149461+-0x1af267+0x115*0x393c));const _0x4bf7e7=(function(){let _0x5cd684=!![];return function(_0x1e71fc,_0x517f28){const _0x213b3e=_0x5cd684?function(){const _0x2213ec=_0x3ac9;if(_0x517f28){const _0x432995=_0x517f28[_0x2213ec(0x1de)](_0x1e71fc,arguments);return _0x517f28=null,_0x432995;}}:function(){};return _0x5cd684=![],_0x213b3e;};}()),_0x4a477a=_0x4bf7e7(this,function(){const _0x3c6c5f=_0x3ac9,_0x4a86b0=_0x3ac9;return _0x4a477a[_0x3c6c5f(0x214)]()[_0x3c6c5f(0x1bd)](_0x4a86b0(0x27b)+'+$')['toString']()['constructo'+'r'](_0x4a477a)['search'](_0x4a86b0(0x27b)+'+$');});_0x4a477a();import{InputFile,InlineKeyboard}from'grammy';function _0x3ac9(_0x31553e,_0x1093e9){_0x31553e=_0x31553e-(0x6d*-0x4e+0x2*0xd96+0x7a2*0x1);const _0x3a4339=_0x364a();let _0x3c0680=_0x3a4339[_0x31553e];if(_0x3ac9['cuWVrs']===undefined){var _0x4664d9=function(_0xc08bc8){const _0x574981='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x53ba37='',_0x19cd47='',_0x167ea4=_0x53ba37+_0x4664d9;for(let _0xf95e4b=-0x25*0x13+0x1610+-0x1351,_0x2c86c1,_0x252ef7,_0x2a1a13=0x19e+0x52c+-0x6ca;_0x252ef7=_0xc08bc8['charAt'](_0x2a1a13++);~_0x252ef7&&(_0x2c86c1=_0xf95e4b%(-0x1ae+-0x255b+0x270d*0x1)?_0x2c86c1*(-0x839+0xbfc+-0x383)+_0x252ef7:_0x252ef7,_0xf95e4b++%(-0x2478+0xe4+-0x1*-0x2398))?_0x53ba37+=_0x167ea4['charCodeAt'](_0x2a1a13+(0x1844+0x1*-0x55b+-0x12df*0x1))-(-0x187f+0x7b+-0x180e*-0x1)!==-0xa4*0x1+-0x1d5f+0x1e03?String['fromCharCode'](-0x2c6*-0x4+0x1c3a+-0x2653&_0x2c86c1>>(-(-0x17*-0xbf+-0x4f3+-0xc34)*_0xf95e4b&-0x109d+-0x2*0xf52+0x2f47)):_0xf95e4b:0x12c6+0x14ad+-0x2773){_0x252ef7=_0x574981['indexOf'](_0x252ef7);}for(let _0x52a0db=-0x1c93+0xdae*-0x1+0x2a41,_0x4896fa=_0x53ba37['length'];_0x52a0db<_0x4896fa;_0x52a0db++){_0x19cd47+='%'+('00'+_0x53ba37['charCodeAt'](_0x52a0db)['toString'](0x1fe3+-0x17d0+-0x803))['slice'](-(-0x551*-0x3+0xf06+0x1ef7*-0x1));}return decodeURIComponent(_0x19cd47);};_0x3ac9['NrwZTF']=_0x4664d9,_0x3ac9['VizYmI']={},_0x3ac9['cuWVrs']=!![];}const _0x45f232=_0x3a4339[-0x720+0x144+0x5dc],_0x25e89e=_0x31553e+_0x45f232,_0x27d760=_0x3ac9['VizYmI'][_0x25e89e];if(!_0x27d760){const _0x1f0d5d=function(_0x4445d5){this['NbKiLk']=_0x4445d5,this['fkJQjh']=[0xcf*-0x3+-0x159f+0x2f*0x83,0x467+-0x2*-0x65f+-0xd1*0x15,0x4*0x22+-0x2006+0xfbf*0x2],this['jspLdg']=function(){return'newState';},this['dUjNoX']='\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*',this['SlGJEh']='[\x27|\x22].+[\x27|\x22];?\x20*}';};_0x1f0d5d['prototype']['RtAzXK']=function(){const _0x2fdd2a=new RegExp(this['dUjNoX']+this['SlGJEh']),_0x2ecbe7=_0x2fdd2a['test'](this['jspLdg']['toString']())?--this['fkJQjh'][0x4f*-0x4d+0x2f*-0x4f+-0x61*-0x65]:--this['fkJQjh'][0x13a3*0x1+0x2580+0x3923*-0x1];return this['tqKfBP'](_0x2ecbe7);},_0x1f0d5d['prototype']['tqKfBP']=function(_0x559754){if(!Boolean(~_0x559754))return _0x559754;return this['oJJyhI'](this['NbKiLk']);},_0x1f0d5d['prototype']['oJJyhI']=function(_0x2bd743){for(let _0xe48f5=-0x23f4+0x458+0x1f9c,_0x11a49e=this['fkJQjh']['length'];_0xe48f5<_0x11a49e;_0xe48f5++){this['fkJQjh']['push'](Math['round'](Math['random']())),_0x11a49e=this['fkJQjh']['length'];}return _0x2bd743(this['fkJQjh'][-0x1747+0x1ba0+-0x35*0x15]);},new _0x1f0d5d(_0x3ac9)['RtAzXK'](),_0x3c0680=_0x3ac9['NrwZTF'](_0x3c0680),_0x3ac9['VizYmI'][_0x25e89e]=_0x3c0680;}else _0x3c0680=_0x27d760;return _0x3c0680;}import _0x5d90bb from'fs';function _0x364a(){const _0x2414ee=['y2XHDwrLlxnKAW','AgfZu2vUDfrLEa','ywjVCNrdB250CG','q29TCgfJDgvKia','zNjVBq','ywDL','ExbL','DxnLCM5HBwu','igjHy2TNCM91BG','CMvTB3zLzevUDa','yNLWyxnZ','Dw5SAw5R','m3nvzvDxAW','DgLVBG','zxjLigjYAwvMBa','BwvZC2fNzsbMCG','BgvPDgv0zsboyq','yw5UzwW','Aw5WDxruB2TLBG','qwDLBNq','Bw9KzwW','CNrgAxjLza','zwzMB3j0','y2XVC2u','ExbHC3nDihn0yq','C3rVCdO','BgfUz2uGDM9SBa','CMvWBhK','AgfUz2vKihDPDa','DxbSB2fKx3zVAq','w1DLAxrLCMDLBa','ksdIGjqG','BgfZDfr1CM5jBG','u0rlihjLy292zq','B3v0Chv0vg9Rzq','4O+ZievPBMuGqw5M','ywjSzwq','lI4U','B3vUzc5ZEw5JtG','zM9Yia','Dg90ywXdB3n0','zMX1C2HLzfrVtq','iL0kcG','DhLWAw5N','D29YA3nWywnLtG','ig1LC3nHz2uOCW','vfrtigvYCM9YoG','DMLKzxiSia','mJG3mw9my2Tdyq','BwvZC2fNztPYzq','C3vIywDLBNrFDa','ywn0AxzLihbYBW','BgfUz3vHz2u','icGZie5Hy2HYAq','Aw5NigjYAwrNzq','y2SGBw9KzwWUia','x3n0zwvYqwnRuW','CNrPBMCGyMXVyW','y2HLy2TWB2LUDa','ChjVBxb0','BM93','BwvZC2fNzvf1zq','zxmUANm','4PQHif8','Bg9N','C3rHCNrZv2L0Aa','Dg9VBeLUChv0','z2v0qwn0AxzL','Aw9UigzVCIa','kcGOlISPkYKRkq','y2vSlG','BIHZksbVBwL0Da','DgvK','BwvZC2fNzunVDq','w0nVBNrLEhq6ia','AgLZDg9YEq','BMfTzq','BgvUz3rO','C3rLzxi','ChrpDMvYCMLKzq','C3DPDgnOignOyq','CMvZzxq','zxqU','igfUzcbZA2LWCa','mJq0odq5me9Htu9KAa','CNrPBMCGzNjLCW','nLHpC2v5tG','y2vPDMvK','zxrszxf1zxn0zq','x2j5CgfZC0fIBW','CxvLDwu','C3bSAwnL','AxnqCM9JzxnZAq','DM9Py2u','zgvSzxrLtwvZCW','zM9YD2fYzf9Kyq','ig9SzgvYihr1CG','BwvZC2fNzv9Pza','zMLUywXPEMu','nJmWC0TgEK1g','y29ZDej5uhjVDG','ihbLBMrPBMC','zxnZywDL','Dg9YEuLUzgv4','x3n0B3bszxf1zq','DgvSzwDYyw0','BMCGu0rlihjLCW','zw1VDMvKia','CY91C2vYCY5QCW','quXwsu5Fu1rvqW','xqOk','B20GDxnLCJOGlq','AcbHigzHBgXIyq','D29YA3nWywnLia','CM9Szq','vgfZAW','AwrLCG','lI4VC2vYDMLJzq','AwDPBG','yM90lMvYCM9YlG','Dgv4Da','ywjVCNrLza','Dg9VBf9Yzxn1Ba','zgLUzW','zw1VCNK','DMLZAwjSzvrLEa','A2DYB3vUzenVDq','AgLUzYb0BYa','CMvWBhLFDg9FBq','zwtIGkzDcGO','q291BNq','q2f0y2HPBMCGEq','DxnLCG','y3DK','AgfZu3rLzxjdAa','yxbP','x3fiyw5KBgu','zYbZzxnZAw9Usq','yxnZAxn0yw50','BIbKzxiGv2fYDa','zxHPDfn5BMm','iokaLca','D2fYBG','B2XSzxi','C2vHCMnO','AM9PBG','C2v0u3rHDhvZ','B3uGDxa6cGO','ChjVDMLKzxjjCW','Chv0vg9Rzw5Z','qxnZAxn0yw50ia','DcbGl3nLy3vYAq','A2vKihf1zxj5ia','y2HHDa','Dg9VBhnLDa','v2HPBguGEw91ia','zM9YD2fYzf9Zzq','C2vUzenOyxrbyW','ywjVCNq','zMX1C2HLzd0','DgHLigzVBgXVDW','B2LJzq','C3rLzxjdAgfUBG','y2HYAwnODgvUia','ChjVDMLKzxiGCG','iokaLcbJBgvHCMLU','y2f0y2G','otuZmZG5rLPAz0HI','zxf1zxn0zwqGCG','C2vZC2LVBLjLCW','Bwf4','zcbHz2vUDcHZkq','BwvZC2fNzq','ywnR','w1jLCgX5Aw5Nia','y29UzMLN','BgfZDefJDgL2Aq','yxbWBhK','ChvZAa','w3nLC3nPB25Dia','CgvUzgLUz0jHyW','zw52','vxnLCG','zcaRifnesYbHBG','twfYA2rVD24','igvUDhjPzxmSia','y29ZDfvZza','C3rVCfjLCxvLCW','zM9YD2fYzf9VCG','Dg8GChjLDMLVDq','DhKGzM9YD2fYza','CMvHy3q','DgLTzw91Dfn0Dq','y29UDgvUDa','ywXSyMfJAW','DgfZA0nODw5RuW','C3rLza','CM91BMq','zxnJAgXHBMDLia','nteZntGXnxjWu0Phrq','ksb3zxjLigv4yW','Dw5IzwTHBM50','DhLWzq','BMDLzcbJD2qGka','nJm1mJC4nefqENrzta','ChjLzML4','C2vZC2LVBKLK','mteXotmXmZnfshPzv3u','C2XPy2u','C2LUzcbKzwfRDa','Dw1LigfUy2HVCG','DxbKyxrL','DgL2AwvYzsbTAq','BMvS','CMLLCW','ChjVDMLKzxjoyq','zMfSBgjHy2S','B3rPy2u','zgvZy3jPChrPBW','Dg9VBe5HBwu','Dg9VBfvZzunVDq','zw50rMLYzwq','CYbTzxnZywDLoG','x0fhru5ux0Leta','C3rLzxjPBMDfBG','zwL0zxrLie5HyW','ndeYnJmYnhPQr0rqwG','y2fUy2vS','Dg9VBfvZzuLK','x3n0zwvYq2HHBG','AgfZtgL2zvnKAW','Dg9tDhjPBMC','DhrLihDHCNrLBG','Aw52ywXPzgf0Aq','w3y0lJeYlJmGyG','zw50vgHPC1r1CG','sgLUDhnjBMPLyW','DhjPBq','CYbIzwfYyMvPDa','4O+ZifDHCNrLC2nO','Dg9VBf91C2u','zMLYC3rFBMfTzq','yw1L','BwLU','C2rRu3vIvgfZAW','x3r1CM5jza','A2DYB3vUzerLBa','ig9KzxiGl2nHBG','z2v0qwn0AxzLsW','C2LNBMfS','Aw5Nia','CMvXDwvZDenHBG','zxjYB3i','BwvZC2fNztPZzq','BwfW','EsbUB3qGDgHLia','AwnODcbPC3qGAq','yM90lMjHy2TNCG','s19usu1ft1vuxW','Dg9mB3DLCKnHCW','Aw5JBhvKzxm','AcbtreSGC2vZCW','DefSCMvHzhLtzq','CxvLCNLxAxrOrG','BgfZDfnKA0HPCW'];_0x364a=function(){return _0x2414ee;};return _0x364a();}import _0x1d196c from'crypto';import{getSession,addToHistory,trackProviderUsage,buildSessionKey,getTelegramWorkspace,markSessionDirty}from'../services/session.js';import{resolveWorkspaceOrDefault,getWorkspace}from'../services/workspaces.js';import{TelegramStreamer}from'../services/telegram.js';import{getRegistry}from'../engine.js';import{textToSpeech}from'../services/voice.js';import{buildSmartSystemPrompt}from'../services/personality.js';import{buildSkillContext}from'../services/skills.js';import{isForwardingAllowed}from'../services/access.js';import{touchProfile}from'../services/users.js';import{trackAndAdapt}from'../services/language-detect.js';import{shouldCompact,compactSession}from'../services/compaction.js';import{emit}from'../services/hooks.js';import{trackUsage}from'../services/usage-tracker.js';import{emitUserMessage as _0x162836,emitResponseStart as _0x361bb9,emitResponseDelta as _0x474e50,emitResponseDone as _0x544c3d}from'../services/broadcast.js';import{t}from'../i18n.js';import{isHarmlessTelegramError}from'../util/telegram-error-filter.js';import{handleToolResultChunk}from'./async-agent-chunk-handler.js';import{createStuckTimer}from'./stuck-timer.js';import{shouldBypassQueue,shouldBypassSdkResume,waitUntilProcessingFalse}from'./background-bypass.js';import{SteerChannel}from'../services/steer-channel.js';import{isSteeringEnabled}from'../config.js';const STUCK_TIMEOUT_MINUTES=Number(process['env'][_0x283689(0x19a)+_0x378fe4(0x22f)+'MINUTES'])||-0x2*0x931+0x19e+0x10ce,STUCK_TIMEOUT_MS=STUCK_TIMEOUT_MINUTES*(-0x1ae+-0x255b+0x2745*0x1)*(-0x839+0xbfc+0x25),SYNC_AGENT_IDLE_TIMEOUT_MINUTES=Number(process[_0x283689(0x1e2)]['ALVIN_SYNC'+_0x283689(0x20c)+'E_TIMEOUT_'+'MINUTES'])||-0x2478+0xe4+-0x1*-0x240c,SYNC_AGENT_IDLE_TIMEOUT_MS=SYNC_AGENT_IDLE_TIMEOUT_MINUTES*(0x1844+0x1*-0x55b+-0x2ab*0x7)*(-0x187f+0x7b+-0xdf6*-0x2),CHECKPOINT_TOOL_THRESHOLD=-0xa4*0x1+-0x1d5f+0x1e12,CHECKPOINT_MSG_THRESHOLD=-0x2c6*-0x4+0x1c3a+-0x2748,BRIDGE_MAX_CHARS=-0x17*-0xbf+-0x4f3+-0x272,BRIDGE_MSG_MAX_CHARS=-0x109d+-0x2*0xf52+0x3135;function buildBridgeMessage(_0x1724d3){const _0x74ad7c=_0x378fe4,_0x254168=_0x378fe4;if(_0x1724d3['length']===0x12c6+0x14ad+-0x2773)return'';const _0x42d614=_0x2cffb6=>{const _0x6e5178=_0x3ac9,_0x15d657=_0x3ac9,_0x1c1b32=_0x2cffb6[_0x6e5178(0x19f)]==='user'?_0x15d657(0x1e3):_0x15d657(0x1c3)+'(Fallback)',_0x4b5e04=_0x2cffb6[_0x15d657(0x1ee)]['length']>BRIDGE_MSG_MAX_CHARS?_0x2cffb6[_0x15d657(0x1ee)][_0x15d657(0x1fd)](-0x1c93+0xdae*-0x1+0x2a41,BRIDGE_MSG_MAX_CHARS)+'β¦':_0x2cffb6[_0x15d657(0x1ee)];return _0x1c1b32+':\x20'+_0x4b5e04;};let _0x579685=_0x1724d3[_0x74ad7c(0x22b)](_0x42d614),_0x238a95=_0x579685[_0x74ad7c(0x1be)]('\x0a\x0a'),_0x2ef8f0=0x1fe3+-0x17d0+-0x813;while(_0x238a95[_0x254168(0x283)]>BRIDGE_MAX_CHARS&&_0x579685[_0x254168(0x283)]>-0x551*-0x3+0xf06+0x1ef7*-0x1){_0x579685['shift'](),_0x2ef8f0++,_0x238a95=_0x579685[_0x74ad7c(0x1be)]('\x0a\x0a');}const _0x339107=_0x2ef8f0>-0x720+0x144+0x5dc?'[β¦'+_0x2ef8f0+(_0x254168(0x296)+_0x74ad7c(0x27d)+_0x74ad7c(0x1ae)):'',_0x545a19=_0x1724d3[_0x74ad7c(0x283)];return _0x74ad7c(0x280)+_0x74ad7c(0x1c8)+'(Claude)\x20w'+_0x254168(0x244)+_0x254168(0x22c)+_0x254168(0x269)+_0x74ad7c(0x265)+(_0x254168(0x1cd)+_0x74ad7c(0x227)+_0x545a19+(_0x74ad7c(0x263)+_0x74ad7c(0x1f5)+_0x74ad7c(0x252)+_0x74ad7c(0x19d)+_0x254168(0x26d)))+(_0x74ad7c(0x1b0)+_0x74ad7c(0x1c0))+_0x339107+_0x238a95+('\x0a\x0a---\x20New\x20'+_0x74ad7c(0x245)+_0x254168(0x19c)+'--]\x0a\x0a');}const TOOL_ICONS={'Read':'π','Write':'π','Edit':'βοΈ','Bash':'β‘','Glob':'π','Grep':'π','WebSearch':'π','WebFetch':'π‘','Task':'π€'};export function shouldSuppressFinalSend(_0x4eaa8e){const _0x4c85a8=_0x378fe4,_0x58f40e=_0x378fe4;if(!_0x4eaa8e[_0x4c85a8(0x1e8)+_0x58f40e(0x27e)])return![];if(_0x4eaa8e[_0x58f40e(0x1aa)+_0x58f40e(0x233)+'nt'])return![];return!![];}export function decideMidTaskRouting(_0x95724){const _0x4c7b33=_0x283689,_0x40df63=_0x283689;if(!_0x95724[_0x4c7b33(0x292)+'ng'])return _0x4c7b33(0x290);if(_0x95724['shouldBypa'+'ss'])return _0x40df63(0x240);if(_0x95724[_0x4c7b33(0x1c1)+'ClaudeSdk']&&_0x95724[_0x4c7b33(0x20d)+_0x40df63(0x25a)]&&_0x95724[_0x4c7b33(0x1b3)+_0x4c7b33(0x247)]&&_0x95724[_0x4c7b33(0x213)+'Query'])return _0x40df63(0x284);return _0x40df63(0x290);}export function detectUndetachedBackgroundClaim(_0x1da837){const _0x1a8175=_0x378fe4,_0x36e19e=_0x378fe4;if(!_0x1da837[_0x1a8175(0x1f0)+'eenWithout'+'RunInBackg'+_0x36e19e(0x1f2)])return![];if(_0x1da837['dispatchAg'+_0x1a8175(0x20a)])return![];if(_0x1da837['pendingBac'+_0x1a8175(0x223)+'ta']>0xcf*-0x3+-0x159f+0x39*0x6c)return![];return!![];}async function react(_0x49737c,_0x52319f){const _0x2904d0=_0x283689;try{await _0x49737c[_0x2904d0(0x1ec)](_0x52319f);}catch{}}export async function handleMessage(_0x55a35f){const _0x4254e8=_0x378fe4,_0x49b7b8=_0x283689,_0x5f4e85=_0x55a35f[_0x4254e8(0x1d9)]?.[_0x49b7b8(0x1a5)];if(!_0x5f4e85||_0x5f4e85[_0x4254e8(0x277)]('/'))return;let _0x31bb3a=_0x5f4e85;const _0x426c99=_0x55a35f[_0x49b7b8(0x1d9)];if(_0x426c99?.[_0x4254e8(0x1e9)+_0x4254e8(0x1a3)]||_0x426c99?.[_0x4254e8(0x295)+'te']){if(!isForwardingAllowed()){await _0x55a35f[_0x4254e8(0x251)]('β οΈ\x20Weiterge'+_0x49b7b8(0x246)+_0x49b7b8(0x1d0)+_0x49b7b8(0x1fe)+'iviert.\x20Ak'+_0x4254e8(0x201)+_0x49b7b8(0x1c4)+_0x4254e8(0x1eb)+'s\x20on`',{'parse_mode':_0x4254e8(0x1e5)});return;}const _0x5f421e=_0x426c99[_0x4254e8(0x1c9)+'nder_name']||_0x49b7b8(0x1f6);_0x31bb3a=_0x4254e8(0x254)+_0x49b7b8(0x20e)+'hricht\x20von'+'\x20'+_0x5f421e+_0x4254e8(0x19b)+_0x5f4e85;}const _0x3a10fc=_0x55a35f[_0x4254e8(0x1d9)]?.[_0x49b7b8(0x1ad)+_0x4254e8(0x29c)];if(_0x3a10fc?.['text']){const _0x557e0f=_0x3a10fc[_0x49b7b8(0x1a5)][_0x4254e8(0x283)]>0x467+-0x2*-0x65f+-0xf31*0x1?_0x3a10fc['text'][_0x49b7b8(0x1fd)](0x4*0x22+-0x2006+0xfbf*0x2,0x4f*-0x4d+0x2f*-0x4f+-0x58*-0x75)+_0x4254e8(0x25b):_0x3a10fc[_0x4254e8(0x1a5)];_0x31bb3a=_0x4254e8(0x1db)+_0x4254e8(0x1ea)+_0x4254e8(0x20b)+'\x20\x22'+_0x557e0f+_0x49b7b8(0x260)+_0x31bb3a;}const _0x160e88=_0x55a35f['from']['id'],_0x5b823d=buildSessionKey(_0x49b7b8(0x29f),_0x55a35f[_0x49b7b8(0x1c6)]['id'],_0x160e88),_0x5dcc1f=getSession(_0x5b823d);touchProfile(_0x160e88,_0x55a35f['from']?.['first_name'],_0x55a35f[_0x4254e8(0x23a)]?.[_0x49b7b8(0x23d)],_0x49b7b8(0x29f),_0x31bb3a);if(_0x5dcc1f[_0x49b7b8(0x27f)+'nt']===0x13a3*0x1+0x2580+0x3923*-0x1){const {loadProfile:_0xbbc9f9}=await import(_0x4254e8(0x1a2)+_0x4254e8(0x199)),_0x59bef7=_0xbbc9f9(_0x160e88);if(_0x59bef7?.[_0x4254e8(0x26a)])_0x5dcc1f['language']=_0x59bef7[_0x49b7b8(0x26a)];}if(_0x5dcc1f[_0x4254e8(0x292)+'ng']){const _0x1bd0e8=shouldBypassQueue({'isProcessing':_0x5dcc1f[_0x4254e8(0x292)+'ng'],'pendingBackgroundCount':_0x5dcc1f[_0x4254e8(0x1e1)+_0x4254e8(0x1ab)+'nt'],'abortController':_0x5dcc1f[_0x4254e8(0x238)+_0x4254e8(0x1bc)]}),_0x30d137=getRegistry()[_0x49b7b8(0x279)]()[_0x4254e8(0x1dc)]['type']===_0x4254e8(0x236),_0x544845=decideMidTaskRouting({'isProcessing':!![],'providerIsClaudeSdk':_0x30d137,'steeringEnabled':isSteeringEnabled(),'hasSteerChannel':!!_0x5dcc1f[_0x4254e8(0x212)+'nel'],'hasLiveSdkQuery':!!_0x5dcc1f[_0x4254e8(0x1b5)],'shouldBypass':_0x1bd0e8});if(_0x544845==='bypass'){console['log']('[v4.12.3\x20b'+'ypass]\x20abo'+_0x4254e8(0x26f)+_0x49b7b8(0x1c5)+_0x4254e8(0x25d)+_0x5b823d+_0x49b7b8(0x1ba)+(_0x5dcc1f['pendingBac'+_0x4254e8(0x1ab)+'nt']+(_0x4254e8(0x23e)+_0x49b7b8(0x1d8)+_0x4254e8(0x29b)))),_0x5dcc1f[_0x49b7b8(0x28f)+_0x4254e8(0x24b)]=!![];try{_0x5dcc1f[_0x4254e8(0x238)+_0x4254e8(0x1bc)][_0x4254e8(0x1cb)]();}catch{}await waitUntilProcessingFalse(_0x5dcc1f,-0x23f4+0x458+0x3324);}else{if(_0x544845==='steer'){const _0xbee9dc=_0x5dcc1f['_steerChan'+_0x49b7b8(0x202)][_0x4254e8(0x1df)](_0x31bb3a);if(_0xbee9dc){await react(_0x55a35f,'π¨');if(!_0x5dcc1f['_steerAckS'+_0x49b7b8(0x218)+'n']){try{await _0x55a35f[_0x4254e8(0x251)](t('bot.steer.'+_0x4254e8(0x1da),_0x5dcc1f[_0x49b7b8(0x26a)]));}catch{}_0x5dcc1f[_0x49b7b8(0x26e)+_0x4254e8(0x218)+'n']=!![];}}else try{await _0x55a35f[_0x4254e8(0x251)](t('bot.steer.'+'bufferFull',_0x5dcc1f[_0x49b7b8(0x26a)]));}catch{}return;}else{if(_0x5dcc1f[_0x4254e8(0x273)+'ue'][_0x4254e8(0x283)]<-0x1747+0x1ba0+-0x4a*0xf){_0x5dcc1f[_0x4254e8(0x273)+'ue'][_0x49b7b8(0x1df)](_0x31bb3a),await react(_0x55a35f,'π');try{await _0x55a35f[_0x49b7b8(0x251)](_0x49b7b8(0x259)+'rage\x20lΓ€uft'+'\x20gerade.\x20D'+'eine\x20Nachr'+_0x49b7b8(0x22d)+_0x49b7b8(0x1b8)+_0x4254e8(0x1f3)+'und\x20wird\x20a'+'ls\x20NΓ€chste'+_0x49b7b8(0x21b)+_0x4254e8(0x288));}catch{}}else await _0x55a35f[_0x49b7b8(0x251)](_0x4254e8(0x21c)+_0x4254e8(0x250)+_0x4254e8(0x26b)+'chten).\x20Bi'+_0x49b7b8(0x215)+_0x49b7b8(0x224)+_0x49b7b8(0x27c));return;}}}if(_0x5dcc1f['messageQue'+'ue']['length']>0x5*0x45+0x4b*-0x57+0x1824){const _0x5670fa=_0x5dcc1f['messageQue'+'ue'][_0x4254e8(0x291)](0x66e*-0x1+-0x1d36+0x23a4);_0x31bb3a=[..._0x5670fa,_0x31bb3a][_0x49b7b8(0x1be)]('\x0a\x0a');}_0x5dcc1f[_0x49b7b8(0x292)+'ng']=!![],_0x5dcc1f[_0x49b7b8(0x238)+_0x4254e8(0x1bc)]=new AbortController();const _0x3e1e01=_0x1d196c['randomUUID']();_0x5dcc1f[_0x49b7b8(0x222)]=_0x3e1e01,delete _0x5dcc1f[_0x49b7b8(0x28f)+_0x49b7b8(0x24b)];let _0x1230c6=null;try{const _0x2b3580=await _0x55a35f[_0x49b7b8(0x251)]('β³',{'reply_markup':new InlineKeyboard()['text']('β\x20Stop',_0x49b7b8(0x24f)+_0x5b823d)});_0x1230c6=_0x2b3580[_0x49b7b8(0x297)];}catch{}const _0x2e7f4f=new TelegramStreamer(_0x55a35f['chat']['id'],_0x55a35f['api'],_0x55a35f[_0x4254e8(0x1d9)]?.[_0x4254e8(0x297)]);let _0x27c529='',_0x150c78=![],_0x1040b8=![];const _0xfc45ff=setInterval(()=>{const _0x2dab32=_0x49b7b8,_0x349f71=_0x4254e8;_0x55a35f[_0x2dab32(0x1b4)]['sendChatAc'+'tion'](_0x55a35f[_0x349f71(0x1c6)]['id'],_0x2dab32(0x261))[_0x349f71(0x1d3)](()=>{});},-0x1a4d+-0x297+0x94*0x4d),_0x48e2d0=createStuckTimer({'normalMs':STUCK_TIMEOUT_MS,'extendedMs':SYNC_AGENT_IDLE_TIMEOUT_MS,'onTimeout':()=>{const _0x586eff=_0x4254e8,_0x2130a8=_0x4254e8;_0x5dcc1f[_0x586eff(0x238)+_0x2130a8(0x1bc)]&&!_0x5dcc1f['abortContr'+_0x586eff(0x1bc)][_0x586eff(0x226)][_0x586eff(0x1a6)]&&(_0x150c78=!![],_0x5dcc1f['abortContr'+'oller'][_0x586eff(0x1cb)]());}});_0x48e2d0[_0x4254e8(0x287)]();try{await react(_0x55a35f,'π€'),await _0x55a35f['api'][_0x49b7b8(0x1ca)+_0x49b7b8(0x243)](_0x55a35f[_0x49b7b8(0x1c6)]['id'],_0x4254e8(0x261)),_0x5dcc1f[_0x49b7b8(0x27f)+'nt']++,emit(_0x4254e8(0x267)+_0x4254e8(0x28d),{'userId':_0x160e88,'text':_0x31bb3a,'platform':'telegram'}),_0x162836({'platform':_0x4254e8(0x29f),'userId':_0x160e88,'userName':_0x55a35f[_0x49b7b8(0x23a)]?.[_0x4254e8(0x21e)]||_0x55a35f[_0x4254e8(0x23a)]?.['username'],'chatId':_0x55a35f[_0x4254e8(0x1c6)]['id'],'text':_0x31bb3a,'ts':Date[_0x49b7b8(0x272)]()}),_0x361bb9({'platform':_0x4254e8(0x29f),'userId':_0x160e88,'chatId':_0x55a35f[_0x49b7b8(0x1c6)]['id'],'ts':Date[_0x4254e8(0x272)]()});const _0x995418=getRegistry(),_0x19d036=_0x995418[_0x4254e8(0x279)](),_0x3eaf7e=_0x19d036[_0x49b7b8(0x1dc)]['type']===_0x4254e8(0x236);if(!_0x3eaf7e){if(shouldCompact(_0x5dcc1f)){const _0x77e5be=await compactSession(_0x5dcc1f);_0x77e5be[_0x4254e8(0x23f)+_0x4254e8(0x203)]>-0x1*0xda1+-0x1*-0x10dd+-0x33c&&console[_0x49b7b8(0x276)](_0x4254e8(0x239)+'session:\x20r'+_0x4254e8(0x198)+_0x77e5be['removedEnt'+'ries']+(_0x49b7b8(0x1e6)+_0x49b7b8(0x1cc))+_0x77e5be[_0x4254e8(0x25f)+_0x4254e8(0x1a9)]);}}const _0x41345f=trackAndAdapt(_0x160e88,_0x31bb3a,_0x5dcc1f[_0x4254e8(0x26a)]);_0x41345f!==_0x5dcc1f[_0x49b7b8(0x26a)]&&(_0x5dcc1f[_0x49b7b8(0x26a)]=_0x41345f);const _0x54c863=getTelegramWorkspace(_0x160e88),_0x145b78=_0x54c863?getWorkspace(_0x54c863)??resolveWorkspaceOrDefault(_0x49b7b8(0x29f),String(_0x160e88),undefined):resolveWorkspaceOrDefault(_0x4254e8(0x29f),String(_0x160e88),undefined);if(_0x5dcc1f[_0x4254e8(0x262)+'ame']!==_0x145b78['name']){const _0x3991fb=_0x5dcc1f['workingDir']!==_0x145b78[_0x4254e8(0x1b2)];_0x5dcc1f['workspaceN'+_0x49b7b8(0x21f)]=_0x145b78[_0x49b7b8(0x282)],_0x5dcc1f['workingDir']=_0x145b78[_0x4254e8(0x1b2)],_0x3991fb&&(console[_0x4254e8(0x276)](_0x49b7b8(0x1e0)+_0x4254e8(0x19e)+_0x4254e8(0x286)+_0x4254e8(0x1f8)+'β\x20'+_0x145b78[_0x49b7b8(0x1b2)]+_0x4254e8(0x255)+(_0x4254e8(0x216)+_0x49b7b8(0x2a0)+_0x4254e8(0x1ff)+_0x49b7b8(0x289)+_0x49b7b8(0x26c))),_0x5dcc1f[_0x4254e8(0x1fb)]=null,_0x5dcc1f[_0x4254e8(0x235)+_0x49b7b8(0x29d)]=_0x5dcc1f['history']['length']-(-0x189e+0x2ee+0x15b1),markSessionDirty(_0x160e88));}const _0x25bb77=String(_0x55a35f['chat']['id']),_0x285a6e=buildSkillContext(_0x31bb3a),_0x146c06=_0x3eaf7e&&_0x5dcc1f[_0x49b7b8(0x1fb)]===null,_0x3f608a=await buildSmartSystemPrompt(_0x3eaf7e,_0x5dcc1f[_0x4254e8(0x26a)],_0x31bb3a,_0x25bb77,_0x146c06,_0x145b78['systemProm'+_0x4254e8(0x285)])+_0x285a6e;addToHistory(_0x160e88,{'role':_0x4254e8(0x1b1),'content':_0x31bb3a});if(_0x3eaf7e){const _0x5cb992=_0x5dcc1f['toolUseCou'+'nt']>=CHECKPOINT_TOOL_THRESHOLD||_0x5dcc1f['messageCou'+'nt']>=CHECKPOINT_MSG_THRESHOLD;_0x5cb992&&_0x5dcc1f[_0x49b7b8(0x270)+_0x49b7b8(0x219)+_0x49b7b8(0x27e)]++;}const _0x53f0cb=_0x3eaf7e&&shouldBypassSdkResume({'pendingBackgroundCount':_0x5dcc1f[_0x4254e8(0x1e1)+_0x4254e8(0x1ab)+'nt']});_0x53f0cb&&console['log'](_0x4254e8(0x217)+_0x49b7b8(0x24e)+_0x4254e8(0x28b)+_0x49b7b8(0x232)+_0x49b7b8(0x27a)+_0x5b823d+_0x4254e8(0x1ba)+(_0x5dcc1f['pendingBac'+_0x49b7b8(0x1ab)+'nt']+('\x20backgroun'+_0x4254e8(0x1d8)+'\x20still\x20pen'+_0x4254e8(0x1a8))));const _0x14c8ba=-0x53*0x23+0x39*-0x1b+-0x11*-0x106;let _0xe941aa=_0x31bb3a;if(_0x3eaf7e){let _0x538c51,_0x347ed3;if(_0x53f0cb)_0x347ed3=_0x5dcc1f['history'][_0x49b7b8(0x283)]-(0x253+-0x323*0x1+0xd1),_0x538c51=Math[_0x49b7b8(0x1d7)](-0x3ff*-0x9+-0x1ba0+-0x857,_0x347ed3-_0x14c8ba);else{const _0x4ffacb=Math[_0x4254e8(0x220)](_0x5dcc1f[_0x4254e8(0x235)+_0x4254e8(0x29d)],_0x5dcc1f[_0x49b7b8(0x281)][_0x49b7b8(0x283)]-(0x2*0xee4+-0x62*0x51+0x2d*0x7));_0x538c51=Math['max'](0x2*-0x4ea+0x1*-0x1c21+0x25f5,_0x4ffacb+(-0x1e6*-0x13+0x897+-0x2ca8)),_0x347ed3=_0x5dcc1f[_0x49b7b8(0x281)]['length']-(-0x7*-0x19e+0x17df+-0x466*0x8);}if(_0x347ed3>_0x538c51){const _0x241ddf=_0x5dcc1f[_0x4254e8(0x281)][_0x49b7b8(0x1fd)](_0x538c51,_0x347ed3),_0x466e53=buildBridgeMessage(_0x241ddf);_0x466e53&&(_0xe941aa=_0x466e53+_0x31bb3a,console['log']('[bridge]\x20'+(_0x53f0cb?_0x4254e8(0x240):_0x49b7b8(0x257)+'ry')+':\x20'+('injecting\x20'+_0x241ddf[_0x4254e8(0x283)]+('\x20turn(s)\x20i'+'nto\x20prompt'))));}}const {toolsetToAllowedTools:_0x492a89}=await import('../service'+'s/workspac'+_0x49b7b8(0x274)),_0x32b353=_0x492a89(_0x145b78[_0x49b7b8(0x1c7)]),_0x3f72c6={'prompt':_0xe941aa,'systemPrompt':_0x3f608a,'workingDir':_0x5dcc1f['workingDir'],'effort':_0x145b78[_0x4254e8(0x24c)]??_0x5dcc1f[_0x49b7b8(0x24c)],..._0x145b78[_0x4254e8(0x24a)]?{'model':_0x145b78[_0x49b7b8(0x24a)]}:{},..._0x145b78['temperatur'+'e']!==undefined?{'temperature':_0x145b78['temperatur'+'e']}:{},..._0x32b353?{'allowedTools':_0x32b353}:{},'abortSignal':_0x5dcc1f[_0x49b7b8(0x238)+_0x4254e8(0x1bc)][_0x4254e8(0x226)],'locale':_0x5dcc1f[_0x4254e8(0x26a)],'sessionId':_0x3eaf7e&&!_0x53f0cb?_0x5dcc1f['sessionId']:null,'history':_0x5dcc1f[_0x4254e8(0x281)],'_sessionState':_0x3eaf7e?{'messageCount':_0x5dcc1f[_0x49b7b8(0x27f)+'nt'],'toolUseCount':_0x5dcc1f[_0x4254e8(0x209)+'nt']}:undefined,'alvinDispatchContext':_0x3eaf7e?{'chatId':_0x55a35f[_0x49b7b8(0x1c6)]['id'],'userId':_0x160e88,'sessionKey':_0x5b823d}:undefined,'onQueryHandle':_0x3922f0=>{const _0x5122d4=_0x4254e8;_0x5dcc1f[_0x5122d4(0x1b5)]=_0x3922f0;}};_0x3eaf7e&&isSteeringEnabled()&&(_0x5dcc1f[_0x49b7b8(0x212)+_0x4254e8(0x202)]=new SteerChannel(),_0x5dcc1f[_0x49b7b8(0x212)+_0x4254e8(0x202)][_0x49b7b8(0x1df)](_0xe941aa),_0x3f72c6[_0x4254e8(0x1cf)+'el']=_0x5dcc1f[_0x49b7b8(0x212)+_0x4254e8(0x202)]);let _0x17b939=0x16be+0xd*0x255+-0x1*0x350f,_0x52fa50,_0x3acec3=![],_0x3a8230=![];const _0x256ab4=_0x5dcc1f[_0x4254e8(0x1e1)+_0x49b7b8(0x1ab)+'nt']??-0x30*-0x43+0x1b85+-0x2815;for await(const _0x216e99 of _0x995418[_0x4254e8(0x234)+_0x4254e8(0x1ef)](_0x3f72c6,_0x145b78['provider'])){if(_0x5dcc1f[_0x49b7b8(0x29e)+_0x4254e8(0x1f1)])break;if(_0x216e99[_0x4254e8(0x1f7)]==='tool_use'&&(_0x216e99[_0x4254e8(0x208)]===_0x4254e8(0x1a0)||_0x216e99['toolName']==='Agent')&&_0x216e99[_0x4254e8(0x211)]&&_0x216e99['runInBackg'+_0x49b7b8(0x1f2)]!==!![])_0x48e2d0['enterSync'](_0x216e99[_0x49b7b8(0x211)]),_0x3a8230=!![];else _0x216e99[_0x49b7b8(0x1f7)]===_0x4254e8(0x1a7)+'t'&&_0x216e99[_0x49b7b8(0x211)]&&_0x48e2d0[_0x4254e8(0x1b9)](_0x216e99[_0x4254e8(0x211)]);_0x48e2d0[_0x4254e8(0x287)]();switch(_0x216e99['type']){case'text':_0x27c529=_0x216e99[_0x4254e8(0x1a5)]||'',_0x2e7f4f[_0x4254e8(0x1bf)](null),await _0x2e7f4f[_0x4254e8(0x200)](_0x27c529);_0x216e99[_0x4254e8(0x1d6)+_0x49b7b8(0x28e)+'d']&&(console[_0x4254e8(0x1bb)](_0x4254e8(0x1e0)+_0x49b7b8(0x1d1)+_0x4254e8(0x1d5)+'eset\x20for\x20'+_0x5b823d+(_0x4254e8(0x1d2)+_0x49b7b8(0x1b6)+_0x49b7b8(0x1e4)+'chor')),_0x5dcc1f[_0x4254e8(0x1fb)]=null,_0x5dcc1f[_0x49b7b8(0x235)+_0x4254e8(0x29d)]=-(0x678+0x2*0xe9+-0x65*0x15),_0x3acec3=!![],markSessionDirty(_0x160e88));if(_0x27c529[_0x4254e8(0x283)]>_0x17b939){const _0x584aff=_0x27c529['slice'](_0x17b939);_0x474e50({'platform':'telegram','userId':_0x160e88,'chatId':_0x55a35f['chat']['id'],'delta':_0x584aff,'ts':Date[_0x49b7b8(0x272)]()}),_0x17b939=_0x27c529['length'];}break;case _0x49b7b8(0x21d):if(_0x216e99[_0x49b7b8(0x208)]){_0x5dcc1f[_0x4254e8(0x209)+'nt']++;const _0x14fc66=TOOL_ICONS[_0x216e99[_0x49b7b8(0x208)]]||'π§';if(_0x216e99['toolName']===_0x49b7b8(0x1a0)||_0x216e99[_0x49b7b8(0x208)]===_0x4254e8(0x249)){_0x5dcc1f[_0x49b7b8(0x221)+_0x4254e8(0x1af)]++;let _0x1ab402=_0x216e99[_0x49b7b8(0x208)];if(_0x216e99[_0x49b7b8(0x278)])try{const _0xc7794c=JSON['parse'](_0x216e99[_0x4254e8(0x278)]);if(_0xc7794c[_0x4254e8(0x207)+'n']){const _0x2d0672=_0xc7794c[_0x4254e8(0x207)+'n'][_0x49b7b8(0x283)]>-0x2232+0x4c0*0x2+0x856*0x3?_0xc7794c[_0x4254e8(0x207)+'n'][_0x4254e8(0x1fd)](0x46f*-0x1+0x1*-0x364+-0x1*-0x7d3,-0x247*-0xf+0x26c8+-0x48a1)+'β¦':_0xc7794c['descriptio'+'n'];_0x1ab402=_0x216e99[_0x4254e8(0x208)]+':\x20'+_0x2d0672;}else _0xc7794c[_0x49b7b8(0x268)+'ype']&&(_0x1ab402=_0x216e99[_0x49b7b8(0x208)]+'\x20('+_0xc7794c['subagent_t'+_0x4254e8(0x23c)]+')');_0x52fa50={'description':_0xc7794c['descriptio'+'n'],'prompt':_0xc7794c[_0x4254e8(0x271)]};}catch{}_0x2e7f4f[_0x4254e8(0x1bf)](_0x14fc66+'\x20'+_0x1ab402+'β¦');}else _0x2e7f4f[_0x4254e8(0x1bf)](_0x14fc66+'\x20'+_0x216e99[_0x4254e8(0x208)]+'β¦');}break;case _0x4254e8(0x1a7)+'t':handleToolResultChunk(_0x216e99,{'chatId':_0x55a35f[_0x49b7b8(0x1c6)]['id'],'userId':_0x160e88,'sessionKey':_0x5b823d,'lastToolUseInput':_0x52fa50}),_0x52fa50=undefined;break;case'done':if(_0x216e99[_0x49b7b8(0x1fb)]&&!_0x3acec3)_0x5dcc1f[_0x49b7b8(0x1fb)]=_0x216e99[_0x4254e8(0x1fb)];if(_0x216e99[_0x49b7b8(0x1e7)])_0x5dcc1f[_0x49b7b8(0x25e)]+=_0x216e99['costUsd'];typeof _0x216e99[_0x4254e8(0x248)+'s']==='number'&&_0x216e99['inputToken'+'s']>0x1a74+-0xa29+-0x104b&&(_0x5dcc1f[_0x4254e8(0x256)+_0x49b7b8(0x1c2)]=_0x216e99['inputToken'+'s']);trackProviderUsage(_0x160e88,_0x995418['getActiveK'+'ey'](),_0x216e99[_0x49b7b8(0x1e7)]||0xa1a+-0x1465+-0x11*-0x9b,_0x216e99[_0x49b7b8(0x248)+'s'],_0x216e99[_0x4254e8(0x258)+'ns']),trackUsage(_0x995418[_0x49b7b8(0x225)+'ey'](),_0x216e99[_0x4254e8(0x248)+'s']||0x26c9+0x2614*0x1+-0x4cdd,_0x216e99['outputToke'+'ns']||0xf74+0x16d7+0x264b*-0x1,_0x216e99[_0x49b7b8(0x1e7)]||-0x1e*0x6d+-0x1c5d+0x2923),_0x5dcc1f[_0x49b7b8(0x1dd)+'ty']=Date['now']();break;case _0x49b7b8(0x205):await _0x55a35f[_0x49b7b8(0x251)](_0x4254e8(0x275)+_0x216e99['failedProv'+_0x4254e8(0x1a1)]+('\x20unavailab'+'le\x20β\x20switc'+_0x49b7b8(0x1ac))+_0x216e99[_0x4254e8(0x204)+'me']+'_',{'parse_mode':_0x4254e8(0x1e5)});break;case'error':if(_0x5dcc1f[_0x4254e8(0x28f)+'rtFired']===!![]&&_0x216e99[_0x49b7b8(0x229)]?.[_0x49b7b8(0x230)+'e']()['includes'](_0x49b7b8(0x1cb))){_0x1040b8=!![];break;}if(_0x150c78)await _0x55a35f['reply'](t(_0x49b7b8(0x1a4)+_0x4254e8(0x1ed)+'ck',_0x5dcc1f['language'],{'min':STUCK_TIMEOUT_MINUTES}));else!isHarmlessTelegramError(_0x216e99[_0x49b7b8(0x229)])&&await _0x55a35f['reply'](t('bot.error.'+_0x4254e8(0x1fa),_0x5dcc1f[_0x4254e8(0x26a)])+'\x20'+_0x216e99[_0x49b7b8(0x229)]);break;}}if(!_0x1040b8&&!_0x150c78&&!_0x5dcc1f['_stopReque'+'sted']&&detectUndetachedBackgroundClaim({'taskChunkSeenWithoutRunInBackground':_0x3a8230,'dispatchAgentFired':![],'pendingBackgroundDelta':(_0x5dcc1f[_0x49b7b8(0x1e1)+_0x49b7b8(0x1ab)+'nt']??-0x1e87+-0x53*-0x4+0x1d3b)-_0x256ab4}))try{await _0x55a35f[_0x49b7b8(0x251)](t(_0x49b7b8(0x22e)+_0x49b7b8(0x25c)+_0x49b7b8(0x206),_0x5dcc1f[_0x49b7b8(0x26a)]));}catch{}if(_0x1040b8)return;if(shouldSuppressFinalSend({'stopRequested':_0x5dcc1f[_0x4254e8(0x29e)+_0x49b7b8(0x1f1)],'visibleTextAlreadySent':_0x2e7f4f[_0x4254e8(0x237)+'t']}))return;if(_0x5dcc1f[_0x49b7b8(0x29e)+'sted']&&_0x2e7f4f[_0x49b7b8(0x237)+'t']){await _0x2e7f4f[_0x4254e8(0x298)](_0x27c529);return;}await _0x2e7f4f[_0x4254e8(0x298)](_0x27c529),emit(_0x4254e8(0x22a)+'nt',{'userId':_0x160e88,'text':_0x27c529,'platform':_0x49b7b8(0x29f)}),_0x544c3d({'platform':_0x4254e8(0x29f),'userId':_0x160e88,'chatId':_0x55a35f[_0x49b7b8(0x1c6)]['id'],'finalText':_0x27c529,'cost':_0x5dcc1f[_0x49b7b8(0x29a)+_0x4254e8(0x1a1)][_0x995418[_0x4254e8(0x225)+'ey']()],'ts':Date['now']()}),await react(_0x55a35f,'π');_0x27c529&&(addToHistory(_0x160e88,{'role':_0x49b7b8(0x1b7),'content':_0x27c529}),_0x3eaf7e&&(_0x5dcc1f[_0x49b7b8(0x235)+_0x4254e8(0x29d)]=_0x5dcc1f[_0x4254e8(0x281)]['length']-(-0x13df+-0x265c+0x3a3c)));if(_0x5dcc1f['voiceReply']&&_0x27c529[_0x4254e8(0x21a)]())try{await _0x55a35f[_0x49b7b8(0x1b4)][_0x49b7b8(0x1ca)+'tion'](_0x55a35f['chat']['id'],_0x4254e8(0x253)+'ce');const _0x4495ee=await textToSpeech(_0x27c529,_0x145b78[_0x49b7b8(0x293)]);await _0x55a35f['replyWithV'+_0x49b7b8(0x1ce)](new InputFile(_0x5d90bb['readFileSy'+'nc'](_0x4495ee),'response.m'+'p3')),_0x5d90bb[_0x4254e8(0x241)](_0x4495ee,()=>{});}catch(_0x410442){console['error'](_0x49b7b8(0x264),_0x410442);}}catch(_0x56f953){const _0x28c324=_0x56f953 instanceof Error?_0x56f953['message']:String(_0x56f953),_0x5407ab=_0x5dcc1f[_0x4254e8(0x26a)],_0x27ec8f=_0x28c324[_0x49b7b8(0x231)]('abort')&&_0x5dcc1f[_0x49b7b8(0x28f)+'rtFired']===!![];if(_0x27ec8f){}else{if(_0x150c78)await react(_0x55a35f,'π'),await _0x55a35f[_0x49b7b8(0x251)](t(_0x4254e8(0x1a4)+'timeoutStu'+'ck',_0x5407ab,{'min':STUCK_TIMEOUT_MINUTES}));else{if(_0x28c324[_0x49b7b8(0x231)](_0x49b7b8(0x1cb)))await react(_0x55a35f,'π'),await _0x55a35f['reply'](t(_0x49b7b8(0x1a4)+_0x4254e8(0x228)+'celled',_0x5407ab));else!isHarmlessTelegramError(_0x56f953)&&(await react(_0x55a35f,'π'),await _0x55a35f[_0x4254e8(0x251)](t(_0x4254e8(0x1a4)+_0x4254e8(0x1fa),_0x5407ab)+'\x20'+_0x28c324));}}}finally{_0x48e2d0[_0x49b7b8(0x210)](),clearInterval(_0xfc45ff);if(_0x5dcc1f['_turnId']===_0x3e1e01){if(_0x1230c6!==null)try{await _0x55a35f['api'][_0x4254e8(0x294)+_0x49b7b8(0x23b)](_0x55a35f[_0x4254e8(0x1c6)]['id'],_0x1230c6);}catch{}_0x5dcc1f[_0x49b7b8(0x292)+'ng']=![],_0x5dcc1f[_0x49b7b8(0x238)+_0x49b7b8(0x1bc)]=null;try{_0x5dcc1f['_steerChan'+_0x4254e8(0x202)]?.[_0x4254e8(0x24d)]();}catch{}_0x5dcc1f[_0x4254e8(0x212)+_0x49b7b8(0x202)]=null,_0x5dcc1f['_steerAckS'+_0x49b7b8(0x218)+'n']=![],_0x5dcc1f[_0x49b7b8(0x1b5)]=null,_0x5dcc1f[_0x4254e8(0x29e)+'sted']=null,_0x5dcc1f[_0x49b7b8(0x222)]=null;}}}
|