alvin-bot 5.7.0 → 5.8.1

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.
Files changed (137) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +25 -31
  3. package/dist/claude.js +1 -102
  4. package/dist/config.js +1 -96
  5. package/dist/engine.js +1 -90
  6. package/dist/find-claude-binary.js +1 -98
  7. package/dist/handlers/async-agent-chunk-handler.js +1 -50
  8. package/dist/handlers/background-bypass.js +1 -75
  9. package/dist/handlers/commands.js +1 -2336
  10. package/dist/handlers/cron-progress.js +1 -52
  11. package/dist/handlers/document.js +1 -194
  12. package/dist/handlers/message.js +1 -959
  13. package/dist/handlers/photo.js +1 -154
  14. package/dist/handlers/platform-message.js +1 -360
  15. package/dist/handlers/stuck-timer.js +1 -54
  16. package/dist/handlers/video.js +1 -237
  17. package/dist/handlers/voice.js +1 -148
  18. package/dist/i18n.js +1 -805
  19. package/dist/index.js +1 -697
  20. package/dist/init-data-dir.js +1 -98
  21. package/dist/middleware/auth.js +1 -233
  22. package/dist/migrate.js +1 -162
  23. package/dist/paths.js +1 -146
  24. package/dist/platforms/discord.js +1 -175
  25. package/dist/platforms/index.js +1 -130
  26. package/dist/platforms/signal.js +1 -205
  27. package/dist/platforms/slack-slash-parser.js +1 -32
  28. package/dist/platforms/slack.js +1 -501
  29. package/dist/platforms/telegram.js +1 -111
  30. package/dist/platforms/types.js +1 -8
  31. package/dist/platforms/whatsapp-auth-helpers.js +1 -53
  32. package/dist/platforms/whatsapp.js +1 -707
  33. package/dist/providers/claude-sdk-provider.js +1 -565
  34. package/dist/providers/codex-cli-provider.js +1 -134
  35. package/dist/providers/index.js +1 -7
  36. package/dist/providers/ollama-provider.js +1 -32
  37. package/dist/providers/openai-compatible.js +1 -406
  38. package/dist/providers/registry.js +1 -352
  39. package/dist/providers/runtime-header.js +1 -45
  40. package/dist/providers/tool-executor.js +1 -475
  41. package/dist/providers/types.js +1 -227
  42. package/dist/services/access.js +1 -144
  43. package/dist/services/allowed-users-gate.js +1 -56
  44. package/dist/services/alvin-dispatch.js +1 -174
  45. package/dist/services/alvin-mcp-tools.js +1 -104
  46. package/dist/services/asset-index.js +1 -224
  47. package/dist/services/async-agent-parser.js +1 -418
  48. package/dist/services/async-agent-watcher.js +1 -583
  49. package/dist/services/auto-diagnostic.js +1 -228
  50. package/dist/services/broadcast.js +1 -52
  51. package/dist/services/browser-manager.js +1 -562
  52. package/dist/services/browser-webfetch.js +1 -127
  53. package/dist/services/browser.js +1 -121
  54. package/dist/services/cdp-bootstrap.js +1 -357
  55. package/dist/services/compaction.js +1 -144
  56. package/dist/services/critical-notify.js +1 -203
  57. package/dist/services/cron-resolver.js +1 -58
  58. package/dist/services/cron-scheduling.js +1 -310
  59. package/dist/services/cron.js +1 -861
  60. package/dist/services/custom-tools.js +1 -317
  61. package/dist/services/delivery-queue.js +1 -173
  62. package/dist/services/delivery-registry.js +1 -21
  63. package/dist/services/disk-cleanup.js +1 -203
  64. package/dist/services/elevenlabs.js +1 -58
  65. package/dist/services/embeddings/auto-detect.js +1 -74
  66. package/dist/services/embeddings/fts5.js +1 -108
  67. package/dist/services/embeddings/gemini.js +1 -65
  68. package/dist/services/embeddings/index.js +1 -496
  69. package/dist/services/embeddings/ollama.js +1 -78
  70. package/dist/services/embeddings/openai.js +1 -49
  71. package/dist/services/embeddings/provider.js +1 -22
  72. package/dist/services/embeddings/vector-base.js +1 -113
  73. package/dist/services/embeddings-migration.js +1 -193
  74. package/dist/services/embeddings.js +1 -9
  75. package/dist/services/env-file.js +1 -50
  76. package/dist/services/exec-guard.js +1 -71
  77. package/dist/services/fallback-order.js +1 -154
  78. package/dist/services/file-permissions.js +1 -93
  79. package/dist/services/heartbeat-file.js +1 -65
  80. package/dist/services/heartbeat.js +1 -313
  81. package/dist/services/hooks.js +1 -44
  82. package/dist/services/imagegen.js +1 -72
  83. package/dist/services/language-detect.js +1 -154
  84. package/dist/services/markdown.js +1 -63
  85. package/dist/services/mcp.js +1 -263
  86. package/dist/services/memory-extractor.js +1 -178
  87. package/dist/services/memory-inject-mode.js +1 -43
  88. package/dist/services/memory-layers.js +1 -156
  89. package/dist/services/memory.js +1 -146
  90. package/dist/services/ollama-manager.js +1 -339
  91. package/dist/services/permissions-wizard.js +1 -291
  92. package/dist/services/personality.js +1 -376
  93. package/dist/services/plugins.js +1 -171
  94. package/dist/services/preflight.js +1 -292
  95. package/dist/services/process-manager.js +1 -291
  96. package/dist/services/release-highlights.js +1 -79
  97. package/dist/services/reminders.js +1 -97
  98. package/dist/services/restart.js +1 -48
  99. package/dist/services/security-audit.js +1 -74
  100. package/dist/services/self-diagnosis.js +1 -272
  101. package/dist/services/self-search.js +1 -129
  102. package/dist/services/session-persistence.js +1 -237
  103. package/dist/services/session.js +1 -282
  104. package/dist/services/skills.js +1 -290
  105. package/dist/services/ssrf-guard.js +1 -162
  106. package/dist/services/standing-orders.js +1 -29
  107. package/dist/services/steer-channel.js +1 -46
  108. package/dist/services/stop-controller.js +1 -52
  109. package/dist/services/subagent-dedup.js +1 -86
  110. package/dist/services/subagent-delivery.js +1 -452
  111. package/dist/services/subagent-stats.js +1 -123
  112. package/dist/services/subagents.js +1 -814
  113. package/dist/services/sudo.js +1 -329
  114. package/dist/services/telegram.js +1 -158
  115. package/dist/services/timing-safe-bearer.js +1 -51
  116. package/dist/services/tool-discovery.js +1 -214
  117. package/dist/services/trends.js +1 -580
  118. package/dist/services/updater.js +1 -291
  119. package/dist/services/usage-tracker.js +1 -144
  120. package/dist/services/users.js +1 -271
  121. package/dist/services/voice.js +1 -104
  122. package/dist/services/watchdog-brake.js +1 -154
  123. package/dist/services/watchdog.js +1 -311
  124. package/dist/services/workspaces.js +1 -276
  125. package/dist/tui/index.js +1 -667
  126. package/dist/util/console-formatter.js +1 -109
  127. package/dist/util/debounce.js +1 -24
  128. package/dist/util/telegram-error-filter.js +1 -62
  129. package/dist/version.js +1 -24
  130. package/dist/web/bind-strategy.js +1 -42
  131. package/dist/web/canvas.js +1 -30
  132. package/dist/web/doctor-api.js +1 -604
  133. package/dist/web/openai-compat.js +1 -252
  134. package/dist/web/server.js +1 -1902
  135. package/dist/web/setup-api.js +1 -1101
  136. package/package.json +5 -2
  137. package/dist/.metadata_never_index +0 -0
