@songsid/agend 2.1.4-beta.4 → 2.1.4-beta.41

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 (168) hide show
  1. package/README.md +1 -1
  2. package/README.zh-TW.md +1 -1
  3. package/dist/agent-endpoint.js +1 -1
  4. package/dist/agent-endpoint.js.map +1 -1
  5. package/dist/backend/antigravity.d.ts +9 -5
  6. package/dist/backend/antigravity.js +156 -31
  7. package/dist/backend/antigravity.js.map +1 -1
  8. package/dist/backend/claude-code.d.ts +62 -23
  9. package/dist/backend/claude-code.js +296 -43
  10. package/dist/backend/claude-code.js.map +1 -1
  11. package/dist/backend/codex.d.ts +18 -0
  12. package/dist/backend/codex.js +148 -8
  13. package/dist/backend/codex.js.map +1 -1
  14. package/dist/backend/gemini-cli.js +2 -2
  15. package/dist/backend/gemini-cli.js.map +1 -1
  16. package/dist/backend/grok.js +2 -2
  17. package/dist/backend/grok.js.map +1 -1
  18. package/dist/backend/kiro.d.ts +2 -0
  19. package/dist/backend/kiro.js +24 -3
  20. package/dist/backend/kiro.js.map +1 -1
  21. package/dist/backend/opencode.d.ts +27 -11
  22. package/dist/backend/opencode.js +65 -41
  23. package/dist/backend/opencode.js.map +1 -1
  24. package/dist/backend/types.d.ts +45 -3
  25. package/dist/backend/types.js +12 -1
  26. package/dist/backend/types.js.map +1 -1
  27. package/dist/channel/adapters/discord.d.ts +44 -2
  28. package/dist/channel/adapters/discord.js +526 -78
  29. package/dist/channel/adapters/discord.js.map +1 -1
  30. package/dist/channel/adapters/telegram.d.ts +30 -0
  31. package/dist/channel/adapters/telegram.js +212 -18
  32. package/dist/channel/adapters/telegram.js.map +1 -1
  33. package/dist/channel/agy-mcp-launcher.d.ts +2 -0
  34. package/dist/channel/agy-mcp-launcher.js +28 -0
  35. package/dist/channel/agy-mcp-launcher.js.map +1 -0
  36. package/dist/channel/ipc-bridge.d.ts +2 -1
  37. package/dist/channel/ipc-bridge.js +33 -4
  38. package/dist/channel/ipc-bridge.js.map +1 -1
  39. package/dist/channel/mcp-server.js +28 -25
  40. package/dist/channel/mcp-server.js.map +1 -1
  41. package/dist/channel/mcp-tools.js +4 -2
  42. package/dist/channel/mcp-tools.js.map +1 -1
  43. package/dist/channel/message-queue.js +62 -1
  44. package/dist/channel/message-queue.js.map +1 -1
  45. package/dist/channel/types.d.ts +31 -1
  46. package/dist/classic-channel-manager.d.ts +40 -3
  47. package/dist/classic-channel-manager.js +218 -31
  48. package/dist/classic-channel-manager.js.map +1 -1
  49. package/dist/cli.js +109 -37
  50. package/dist/cli.js.map +1 -1
  51. package/dist/config-validator.js +48 -7
  52. package/dist/config-validator.js.map +1 -1
  53. package/dist/config.d.ts +4 -0
  54. package/dist/config.js +12 -1
  55. package/dist/config.js.map +1 -1
  56. package/dist/cross-instance-envelope.d.ts +6 -0
  57. package/dist/cross-instance-envelope.js +30 -0
  58. package/dist/cross-instance-envelope.js.map +1 -0
  59. package/dist/daemon.d.ts +319 -9
  60. package/dist/daemon.js +1402 -255
  61. package/dist/daemon.js.map +1 -1
  62. package/dist/doctor.d.ts +41 -0
  63. package/dist/doctor.js +267 -0
  64. package/dist/doctor.js.map +1 -0
  65. package/dist/fleet-context.d.ts +47 -0
  66. package/dist/fleet-manager.d.ts +265 -11
  67. package/dist/fleet-manager.js +2273 -455
  68. package/dist/fleet-manager.js.map +1 -1
  69. package/dist/fleet-yaml-slim.d.ts +10 -0
  70. package/dist/fleet-yaml-slim.js +59 -0
  71. package/dist/fleet-yaml-slim.js.map +1 -0
  72. package/dist/general-knowledge/skills/backend-providers/SKILL.md +114 -0
  73. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +3 -1
  74. package/dist/general-knowledge/skills/delegation-playbook/SKILL.md +52 -0
  75. package/dist/general-knowledge/skills/development-workflow/SKILL.md +28 -0
  76. package/dist/general-knowledge/skills/fleet-config/SKILL.md +1 -0
  77. package/dist/general-knowledge/skills/fleet-health/SKILL.md +1 -0
  78. package/dist/general-knowledge/skills/fleet-restart/SKILL.md +1 -0
  79. package/dist/general-knowledge/skills/instance-lifecycle/SKILL.md +1 -0
  80. package/dist/general-knowledge/skills/model-discovery/SKILL.md +64 -16
  81. package/dist/general-knowledge/skills/multi-channel/SKILL.md +1 -0
  82. package/dist/general-knowledge/skills/scheduling/SKILL.md +1 -0
  83. package/dist/general-knowledge/skills/session-management/SKILL.md +1 -0
  84. package/dist/general-knowledge/skills/tui-effort/SKILL.md +1 -0
  85. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +30 -0
  86. package/dist/instance-lifecycle.d.ts +67 -0
  87. package/dist/instance-lifecycle.js +273 -8
  88. package/dist/instance-lifecycle.js.map +1 -1
  89. package/dist/instructions.d.ts +12 -0
  90. package/dist/instructions.js +35 -1
  91. package/dist/instructions.js.map +1 -1
  92. package/dist/locale.js +966 -288
  93. package/dist/locale.js.map +1 -1
  94. package/dist/login-flows.d.ts +91 -0
  95. package/dist/login-flows.js +170 -0
  96. package/dist/login-flows.js.map +1 -0
  97. package/dist/login-manager.d.ts +63 -0
  98. package/dist/login-manager.js +134 -0
  99. package/dist/login-manager.js.map +1 -0
  100. package/dist/network-family.d.ts +18 -0
  101. package/dist/network-family.js +20 -0
  102. package/dist/network-family.js.map +1 -0
  103. package/dist/outbound-handlers.d.ts +8 -0
  104. package/dist/outbound-handlers.js +101 -18
  105. package/dist/outbound-handlers.js.map +1 -1
  106. package/dist/outbound-schemas.d.ts +7 -1
  107. package/dist/outbound-schemas.js +9 -0
  108. package/dist/outbound-schemas.js.map +1 -1
  109. package/dist/reply-dedup.d.ts +7 -8
  110. package/dist/reply-dedup.js +0 -0
  111. package/dist/reply-dedup.js.map +1 -1
  112. package/dist/restart-progress.d.ts +2 -0
  113. package/dist/restart-progress.js +3 -0
  114. package/dist/restart-progress.js.map +1 -1
  115. package/dist/scheduler/db.d.ts +12 -0
  116. package/dist/scheduler/db.js +59 -0
  117. package/dist/scheduler/db.js.map +1 -1
  118. package/dist/service-installer.d.ts +11 -0
  119. package/dist/service-installer.js +84 -18
  120. package/dist/service-installer.js.map +1 -1
  121. package/dist/settings-api.js +1 -1
  122. package/dist/settings-api.js.map +1 -1
  123. package/dist/setup-wizard.js +2 -2
  124. package/dist/setup-wizard.js.map +1 -1
  125. package/dist/spawn-gate.d.ts +30 -0
  126. package/dist/spawn-gate.js +79 -0
  127. package/dist/spawn-gate.js.map +1 -0
  128. package/dist/storm-window.d.ts +83 -0
  129. package/dist/storm-window.js +251 -0
  130. package/dist/storm-window.js.map +1 -0
  131. package/dist/tips.d.ts +44 -0
  132. package/dist/tips.js +355 -0
  133. package/dist/tips.js.map +1 -0
  134. package/dist/tmux-manager.d.ts +16 -0
  135. package/dist/tmux-manager.js +110 -25
  136. package/dist/tmux-manager.js.map +1 -1
  137. package/dist/tool-progress.d.ts +40 -0
  138. package/dist/tool-progress.js +289 -0
  139. package/dist/tool-progress.js.map +1 -0
  140. package/dist/topic-commands.d.ts +42 -4
  141. package/dist/topic-commands.js +361 -67
  142. package/dist/topic-commands.js.map +1 -1
  143. package/dist/transcript-monitor.d.ts +15 -2
  144. package/dist/transcript-monitor.js +63 -17
  145. package/dist/transcript-monitor.js.map +1 -1
  146. package/dist/transcript-sources.d.ts +131 -0
  147. package/dist/transcript-sources.js +580 -0
  148. package/dist/transcript-sources.js.map +1 -0
  149. package/dist/types.d.ts +13 -0
  150. package/dist/ui/dashboard.html +55 -32
  151. package/dist/ui/settings.html +200 -60
  152. package/dist/ui/view.html +147 -30
  153. package/dist/usage/format-rich.js +24 -23
  154. package/dist/usage/format-rich.js.map +1 -1
  155. package/dist/usage/i18n-keys.d.ts +7 -0
  156. package/dist/usage/i18n-keys.js +34 -0
  157. package/dist/usage/i18n-keys.js.map +1 -0
  158. package/dist/usage/i18n.d.ts +5 -0
  159. package/dist/usage/i18n.js +27 -0
  160. package/dist/usage/i18n.js.map +1 -0
  161. package/dist/usage/providers.d.ts +19 -0
  162. package/dist/usage/providers.js +150 -74
  163. package/dist/usage/providers.js.map +1 -1
  164. package/dist/usage/usage-api.d.ts +8 -2
  165. package/dist/usage/usage-api.js +45 -23
  166. package/dist/usage/usage-api.js.map +1 -1
  167. package/dist/workflow-templates/default.md +2 -1
  168. package/package.json +2 -2
