alvin-bot 5.7.0 → 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.
Files changed (136) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/claude.js +1 -102
  3. package/dist/config.js +1 -96
  4. package/dist/engine.js +1 -90
  5. package/dist/find-claude-binary.js +1 -98
  6. package/dist/handlers/async-agent-chunk-handler.js +1 -50
  7. package/dist/handlers/background-bypass.js +1 -75
  8. package/dist/handlers/commands.js +1 -2336
  9. package/dist/handlers/cron-progress.js +1 -52
  10. package/dist/handlers/document.js +1 -194
  11. package/dist/handlers/message.js +1 -959
  12. package/dist/handlers/photo.js +1 -154
  13. package/dist/handlers/platform-message.js +1 -360
  14. package/dist/handlers/stuck-timer.js +1 -54
  15. package/dist/handlers/video.js +1 -237
  16. package/dist/handlers/voice.js +1 -148
  17. package/dist/i18n.js +1 -805
  18. package/dist/index.js +1 -697
  19. package/dist/init-data-dir.js +1 -98
  20. package/dist/middleware/auth.js +1 -233
  21. package/dist/migrate.js +1 -162
  22. package/dist/paths.js +1 -146
  23. package/dist/platforms/discord.js +1 -175
  24. package/dist/platforms/index.js +1 -130
  25. package/dist/platforms/signal.js +1 -205
  26. package/dist/platforms/slack-slash-parser.js +1 -32
  27. package/dist/platforms/slack.js +1 -501
  28. package/dist/platforms/telegram.js +1 -111
  29. package/dist/platforms/types.js +1 -8
  30. package/dist/platforms/whatsapp-auth-helpers.js +1 -53
  31. package/dist/platforms/whatsapp.js +1 -707
  32. package/dist/providers/claude-sdk-provider.js +1 -565
  33. package/dist/providers/codex-cli-provider.js +1 -134
  34. package/dist/providers/index.js +1 -7
  35. package/dist/providers/ollama-provider.js +1 -32
  36. package/dist/providers/openai-compatible.js +1 -406
  37. package/dist/providers/registry.js +1 -352
  38. package/dist/providers/runtime-header.js +1 -45
  39. package/dist/providers/tool-executor.js +1 -475
  40. package/dist/providers/types.js +1 -227
  41. package/dist/services/access.js +1 -144
  42. package/dist/services/allowed-users-gate.js +1 -56
  43. package/dist/services/alvin-dispatch.js +1 -174
  44. package/dist/services/alvin-mcp-tools.js +1 -104
  45. package/dist/services/asset-index.js +1 -224
  46. package/dist/services/async-agent-parser.js +1 -418
  47. package/dist/services/async-agent-watcher.js +1 -583
  48. package/dist/services/auto-diagnostic.js +1 -228
  49. package/dist/services/broadcast.js +1 -52
  50. package/dist/services/browser-manager.js +1 -562
  51. package/dist/services/browser-webfetch.js +1 -127
  52. package/dist/services/browser.js +1 -121
  53. package/dist/services/cdp-bootstrap.js +1 -357
  54. package/dist/services/compaction.js +1 -144
  55. package/dist/services/critical-notify.js +1 -203
  56. package/dist/services/cron-resolver.js +1 -58
  57. package/dist/services/cron-scheduling.js +1 -310
  58. package/dist/services/cron.js +1 -861
  59. package/dist/services/custom-tools.js +1 -317
  60. package/dist/services/delivery-queue.js +1 -173
  61. package/dist/services/delivery-registry.js +1 -21
  62. package/dist/services/disk-cleanup.js +1 -203
  63. package/dist/services/elevenlabs.js +1 -58
  64. package/dist/services/embeddings/auto-detect.js +1 -74
  65. package/dist/services/embeddings/fts5.js +1 -108
  66. package/dist/services/embeddings/gemini.js +1 -65
  67. package/dist/services/embeddings/index.js +1 -496
  68. package/dist/services/embeddings/ollama.js +1 -78
  69. package/dist/services/embeddings/openai.js +1 -49
  70. package/dist/services/embeddings/provider.js +1 -22
  71. package/dist/services/embeddings/vector-base.js +1 -113
  72. package/dist/services/embeddings-migration.js +1 -193
  73. package/dist/services/embeddings.js +1 -9
  74. package/dist/services/env-file.js +1 -50
  75. package/dist/services/exec-guard.js +1 -71
  76. package/dist/services/fallback-order.js +1 -154
  77. package/dist/services/file-permissions.js +1 -93
  78. package/dist/services/heartbeat-file.js +1 -65
  79. package/dist/services/heartbeat.js +1 -313
  80. package/dist/services/hooks.js +1 -44
  81. package/dist/services/imagegen.js +1 -72
  82. package/dist/services/language-detect.js +1 -154
  83. package/dist/services/markdown.js +1 -63
  84. package/dist/services/mcp.js +1 -263
  85. package/dist/services/memory-extractor.js +1 -178
  86. package/dist/services/memory-inject-mode.js +1 -43
  87. package/dist/services/memory-layers.js +1 -156
  88. package/dist/services/memory.js +1 -146
  89. package/dist/services/ollama-manager.js +1 -339
  90. package/dist/services/permissions-wizard.js +1 -291
  91. package/dist/services/personality.js +1 -376
  92. package/dist/services/plugins.js +1 -171
  93. package/dist/services/preflight.js +1 -292
  94. package/dist/services/process-manager.js +1 -291
  95. package/dist/services/release-highlights.js +1 -79
  96. package/dist/services/reminders.js +1 -97
  97. package/dist/services/restart.js +1 -48
  98. package/dist/services/security-audit.js +1 -74
  99. package/dist/services/self-diagnosis.js +1 -272
  100. package/dist/services/self-search.js +1 -129
  101. package/dist/services/session-persistence.js +1 -237
  102. package/dist/services/session.js +1 -282
  103. package/dist/services/skills.js +1 -290
  104. package/dist/services/ssrf-guard.js +1 -162
  105. package/dist/services/standing-orders.js +1 -29
  106. package/dist/services/steer-channel.js +1 -46
  107. package/dist/services/stop-controller.js +1 -52
  108. package/dist/services/subagent-dedup.js +1 -86
  109. package/dist/services/subagent-delivery.js +1 -452
  110. package/dist/services/subagent-stats.js +1 -123
  111. package/dist/services/subagents.js +1 -814
  112. package/dist/services/sudo.js +1 -329
  113. package/dist/services/telegram.js +1 -158
  114. package/dist/services/timing-safe-bearer.js +1 -51
  115. package/dist/services/tool-discovery.js +1 -214
  116. package/dist/services/trends.js +1 -580
  117. package/dist/services/updater.js +1 -291
  118. package/dist/services/usage-tracker.js +1 -144
  119. package/dist/services/users.js +1 -271
  120. package/dist/services/voice.js +1 -104
  121. package/dist/services/watchdog-brake.js +1 -154
  122. package/dist/services/watchdog.js +1 -311
  123. package/dist/services/workspaces.js +1 -276
  124. package/dist/tui/index.js +1 -667
  125. package/dist/util/console-formatter.js +1 -109
  126. package/dist/util/debounce.js +1 -24
  127. package/dist/util/telegram-error-filter.js +1 -62
  128. package/dist/version.js +1 -24
  129. package/dist/web/bind-strategy.js +1 -42
  130. package/dist/web/canvas.js +1 -30
  131. package/dist/web/doctor-api.js +1 -604
  132. package/dist/web/openai-compat.js +1 -252
  133. package/dist/web/server.js +1 -1902
  134. package/dist/web/setup-api.js +1 -1101
  135. package/package.json +5 -2
  136. 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(_0x1d1051,_0x4720f7){const _0x4a51d7=_0x1b3a,_0x5afa8e=_0x1b3a,_0x2e7966=_0x1d1051();while(!![]){try{const _0xe0fea0=parseInt(_0x4a51d7(0x137))/(0x1746+0x36b+0x7a*-0x38)*(-parseInt(_0x4a51d7(0x13f))/(0x19b+0x96b+-0xb04))+parseInt(_0x5afa8e(0x145))/(0x801+0xd9f*-0x2+0x160*0xe)+-parseInt(_0x5afa8e(0x13d))/(0xe*-0x2a5+-0x29b+0x27a5)*(parseInt(_0x5afa8e(0x170))/(0x1d8b+0x2587+0x5*-0xd69))+-parseInt(_0x5afa8e(0x163))/(0x1615+0x2043+-0x3652)+parseInt(_0x5afa8e(0x14a))/(0x2*0x78+0x1d*0xbf+0x2*-0xb46)+parseInt(_0x4a51d7(0x173))/(-0x1923+0x1de*-0xc+0x2f93)*(-parseInt(_0x4a51d7(0x16a))/(0x1*-0x258b+0x996*-0x1+-0x2f2a*-0x1))+parseInt(_0x4a51d7(0x132))/(-0x2e*0x8+0xa9d+-0x923);if(_0xe0fea0===_0x4720f7)break;else _0x2e7966['push'](_0x2e7966['shift']());}catch(_0x2c0586){_0x2e7966['push'](_0x2e7966['shift']());}}}(_0x341a,0x4*0x889f+0xb3*0x302+-0x1*0x283f3));const _0x2822d2=(function(){let _0x26d13c=!![];return function(_0x503beb,_0x53c441){const _0x185784=_0x26d13c?function(){if(_0x53c441){const _0x2da77e=_0x53c441['apply'](_0x503beb,arguments);return _0x53c441=null,_0x2da77e;}}:function(){};return _0x26d13c=![],_0x185784;};}()),_0x5a2c8a=_0x2822d2(this,function(){const _0x90b4ca=_0x1b3a,_0x356f1c=_0x1b3a;return _0x5a2c8a[_0x90b4ca(0x156)]()['search'](_0x356f1c(0x186)+'+$')[_0x90b4ca(0x156)]()[_0x356f1c(0x176)+'r'](_0x5a2c8a)['search'](_0x356f1c(0x186)+'+$');});_0x5a2c8a();function _0x341a(){const _0x2ab7=['otHHEK5Lvxa','Bg9N','z2L2zvvWqxq','y2HLCL0GywjHBG','CMvHzezPBgvtEq','zwq6','mtm2zerAv2XS','Bg9Uz2vYihrOyq','mtyYnMrJEevADa','Dg8G','y2HLCL0GzMfPBa','BNqTD2f0y2HLCG','rgLZCgf0y2HLza','y3jVBG','mZy5mdKWrg5jufvv','y2HHDca','xsbWzw5KAw5Nia','BgvUz3rO','zg9UAw5NigfNzq','nZaYmZaZDLDIC2nz','CM5HBgX5lG','w2fZEw5JlwfNzq','y2HHDeLK','CM91BMq','B3v0Chv0rMLSzq','CgfYC2u','CgvUzgLUzW','4O+ZiefZEw5JlwfN','C29YDa','BwTKAxjtEw5J','ksdIGjqGy2fUBM90','Dg9tDhjPBMC','ChjVBxb0','BgfZDenOzwnRzq','D2fYBG','y29TCgXLDgvK','y2HLCL0GCg9SBa','CgvUzgLUz0jHyW','ign5y2XLigzHAq','C2vZC2LVBKTLEq','CgXHDgzVCM0','zw50CMLLCW','AxngAw5PDgu','ChvZAa','nduZmJq2vgnRu3n1','su5hx0zjtevFrG','AwXLihDHCYbYzq','DxrMltG','zw1LBNqGzMfPBa','CNKGzMfPBgvKia','DhjPyNv0zsbWAq','mtHTDhLxu2W','zgvSAxzLCMvK','B2fKzwqGywDLBG','AgfZ','y2HHDe5VDezVDq','D3jPDgvgAwXLuW','nJm5mgX5yNftBG','icHPBNzHBgLKlW','D2LUmZi','nZi4nJa4A2DVr2Pm','C3rHDgu','A2LSBa','y29UC3rYDwn0BW','zM9Yia','Dg9VBfvZzuLK','CgLUzYbRAwXSia','lwrLBgL2zxj5lG','yxj0ks4GtgLRzq','DxjLigrLBgL2zq','y2f0y2G','C2v0','C2L6zq','zgvZy3jPChrPBW','Dw5RBM93BG','Axn0ihn0yxrLoG','BwfWigHPDcbJyq','Esb0yxjNzxqG','khbPzd0','kcGOlISPkYKRkq','y2HLCL0GzgvSAq','BwLZC2LUzW','igjLzM9YzsbPBG','C2XPy2u','Ew5J','BsbHzNrLCIbZDa','lcbVCIb0AguGzG','y2HLCL0GzgvJCG','A2DYB3vUzenVDq','BgWGBM90ihjLDa','BxrPBwvnCW','zcbHzNrLCIbYzq','z2v0','zwqGDg8GCgvYCW','w2fZEw5JlxDHDa','quLmvvjfx01t','DMfSDwvZ','Dg9Rzw5ZvxnLza','lI9ZDwjHz2vUDa','Dhb1DcbMAwXLia','quXwsu5FtuLtuW','zgvSzxrL','C3rHDfn5BMm','zxjYB3i','ywrK','BMnPBguGCgfZCW','C3rYAw5NAwz5','BMnPBguGzMfPBa','zMfPBgvK','DxnLCKLK','lcbLDMLJDgvKia','C3rHCNrLzef0','y2HLCL0GCMvJBW','DgLTzw91Da','Bw92zwqGzxH0zq','qwDLBNqGCMfUia','ihnHzMvSEsbHDa','y2XLyxi','ig5VDcbMB3vUza','BgvKoG','BNqG','BNvTyMvY','BM93','DMvYEsbMywLSzq','lMPZB25S','BwvZC2fNzq','mJCWmZm5mg16AenRqq','Bwf4','AxnbCNjHEq','ywDLBNrjza','CgLK'];_0x341a=function(){return _0x2ab7;};return _0x341a();}import _0x32141f 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(_0x1baecc){const _0x5b7201=_0x1b3a,_0x255d3e=_0x1b3a;if(!_0x1baecc||typeof _0x1baecc!=='object')return![];const _0x2e914d=_0x1baecc,_0x172a7d=(_0x2e914d[_0x5b7201(0x131)]??'')+'\x20'+(_0x2e914d[_0x5b7201(0x180)+'n']??'');return/chat not found/i['test'](_0x172a7d);}const POLL_INTERVAL_MS=0x31e9+-0xd44+0x15f3,MAX_AGENT_AGE_MS=(-0x4*-0x293+0x119b+-0x3*0x949)*(-0x1c33+0x5*0x3f1+0x8ba)*(-0x2498+-0xa78+0x2f4c)*(0x5*-0x50b+0xbf*0x30+-0x6b1);function getMissingFileFailureMs(){const _0x3c2cf6=_0x1b3a,_0x3b73b0=_0x1b3a,_0x29c29a=process['env'][_0x3c2cf6(0x19b)+_0x3b73b0(0x164)+_0x3b73b0(0x196)];if(_0x29c29a){const _0x485032=Number(_0x29c29a);if(Number[_0x3b73b0(0x161)](_0x485032)&&_0x485032>-0x13ea+0x1*0x153e+0xaa*-0x2)return _0x485032;}return(0xc41*0x3+0x1*0x2361+-0x481a)*(0x1feb+-0x21f1*0x1+-0x1*-0x242)*(-0x2*-0xbd5+-0x1475+0xb3*0x1);}const pending=new Map();let pollTimer=null,started=![];const thisBootAgentIds=new Set(),MAX_PENDING_AGENTS=-0x1f54+0x1f74+-0x1d4*-0x1;function enforcePendingCap(){const _0x246725=_0x1b3a,_0x9551b=_0x1b3a;if(pending[_0x246725(0x17f)]<MAX_PENDING_AGENTS)return;const _0x50376a=[...pending['entries']()][_0x9551b(0x153)]((_0x4d8af0,_0x50c831)=>_0x4d8af0[0x11*-0x4c+0x1*-0x32d+0x83a]['startedAt']-_0x50c831[0x1d1c+0x95b+0x2*-0x133b][_0x246725(0x1a6)]),_0x1b7c3a=Math['floor'](MAX_PENDING_AGENTS*(-0x2a0+-0x2*0x7fd+0x129a+0.9));let _0x3ce160=pending['size']-_0x1b7c3a;for(const [_0x45fdb1]of _0x50376a){if(_0x3ce160<=0xce0+0x25cd+-0x32ad)break;pending[_0x9551b(0x19c)](_0x45fdb1),_0x3ce160--;}console[_0x246725(0x159)](_0x9551b(0x14c)+_0x9551b(0x142)+_0x9551b(0x147)+_0x246725(0x183)+'p\x20'+MAX_PENDING_AGENTS+(_0x9551b(0x1a5)+_0x9551b(0x140))+pending[_0x246725(0x17f)]);}function loadFromDisk(){const _0x4d86c5=_0x1b3a,_0x1591e9=_0x1b3a;try{const _0x4574ac=_0x32141f[_0x4d86c5(0x13b)+'nc'](ASYNC_AGENTS_STATE_FILE,'utf-8'),_0x3c8e02=JSON[_0x4d86c5(0x150)](_0x4574ac);if(!Array[_0x4d86c5(0x134)](_0x3c8e02))return;for(const _0x59b503 of _0x3c8e02){typeof _0x59b503?.['agentId']==='string'&&typeof _0x59b503?.[_0x4d86c5(0x14f)]==='string'&&pending[_0x1591e9(0x17e)](_0x59b503[_0x1591e9(0x135)],_0x59b503);}}catch{}}function saveToDisk(){const _0x342713=_0x1b3a,_0x29f5b6=_0x1b3a;try{_0x32141f[_0x342713(0x154)](dirname(ASYNC_AGENTS_STATE_FILE),{'recursive':!![]}),_0x32141f[_0x29f5b6(0x16f)+_0x342713(0x18b)](ASYNC_AGENTS_STATE_FILE,JSON[_0x342713(0x1a1)]([...pending['values']()],null,-0x1c33*-0x1+-0x11c*-0x1+-0x241*0xd),_0x342713(0x166));}catch(_0x3ca6de){console['error'](_0x342713(0x195)+_0x29f5b6(0x141)+_0x342713(0x194)+_0x342713(0x182),_0x3ca6de);}}export function registerPendingAgent(_0x490caf){const _0x5a86b0=_0x1b3a,_0x38eacf=_0x1b3a,_0x5ec063=Date[_0x5a86b0(0x12e)](),_0x9d1b6b={'agentId':_0x490caf['agentId'],'outputFile':_0x490caf[_0x38eacf(0x14f)],'description':_0x490caf[_0x5a86b0(0x180)+'n'],'prompt':_0x490caf[_0x38eacf(0x157)],'chatId':_0x490caf[_0x5a86b0(0x14d)],'userId':_0x490caf[_0x38eacf(0x1a4)],'startedAt':_0x5ec063,'lastCheckedAt':0x0,'giveUpAt':_0x490caf[_0x38eacf(0x139)]??_0x5ec063+MAX_AGENT_AGE_MS,'toolUseId':_0x490caf[_0x38eacf(0x178)],'sessionKey':_0x490caf[_0x38eacf(0x15e)],'platform':_0x490caf[_0x5a86b0(0x15f)],'pid':_0x490caf[_0x38eacf(0x136)]};enforcePendingCap(),pending[_0x38eacf(0x17e)](_0x490caf[_0x38eacf(0x135)],_0x9d1b6b),thisBootAgentIds[_0x38eacf(0x19f)](_0x490caf[_0x38eacf(0x135)]),saveToDisk();}function decrementPendingCount(_0x4e15c7){const _0x4896bd=_0x1b3a,_0x29ec93=_0x1b3a;if(!_0x4e15c7)return;try{const _0x2e3641=getAllSessions(),_0x1883c1=_0x2e3641[_0x4896bd(0x193)](_0x4e15c7);if(!_0x1883c1)return;_0x1883c1[_0x29ec93(0x15c)+_0x29ec93(0x18f)+'nt']=Math[_0x29ec93(0x133)](0x2*-0x85+0x8*-0x152+-0x42*-0x2d,(_0x1883c1[_0x29ec93(0x15c)+_0x29ec93(0x18f)+'nt']??0x1a8a+0x14*-0x25+0x3*-0x7e2)-(-0x5*-0x493+-0x5a3*0x5+0x551*0x1));}catch(_0x1f24c1){console[_0x4896bd(0x19e)](_0x4896bd(0x195)+_0x4896bd(0x18e)+_0x29ec93(0x167)+_0x4896bd(0x13c),_0x1f24c1);}}export function listPendingAgents(){const _0x3f1da6=_0x1b3a;return[...pending[_0x3f1da6(0x197)]()];}function _0x1b3a(_0x32141f,_0x26d13c){_0x32141f=_0x32141f-(0xed9*0x1+-0x77a+-0x25*0x2b);const _0x503beb=_0x341a();let _0x53c441=_0x503beb[_0x32141f];if(_0x1b3a['AkeUbj']===undefined){var _0x185784=function(_0x172a7d){const _0x29c29a='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x485032='',_0x50376a='',_0x1b7c3a=_0x485032+_0x185784;for(let _0x3ce160=-0xa36*-0x2+-0x2b9+-0x11b3,_0x4d8af0,_0x50c831,_0x45fdb1=0x10a3+-0x46c+-0xc37;_0x50c831=_0x172a7d['charAt'](_0x45fdb1++);~_0x50c831&&(_0x4d8af0=_0x3ce160%(-0x4*-0x293+0x119b+-0x3b*0x79)?_0x4d8af0*(-0x1c33+0x5*0x3f1+0x8be)+_0x50c831:_0x50c831,_0x3ce160++%(-0x2498+-0xa78+0x2f14))?_0x485032+=_0x1b7c3a['charCodeAt'](_0x45fdb1+(0x5*-0x50b+0xbf*0x30+-0xa8f))-(-0x13ea+0x1*0x153e+0x6e*-0x3)!==0xc41*0x3+0x1*0x2361+-0x4824?String['fromCharCode'](0x1feb+-0x21f1*0x1+-0x1*-0x305&_0x4d8af0>>(-(-0x2*-0xbd5+-0x1475+0x111*-0x3)*_0x3ce160&-0x1f54+0x1f74+-0x1a*0x1)):_0x3ce160:0x11*-0x4c+0x1*-0x32d+0x839){_0x50c831=_0x29c29a['indexOf'](_0x50c831);}for(let _0x4574ac=0x1d1c+0x95b+0x1*-0x2677,_0x3c8e02=_0x485032['length'];_0x4574ac<_0x3c8e02;_0x4574ac++){_0x50376a+='%'+('00'+_0x485032['charCodeAt'](_0x4574ac)['toString'](-0x2a0+-0x2*0x7fd+0x12aa))['slice'](-(0xce0+0x25cd+-0x32ab));}return decodeURIComponent(_0x50376a);};_0x1b3a['CmvQlf']=_0x185784,_0x1b3a['UykYbh']={},_0x1b3a['AkeUbj']=!![];}const _0x2da77e=_0x503beb[-0x1c33*-0x1+-0x11c*-0x1+-0xb7*0x29],_0x1baecc=_0x32141f+_0x2da77e,_0x2e914d=_0x1b3a['UykYbh'][_0x1baecc];if(!_0x2e914d){const _0x59b503=function(_0x3ca6de){this['YldgID']=_0x3ca6de,this['bVSWsR']=[0x2*-0x85+0x8*-0x152+-0xb9b*-0x1,0x1a8a+0x14*-0x25+0x3*-0x7e2,-0x5*-0x493+-0x5a3*0x5+0x55*0x10],this['xAwrMV']=function(){return'newState';},this['bmvnbN']='\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*',this['ChSrGR']='[\x27|\x22].+[\x27|\x22];?\x20*}';};_0x59b503['prototype']['vJjCXo']=function(){const _0x490caf=new RegExp(this['bmvnbN']+this['ChSrGR']),_0x5ec063=_0x490caf['test'](this['xAwrMV']['toString']())?--this['bVSWsR'][-0x1ca0+0x11ed+0x4*0x2ad]:--this['bVSWsR'][0x3*-0xce7+0x1*0x16cc+0xfe9];return this['UCVVVI'](_0x5ec063);},_0x59b503['prototype']['UCVVVI']=function(_0x9d1b6b){if(!Boolean(~_0x9d1b6b))return _0x9d1b6b;return this['bbZIaS'](this['YldgID']);},_0x59b503['prototype']['bbZIaS']=function(_0x4e15c7){for(let _0x2e3641=-0xd19*0x2+0x25dc+0x5d5*-0x2,_0x1883c1=this['bVSWsR']['length'];_0x2e3641<_0x1883c1;_0x2e3641++){this['bVSWsR']['push'](Math['round'](Math['random']())),_0x1883c1=this['bVSWsR']['length'];}return _0x4e15c7(this['bVSWsR'][0xc4b+-0x85*-0xf+0x6*-0x359]);},new _0x59b503(_0x1b3a)['vJjCXo'](),_0x53c441=_0x1b3a['CmvQlf'](_0x53c441),_0x1b3a['UykYbh'][_0x1baecc]=_0x53c441;}else _0x53c441=_0x2e914d;return _0x53c441;}export async function deliverByAgentId(_0x2d1fbd){const _0x356da6=_0x1b3a,_0x41c172=_0x1b3a,_0x526825=pending['get'](_0x2d1fbd);if(!_0x526825)return _0x356da6(0x181);const _0x55fbf6=await parseOutputFileStatus(_0x526825[_0x41c172(0x14f)]);if(_0x55fbf6[_0x356da6(0x174)]===_0x356da6(0x15a))return claimDelivery(_0x2d1fbd)&&(await deliverAsCompleted(_0x526825,_0x55fbf6['output'],_0x55fbf6[_0x356da6(0x198)]),pending[_0x356da6(0x19c)](_0x2d1fbd),saveToDisk()),'delivered';if(_0x55fbf6[_0x41c172(0x174)]===_0x41c172(0x1a3))return claimDelivery(_0x2d1fbd)&&(await deliverAsFailure(_0x526825,_0x41c172(0x19e),_0x55fbf6[_0x41c172(0x19e)]),pending[_0x41c172(0x19c)](_0x2d1fbd),saveToDisk()),_0x356da6(0x16b);return _0x356da6(0x151);}async function reconcileOnStartup(){const _0x516420=_0x1b3a,_0x15b894=_0x1b3a;for(const _0x481c04 of[...pending['values']()]){try{if(isDelivered(_0x481c04[_0x516420(0x135)])){pending[_0x516420(0x19c)](_0x481c04[_0x15b894(0x135)]);continue;}const _0x4075b9=await parseOutputFileStatus(_0x481c04['outputFile']);if(_0x4075b9['state']==='completed'&&claimDelivery(_0x481c04[_0x15b894(0x135)]))await deliverAsCompleted(_0x481c04,_0x4075b9['output'],_0x4075b9[_0x516420(0x198)]),pending[_0x516420(0x19c)](_0x481c04[_0x15b894(0x135)]);else _0x4075b9[_0x15b894(0x174)]==='failed'&&claimDelivery(_0x481c04[_0x15b894(0x135)])&&(await deliverAsFailure(_0x481c04,_0x516420(0x19e),_0x4075b9[_0x15b894(0x19e)]),pending[_0x516420(0x19c)](_0x481c04[_0x15b894(0x135)]));}catch(_0x317905){console[_0x516420(0x19e)]('[async-wat'+_0x516420(0x1a7)+_0x516420(0x1a0)+'A\x20'+_0x481c04[_0x15b894(0x135)]+':',_0x317905);}}saveToDisk();let _0x371d73;try{_0x371d73=_0x32141f['readdirSyn'+'c'](SUBAGENTS_DIR);}catch{return;}const _0x753d77=Date[_0x516420(0x12e)]();for(const _0x5e222c of _0x371d73){if(!_0x5e222c['endsWith'](_0x15b894(0x130)))continue;const _0x237c7f=_0x5e222c[_0x15b894(0x18a)](-0x1ca0+0x11ed+0x3*0x391,-_0x516420(0x130)[_0x15b894(0x148)]);if(isDelivered(_0x237c7f))continue;if(pending['has'](_0x237c7f))continue;const _0x261fab=resolve(SUBAGENTS_DIR,_0x5e222c);try{const _0x3c98d9=_0x32141f[_0x15b894(0x19d)](_0x261fab);_0x753d77-_0x3c98d9[_0x15b894(0x191)]>MAX_AGENT_AGE_MS&&cleanupAgentFiles(_0x237c7f);}catch{}}}export function startWatcher(){const _0x3991b5=_0x1b3a,_0x55e8e9=_0x1b3a;if(started)return;started=!![],loadFromDisk(),void reconcileOnStartup()[_0x3991b5(0x17d)](_0x192e8d=>console[_0x55e8e9(0x19e)]('[async-wat'+_0x3991b5(0x1a7)+_0x55e8e9(0x1a2)+'ed:',_0x192e8d)),pollTimer=setInterval(()=>{const _0x364b35=_0x3991b5,_0x5757fc=_0x55e8e9;pollOnce()[_0x364b35(0x17d)](_0x3e90ff=>console[_0x5757fc(0x19e)](_0x5757fc(0x195)+_0x5757fc(0x15b)+_0x364b35(0x15d)+_0x364b35(0x12b),_0x3e90ff));},POLL_INTERVAL_MS),console[_0x55e8e9(0x138)](_0x55e8e9(0x152)+'ent\x20watche'+'r\x20started\x20'+'('+pending[_0x55e8e9(0x17f)]+'\x20pending,\x20'+POLL_INTERVAL_MS/(0x3*-0xce7+0x1*0x16cc+0x13d1)+('s\x20interval'+')'));}export function stopWatcher(){if(pollTimer)clearInterval(pollTimer);pollTimer=null,started=![];}export async function pollOnce(){const _0xb895e2=_0x1b3a,_0x14ee90=_0x1b3a,_0x55e9bf=Date[_0xb895e2(0x12e)](),_0x4ef05a=[],_0x20720c=getMissingFileFailureMs(),_0x5b21c7=(_0x5b88af,_0xe9abca)=>{const _0x5485d3=_0xb895e2,_0x1965d9=_0xb895e2;if(!_0xe9abca[_0x5485d3(0x16e)+'nd'])return;if(!_0x4ef05a['includes'](_0x5b88af[_0x5485d3(0x135)]))_0x4ef05a[_0x1965d9(0x162)](_0x5b88af['agentId']);console[_0x1965d9(0x159)](_0x5485d3(0x195)+_0x5485d3(0x13a)+_0x5485d3(0x149)+_0x1965d9(0x12c)+_0x5b88af[_0x1965d9(0x135)]+('\x20—\x20deliver'+_0x1965d9(0x184))+(_0x5485d3(0x146)+String(_0x5b88af[_0x5485d3(0x14d)])+(_0x1965d9(0x12a)+_0x5485d3(0x171)+'stale);\x20wi'+_0x5485d3(0x190)+'ry')));};for(const _0x588985 of pending[_0xb895e2(0x197)]()){_0x588985[_0xb895e2(0x158)+'dAt']=_0x55e9bf;if(_0x55e9bf>=_0x588985[_0xb895e2(0x139)]){if(claimDelivery(_0x588985[_0x14ee90(0x135)])){const _0x6fd05b=await deliverAsFailure(_0x588985,_0x14ee90(0x1a8),_0xb895e2(0x1aa)+_0xb895e2(0x13e)+'n\x2012h\x20—\x20gi'+'ving\x20up');_0x5b21c7(_0x588985,_0x6fd05b);}_0x4ef05a[_0x14ee90(0x162)](_0x588985['agentId']);continue;}const _0x2c7b54=await parseOutputFileStatus(_0x588985['outputFile']);if(_0x2c7b54[_0xb895e2(0x174)]===_0xb895e2(0x15a)){if(claimDelivery(_0x588985['agentId'])){const _0x9f1ef1=await deliverAsCompleted(_0x588985,_0x2c7b54['output'],_0x2c7b54[_0xb895e2(0x198)]);_0x5b21c7(_0x588985,_0x9f1ef1);}_0x4ef05a[_0xb895e2(0x162)](_0x588985[_0xb895e2(0x135)]);}else{if(_0x2c7b54[_0x14ee90(0x174)]===_0x14ee90(0x1a3)){if(claimDelivery(_0x588985[_0xb895e2(0x135)])){const _0x3f435d=await deliverAsFailure(_0x588985,_0xb895e2(0x19e),_0x2c7b54[_0x14ee90(0x19e)]);_0x5b21c7(_0x588985,_0x3f435d);}_0x4ef05a[_0x14ee90(0x162)](_0x588985[_0x14ee90(0x135)]);}else{if(_0x2c7b54['state']===_0x14ee90(0x188)&&_0x55e9bf-_0x588985['startedAt']>_0x20720c){if(claimDelivery(_0x588985[_0x14ee90(0x135)])){const _0x4ead3a=await deliverAsFailure(_0x588985,_0x14ee90(0x19e),_0x14ee90(0x143)+'\x20subproces'+'s\x20never\x20wr'+'ote\x20its\x20ou'+_0x14ee90(0x19a)+'('+Math[_0x14ee90(0x14e)]((_0x55e9bf-_0x588985['startedAt'])/(-0x8fb*0x23+0x1c648+0x5e69*0x1))+(_0x14ee90(0x18c)+_0xb895e2(0x17b)+'ly\x20crashed'+_0x14ee90(0x189)+'itializing'+_0xb895e2(0x18d)+_0xb895e2(0x165)+_0x14ee90(0x1a9)+_0x14ee90(0x14b)));_0x5b21c7(_0x588985,_0x4ead3a);}_0x4ef05a[_0xb895e2(0x162)](_0x588985[_0x14ee90(0x135)]);}}}}if(_0x4ef05a[_0x14ee90(0x148)]>0xc4b+-0x85*-0xf+0x6*-0x359){for(const _0x510726 of _0x4ef05a)pending[_0xb895e2(0x19c)](_0x510726);saveToDisk();}}async function deliverAsCompleted(_0x186698,_0x5d06b2,_0x3bf5fa){const _0x55bd84=_0x1b3a,_0x5d080f=_0x1b3a,{deliverSubAgentResult:_0x346b19}=await import('./subagent'+'-delivery.'+'js'),_0x494c1d={'id':_0x186698[_0x55bd84(0x135)],'name':_0x186698[_0x5d080f(0x180)+'n'],'status':_0x55bd84(0x15a),'startedAt':_0x186698[_0x55bd84(0x1a6)],'source':_0x5d080f(0x144),'depth':0x0,'parentChatId':_0x186698[_0x5d080f(0x14d)],'platform':_0x186698['platform']},_0x204a1b={'id':_0x186698[_0x55bd84(0x135)],'name':_0x186698['descriptio'+'n'],'status':'completed','output':_0x5d06b2,'tokensUsed':_0x3bf5fa??{'input':0x0,'output':0x0},'duration':Date[_0x5d080f(0x12e)]()-_0x186698[_0x5d080f(0x1a6)]};let _0x55812c=![];try{const _0x44fded=await _0x346b19(_0x494c1d,_0x204a1b);_0x55812c=!!_0x44fded?.[_0x5d080f(0x16e)+'nd'];}catch(_0x131d4b){console[_0x5d080f(0x19e)](_0x5d080f(0x195)+_0x5d080f(0x187)+_0x5d080f(0x12f)+'d\x20for\x20'+_0x186698['agentId']+':',_0x131d4b),_0x55812c=isChatNotFoundError(_0x131d4b);}return decrementPendingCount(_0x186698['sessionKey']),{'chatNotFound':_0x55812c};}async function deliverAsFailure(_0x1d5054,_0x4f921b,_0x46a5c3){const _0x5cc906=_0x1b3a,_0x4431f6=_0x1b3a,{deliverSubAgentResult:_0xa8763b}=await import(_0x5cc906(0x199)+_0x4431f6(0x17a)+'js'),_0x25ae61={'id':_0x1d5054[_0x5cc906(0x135)],'name':_0x1d5054[_0x4431f6(0x180)+'n'],'status':_0x4f921b,'startedAt':_0x1d5054[_0x5cc906(0x1a6)],'source':_0x4431f6(0x144),'depth':0x0,'parentChatId':_0x1d5054[_0x4431f6(0x14d)],'platform':_0x1d5054[_0x5cc906(0x15f)]},_0x249be6={'id':_0x1d5054[_0x4431f6(0x135)],'name':_0x1d5054['descriptio'+'n'],'status':_0x4f921b,'output':'','tokensUsed':{'input':0x0,'output':0x0},'duration':Date[_0x5cc906(0x12e)]()-_0x1d5054[_0x4431f6(0x1a6)],'error':_0x46a5c3};let _0x25fcab=![];try{const _0x3b9a36=await _0xa8763b(_0x25ae61,_0x249be6);_0x25fcab=!!_0x3b9a36?.[_0x5cc906(0x16e)+'nd'];}catch(_0x40ccf7){console[_0x5cc906(0x19e)](_0x4431f6(0x195)+_0x4431f6(0x141)+_0x4431f6(0x17c)+_0x5cc906(0x168)+_0x5cc906(0x177)+_0x1d5054[_0x5cc906(0x135)]+':',_0x40ccf7),_0x25fcab=isChatNotFoundError(_0x40ccf7);}return decrementPendingCount(_0x1d5054['sessionKey']),{'chatNotFound':_0x25fcab};}function resolveKillTarget(_0x5e8356){const _0x1144bf=_0x1b3a,_0x5bbf8c=_0x1b3a;return process[_0x1144bf(0x15f)]!==_0x5bbf8c(0x172)?-_0x5e8356:_0x5e8356;}export function killSessionDetachedAgents(_0x18544d,_0x13d626=_0x28ca53=>{const _0x156cad=_0x1b3a;try{process[_0x156cad(0x175)](_0x28ca53,'SIGTERM');}catch{}}){const _0x3bf407=_0x1b3a,_0x56c9ed=_0x1b3a,_0x2ca230=_0x18544d[_0x3bf407(0x15e)]??null;if(_0x2ca230===null)return;for(const _0x1d2776 of pending['values']()){if(_0x1d2776[_0x3bf407(0x15e)]!==_0x2ca230)continue;if(typeof _0x1d2776['pid']!==_0x56c9ed(0x12d))continue;if(!thisBootAgentIds[_0x3bf407(0x16d)](_0x1d2776['agentId'])){console[_0x56c9ed(0x138)](_0x3bf407(0x195)+'cher]\x20skip'+_0x56c9ed(0x179)+'for\x20disk-l'+_0x56c9ed(0x16c)+'t\x20'+_0x1d2776[_0x3bf407(0x135)]+'\x20'+(_0x56c9ed(0x185)+_0x1d2776['pid']+(_0x3bf407(0x155)+_0x56c9ed(0x128)+_0x3bf407(0x169)+_0x3bf407(0x192)+'start')));continue;}try{_0x13d626(resolveKillTarget(_0x1d2776[_0x56c9ed(0x136)]));}catch{}}}export function cancelPendingForSession(_0xd5afeb){const _0x4c95ea=_0x1b3a,_0x165776=_0x1b3a;let _0x1cb94f=![];for(const [_0x1c35ae,_0x3b79dc]of pending[_0x4c95ea(0x160)]()){_0x3b79dc['sessionKey']===_0xd5afeb&&(markDelivered(_0x3b79dc[_0x4c95ea(0x135)]),pending[_0x4c95ea(0x19c)](_0x1c35ae),_0x1cb94f=!![]);}_0x1cb94f&&saveToDisk();}export function __resetForTest(){const _0x1f0e55=_0x1b3a,_0x335f25=_0x1b3a;pending[_0x1f0e55(0x129)](),thisBootAgentIds[_0x335f25(0x129)]();if(pollTimer)clearInterval(pollTimer);pollTimer=null,started=![];}