@@ -1,583 +1 @@
1
- /**
2
- * Async Sub-Agent Watcher (Fix #17 Stage 2)
3
- *
4
- * Tracks pending background sub-agents that Claude launched with
5
- * `run_in_background: true`. Polls each agent's outputFile every
6
- * POLL_INTERVAL_MS, detects completion (success/failure/timeout),
7
- * and delivers the final result as a separate Telegram message via
8
- * the existing subagent-delivery.ts pipeline.
9
- *
10
- * Persistence: pending agents survive bot restarts via
11
- * ~/.alvin-bot/state/async-agents.json. On boot, startWatcher() loads
12
- * the file and resumes polling — same catchup pattern as the v4.9.0
13
- * cron scheduler.
14
- *
15
- * Why this exists: Claude's Agent tool defaults to synchronous, which
16
- * blocks the main Telegram session for 10+ minutes during long audits.
17
- * Stage 1 of the fix tells Claude to use run_in_background; Stage 2
18
- * (this file) catches the resulting outputFile and delivers the result
19
- * when ready, so the user can keep chatting while the agent works.
20
- *
21
- * See docs/superpowers/plans/2026-04-13-async-subagents.md for the
22
- * full plan and docs/superpowers/specs/sdk-async-agent-outputfile-format.md
23
- * for the JSONL format details.
24
- */
25
- import fs from "fs";
26
- import { dirname, resolve } from "path";
27
- import { parseOutputFileStatus } from "./async-agent-parser.js";
28
- import { claimDelivery, markDelivered, isDelivered, cleanupAgentFiles, } from "./subagent-dedup.js";
29
- import { ASYNC_AGENTS_STATE_FILE, SUBAGENTS_DIR } from "../paths.js";
30
- import { getAllSessions } from "./session.js";
31
- /**
32
- * B3 — Detect a permanent "target chat does not exist" delivery failure
33
- * (Telegram 400 "Bad Request: chat not found"), e.g. the stale chat_id:1
34
- * test agent. Such an agent must be abandoned, not retried forever.
35
- *
36
- * Kept as a local predicate (mirrors isChatNotFoundError in
37
- * subagent-delivery.ts) so the watcher does NOT take a new hard
38
- * dependency on a fresh subagent-delivery export — many test suites mock
39
- * that module with only deliverSubAgentResult, and a destructured import
40
- * of a non-mocked symbol would throw. Matched narrowly on the
41
- * chat-not-found signature only.
42
- */
43
- function isChatNotFoundError(err) {
44
- if (!err || typeof err !== "object")
45
- return false;
46
- const e = err;
47
- const haystack = `${e.message ?? ""} ${e.description ?? ""}`;
48
- return /chat not found/i.test(haystack);
49
- }
50
- /** How often the polling loop runs against each pending agent. */
51
- const POLL_INTERVAL_MS = 15_000;
52
- /** Hard ceiling per agent — 12h. After this, give up and deliver
53
- * a timeout banner. SEO audits historically take ~13 min, so 12h
54
- * is absurdly generous and protects against state-file growth. */
55
- const MAX_AGENT_AGE_MS = 12 * 60 * 60 * 1000;
56
- /**
57
- * v4.14.2 — When a dispatched subprocess never creates its outputFile
58
- * (spawn failure, crash before first write, file deleted externally),
59
- * `parseOutputFileStatus` returns "missing" on every poll. Pre-v4.14.2
60
- * that meant waiting the full 12h MAX_AGENT_AGE_MS before delivering a
61
- * timeout — a 12-hour zombie in `/subagents list`.
62
- *
63
- * This threshold caps how long we tolerate a missing file before
64
- * declaring the agent failed. `claude -p` normally writes its first
65
- * JSONL line within seconds of spawn; 10 minutes is way above any
66
- * legitimate startup variance and well below the 12h ceiling.
67
- *
68
- * Configurable via the ALVIN_MISSING_FILE_FAILURE_MS env var. Tests
69
- * use shorter values via the same hook. Only the getter is exposed
70
- * so callers always see the current env value, not a stale constant.
71
- */
72
- function getMissingFileFailureMs() {
73
- const raw = process.env.ALVIN_MISSING_FILE_FAILURE_MS;
74
- if (raw) {
75
- const n = Number(raw);
76
- if (Number.isFinite(n) && n > 0)
77
- return n;
78
- }
79
- return 10 * 60 * 1000; // default 10 min
80
- }
81
- // ── Module state ──────────────────────────────────────────────────
82
- const pending = new Map();
83
- let pollTimer = null;
84
- let started = false;
85
- /**
86
- * C-M2 — Set of agent IDs registered in THIS boot (not loaded from disk).
87
- * Only in-memory-registered agents have a pid we can safely attribute to
88
- * our own subprocess — disk-loaded pids may have been reused by the OS
89
- * after a restart. We never kill a disk-loaded pid; only pids in this set.
90
- */
91
- const thisBootAgentIds = new Set();
92
- /**
93
- * Hard cap on the pending-agents map. Without this, a bot that runs many
94
- * async agents but sees some fail to write their outputFile would see
95
- * entries linger up to `giveUpAt` (12h default). If the rate of
96
- * registerPending() outpaces resolutions for days, memory and the disk
97
- * state file grow unbounded. We evict oldest-first when over the cap.
98
- */
99
- const MAX_PENDING_AGENTS = 500;
100
- function enforcePendingCap() {
101
- if (pending.size < MAX_PENDING_AGENTS)
102
- return;
103
- const entries = [...pending.entries()].sort((a, b) => a[1].startedAt - b[1].startedAt);
104
- const target = Math.floor(MAX_PENDING_AGENTS * 0.9);
105
- let toEvict = pending.size - target;
106
- for (const [id] of entries) {
107
- if (toEvict <= 0)
108
- break;
109
- pending.delete(id);
110
- toEvict--;
111
- }
112
- console.warn(`[async-agent-watcher] pending map hit cap ${MAX_PENDING_AGENTS}, evicted to ${pending.size}`);
113
- }
114
- // ── Persistence ───────────────────────────────────────────────────
115
- function loadFromDisk() {
116
- try {
117
- const raw = fs.readFileSync(ASYNC_AGENTS_STATE_FILE, "utf-8");
118
- const arr = JSON.parse(raw);
119
- if (!Array.isArray(arr))
120
- return;
121
- for (const entry of arr) {
122
- if (typeof entry?.agentId === "string" && typeof entry?.outputFile === "string") {
123
- pending.set(entry.agentId, entry);
124
- }
125
- }
126
- }
127
- catch {
128
- // No state file yet — fresh start. Not an error.
129
- }
130
- }
131
- function saveToDisk() {
132
- try {
133
- fs.mkdirSync(dirname(ASYNC_AGENTS_STATE_FILE), { recursive: true });
134
- fs.writeFileSync(ASYNC_AGENTS_STATE_FILE, JSON.stringify([...pending.values()], null, 2), "utf-8");
135
- }
136
- catch (err) {
137
- console.error("[async-watcher] failed to persist state:", err);
138
- }
139
- }
140
- // ── Public API ────────────────────────────────────────────────────
141
- /**
142
- * Register a new async agent that Claude just launched. Persists
143
- * immediately so a crash right after registration still delivers
144
- * the result on the next boot.
145
- */
146
- export function registerPendingAgent(input) {
147
- const now = Date.now();
148
- const entry = {
149
- agentId: input.agentId,
150
- outputFile: input.outputFile,
151
- description: input.description,
152
- prompt: input.prompt,
153
- chatId: input.chatId,
154
- userId: input.userId,
155
- startedAt: now,
156
- lastCheckedAt: 0,
157
- giveUpAt: input.giveUpAt ?? now + MAX_AGENT_AGE_MS,
158
- toolUseId: input.toolUseId,
159
- sessionKey: input.sessionKey,
160
- platform: input.platform,
161
- pid: input.pid,
162
- };
163
- enforcePendingCap();
164
- pending.set(input.agentId, entry);
165
- // C-M2: mark this agent as registered in the current boot.
166
- // Only this-boot agents have pids we can safely attribute to our own subprocess.
167
- thisBootAgentIds.add(input.agentId);
168
- saveToDisk();
169
- }
170
- /**
171
- * v4.12.3 — Decrement the session's pendingBackgroundCount. Called on
172
- * every delivery (completed/failed/timeout). Clamped at 0 so drift
173
- * scenarios (counter was already 0, or session was reset) never crash.
174
- * Missing/unknown sessionKey → no-op. Never throws.
175
- */
176
- function decrementPendingCount(sessionKey) {
177
- if (!sessionKey)
178
- return;
179
- try {
180
- const all = getAllSessions();
181
- const s = all.get(sessionKey);
182
- if (!s)
183
- return;
184
- s.pendingBackgroundCount = Math.max(0, (s.pendingBackgroundCount ?? 0) - 1);
185
- }
186
- catch (err) {
187
- // Never let a decrement failure break delivery.
188
- console.error("[async-watcher] decrement failed:", err);
189
- }
190
- }
191
- /** Returns a snapshot of in-memory pending agents (for /subagents + diagnostics). */
192
- export function listPendingAgents() {
193
- return [...pending.values()];
194
- }
195
- /**
196
- * v5.7.0 push entry point — called by POST /internal/subagent-exit when
197
- * a detached subprocess exits. Looks the agent up, classifies its jsonl,
198
- * and delivers immediately (claim-gated, so push / the poll backstop /
199
- * reconciliation never double-deliver). Returns a coarse status for the
200
- * HTTP layer:
201
- *
202
- * - "unknown" → no matching pending entry (cancelled, already
203
- * delivered, or never tracked). HTTP 404. No side effect.
204
- * - "delivered" → terminal jsonl found and delivered (or the claim was
205
- * lost to another path — either way it is handled and
206
- * the entry is dropped). HTTP 200.
207
- * - "pending" → exit fired but the jsonl is not yet terminal (rare
208
- * flush race). Left to the poll backstop. HTTP 202.
209
- */
210
- export async function deliverByAgentId(agentId) {
211
- const entry = pending.get(agentId);
212
- if (!entry)
213
- return "unknown";
214
- const status = await parseOutputFileStatus(entry.outputFile);
215
- if (status.state === "completed") {
216
- // Invariant: the path that WINS the claim owns removal of the
217
- // shared pending entry. A claim-loser must not mutate shared state
218
- // — the winner (this push, the poll backstop, or reconcile) removes
219
- // it. If a winner ever crashes mid-delivery the next poll tick
220
- // self-heals: claimDelivery() is false there too, so it skips
221
- // re-delivery but still drops the entry. Either way "delivered".
222
- if (claimDelivery(agentId)) {
223
- await deliverAsCompleted(entry, status.output, status.tokensUsed);
224
- pending.delete(agentId);
225
- saveToDisk();
226
- }
227
- return "delivered";
228
- }
229
- if (status.state === "failed") {
230
- if (claimDelivery(agentId)) {
231
- await deliverAsFailure(entry, "error", status.error);
232
- pending.delete(agentId);
233
- saveToDisk();
234
- }
235
- return "delivered";
236
- }
237
- // running / missing → not yet flushed; the 15s poll backstop will get it.
238
- return "pending";
239
- }
240
- /**
241
- * v5.7.0 — Startup reconciliation. Runs once inside startWatcher() after
242
- * loadFromDisk(). Two passes:
243
- *
244
- * Pass A — immediate terminal drain. For every entry already in the
245
- * pending map (full delivery metadata present, restored by
246
- * loadFromDisk), check its jsonl ONCE: if terminal, deliver immediately
247
- * at startup instead of waiting up to 15s for the first poll tick. This
248
- * is what removes the post-restart latency the BACKLOG observed and
249
- * works even if the poll loop were disabled. Claim-gated.
250
- *
251
- * Pass B — disk hygiene, never delivers. Walk SUBAGENTS_DIR: skip
252
- * delivered/tombstoned agents, age-cap-clean ancient files, and leave
253
- * fresh orphans (jsonl with no persisted state entry → no delivery
254
- * target) untouched for a later age-cap. We never fabricate a target.
255
- *
256
- * Idempotent (re-runnable), dedup-guarded by the .delivered marker.
257
- */
258
- async function reconcileOnStartup() {
259
- // Pass A — drain pending entries with full metadata.
260
- for (const entry of [...pending.values()]) {
261
- try {
262
- if (isDelivered(entry.agentId)) {
263
- pending.delete(entry.agentId);
264
- continue;
265
- }
266
- const status = await parseOutputFileStatus(entry.outputFile);
267
- if (status.state === "completed" && claimDelivery(entry.agentId)) {
268
- await deliverAsCompleted(entry, status.output, status.tokensUsed);
269
- pending.delete(entry.agentId);
270
- }
271
- else if (status.state === "failed" && claimDelivery(entry.agentId)) {
272
- await deliverAsFailure(entry, "error", status.error);
273
- pending.delete(entry.agentId);
274
- }
275
- }
276
- catch (err) {
277
- console.error(`[async-watcher] reconcile passA ${entry.agentId}:`, err);
278
- }
279
- }
280
- saveToDisk();
281
- // Pass B — disk hygiene. Never delivers.
282
- let files;
283
- try {
284
- files = fs.readdirSync(SUBAGENTS_DIR);
285
- }
286
- catch {
287
- return; // no subagents dir yet — nothing to reconcile
288
- }
289
- const now = Date.now();
290
- for (const f of files) {
291
- if (!f.endsWith(".jsonl"))
292
- continue;
293
- const agentId = f.slice(0, -".jsonl".length);
294
- if (isDelivered(agentId))
295
- continue; // delivered or cancel-tombstone
296
- if (pending.has(agentId))
297
- continue; // Pass A / poll / push own it
298
- const jsonlPath = resolve(SUBAGENTS_DIR, f);
299
- try {
300
- const st = fs.statSync(jsonlPath);
301
- if (now - st.mtimeMs > MAX_AGENT_AGE_MS) {
302
- cleanupAgentFiles(agentId); // ancient orphan → clean
303
- }
304
- // else: orphan within age, no persisted target → cannot deliver;
305
- // leave it (age-cap cleans it on a later boot). No spam, no
306
- // fabricated delivery target.
307
- }
308
- catch {
309
- /* stat race — ignore */
310
- }
311
- }
312
- }
313
- /** Start the polling loop. Idempotent. Loads any persisted state from
314
- * disk, then runs startup reconciliation (immediate terminal drain +
315
- * disk hygiene) before the 15s poll backstop begins. */
316
- export function startWatcher() {
317
- if (started)
318
- return;
319
- started = true;
320
- loadFromDisk();
321
- void reconcileOnStartup().catch((err) => console.error("[async-watcher] reconcile failed:", err));
322
- pollTimer = setInterval(() => {
323
- pollOnce().catch((err) => console.error("[async-watcher] poll cycle failed:", err));
324
- }, POLL_INTERVAL_MS);
325
- console.log(`⏳ Async-agent watcher started (${pending.size} pending, ${POLL_INTERVAL_MS / 1000}s interval)`);
326
- }
327
- /** Stop the polling loop. Idempotent. */
328
- export function stopWatcher() {
329
- if (pollTimer)
330
- clearInterval(pollTimer);
331
- pollTimer = null;
332
- started = false;
333
- }
334
- /**
335
- * Run one poll cycle: check every pending agent, deliver the completed
336
- * ones, drop them from the in-memory + on-disk state. Exported for
337
- * tests; production uses the setInterval from startWatcher().
338
- */
339
- export async function pollOnce() {
340
- const now = Date.now();
341
- const toRemove = [];
342
- const missingFileFailureMs = getMissingFileFailureMs();
343
- // B3 — when a delivery attempt proves the target chat is permanently
344
- // invalid ("chat not found", e.g. the stale chat_id:1 test agent),
345
- // abandon the agent so the watcher never retries it. Without this, a
346
- // pending agent with an invalid target spams stderr on every poll
347
- // cycle (inflating errors_24h) and lingers until the 12h giveUpAt.
348
- const abandonIfInvalidTarget = (entry, outcome) => {
349
- if (!outcome.chatNotFound)
350
- return;
351
- if (!toRemove.includes(entry.agentId))
352
- toRemove.push(entry.agentId);
353
- console.warn(`[async-watcher] abandoning agent ${entry.agentId} — delivery target ` +
354
- `chat ${String(entry.chatId)} not found (invalid/stale); will not retry`);
355
- };
356
- for (const entry of pending.values()) {
357
- entry.lastCheckedAt = now;
358
- // Timeout check first — if the agent is past its giveUpAt, give up
359
- // regardless of whether the file shows progress.
360
- // v5.7.0 — every terminal delivery is claim-gated so push,
361
- // this poll backstop, and startup reconciliation never
362
- // double-deliver. A lost claim means another path already
363
- // handled it: just drop the entry without re-delivering.
364
- if (now >= entry.giveUpAt) {
365
- if (claimDelivery(entry.agentId)) {
366
- const outcome = await deliverAsFailure(entry, "timeout", "Agent ran longer than 12h — giving up");
367
- abandonIfInvalidTarget(entry, outcome);
368
- }
369
- toRemove.push(entry.agentId);
370
- continue;
371
- }
372
- const status = await parseOutputFileStatus(entry.outputFile);
373
- if (status.state === "completed") {
374
- if (claimDelivery(entry.agentId)) {
375
- const outcome = await deliverAsCompleted(entry, status.output, status.tokensUsed);
376
- abandonIfInvalidTarget(entry, outcome);
377
- }
378
- toRemove.push(entry.agentId);
379
- }
380
- else if (status.state === "failed") {
381
- if (claimDelivery(entry.agentId)) {
382
- const outcome = await deliverAsFailure(entry, "error", status.error);
383
- abandonIfInvalidTarget(entry, outcome);
384
- }
385
- toRemove.push(entry.agentId);
386
- }
387
- else if (status.state === "missing" &&
388
- now - entry.startedAt > missingFileFailureMs) {
389
- // v4.14.2 — Zombie guard: the subprocess never created its
390
- // output file within `missingFileFailureMs` (default 10 min).
391
- // Declare failed instead of polling until the 12h giveUpAt.
392
- if (claimDelivery(entry.agentId)) {
393
- const outcome = await deliverAsFailure(entry, "error", `Dispatched subprocess never wrote its output file (${Math.round((now - entry.startedAt) / 60_000)}m after start). Likely crashed before initializing, or the file was removed externally.`);
394
- abandonIfInvalidTarget(entry, outcome);
395
- }
396
- toRemove.push(entry.agentId);
397
- }
398
- // running / missing-but-young → keep polling next cycle
399
- }
400
- if (toRemove.length > 0) {
401
- for (const id of toRemove)
402
- pending.delete(id);
403
- saveToDisk();
404
- }
405
- }
406
- // ── Delivery helpers ──────────────────────────────────────────────
407
- async function deliverAsCompleted(entry, output, tokensUsed) {
408
- const { deliverSubAgentResult } = await import("./subagent-delivery.js");
409
- const info = {
410
- id: entry.agentId,
411
- name: entry.description,
412
- status: "completed",
413
- startedAt: entry.startedAt,
414
- source: "cron", // Reuse cron banner format — fits async background agents.
415
- depth: 0,
416
- parentChatId: entry.chatId,
417
- platform: entry.platform,
418
- };
419
- const result = {
420
- id: entry.agentId,
421
- name: entry.description,
422
- status: "completed",
423
- output,
424
- tokensUsed: tokensUsed ?? { input: 0, output: 0 },
425
- duration: Date.now() - entry.startedAt,
426
- };
427
- let chatNotFound = false;
428
- try {
429
- const outcome = await deliverSubAgentResult(info, result);
430
- chatNotFound = !!outcome?.chatNotFound;
431
- }
432
- catch (err) {
433
- console.error(`[async-watcher] delivery failed for ${entry.agentId}:`, err);
434
- // deliverSubAgentResult normally swallows send errors and reports
435
- // chatNotFound via its return value; if it ever throws, still detect
436
- // the permanent invalid-target case here.
437
- chatNotFound = isChatNotFoundError(err);
438
- }
439
- decrementPendingCount(entry.sessionKey);
440
- return { chatNotFound };
441
- }
442
- async function deliverAsFailure(entry, status, error) {
443
- const { deliverSubAgentResult } = await import("./subagent-delivery.js");
444
- const info = {
445
- id: entry.agentId,
446
- name: entry.description,
447
- status,
448
- startedAt: entry.startedAt,
449
- source: "cron",
450
- depth: 0,
451
- parentChatId: entry.chatId,
452
- platform: entry.platform,
453
- };
454
- const result = {
455
- id: entry.agentId,
456
- name: entry.description,
457
- status,
458
- output: "",
459
- tokensUsed: { input: 0, output: 0 },
460
- duration: Date.now() - entry.startedAt,
461
- error,
462
- };
463
- let chatNotFound = false;
464
- try {
465
- const outcome = await deliverSubAgentResult(info, result);
466
- chatNotFound = !!outcome?.chatNotFound;
467
- }
468
- catch (err) {
469
- console.error(`[async-watcher] failure delivery failed for ${entry.agentId}:`, err);
470
- chatNotFound = isChatNotFoundError(err);
471
- }
472
- decrementPendingCount(entry.sessionKey);
473
- return { chatNotFound };
474
- }
475
- // ── Test helpers ──────────────────────────────────────────────────
476
- /**
477
- * v5.1 — Kill detached sub-agent processes scoped to a session.
478
- *
479
- * Reads the canonical session key from `session.sessionKey` (stamped by
480
- * getSession() / loadPersistedSessions() — always a real string, never null
481
- * after the v5.1.x fix). Iterates watcher entries whose `sessionKey` matches
482
- * and calls `killFn(entry.pid)` for entries that have a pid.
483
- *
484
- * The function accepts an injectable `killFn` for testability. The default
485
- * implementation sends SIGTERM; callers in tests pass a spy.
486
- *
487
- * Never throws — all per-entry errors are swallowed.
488
- */
489
- /**
490
- * C-M1 — Compute the signal target for a detached subprocess pid.
491
- *
492
- * Since agents are spawned `detached:true` they become process-group
493
- * leaders. `claude -p` typically forks further (sub-agents), leaving
494
- * grandchildren in the same group. Signalling only the group-leader PID
495
- * lets those grandchildren survive. Instead, we signal the entire group
496
- * by negating the pid (POSIX: kill(-pgid, sig) = signal the group).
497
- *
498
- * Windows does not support negative-pid group signals; on win32 we fall
499
- * back to the positive pid (signals the leader only). A full win32 group-
500
- * kill would require `taskkill /T /PID` — that can be layered later if
501
- * Windows support becomes important.
502
- *
503
- * The injectable `killFn` always receives the already-transformed value
504
- * (negative on POSIX, positive on win32) so tests can assert the correct
505
- * target without needing platform-specific logic in test code.
506
- */
507
- function resolveKillTarget(pid) {
508
- return process.platform !== "win32" ? -pid : pid;
509
- }
510
- export function killSessionDetachedAgents(session, killFn = (target) => {
511
- try {
512
- process.kill(target, "SIGTERM");
513
- }
514
- catch { /* already gone — ESRCH is fine */ }
515
- }) {
516
- // Use session.sessionKey — the real canonical key stamped by getSession().
517
- // Before v5.1.x this field did not exist on UserSession, causing a silent
518
- // no-op; the fix in session.ts guarantees it is always a non-empty string.
519
- const key = session.sessionKey ?? null;
520
- if (key === null)
521
- return;
522
- for (const entry of pending.values()) {
523
- if (entry.sessionKey !== key)
524
- continue;
525
- if (typeof entry.pid !== "number")
526
- continue;
527
- // C-M2: only kill pids that are attributable to our own subprocess.
528
- // Pids loaded from disk on a previous boot may have been reused by
529
- // the OS for an unrelated process. We guard by only killing agents
530
- // registered in THIS boot (thisBootAgentIds). Disk-loaded entries
531
- // (those not in the set) are skipped — their subprocess may have
532
- // already exited and the pid may point at an innocent process.
533
- if (!thisBootAgentIds.has(entry.agentId)) {
534
- console.log(`[async-watcher] skipping kill for disk-loaded agent ${entry.agentId} ` +
535
- `(pid=${entry.pid}) — cannot safely attribute pid after restart`);
536
- continue;
537
- }
538
- // C-M1: pass the group-kill target (negative pid on POSIX) to killFn.
539
- try {
540
- killFn(resolveKillTarget(entry.pid));
541
- }
542
- catch { /* best-effort */ }
543
- }
544
- }
545
- /**
546
- * v5.1 — Mark/remove all pending watcher entries for a session so the
547
- * watcher does NOT deliver them after a hard stop.
548
- *
549
- * Mirrors the pattern used in pollOnce(): remove from the in-memory map
550
- * and persist immediately. This is a remove-not-flag approach because
551
- * there is no `cancelled` flag on PendingAsyncAgent and adding one would
552
- * require touching all poll-cycle delivery paths.
553
- *
554
- * Scoped strictly to entries whose `sessionKey` matches — never global,
555
- * never name-based. Never throws.
556
- */
557
- export function cancelPendingForSession(sessionKey) {
558
- let changed = false;
559
- for (const [id, entry] of pending.entries()) {
560
- if (entry.sessionKey === sessionKey) {
561
- // v5.7.0 — tombstone before removal: a cancelled agent's
562
- // subprocess may still exit later and POST, or its leftover jsonl
563
- // may be rediscovered by reconciliation on a future boot. The
564
- // .delivered marker makes "cancelled" authoritative and
565
- // restart-proof so neither path can resurrect it.
566
- markDelivered(entry.agentId);
567
- pending.delete(id);
568
- changed = true;
569
- }
570
- }
571
- if (changed) {
572
- saveToDisk();
573
- }
574
- }
575
- /** Test-only: drop in-memory state. Doesn't touch disk. */
576
- export function __resetForTest() {
577
- pending.clear();
578
- thisBootAgentIds.clear();
579
- if (pollTimer)
580
- clearInterval(pollTimer);
581
- pollTimer = null;
582
- started = false;
583
- }
1
+ (function(_0x2268d4,_0x34be4e){const _0x363dc0=_0x2eca,_0x5c7537=_0x2eca,_0xa5317b=_0x2268d4();while(!![]){try{const _0x5418fa=parseInt(_0x363dc0(0xfa))/(-0x1c34+0x4e*-0x79+0x1b*0x269)+parseInt(_0x363dc0(0x130))/(0xbf*0x1+-0x1bbb+0x1afe)+-parseInt(_0x5c7537(0xf9))/(-0x915+-0x1*0x2678+0x2f90)+-parseInt(_0x363dc0(0x138))/(0x1feb*0x1+-0x1b38+-0x6d*0xb)+parseInt(_0x363dc0(0xfd))/(-0x1*0x233c+-0x1*0x1345+-0x1b43*-0x2)*(parseInt(_0x5c7537(0x12d))/(0xf5+-0x2*0xe+-0xd3))+parseInt(_0x363dc0(0x126))/(-0x3*0xac1+-0xf26+0x1*0x2f70)+-parseInt(_0x363dc0(0x150))/(-0x86*-0x6+0xeec+-0x1208);if(_0x5418fa===_0x34be4e)break;else _0xa5317b['push'](_0xa5317b['shift']());}catch(_0x3b8150){_0xa5317b['push'](_0xa5317b['shift']());}}}(_0x4862,0x33b*-0x663+-0x1622f0+0x12c24c*0x3));const _0x4dc70e=(function(){let _0x86b2e4=!![];return function(_0x3d8aeb,_0x73ffd5){const _0x247f7c=_0x86b2e4?function(){const _0x3f90cb=_0x2eca;if(_0x73ffd5){const _0x1d7ce8=_0x73ffd5[_0x3f90cb(0xef)](_0x3d8aeb,arguments);return _0x73ffd5=null,_0x1d7ce8;}}:function(){};return _0x86b2e4=![],_0x247f7c;};}()),_0x5687c7=_0x4dc70e(this,function(){const _0x11cbd0=_0x2eca,_0x28360b=_0x2eca;return _0x5687c7[_0x11cbd0(0xeb)]()[_0x28360b(0x105)](_0x28360b(0x164)+'+$')[_0x11cbd0(0xeb)]()[_0x28360b(0x14c)+'r'](_0x5687c7)[_0x11cbd0(0x105)](_0x28360b(0x164)+'+$');});_0x5687c7();import _0x225918 from'fs';import{dirname,resolve}from'path';import{parseOutputFileStatus}from'./async-agent-parser.js';import{claimDelivery,markDelivered,isDelivered,cleanupAgentFiles}from'./subagent-dedup.js';import{ASYNC_AGENTS_STATE_FILE,SUBAGENTS_DIR}from'../paths.js';import{getAllSessions}from'./session.js';function isChatNotFoundError(_0x34d94f){const _0x38ee0f=_0x2eca,_0x470818=_0x2eca;if(!_0x34d94f||typeof _0x34d94f!==_0x38ee0f(0xf3))return![];const _0x3941a5=_0x34d94f,_0x263614=(_0x3941a5['message']??'')+'\x20'+(_0x3941a5[_0x38ee0f(0x12c)+'n']??'');return/chat not found/i[_0x470818(0x127)](_0x263614);}const POLL_INTERVAL_MS=0x61c9+-0x1f4d+-0xa*0xca,MAX_AGENT_AGE_MS=(-0x1*0x2176+-0x1396+-0x6a3*-0x8)*(-0x1c7*0xe+0x19fb+-0xdd)*(0x91+-0x126f+0x121a)*(0x177+0x1*0x1557+-0x76*0x29);function getMissingFileFailureMs(){const _0x5cfdec=_0x2eca,_0x1d08c6=_0x2eca,_0x122d71=process['env'][_0x5cfdec(0x161)+_0x5cfdec(0x156)+_0x1d08c6(0x135)];if(_0x122d71){const _0x16539c=Number(_0x122d71);if(Number[_0x1d08c6(0x14b)](_0x16539c)&&_0x16539c>-0x20a9+-0x1*0x2345+0x43ee)return _0x16539c;}return(-0x5b0+-0xd9b+0x2c3*0x7)*(-0x2*-0x6e6+-0x1*-0x39e+-0x112e)*(-0xd*0x1f3+-0x1b1b*0x1+0x385a);}const pending=new Map();let pollTimer=null,started=![];function _0x2eca(_0x225918,_0x86b2e4){_0x225918=_0x225918-(0x877*-0x3+0x2c*-0x22+0x2026);const _0x3d8aeb=_0x4862();let _0x73ffd5=_0x3d8aeb[_0x225918];if(_0x2eca['RCFEOs']===undefined){var _0x247f7c=function(_0x263614){const _0x122d71='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x16539c='',_0x129b7e='',_0x38d74b=_0x16539c+_0x247f7c;for(let _0x56a0e8=-0x1ef5+-0x3b5*0x1+0x5c7*0x6,_0x450421,_0x467b92,_0x2cf362=-0xa6f+-0x1*0x175d+0x21cc;_0x467b92=_0x263614['charAt'](_0x2cf362++);~_0x467b92&&(_0x450421=_0x56a0e8%(-0x1396+0x1879+-0x2b*0x1d)?_0x450421*(-0x1c7*0xe+0x19fb+-0xd9)+_0x467b92:_0x467b92,_0x56a0e8++%(0x91+-0x126f+0x11e2))?_0x16539c+=_0x38d74b['charCodeAt'](_0x2cf362+(0x177+0x1*0x1557+-0x7c*0x2f))-(-0x20a9+-0x1*0x2345+0x43f8)!==-0x5b0+-0xd9b+0x1c1*0xb?String['fromCharCode'](-0x2*-0x6e6+-0x1*-0x39e+-0x106b&_0x450421>>(-(-0xd*0x1f3+-0x1b1b*0x1+0x3474)*_0x56a0e8&-0x1*-0x1922+-0x1e0b+0x4ef)):_0x56a0e8:-0x1a0c+-0x8*0x454+0x2c2*0x16){_0x467b92=_0x122d71['indexOf'](_0x467b92);}for(let _0x107abb=-0x3*-0x480+0x1f78+-0x2cf8,_0x4eb523=_0x16539c['length'];_0x107abb<_0x4eb523;_0x107abb++){_0x129b7e+='%'+('00'+_0x16539c['charCodeAt'](_0x107abb)['toString'](0x17c3+-0x1904+0x151))['slice'](-(-0x395*-0x8+0x285+-0x1f2b));}return decodeURIComponent(_0x129b7e);};_0x2eca['UWkyvz']=_0x247f7c,_0x2eca['TxyNiy']={},_0x2eca['RCFEOs']=!![];}const _0x1d7ce8=_0x3d8aeb[0xf6f+0xb9a+-0x1b09],_0x34d94f=_0x225918+_0x1d7ce8,_0x3941a5=_0x2eca['TxyNiy'][_0x34d94f];if(!_0x3941a5){const _0x3ab8aa=function(_0x2cccd1){this['XiZYuD']=_0x2cccd1,this['YVosPI']=[-0x322+-0x3d7+0x6fa,-0x1729+0xf3d+0x7ec,0x1*-0x9a9+-0x24d1+-0x2*-0x173d],this['rKpaSf']=function(){return'newState';},this['krFzvy']='\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*',this['DozYlM']='[\x27|\x22].+[\x27|\x22];?\x20*}';};_0x3ab8aa['prototype']['hpSpMg']=function(){const _0x3c2604=new RegExp(this['krFzvy']+this['DozYlM']),_0x462585=_0x3c2604['test'](this['rKpaSf']['toString']())?--this['YVosPI'][0x191f+-0x26c6+-0x36a*-0x4]:--this['YVosPI'][0x165f+0xe*-0x1a8+0xd1];return this['TAobXv'](_0x462585);},_0x3ab8aa['prototype']['TAobXv']=function(_0x17f19c){if(!Boolean(~_0x17f19c))return _0x17f19c;return this['OvwVBW'](this['XiZYuD']);},_0x3ab8aa['prototype']['OvwVBW']=function(_0x3d7f26){for(let _0x5c8da9=0x239d+-0x14*-0x79+-0x1*0x2d11,_0x1d71b3=this['YVosPI']['length'];_0x5c8da9<_0x1d71b3;_0x5c8da9++){this['YVosPI']['push'](Math['round'](Math['random']())),_0x1d71b3=this['YVosPI']['length'];}return _0x3d7f26(this['YVosPI'][-0x1d9e+-0x619+-0x23b7*-0x1]);},new _0x3ab8aa(_0x2eca)['hpSpMg'](),_0x73ffd5=_0x2eca['UWkyvz'](_0x73ffd5),_0x2eca['TxyNiy'][_0x34d94f]=_0x73ffd5;}else _0x73ffd5=_0x3941a5;return _0x73ffd5;}const thisBootAgentIds=new Set(),MAX_PENDING_AGENTS=-0x1*-0x1922+-0x1e0b+0x6dd;function enforcePendingCap(){const _0x7c98a9=_0x2eca,_0x441d00=_0x2eca;if(pending[_0x7c98a9(0xf0)]<MAX_PENDING_AGENTS)return;const _0x129b7e=[...pending[_0x7c98a9(0x133)]()]['sort']((_0x450421,_0x467b92)=>_0x450421[-0x1a0c+-0x8*0x454+0x13d*0x31][_0x7c98a9(0x15a)]-_0x467b92[-0x3*-0x480+0x1f78+-0x2cf7][_0x7c98a9(0x15a)]),_0x38d74b=Math['floor'](MAX_PENDING_AGENTS*(0x17c3+-0x1904+0x141+0.9));let _0x56a0e8=pending[_0x441d00(0xf0)]-_0x38d74b;for(const [_0x2cf362]of _0x129b7e){if(_0x56a0e8<=-0x395*-0x8+0x285+-0x1f2d)break;pending['delete'](_0x2cf362),_0x56a0e8--;}console[_0x441d00(0x160)]('[async-age'+_0x7c98a9(0x11c)+_0x7c98a9(0x123)+_0x7c98a9(0x11b)+'p\x20'+MAX_PENDING_AGENTS+(_0x7c98a9(0x12e)+_0x441d00(0x121))+pending[_0x441d00(0xf0)]);}function loadFromDisk(){const _0x18affa=_0x2eca,_0xb08f9c=_0x2eca;try{const _0x107abb=_0x225918[_0x18affa(0x11f)+'nc'](ASYNC_AGENTS_STATE_FILE,_0x18affa(0xfc)),_0x4eb523=JSON['parse'](_0x107abb);if(!Array[_0xb08f9c(0x11d)](_0x4eb523))return;for(const _0x3ab8aa of _0x4eb523){typeof _0x3ab8aa?.['agentId']==='string'&&typeof _0x3ab8aa?.[_0xb08f9c(0x107)]==='string'&&pending[_0x18affa(0x146)](_0x3ab8aa[_0xb08f9c(0x12b)],_0x3ab8aa);}}catch{}}function saveToDisk(){const _0x30ebe0=_0x2eca,_0x2e8433=_0x2eca;try{_0x225918[_0x30ebe0(0x163)](dirname(ASYNC_AGENTS_STATE_FILE),{'recursive':!![]}),_0x225918[_0x2e8433(0x112)+_0x30ebe0(0x139)](ASYNC_AGENTS_STATE_FILE,JSON[_0x2e8433(0xf5)]([...pending['values']()],null,0xf6f+0xb9a+-0x1b07),_0x30ebe0(0xfc));}catch(_0x2cccd1){console[_0x2e8433(0x14e)](_0x30ebe0(0x10f)+_0x30ebe0(0xe9)+_0x2e8433(0x147)+_0x30ebe0(0x125),_0x2cccd1);}}export function registerPendingAgent(_0x3c2604){const _0x181a48=_0x2eca,_0x5817c5=_0x2eca,_0x462585=Date['now'](),_0x17f19c={'agentId':_0x3c2604[_0x181a48(0x12b)],'outputFile':_0x3c2604[_0x181a48(0x107)],'description':_0x3c2604[_0x181a48(0x12c)+'n'],'prompt':_0x3c2604[_0x5817c5(0x143)],'chatId':_0x3c2604[_0x181a48(0x11a)],'userId':_0x3c2604[_0x5817c5(0x157)],'startedAt':_0x462585,'lastCheckedAt':0x0,'giveUpAt':_0x3c2604[_0x181a48(0x134)]??_0x462585+MAX_AGENT_AGE_MS,'toolUseId':_0x3c2604[_0x5817c5(0x10d)],'sessionKey':_0x3c2604[_0x181a48(0x148)],'platform':_0x3c2604['platform'],'pid':_0x3c2604['pid']};enforcePendingCap(),pending[_0x5817c5(0x146)](_0x3c2604[_0x181a48(0x12b)],_0x17f19c),thisBootAgentIds[_0x5817c5(0x104)](_0x3c2604[_0x5817c5(0x12b)]),saveToDisk();}function decrementPendingCount(_0x3d7f26){const _0x1661cb=_0x2eca,_0x50e8f5=_0x2eca;if(!_0x3d7f26)return;try{const _0x5c8da9=getAllSessions(),_0x1d71b3=_0x5c8da9[_0x1661cb(0x145)](_0x3d7f26);if(!_0x1d71b3)return;_0x1d71b3['pendingBac'+_0x50e8f5(0x119)+'nt']=Math[_0x50e8f5(0x15b)](-0x322+-0x3d7+0x6f9,(_0x1d71b3['pendingBac'+_0x1661cb(0x119)+'nt']??-0x1729+0xf3d+0x7ec)-(0x1*-0x9a9+-0x24d1+-0x1*-0x2e7b));}catch(_0x43c61d){console['error'](_0x1661cb(0x10f)+'cher]\x20decr'+_0x50e8f5(0x110)+_0x50e8f5(0x140),_0x43c61d);}}export function listPendingAgents(){const _0x2ee822=_0x2eca;return[...pending[_0x2ee822(0x122)]()];}export async function deliverByAgentId(_0x1a84c0){const _0x172cfe=_0x2eca,_0x568227=_0x2eca,_0x5d9e6c=pending[_0x172cfe(0x145)](_0x1a84c0);if(!_0x5d9e6c)return _0x568227(0x10a);const _0x1ab216=await parseOutputFileStatus(_0x5d9e6c[_0x568227(0x107)]);if(_0x1ab216[_0x568227(0x13c)]==='completed')return claimDelivery(_0x1a84c0)&&(await deliverAsCompleted(_0x5d9e6c,_0x1ab216['output'],_0x1ab216['tokensUsed']),pending[_0x172cfe(0x15e)](_0x1a84c0),saveToDisk()),_0x568227(0x15d);if(_0x1ab216['state']==='failed')return claimDelivery(_0x1a84c0)&&(await deliverAsFailure(_0x5d9e6c,_0x568227(0x14e),_0x1ab216[_0x172cfe(0x14e)]),pending[_0x172cfe(0x15e)](_0x1a84c0),saveToDisk()),_0x172cfe(0x15d);return'pending';}async function reconcileOnStartup(){const _0x11c2c6=_0x2eca,_0x554fd2=_0x2eca;for(const _0x2a112b of[...pending[_0x11c2c6(0x122)]()]){try{if(isDelivered(_0x2a112b[_0x554fd2(0x12b)])){pending[_0x554fd2(0x15e)](_0x2a112b[_0x11c2c6(0x12b)]);continue;}const _0x5f003d=await parseOutputFileStatus(_0x2a112b[_0x554fd2(0x107)]);if(_0x5f003d['state']===_0x554fd2(0x102)&&claimDelivery(_0x2a112b[_0x11c2c6(0x12b)]))await deliverAsCompleted(_0x2a112b,_0x5f003d[_0x11c2c6(0x11e)],_0x5f003d['tokensUsed']),pending[_0x11c2c6(0x15e)](_0x2a112b['agentId']);else _0x5f003d['state']===_0x554fd2(0x120)&&claimDelivery(_0x2a112b[_0x554fd2(0x12b)])&&(await deliverAsFailure(_0x2a112b,_0x11c2c6(0x14e),_0x5f003d['error']),pending[_0x11c2c6(0x15e)](_0x2a112b[_0x554fd2(0x12b)]));}catch(_0x485ff5){console['error'](_0x554fd2(0x10f)+_0x554fd2(0xf2)+_0x11c2c6(0x10e)+'A\x20'+_0x2a112b[_0x11c2c6(0x12b)]+':',_0x485ff5);}}saveToDisk();let _0x2742f0;try{_0x2742f0=_0x225918[_0x11c2c6(0x159)+'c'](SUBAGENTS_DIR);}catch{return;}const _0x456af5=Date['now']();for(const _0x2f0cd4 of _0x2742f0){if(!_0x2f0cd4['endsWith'](_0x554fd2(0x118)))continue;const _0x14df4f=_0x2f0cd4['slice'](0x191f+-0x26c6+-0x48d*-0x3,-_0x554fd2(0x118)['length']);if(isDelivered(_0x14df4f))continue;if(pending['has'](_0x14df4f))continue;const _0x3d128d=resolve(SUBAGENTS_DIR,_0x2f0cd4);try{const _0xafacb2=_0x225918[_0x11c2c6(0x14d)](_0x3d128d);_0x456af5-_0xafacb2[_0x11c2c6(0x124)]>MAX_AGENT_AGE_MS&&cleanupAgentFiles(_0x14df4f);}catch{}}}export function startWatcher(){const _0x4767c2=_0x2eca,_0x3cec52=_0x2eca;if(started)return;started=!![],loadFromDisk(),void reconcileOnStartup()[_0x4767c2(0x115)](_0x1a0620=>console['error']('[async-wat'+_0x4767c2(0xf2)+_0x4767c2(0x151)+_0x4767c2(0x140),_0x1a0620)),pollTimer=setInterval(()=>{const _0x39aeb5=_0x3cec52,_0x3423f6=_0x4767c2;pollOnce()[_0x39aeb5(0x115)](_0x49a18b=>console[_0x3423f6(0x14e)]('[async-wat'+_0x3423f6(0x129)+_0x3423f6(0x13d)+_0x3423f6(0x165),_0x49a18b));},POLL_INTERVAL_MS),console['log']('⏳\x20Async-ag'+_0x3cec52(0x14f)+_0x3cec52(0xfb)+'('+pending[_0x3cec52(0xf0)]+'\x20pending,\x20'+POLL_INTERVAL_MS/(0x165f+0xe*-0x1a8+0x4b9)+(_0x3cec52(0xf6)+')'));}export function stopWatcher(){if(pollTimer)clearInterval(pollTimer);pollTimer=null,started=![];}export async function pollOnce(){const _0x56631a=_0x2eca,_0x1b0afd=_0x2eca,_0x42aaff=Date[_0x56631a(0x12a)](),_0x5c0c2f=[],_0x305e0f=getMissingFileFailureMs(),_0x385272=(_0x44eb3,_0x28cac1)=>{const _0x133a15=_0x56631a,_0xb28cc8=_0x56631a;if(!_0x28cac1[_0x133a15(0x117)+'nd'])return;if(!_0x5c0c2f[_0xb28cc8(0x144)](_0x44eb3[_0xb28cc8(0x12b)]))_0x5c0c2f[_0xb28cc8(0x137)](_0x44eb3[_0x133a15(0x12b)]);console[_0x133a15(0x160)]('[async-wat'+_0x133a15(0x149)+_0x133a15(0x13f)+_0xb28cc8(0xf8)+_0x44eb3[_0x133a15(0x12b)]+(_0x133a15(0x136)+'y\x20target\x20')+('chat\x20'+String(_0x44eb3[_0xb28cc8(0x11a)])+('\x20not\x20found'+_0xb28cc8(0x13b)+_0x133a15(0x15f)+_0x133a15(0x114)+'ry')));};for(const _0x27b1f9 of pending['values']()){_0x27b1f9[_0x56631a(0x162)+'dAt']=_0x42aaff;if(_0x42aaff>=_0x27b1f9['giveUpAt']){if(claimDelivery(_0x27b1f9['agentId'])){const _0x4316d7=await deliverAsFailure(_0x27b1f9,_0x1b0afd(0x111),_0x1b0afd(0x154)+_0x1b0afd(0x14a)+_0x56631a(0xff)+_0x1b0afd(0x152));_0x385272(_0x27b1f9,_0x4316d7);}_0x5c0c2f[_0x1b0afd(0x137)](_0x27b1f9[_0x1b0afd(0x12b)]);continue;}const _0x5c319a=await parseOutputFileStatus(_0x27b1f9[_0x1b0afd(0x107)]);if(_0x5c319a[_0x1b0afd(0x13c)]===_0x56631a(0x102)){if(claimDelivery(_0x27b1f9[_0x56631a(0x12b)])){const _0x548a11=await deliverAsCompleted(_0x27b1f9,_0x5c319a[_0x56631a(0x11e)],_0x5c319a['tokensUsed']);_0x385272(_0x27b1f9,_0x548a11);}_0x5c0c2f['push'](_0x27b1f9[_0x1b0afd(0x12b)]);}else{if(_0x5c319a['state']===_0x1b0afd(0x120)){if(claimDelivery(_0x27b1f9[_0x56631a(0x12b)])){const _0x3c84ff=await deliverAsFailure(_0x27b1f9,_0x1b0afd(0x14e),_0x5c319a['error']);_0x385272(_0x27b1f9,_0x3c84ff);}_0x5c0c2f['push'](_0x27b1f9[_0x1b0afd(0x12b)]);}else{if(_0x5c319a[_0x56631a(0x13c)]===_0x1b0afd(0x158)&&_0x42aaff-_0x27b1f9['startedAt']>_0x305e0f){if(claimDelivery(_0x27b1f9[_0x1b0afd(0x12b)])){const _0x45a901=await deliverAsFailure(_0x27b1f9,'error',_0x1b0afd(0xf4)+'\x20subproces'+'s\x20never\x20wr'+'ote\x20its\x20ou'+_0x56631a(0x10b)+'('+Math[_0x56631a(0x155)]((_0x42aaff-_0x27b1f9[_0x1b0afd(0x15a)])/(0x1ab5b+-0xd*-0x8ba+-0x1*0x1326d))+(_0x56631a(0x12f)+_0x1b0afd(0x132)+'ly\x20crashed'+_0x56631a(0x108)+_0x56631a(0x13a)+_0x1b0afd(0x141)+_0x56631a(0xf7)+'moved\x20exte'+_0x56631a(0x142)));_0x385272(_0x27b1f9,_0x45a901);}_0x5c0c2f['push'](_0x27b1f9[_0x1b0afd(0x12b)]);}}}}if(_0x5c0c2f[_0x56631a(0x166)]>-0x1d9e+-0x619+-0x23b7*-0x1){for(const _0x115154 of _0x5c0c2f)pending[_0x56631a(0x15e)](_0x115154);saveToDisk();}}async function deliverAsCompleted(_0x19e6c9,_0x157811,_0x47f67f){const _0x556809=_0x2eca,_0x3483ef=_0x2eca,{deliverSubAgentResult:_0x226b61}=await import(_0x556809(0x131)+_0x556809(0x153)+'js'),_0x5214ee={'id':_0x19e6c9[_0x556809(0x12b)],'name':_0x19e6c9[_0x556809(0x12c)+'n'],'status':'completed','startedAt':_0x19e6c9[_0x556809(0x15a)],'source':'cron','depth':0x0,'parentChatId':_0x19e6c9[_0x556809(0x11a)],'platform':_0x19e6c9['platform']},_0x643d14={'id':_0x19e6c9[_0x556809(0x12b)],'name':_0x19e6c9['descriptio'+'n'],'status':_0x3483ef(0x102),'output':_0x157811,'tokensUsed':_0x47f67f??{'input':0x0,'output':0x0},'duration':Date[_0x556809(0x12a)]()-_0x19e6c9[_0x556809(0x15a)]};let _0x38b7eb=![];try{const _0x3e80bb=await _0x226b61(_0x5214ee,_0x643d14);_0x38b7eb=!!_0x3e80bb?.[_0x556809(0x117)+'nd'];}catch(_0xa1c4d2){console['error'](_0x3483ef(0x10f)+_0x3483ef(0x109)+_0x3483ef(0x13e)+'d\x20for\x20'+_0x19e6c9[_0x556809(0x12b)]+':',_0xa1c4d2),_0x38b7eb=isChatNotFoundError(_0xa1c4d2);}return decrementPendingCount(_0x19e6c9[_0x3483ef(0x148)]),{'chatNotFound':_0x38b7eb};}async function deliverAsFailure(_0xfd2e66,_0x111349,_0x4068b2){const _0x676e59=_0x2eca,_0x160c84=_0x2eca,{deliverSubAgentResult:_0x53f53f}=await import(_0x676e59(0x131)+_0x676e59(0x153)+'js'),_0x40d436={'id':_0xfd2e66[_0x676e59(0x12b)],'name':_0xfd2e66[_0x676e59(0x12c)+'n'],'status':_0x111349,'startedAt':_0xfd2e66[_0x676e59(0x15a)],'source':_0x160c84(0x100),'depth':0x0,'parentChatId':_0xfd2e66[_0x676e59(0x11a)],'platform':_0xfd2e66['platform']},_0x1595ad={'id':_0xfd2e66[_0x160c84(0x12b)],'name':_0xfd2e66[_0x676e59(0x12c)+'n'],'status':_0x111349,'output':'','tokensUsed':{'input':0x0,'output':0x0},'duration':Date[_0x160c84(0x12a)]()-_0xfd2e66['startedAt'],'error':_0x4068b2};let _0x21998e=![];try{const _0x1d2ceb=await _0x53f53f(_0x40d436,_0x1595ad);_0x21998e=!!_0x1d2ceb?.[_0x160c84(0x117)+'nd'];}catch(_0x23eb88){console[_0x160c84(0x14e)](_0x676e59(0x10f)+_0x160c84(0xe9)+_0x160c84(0xec)+_0x676e59(0x106)+_0x160c84(0xee)+_0xfd2e66[_0x160c84(0x12b)]+':',_0x23eb88),_0x21998e=isChatNotFoundError(_0x23eb88);}return decrementPendingCount(_0xfd2e66[_0x676e59(0x148)]),{'chatNotFound':_0x21998e};}function resolveKillTarget(_0x2f67ae){const _0x1f2423=_0x2eca;return process['platform']!==_0x1f2423(0x103)?-_0x2f67ae:_0x2f67ae;}export function killSessionDetachedAgents(_0xee4937,_0x3c3551=_0x21da8d=>{const _0x572935=_0x2eca,_0x1a5504=_0x2eca;try{process[_0x572935(0x128)](_0x21da8d,_0x572935(0xed));}catch{}}){const _0x3c22dd=_0x2eca,_0x2a4a04=_0x2eca,_0x4a2f99=_0xee4937[_0x3c22dd(0x148)]??null;if(_0x4a2f99===null)return;for(const _0x3edc32 of pending[_0x3c22dd(0x122)]()){if(_0x3edc32['sessionKey']!==_0x4a2f99)continue;if(typeof _0x3edc32[_0x3c22dd(0x15c)]!==_0x3c22dd(0xea))continue;if(!thisBootAgentIds['has'](_0x3edc32['agentId'])){console[_0x2a4a04(0x116)](_0x2a4a04(0x10f)+'cher]\x20skip'+'ping\x20kill\x20'+'for\x20disk-l'+_0x3c22dd(0x113)+'t\x20'+_0x3edc32[_0x2a4a04(0x12b)]+'\x20'+(_0x2a4a04(0x10c)+_0x3edc32[_0x3c22dd(0x15c)]+(_0x2a4a04(0x167)+'\x20safely\x20at'+'tribute\x20pi'+_0x2a4a04(0x101)+_0x3c22dd(0xfe))));continue;}try{_0x3c3551(resolveKillTarget(_0x3edc32[_0x3c22dd(0x15c)]));}catch{}}}function _0x4862(){const _0x59ed5d=['BgWGBM90ihjLDa','y2f0y2G','Bg9N','y2HHDe5VDezVDq','lMPZB25S','A2DYB3vUzenVDq','y2HHDeLK','BwfWigHPDcbJyq','BNqTD2f0y2HLCG','AxnbCNjHEq','B3v0Chv0','CMvHzezPBgvtEq','zMfPBgvK','Dg8G','DMfSDwvZ','xsbWzw5KAw5Nia','BxrPBwvnCW','Axn0ihn0yxrLoG','nZaXodqXmePVEuznrG','DgvZDa','A2LSBa','y2HLCL0GCg9SBa','BM93','ywDLBNrjza','zgvZy3jPChrPBW','mZKZmdb1tujYqve','lcbLDMLJDgvKia','BsbHzNrLCIbZDa','mJqXnZK1ng1tChzWzq','lI9ZDwjHz2vUDa','yxj0ks4GtgLRzq','zw50CMLLCW','z2L2zvvWqxq','quLmvvjfx01t','iokaLcbKzwXPDMvY','ChvZAa','mta0mtu4nerlv2rXqW','Ew5J','AxrPywXPEMLUzW','icHPBNzHBgLKlW','C3rHDgu','ign5y2XLigzHAq','DMvYEsbMywLSzq','zg9UAw5NigfNzq','zwq6','lcbVCIb0AguGzG','CM5HBgX5lG','ChjVBxb0','Aw5JBhvKzxm','z2v0','C2v0','zwqGDg8GCgvYCW','C2vZC2LVBKTLEq','y2HLCL0GywjHBG','Bg9Uz2vYihrOyq','AxngAw5PDgu','y29UC3rYDwn0BW','C3rHDfn5BMm','zxjYB3i','zw50ihDHDgnOzq','mtm1mJC2ntzOy3LRzvG','BMnPBguGzMfPBa','DMLUzYb1Ca','lwrLBgL2zxj5lG','qwDLBNqGCMfUia','CM91BMq','su5hx0zjtevFrG','DxnLCKLK','BwLZC2LUzW','CMvHzgrPCLn5BG','C3rHCNrLzef0','Bwf4','CgLK','zgvSAxzLCMvK','zgvSzxrL','C3rHBguPoYb3Aq','D2fYBG','quXwsu5FtuLtuW','BgfZDenOzwnRzq','BwTKAxjtEw5J','kcGOlISPkYKRkq','BgvKoG','BgvUz3rO','ksdIGjqGy2fUBM90','y2HLCL0GzMfPBa','BNvTyMvY','Dg9tDhjPBMC','DxjLigrLBgL2zq','u0Lhvevstq','zM9Yia','yxbWBhK','C2L6zq','y2XLyxi','y2HLCL0GCMvJBW','B2jQzwn0','rgLZCgf0y2HLza','C3rYAw5NAwz5','CYbPBNrLCNzHBa','AwXLihDHCYbYzq','BNqG','ndu3mJm2mhn1CwXfzq','ndi2ndK5r2TrCgHr','CIbZDgfYDgvKia','DxrMltG','mtmXnuX0DKvjtq','C3rHCNq','BIaXmMGG4OcuigDP','y3jVBG','zcbHzNrLCIbYzq','y29TCgXLDgvK','D2LUmZi','ywrK','C2vHCMnO','CNKGzMfPBgvKia','B3v0Chv0rMLSzq','igjLzM9YzsbPBG','y2HLCL0GzgvSAq','Dw5RBM93BG','Dhb1DcbMAwXLia','khbPzd0','Dg9VBfvZzuLK','BMnPBguGCgfZCW','w2fZEw5JlxDHDa','zw1LBNqGzMfPBa','DgLTzw91Da','D3jPDgvgAwXLuW','B2fKzwqGywDLBG'];_0x4862=function(){return _0x59ed5d;};return _0x4862();}export function cancelPendingForSession(_0x10d14b){const _0x2662e4=_0x2eca,_0x56bb65=_0x2eca;let _0x2934bd=![];for(const [_0x1074fb,_0x48af5a]of pending[_0x2662e4(0x133)]()){_0x48af5a[_0x56bb65(0x148)]===_0x10d14b&&(markDelivered(_0x48af5a[_0x2662e4(0x12b)]),pending[_0x2662e4(0x15e)](_0x1074fb),_0x2934bd=!![]);}_0x2934bd&&saveToDisk();}export function __resetForTest(){const _0x509905=_0x2eca;pending[_0x509905(0xf1)](),thisBootAgentIds['clear']();if(pollTimer)clearInterval(pollTimer);pollTimer=null,started=![];}