package/dist/daemon.d.ts CHANGED
@@ -6,11 +6,17 @@ import type { CliBackend, InstanceState, InstanceStateSnapshot } from "./backend
6
6
  import { HangDetector } from "./hang-detector.js";
7
7
  import type { TmuxControlClient } from "./tmux-control.js";
8
8
  import type { FleetInstructionsParams } from "./instructions.js";
9
+ import type { SpawnGate } from "./spawn-gate.js";
10
+ import type { StormWindow } from "./storm-window.js";
9
11
  /** Point a resumed CLI at its one backend-native instruction source. */
10
12
  export declare function buildInstructionReloadNotice(binaryName: string, instanceName: string, instanceDir: string): string;
11
13
  export declare const DEFAULT_STUCK_TIMEOUT_MS: number;
12
14
  export declare const DEFAULT_STATE_IDLE_DEBOUNCE_MS = 2000;
13
15
  export declare const DEFAULT_STATE_SAFETY_SWEEP_MS = 60000;
16
+ /** Coalesce one cosmetic-redraw burst before comparing the visible pane. */
17
+ export declare const PERIODIC_REDRAW_PROBE_MS = 25;
18
+ /** A foreground server/port-forward should hand control back or be acknowledged. */
19
+ export declare const DEFAULT_BLOCKING_PROCESS_GRACE_MS: number;
14
20
  /**
15
21
  * Whether two working directories belong to the same project, so a fleet-scoped
16
22
  * decision recorded in one reaches the other. Covers the worktree/checkout
@@ -62,11 +68,13 @@ export declare function writeLastInboundAt(instanceDir: string, timestamp: numbe
62
68
  export declare function renderHandoffMetadata(meta: Record<string, string>): string;
63
69
  /** Headless inactivity timer used by the daemon and unit tests. */
64
70
  export declare class AutoPauseController {
65
- private readonly thresholdMs;
71
+ private thresholdMs;
66
72
  private lastActivityAt;
67
73
  private pausedAt;
68
74
  constructor(thresholdMs: number, lastActivityAt?: number);
69
75
  recordActivity(now?: number): void;
76
+ /** Apply a new idle threshold and start a fresh countdown from this update. */
77
+ reconfigure(thresholdMs: number, now?: number): void;
70
78
  observe(state: InstanceState, now?: number): boolean;
71
79
  markPaused(now?: number): void;
72
80
  markAwake(now?: number): void;
@@ -142,10 +150,13 @@ export declare class PaneStateMachine {
142
150
  * working" and a finished turn would not be seen as idle until the next 60s
143
151
  * safety sweep. Output timestamps are the honest liveness signal here, and
144
152
  * the caller has them.
153
+ * @param opts.forceBusy the backend has positively identified a still-running
154
+ * foreground tool even though its TUI keeps an old ready footer visible.
145
155
  */
146
156
  observe(pane: string, now?: number, opts?: {
147
157
  settled?: boolean;
148
158
  changeAt?: number;
159
+ forceBusy?: boolean;
149
160
  }): InstanceStateSnapshot;
150
161
  /** Record pane motion from tmux control mode without capturing pane content. */
151
162
  recordOutput(now?: number): InstanceStateSnapshot;
@@ -237,6 +248,26 @@ export declare function extractProxyReplyText(pane: string, opts?: {
237
248
  maxLines?: number;
238
249
  maxChars?: number;
239
250
  }): string | null;
251
+ export interface MalformedClaudeToolCallDetection {
252
+ /** Stable, non-secret fingerprint used to suppress a stale pane fragment. */
253
+ signature: string;
254
+ /** Sanitized reply argument, or null when only a dangling closing tag survived. */
255
+ text: string | null;
256
+ }
257
+ /**
258
+ * Find the narrow Claude XML failure signature reported in #648.
259
+ *
260
+ * This intentionally runs only at the daemon's busy→idle edge (the caller
261
+ * enforces that) and only scans output after the current inbound marker. A
262
+ * partial tool call while Claude is still streaming is therefore never acted
263
+ * on. The extracted argument goes through the same credential redaction as the
264
+ * dead-MCP pane proxy before it can leave the host.
265
+ */
266
+ export declare function detectMalformedClaudeToolCall(pane: string, opts?: {
267
+ inboundMarker?: string;
268
+ maxLines?: number;
269
+ maxChars?: number;
270
+ }): MalformedClaudeToolCallDetection | null;
240
271
  export type InteractivePromptKind = "sudo_password" | "password" | "confirmation" | "press_enter";
241
272
  export interface InteractivePromptDetection {
242
273
  kind: InteractivePromptKind;
@@ -258,6 +289,30 @@ export declare class InteractivePromptDetector {
258
289
  observe(pane: string, now?: number, outputAt?: number): InteractivePromptDetection | null;
259
290
  reset(): void;
260
291
  }
292
+ export interface BlockingProcessDetection {
293
+ activity: string;
294
+ evidence: string;
295
+ blockedForMs: number;
296
+ }
297
+ /**
298
+ * Detect a foreground shell tool which has become a long-lived server.
299
+ *
300
+ * Unlike the generic pane-stuck clock, this clock deliberately survives new
301
+ * output: access logs and `Handling connection` lines are exactly why a
302
+ * foreground server can remain blocked forever without ever looking silent.
303
+ * Once a server marker is seen, the still-running tool identity is the stable
304
+ * signal; the marker may scroll out of the 32-row pane.
305
+ */
306
+ export declare class BlockingProcessDetector {
307
+ private readonly graceMs;
308
+ private activity;
309
+ private evidence;
310
+ private detectedAt;
311
+ private notified;
312
+ constructor(graceMs?: number);
313
+ observe(pane: string, activity: string | null, now?: number): BlockingProcessDetection | null;
314
+ reset(): void;
315
+ }
261
316
  export declare class Daemon extends EventEmitter {
262
317
  private name;
263
318
  private config;
@@ -266,6 +321,8 @@ export declare class Daemon extends EventEmitter {
266
321
  private backend?;
267
322
  private controlClient?;
268
323
  private runtimeIdentity?;
324
+ private spawnGate?;
325
+ private stormWindow?;
269
326
  private logger;
270
327
  private tmuxSessionName;
271
328
  private tmux;
@@ -275,6 +332,13 @@ export declare class Daemon extends EventEmitter {
275
332
  private guardian;
276
333
  private adapter;
277
334
  private pendingIpcRequests;
335
+ /**
336
+ * Daemon-global request sequence. MCP request ids are only process-local:
337
+ * Claude can have more than one MCP child connected to the same daemon and
338
+ * each starts at 1, so using requestId alone lets one caller steal another's
339
+ * response.
340
+ */
341
+ private fleetRequestSeq;
278
342
  private lastChatId;
279
343
  private lastThreadId;
280
344
  private lastAdapterId;
@@ -282,9 +346,6 @@ export declare class Daemon extends EventEmitter {
282
346
  /** Last activity published to the fleet manager; null when nothing is running. */
283
347
  private currentActivity;
284
348
  private socketSessionNames;
285
- private static tmuxServerCrashTimestamps;
286
- private static tmuxServerPaused;
287
- private static tmuxServerRecoveryTimer;
288
349
  private healthCheckTimer;
289
350
  private crashCount;
290
351
  private lastCrashAt;
@@ -317,6 +378,7 @@ export declare class Daemon extends EventEmitter {
317
378
  /** Fallback safety sweep used only when no shared tmux control client exists. */
318
379
  private instanceStateMonitorTimer;
319
380
  private instanceStateIdleTimer;
381
+ private instanceStateOutputProbeTimer;
320
382
  private instanceStateStuckTimer;
321
383
  private instanceStateOutputListener;
322
384
  private instanceStateOutputEventName;
@@ -327,11 +389,16 @@ export declare class Daemon extends EventEmitter {
327
389
  private instanceStateReadyPattern;
328
390
  private instanceStateBusyPattern;
329
391
  private instanceStateMonitorActive;
392
+ private sessionCheckpointWarningEmitted;
330
393
  private statePollInFlight;
331
394
  private turnHadInbound;
332
395
  private turnOutboundDelivered;
396
+ /** Successful `reply` specifically; cross-instance tools do not satisfy #648. */
397
+ private turnReplyDelivered;
333
398
  private turnCorrelationId;
334
399
  private turnInboundMarker;
400
+ /** Prevent a visible stale XML fragment from being recovered on later turns. */
401
+ private lastMalformedToolCallSignature;
335
402
  private proxyReplySeq;
336
403
  private autoPauseController;
337
404
  private pauseRequested;
@@ -342,6 +409,35 @@ export declare class Daemon extends EventEmitter {
342
409
  * would silently no-op exactly when we most need to stop feeding the CLI.
343
410
  */
344
411
  private pausePending;
412
+ /**
413
+ * An auth failure was detected and nothing has resolved it yet.
414
+ *
415
+ * Separate from `pausePending` because it must outlive it: the deferred pause
416
+ * clears `pausePending` as soon as it fires, but if that pause is refused the
417
+ * instance is still auth-broken and must not resume raising alarms.
418
+ */
419
+ private authFailureUnresolved;
420
+ /** One login-screen auth report per spawn — the screen persists across polls. */
421
+ private loginScreenReported;
422
+ /** A fatal startup screen (see StartupDialog.fatal) was already reported this run. */
423
+ private fatalStartupReported;
424
+ /**
425
+ * The CLI is parked on a fatal startup screen and every pane write must be
426
+ * refused. This is deliberately separate from the pause that the reported
427
+ * pty_error triggers: the pause is applied by an async handler, and a queued
428
+ * delivery would race it into the modal (where paste+Enter can confirm
429
+ * "Exit and fix manually"). Set synchronously at dialog-match time; cleared
430
+ * when a startup scan reaches a real ready prompt, or on wake().
431
+ */
432
+ private fatalStartupBlocked;
433
+ /** model_error seen mid-turn: notify only if it survives to the idle screen. */
434
+ private pendingModelErrorKey;
435
+ /**
436
+ * Let the next pause() proceed from a stuck pane. Set only for the auth-deferred
437
+ * pause — see the pausePending consumption site for why waiting for idle there
438
+ * is waiting forever.
439
+ */
440
+ private pauseAllowStuck;
345
441
  private pauseWakeState;
346
442
  private pauseWakeTransition;
347
443
  private modelOverride;
@@ -353,6 +449,12 @@ export declare class Daemon extends EventEmitter {
353
449
  * and ⏳/👀/✅ reactions live here). It does NOT cover the other writers that
354
450
  * reach the pane — {@link paneWriteLock} does. */
355
451
  private pasteLock;
452
+ /**
453
+ * Fleet cancellation epoch. Queue entries retain the epoch they arrived with;
454
+ * advancing it invalidates only work received before the cancel, never a new
455
+ * user message which arrives immediately afterwards.
456
+ */
457
+ private deliveryEpoch;
356
458
  /** Mutual exclusion for *every* write into the pane, whichever subsystem it
357
459
  * comes from. See PaneWriteLock for why interleaving is destructive. */
358
460
  private readonly paneWriteLock;
@@ -362,8 +464,20 @@ export declare class Daemon extends EventEmitter {
362
464
  private lastBuiltInstructions;
363
465
  private pasteQueueDepth;
364
466
  private firstDeliveryDelay;
467
+ /** Orders /steer pastes against each other. Deliberately NOT pasteLock:
468
+ * a steer must not queue behind the normal deliveries it exists to
469
+ * overtake. Pane-level exclusion still comes from paneWriteLock inside
470
+ * deliverMessage, so a steer and a normal delivery can never interleave
471
+ * their PTY writes — the steer just doesn't wait for idle. */
472
+ private steerLock;
473
+ /** Tool lines shown in the channel processing bubble for the current turn. */
474
+ private readonly turnProgress;
475
+ private lastProgressBroadcast;
476
+ private progressBroadcastTimer;
477
+ private lastProgressBroadcastAt;
365
478
  private errorMonitorTimer;
366
479
  private readonly interactivePromptDetector;
480
+ private readonly blockingProcessDetector;
367
481
  /** Same 5-min gate the error monitor uses, so a dead MCP server alerts once. */
368
482
  private static readonly MCP_DEATH_COOLDOWN_MS;
369
483
  private lastMcpDeathNotifiedAt;
@@ -375,6 +489,16 @@ export declare class Daemon extends EventEmitter {
375
489
  * past this point interrupting the turn costs less than staying mute.
376
490
  */
377
491
  private static readonly MCP_RESTART_MAX_WAIT_MS;
492
+ /**
493
+ * How long the revival restart waits, at the point of firing, for a
494
+ * CLI-respawned MCP server to announce itself.
495
+ *
496
+ * Codex <0.146 tears down and recreates ALL MCP connections on an auth/config
497
+ * change — healthy ones included — so the daemon sees the old pid die while
498
+ * the CLI is fine and a replacement is seconds away. Module load measured
499
+ * ~0.36s, so 2s is roughly 5x headroom without making a genuine loss wait.
500
+ */
501
+ private static readonly MCP_REPLACEMENT_GRACE_MS;
378
502
  /**
379
503
  * Sticky "restart to revive the MCP server as soon as the pane idles" — same
380
504
  * shape as pausePending, because the death is usually detected mid-turn when
@@ -383,6 +507,25 @@ export declare class Daemon extends EventEmitter {
383
507
  */
384
508
  private mcpRestartPending;
385
509
  private mcpRestartStaleTimer;
510
+ /** In-flight replacement grace; also the guard against three triggers stacking. */
511
+ private mcpRestartGraceTimer;
512
+ /**
513
+ * Which trigger the in-flight grace will commit under.
514
+ *
515
+ * The single-timer guard would otherwise SWALLOW a stale_timeout that lands
516
+ * during an idle-gated grace — and because the stale timer is one-shot and
517
+ * already cleared itself, the 30-minute backstop would be gone for good on an
518
+ * instance that never idles, which is the exact case it exists for.
519
+ */
520
+ private mcpRestartGraceTrigger;
521
+ /**
522
+ * A dead-MCP sighting held back once, pending a possible replacement.
523
+ *
524
+ * Deliberately NOT the same field as `mcpDeathNotifiedForPid`: deferring must
525
+ * not consume the once-per-pid budget, or a suspicion that turns out to be a
526
+ * real loss could never be reported at all.
527
+ */
528
+ private mcpDeathDeferredForPid;
386
529
  /** Prevent in-flight monitor callbacks from re-arming after a pause. */
387
530
  private runtimeMonitorsFrozen;
388
531
  private errorWaitingForRecovery;
@@ -409,7 +552,7 @@ export declare class Daemon extends EventEmitter {
409
552
  private lastDetectedErrorType;
410
553
  private static errorPatternKey;
411
554
  private clearErrorRecoveryGate;
412
- constructor(name: string, config: InstanceConfig, instanceDir: string, topicMode?: boolean, backend?: CliBackend | undefined, controlClient?: TmuxControlClient | undefined, rootLogger?: Logger, runtimeIdentity?: FleetInstructionsParams["runtimeIdentity"]);
555
+ constructor(name: string, config: InstanceConfig, instanceDir: string, topicMode?: boolean, backend?: CliBackend | undefined, controlClient?: TmuxControlClient | undefined, rootLogger?: Logger, runtimeIdentity?: FleetInstructionsParams["runtimeIdentity"], spawnGate?: SpawnGate | undefined, stormWindow?: StormWindow | undefined);
413
556
  start(): Promise<void>;
414
557
  /**
415
558
  * Detect a CRASHED MCP server and report it once.
@@ -425,7 +568,22 @@ export declare class Daemon extends EventEmitter {
425
568
  * process would have no client reading it. Only the CLI can restore its own
426
569
  * tools — hence notify, and let the operator decide about restarting.
427
570
  */
571
+ /**
572
+ * An MCP server has connected and announced itself.
573
+ *
574
+ * Beyond the existing session bookkeeping this is the earliest evidence that
575
+ * tools are back — roughly 30s ahead of the next health tick, which is
576
+ * exactly the window #663's false restarts fired in.
577
+ */
578
+ private handleMcpReady;
428
579
  private checkMcpServerAlive;
580
+ /**
581
+ * Whether a dead MCP pid on this backend is more likely a CLI-driven
582
+ * replacement than a real loss. Codex only, and deliberately narrow: it is
583
+ * the backend whose released fix (0.146) confirms it tore down healthy MCP
584
+ * connections alongside the one it meant to restart.
585
+ */
586
+ private suspectsTransientMcpReplacement;
429
587
  /**
430
588
  * Arm an automatic instance restart to revive a dead MCP server. Fires on the
431
589
  * next busy→idle edge — or immediately when the pane is already idle, which is
@@ -434,7 +592,36 @@ export declare class Daemon extends EventEmitter {
434
592
  * force-restarted: see that constant for why staying mute costs more.
435
593
  */
436
594
  private armMcpRestartWhenIdle;
595
+ /**
596
+ * Single chokepoint for all three revival triggers (already_idle, idle_edge,
597
+ * stale_timeout) — and therefore the one place worth re-checking reality.
598
+ *
599
+ * The pending flag is armed by a health tick that saw the OLD pid die, and
600
+ * was previously fired without ever looking again. When a CLI legitimately
601
+ * respawns its own MCP child, the replacement is already serving by the time
602
+ * the turn ends — but the idle edge fires inside the ~30s before the next
603
+ * health tick can stand the request down, restarting a healthy instance. One
604
+ * external user was restarted 18 times in two days this way (#663).
605
+ *
606
+ * Re-reading here is safe for every backend: worst case it clears a pending
607
+ * restart that should not have existed.
608
+ */
437
609
  private fireMcpRestartRequest;
610
+ /**
611
+ * A recovery is already reshaping the CLI: crash-loop handling
612
+ * (healthCheckPaused), a respawn in flight (spawning), or a pause
613
+ * (isPaused / frozen monitors). Each ends in a fresh CLI — and with it a
614
+ * fresh MCP server — or in supervision ending, where a restart on top would
615
+ * fight the recovery. Either way the revival restart is moot.
616
+ */
617
+ private mcpRestartCancelledByRecovery;
618
+ /**
619
+ * Emit a dead-MCP report that was held back as a suspected replacement, once
620
+ * it is clear no replacement is coming. Consumes the once-per-pid budget
621
+ * here — deferring was to avoid spending it on a false alarm, not to drop
622
+ * the incident.
623
+ */
624
+ private reportDeferredMcpDeath;
438
625
  private clearMcpRestartRequest;
439
626
  private startHealthCheck;
440
627
  /**
@@ -468,6 +655,8 @@ export declare class Daemon extends EventEmitter {
468
655
  private startErrorMonitor;
469
656
  /** Evaluate one pane snapshot. Kept synchronous so state-machine edges are unit-testable. */
470
657
  private evaluateErrorPatterns;
658
+ /** Arm the recovery gates and emit one detected error pattern. */
659
+ private emitErrorPattern;
471
660
  /**
472
661
  * Notification text for a detected pattern. `formatMessage` patterns build it
473
662
  * from the match so the user gets the specifics (e.g. which period and what
@@ -489,12 +678,37 @@ export declare class Daemon extends EventEmitter {
489
678
  * that message, which is what the user asked for anyway.
490
679
  */
491
680
  sendEscape(): Promise<void>;
681
+ /**
682
+ * Drop every not-yet-started pane delivery which predates a user cancel.
683
+ * Promise chains cannot be removed, so entries become generation-checked
684
+ * no-ops. An entry already past its check is allowed to finish its current PTY
685
+ * transaction; the interrupt key still cancels the CLI generation.
686
+ */
687
+ clearPendingDeliveries(fleetEpoch?: number): void;
688
+ private captureDeliveryEpoch;
689
+ private isDeliveryEpochCurrent;
690
+ private queueRawPaste;
691
+ /**
692
+ * Confirm a backend's terminal-level /clear prompt after the channel-level
693
+ * admin confirmation has already succeeded. Poll instead of pasting `/clear\ny`
694
+ * as one buffer: Kiro versions that clear immediately interpret that queued
695
+ * `y` as a brand-new user message.
696
+ */
697
+ private confirmBackendClearDialog;
492
698
  /** Send the backend-specific graceful quit command/key sequence. */
493
699
  private sendQuitSequence;
494
700
  stop(): Promise<void>;
495
701
  getHangDetector(): HangDetector | null;
496
702
  getInstanceState(): InstanceState | "paused";
497
703
  getInstanceStateSnapshot(): InstanceStateSnapshot;
704
+ /**
705
+ * Answer a FleetManager state query. Most callers only need the cached state,
706
+ * but lifecycle decisions such as post-reply Cancel retirement need an
707
+ * authoritative pane observation: after startup the last transition can stay
708
+ * "working" even though the CLI has since settled without emitting another
709
+ * control-mode output record.
710
+ */
711
+ private respondToInstanceStateQuery;
498
712
  /** Gracefully stop the CLI while keeping its remain-on-exit tmux window. */
499
713
  /**
500
714
  * Mark the instance to pause as soon as its pane is idle. Used for auth
@@ -521,9 +735,21 @@ export declare class Daemon extends EventEmitter {
521
735
  * here (edge-triggered, then reset) is what makes it at most once per turn.
522
736
  */
523
737
  private maybeProxyReplyOnTurnEnd;
738
+ private isClaudeCodeBackend;
524
739
  /** Relay the pane's final text to the channel, marked as a daemon proxy reply. */
525
740
  private sendProxyReply;
741
+ /** Send a narrowly extracted #648 reply; operator notification is a separate event. */
742
+ private sendRecoveredMalformedReply;
743
+ /** Context-bound reply path shared by dead-MCP and malformed-call recovery. */
744
+ private deliverDaemonReply;
526
745
  private clearInstanceStateIdleTimer;
746
+ private clearInstanceStateOutputProbeTimer;
747
+ /**
748
+ * Periodic status-line hooks can generate `%output` without changing a single
749
+ * visible cell. Probe the pane once per burst: a changed capture is real work;
750
+ * an identical ready capture remains idle and emits no state edge.
751
+ */
752
+ private scheduleInstanceStateOutputProbe;
527
753
  private clearInstanceStateStuckTimer;
528
754
  private scheduleInstanceStateStuckDeadline;
529
755
  private scheduleInstanceStateIdleCapture;
@@ -542,14 +768,48 @@ export declare class Daemon extends EventEmitter {
542
768
  * Tell the fleet manager what this instance is doing right now, for the live
543
769
  * progress line on the cancel button.
544
770
  *
545
- * Purely cosmetic — nothing decides anything from it, so it is fine that only
546
- * backends with a transcript feed report at all (claude-code today). For the
547
- * rest the progress line keeps showing just elapsed time.
771
+ * Primarily cosmetic. The foreground-process detector also uses a current
772
+ * shell activity as one of several positive signals, but never makes a state
773
+ * decision from an arbitrary activity label alone.
548
774
  *
549
775
  * Repeats are dropped: the ticker only edits the channel message when the text
550
776
  * changes, and a stream of identical broadcasts would defeat that.
551
777
  */
552
778
  private publishActivity;
779
+ /** Effective tool_progress level, hardened against junk config values.
780
+ * Off unless explicitly enabled: an update must never make bubbles start
781
+ * listing tool activity the operator did not ask to broadcast. */
782
+ private toolProgressLevel;
783
+ /**
784
+ * Apply a tool-progress setting without restarting the CLI session.
785
+ *
786
+ * Drop the current accumulator at the boundary: verbose entries may contain
787
+ * command previews which must not survive a downgrade to standard/off, and a
788
+ * newly enabled level should start with a clean, internally consistent list.
789
+ */
790
+ updateToolProgress(level: InstanceConfig["tool_progress"]): void;
791
+ /** Snapshot the configuration actually owned by this live daemon. */
792
+ getConfigSnapshot(): InstanceConfig;
793
+ /**
794
+ * Apply the whitelisted hot configuration carried by FleetManager IPC.
795
+ * null removes an optional value; invalid/unlisted fields are ignored rather
796
+ * than allowing another client on the per-instance socket to mutate cold
797
+ * process/session settings.
798
+ */
799
+ applyConfigUpdate(value: unknown): void;
800
+ /**
801
+ * Accumulate one semantic progress line for the channel bubble and schedule
802
+ * a coalesced broadcast. Separate from publishActivity on purpose: that one
803
+ * is the single-line statusline detail (terse, operator-facing), this is the
804
+ * multi-line channel list (semantic, argument-free at `standard`) — see
805
+ * tool-progress.ts for why the two labellers must not merge.
806
+ */
807
+ private recordToolProgress;
808
+ /** Coalesce progress broadcasts: at most one per 3s, trailing edge kept. */
809
+ private scheduleProgressBroadcast;
810
+ private broadcastToolProgress;
811
+ /** New turn (or turn over): drop the list and tell the fleet to clear it. */
812
+ private resetToolProgress;
553
813
  /**
554
814
  * Options for every system-initiated paste (startup notice, session snapshot,
555
815
  * runtime-dialog keys).
@@ -561,12 +821,43 @@ export declare class Daemon extends EventEmitter {
561
821
  */
562
822
  private systemPasteOptions;
563
823
  private summarizeTool;
824
+ /**
825
+ * The one place an inbound message grows its metadata wrapper ([user:]/
826
+ * [from:] prefix, pending reactions, handoff metadata, reply instructions).
827
+ * Both the normal queued path (pushChannelMessage) and /steer go through
828
+ * here — a steered message must read EXACTLY like a queued one to the
829
+ * agent, or the two drift apart in what the agent is told about replying.
830
+ */
831
+ private formatInboundMessage;
832
+ /**
833
+ * /steer: interject into the CURRENT turn instead of queueing for idle.
834
+ *
835
+ * Differences from pushChannelMessage, and nothing else:
836
+ * - serialized on steerLock, not pasteLock — it must overtake, not queue
837
+ * - deliverMessage runs with { steer: true }, which takes the busy branch
838
+ * that pastes immediately (the codex native-queue transaction) instead of
839
+ * waiting for idle. Verification and silent-loss fallback are the ones
840
+ * that path already has: pane-capture visibility check, then one
841
+ * idle-gated redelivery — so on a TUI that swallows busy input the steer
842
+ * degrades to "next message after this turn", never silently vanishes.
843
+ *
844
+ * The steered text goes through formatInboundMessage so the agent sees a
845
+ * normal inbound message, with a steering notice prepended for context.
846
+ */
847
+ steerMessage(content: string, meta: Record<string, string>, deliveryEpoch?: number): void;
848
+ /**
849
+ * /btw: submit a separately-labelled side question immediately while asking
850
+ * the agent not to change the active task. Claude's native `/btw` opens a
851
+ * branchAndResume panel whose answer never reaches the channel or transcript,
852
+ * so channel delivery intentionally uses a normal wrapped inbound instead.
853
+ */
854
+ btwMessage(content: string, meta: Record<string, string>, deliveryEpoch?: number): void;
564
855
  /**
565
856
  * Push an inbound channel message to a specific MCP session.
566
857
  * If targetSession is provided, only send to the matching socket.
567
858
  * Otherwise send to the instance's own session (this.name).
568
859
  */
569
- pushChannelMessage(content: string, meta: Record<string, string>, _targetSession?: string): void;
860
+ pushChannelMessage(content: string, meta: Record<string, string>, _targetSession?: string, deliveryEpoch?: number): void;
570
861
  /**
571
862
  * Deliver a single message and drive its status reactions:
572
863
  * ⏳ message_queued — CLI busy; queued locally or by the backend
@@ -584,6 +875,24 @@ export declare class Daemon extends EventEmitter {
584
875
  * serial in both cases so separate PTY writes can never overlap.
585
876
  */
586
877
  private deliverMessage;
878
+ /**
879
+ * While the CLI is parked on a fatal startup screen (see fatalStartupBlocked),
880
+ * no pane write may happen: paste+Enter into the corrupt-config modal would
881
+ * confirm "Exit and fix manually" and exit the CLI. The message is failed (❌)
882
+ * rather than held — the accompanying config_error notification tells the user
883
+ * what to fix, and holding would only build a queue that floods the pane the
884
+ * moment the flag clears.
885
+ */
886
+ private refuseFatalStartupDelivery;
887
+ /**
888
+ * A raw `%output` silence check is authoritative for ordinary TUIs. It is not
889
+ * for a backend such as agy whose status-line hook periodically redraws an
890
+ * identical screen. In that case trust an idle pane-state snapshot only when
891
+ * it was captured after both the latest output and the latest control-client
892
+ * observation reset.
893
+ */
894
+ private isPaneIdleForDelivery;
895
+ private waitForPaneIdleForDelivery;
587
896
  /**
588
897
  * The write half of a delivery: paste → Enter → confirm, with retries.
589
898
  *
@@ -669,6 +978,7 @@ export declare class Daemon extends EventEmitter {
669
978
  * Returns true if CLI is ready, false if it failed or got stuck.
670
979
  */
671
980
  private trySpawn;
981
+ private trySpawnInsideGate;
672
982
  /**
673
983
  * Repeatedly check pane content, dismiss any confirmation dialogs,
674
984
  * and return true once CLI reaches a ready prompt.