@songsid/agend 2.1.2-beta.9 → 2.1.2

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 (172) hide show
  1. package/dist/access-path.js +15 -6
  2. package/dist/access-path.js.map +1 -1
  3. package/dist/agent-cli-instructions.md +1 -0
  4. package/dist/agent-cli.js +14 -0
  5. package/dist/agent-cli.js.map +1 -1
  6. package/dist/agent-endpoint.d.ts +2 -0
  7. package/dist/agent-endpoint.js +25 -1
  8. package/dist/agent-endpoint.js.map +1 -1
  9. package/dist/backend/antigravity.d.ts +20 -0
  10. package/dist/backend/antigravity.js +51 -4
  11. package/dist/backend/antigravity.js.map +1 -1
  12. package/dist/backend/claude-code.d.ts +59 -0
  13. package/dist/backend/claude-code.js +67 -1
  14. package/dist/backend/claude-code.js.map +1 -1
  15. package/dist/backend/codex.d.ts +26 -0
  16. package/dist/backend/codex.js +100 -0
  17. package/dist/backend/codex.js.map +1 -1
  18. package/dist/backend/grok.d.ts +25 -0
  19. package/dist/backend/grok.js +31 -0
  20. package/dist/backend/grok.js.map +1 -1
  21. package/dist/backend/kiro.d.ts +23 -0
  22. package/dist/backend/kiro.js +120 -2
  23. package/dist/backend/kiro.js.map +1 -1
  24. package/dist/backend/types.d.ts +58 -0
  25. package/dist/backend/types.js +13 -1
  26. package/dist/backend/types.js.map +1 -1
  27. package/dist/channel/adapters/discord.d.ts +15 -0
  28. package/dist/channel/adapters/discord.js +120 -8
  29. package/dist/channel/adapters/discord.js.map +1 -1
  30. package/dist/channel/adapters/telegram.d.ts +21 -0
  31. package/dist/channel/adapters/telegram.js +86 -0
  32. package/dist/channel/adapters/telegram.js.map +1 -1
  33. package/dist/channel/ipc-bridge.d.ts +9 -1
  34. package/dist/channel/ipc-bridge.js +12 -3
  35. package/dist/channel/ipc-bridge.js.map +1 -1
  36. package/dist/channel/ipc-timeouts.d.ts +46 -0
  37. package/dist/channel/ipc-timeouts.js +65 -0
  38. package/dist/channel/ipc-timeouts.js.map +1 -0
  39. package/dist/channel/mcp-server.js +25 -14
  40. package/dist/channel/mcp-server.js.map +1 -1
  41. package/dist/channel/mcp-tools.js +30 -1
  42. package/dist/channel/mcp-tools.js.map +1 -1
  43. package/dist/channel/reconnect-backoff.d.ts +17 -0
  44. package/dist/channel/reconnect-backoff.js +21 -0
  45. package/dist/channel/reconnect-backoff.js.map +1 -0
  46. package/dist/channel/types.d.ts +29 -0
  47. package/dist/classic-channel-manager.js +1 -5
  48. package/dist/classic-channel-manager.js.map +1 -1
  49. package/dist/cli.js +242 -91
  50. package/dist/cli.js.map +1 -1
  51. package/dist/completion.d.ts +27 -0
  52. package/dist/completion.js +121 -0
  53. package/dist/completion.js.map +1 -0
  54. package/dist/config-validator.js +21 -0
  55. package/dist/config-validator.js.map +1 -1
  56. package/dist/config.js +2 -0
  57. package/dist/config.js.map +1 -1
  58. package/dist/cost-guard.d.ts +3 -1
  59. package/dist/cost-guard.js +3 -1
  60. package/dist/cost-guard.js.map +1 -1
  61. package/dist/daemon.d.ts +298 -6
  62. package/dist/daemon.js +1142 -313
  63. package/dist/daemon.js.map +1 -1
  64. package/dist/event-log.d.ts +31 -0
  65. package/dist/event-log.js +96 -0
  66. package/dist/event-log.js.map +1 -1
  67. package/dist/fleet-context.d.ts +14 -0
  68. package/dist/fleet-lock.d.ts +28 -0
  69. package/dist/fleet-lock.js +130 -0
  70. package/dist/fleet-lock.js.map +1 -0
  71. package/dist/fleet-manager.d.ts +348 -6
  72. package/dist/fleet-manager.js +1859 -266
  73. package/dist/fleet-manager.js.map +1 -1
  74. package/dist/general-instance.d.ts +9 -0
  75. package/dist/general-instance.js +11 -0
  76. package/dist/general-instance.js.map +1 -0
  77. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +22 -0
  78. package/dist/general-knowledge/skills/model-discovery/SKILL.md +14 -23
  79. package/dist/general-knowledge/skills/session-management/SKILL.md +15 -34
  80. package/dist/hang-detector.d.ts +19 -13
  81. package/dist/hang-detector.js +19 -49
  82. package/dist/hang-detector.js.map +1 -1
  83. package/dist/instance-lifecycle.d.ts +58 -2
  84. package/dist/instance-lifecycle.js +231 -46
  85. package/dist/instance-lifecycle.js.map +1 -1
  86. package/dist/locale.js +7 -1
  87. package/dist/locale.js.map +1 -1
  88. package/dist/mcp-liveness.d.ts +21 -0
  89. package/dist/mcp-liveness.js +27 -0
  90. package/dist/mcp-liveness.js.map +1 -0
  91. package/dist/outbound-handlers.d.ts +9 -0
  92. package/dist/outbound-handlers.js +194 -31
  93. package/dist/outbound-handlers.js.map +1 -1
  94. package/dist/outbound-schemas.d.ts +11 -3
  95. package/dist/outbound-schemas.js +16 -2
  96. package/dist/outbound-schemas.js.map +1 -1
  97. package/dist/pane-write-lock.d.ts +48 -0
  98. package/dist/pane-write-lock.js +73 -0
  99. package/dist/pane-write-lock.js.map +1 -0
  100. package/dist/process-memory.d.ts +31 -0
  101. package/dist/process-memory.js +79 -0
  102. package/dist/process-memory.js.map +1 -0
  103. package/dist/quickstart.js +17 -16
  104. package/dist/quickstart.js.map +1 -1
  105. package/dist/reply-dedup.d.ts +41 -0
  106. package/dist/reply-dedup.js +0 -0
  107. package/dist/reply-dedup.js.map +1 -0
  108. package/dist/restart-progress.d.ts +41 -0
  109. package/dist/restart-progress.js +97 -0
  110. package/dist/restart-progress.js.map +1 -0
  111. package/dist/scheduler/db.js +3 -0
  112. package/dist/scheduler/db.js.map +1 -1
  113. package/dist/sd-notify.d.ts +27 -0
  114. package/dist/sd-notify.js +33 -1
  115. package/dist/sd-notify.js.map +1 -1
  116. package/dist/secret-file.d.ts +33 -0
  117. package/dist/secret-file.js +36 -0
  118. package/dist/secret-file.js.map +1 -0
  119. package/dist/service-installer.d.ts +28 -0
  120. package/dist/service-installer.js +72 -1
  121. package/dist/service-installer.js.map +1 -1
  122. package/dist/settings-api.js +5 -3
  123. package/dist/settings-api.js.map +1 -1
  124. package/dist/setup-wizard.js +9 -7
  125. package/dist/setup-wizard.js.map +1 -1
  126. package/dist/tmux-control.d.ts +58 -5
  127. package/dist/tmux-control.js +102 -14
  128. package/dist/tmux-control.js.map +1 -1
  129. package/dist/tmux-manager.d.ts +31 -1
  130. package/dist/tmux-manager.js +40 -11
  131. package/dist/tmux-manager.js.map +1 -1
  132. package/dist/topic-commands.d.ts +49 -9
  133. package/dist/topic-commands.js +270 -109
  134. package/dist/topic-commands.js.map +1 -1
  135. package/dist/tui-glyphs.d.ts +5 -1
  136. package/dist/tui-glyphs.js +6 -2
  137. package/dist/tui-glyphs.js.map +1 -1
  138. package/dist/types.d.ts +19 -2
  139. package/dist/ui/settings.html +1 -1
  140. package/dist/ui/view.html +123 -2
  141. package/dist/update-marker.d.ts +15 -0
  142. package/dist/update-marker.js +69 -0
  143. package/dist/update-marker.js.map +1 -0
  144. package/dist/usage/format-rich.d.ts +11 -0
  145. package/dist/usage/format-rich.js +121 -0
  146. package/dist/usage/format-rich.js.map +1 -0
  147. package/dist/usage/provider-alerts.d.ts +33 -0
  148. package/dist/usage/provider-alerts.js +57 -0
  149. package/dist/usage/provider-alerts.js.map +1 -0
  150. package/dist/usage/providers.d.ts +130 -0
  151. package/dist/usage/providers.js +1063 -0
  152. package/dist/usage/providers.js.map +1 -0
  153. package/dist/usage/statusline-usage.d.ts +20 -0
  154. package/dist/usage/statusline-usage.js +120 -0
  155. package/dist/usage/statusline-usage.js.map +1 -0
  156. package/dist/usage/usage-api.d.ts +50 -0
  157. package/dist/usage/usage-api.js +197 -0
  158. package/dist/usage/usage-api.js.map +1 -0
  159. package/dist/view-api.js +8 -3
  160. package/dist/view-api.js.map +1 -1
  161. package/dist/web-api.js +7 -3
  162. package/dist/web-api.js.map +1 -1
  163. package/dist/web-auth.d.ts +6 -0
  164. package/dist/web-auth.js +54 -0
  165. package/dist/web-auth.js.map +1 -0
  166. package/package.json +4 -1
  167. package/dist/channel/tool-tracker.d.ts +0 -13
  168. package/dist/channel/tool-tracker.js +0 -58
  169. package/dist/channel/tool-tracker.js.map +0 -1
  170. package/dist/daemon-entry.d.ts +0 -1
  171. package/dist/daemon-entry.js +0 -30
  172. package/dist/daemon-entry.js.map +0 -1
@@ -0,0 +1,9 @@
1
+ import type { FleetConfig } from "./types.js";
2
+ /** Stable user-facing error shared by every manual pause entry point. */
3
+ export declare const GENERAL_PAUSE_ERROR = "General cannot be paused";
4
+ /**
5
+ * General is a coordinator, not an expendable worker. The literal-name fallback
6
+ * preserves the same legacy protection already used by Daemon auto-pause while
7
+ * general_topic covers renamed and per-world General instances.
8
+ */
9
+ export declare function isGeneralInstance(config: FleetConfig | null | undefined, name: string): boolean;
@@ -0,0 +1,11 @@
1
+ /** Stable user-facing error shared by every manual pause entry point. */
2
+ export const GENERAL_PAUSE_ERROR = "General cannot be paused";
3
+ /**
4
+ * General is a coordinator, not an expendable worker. The literal-name fallback
5
+ * preserves the same legacy protection already used by Daemon auto-pause while
6
+ * general_topic covers renamed and per-world General instances.
7
+ */
8
+ export function isGeneralInstance(config, name) {
9
+ return name === "general" || config?.instances[name]?.general_topic === true;
10
+ }
11
+ //# sourceMappingURL=general-instance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"general-instance.js","sourceRoot":"","sources":["../src/general-instance.ts"],"names":[],"mappings":"AAEA,yEAAyE;AACzE,MAAM,CAAC,MAAM,mBAAmB,GAAG,0BAA0B,CAAC;AAE9D;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAsC,EAAE,IAAY;IACpF,OAAO,IAAI,KAAK,SAAS,IAAI,MAAM,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE,aAAa,KAAK,IAAI,CAAC;AAC/E,CAAC"}
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: cross-instance-messaging
3
+ description: Fire-and-queue cross-instance tools — send once, never resend on queued
4
+ ---
5
+
6
+ ## How to send
7
+
8
+ Use fleet tools only (`send_to_instance`, `delegate_task`, `request_information`, `report_result`, `broadcast`):
9
+ - Call returns immediately: `{ sent: true, queued: true }` — success, fleet owns delivery.
10
+ - **Do not wait** for the target to go idle; **do not** treat 30s IPC timeout as failure to re-send.
11
+ - **Error only if the target does not exist** (or similar hard reject) — then fix the name, don't spam.
12
+ - **Never re-send because the reply said `queued`** — that means the message is already queued.
13
+
14
+ ## requires_reply
15
+
16
+ - Means “target should later answer with `report_result` / a real reply”
17
+ - **Not** a synchronous wait for their turn to finish
18
+
19
+ ## Task flow
20
+
21
+ - `delegate_task` → silent work → `report_result` (zero ack-only pings)
22
+ - Cross-instance traffic is `[from:name]` → answer with `send_to_instance` / `report_result`, never `reply`
@@ -1,36 +1,27 @@
1
1
  ---
2
2
  name: model-discovery
3
- description: List available models per backend, configure model in fleet.yaml
3
+ description: Set and discover models pass-through to the CLI, no AgEnD allowlist gate
4
4
  ---
5
5
 
6
- ## Model Names by Backend
6
+ ## How to set a model
7
7
 
8
- Models are specified in fleet.yaml `defaults.model` or per-instance `model` field.
8
+ - fleet.yaml: `defaults.model` or per-instance `model`
9
+ - **Pass-through:** AgEnD no longer blocks unknown model ids — it may **warn**, then still pass the string to the CLI
10
+ - **CLI is source of truth** — if the model is invalid, the backend CLI errors (fix the name there)
9
11
 
10
- | Backend | How to list models | Default |
11
- |---------|-------------------|---------|
12
- | **kiro-cli** | In tmux: send `/model` + Enter → read model list → Esc to close | auto (latest) |
13
- | **claude-code** | `sonnet`, `opus`, `haiku`, `opusplan`, `best`, `sonnet[1m]`, `opus[1m]` | sonnet |
14
- | **antigravity** | Run `agy models` to see available models | Gemini 3.5 Flash (Medium) |
15
- | **codex** | `gpt-4o`, `o3`, `o4-mini` | gpt-4o |
16
- | **opencode** | `opencode models` | depends on provider |
17
- | **grok** | `grok-4.5`, `grok-4.3`, `grok-code`, `grok-build-0.1` | grok default |
12
+ ## Discover real names
18
13
 
19
- **Important for antigravity (agy):**
20
- - `agy models` shows names like `Gemini 3.5 Flash (Medium)` — the parenthetical suffix (Medium/High/Low/Thinking) is the **effort level**, NOT part of the model name.
21
- - When setting model in fleet.yaml, use only the base name WITHOUT the effort suffix.
22
- - Example: `agy models` shows `Gemini 3.5 Flash (Medium)` set `model: "Gemini 3.5 Flash"`
23
- - Example: `agy models` shows `Claude Opus 4.6 (Thinking)` → set `model: "Claude Opus 4.6"`
14
+ | Backend | How |
15
+ |---------|-----|
16
+ | kiro-cli | In pane: `/model` (gpt-*, deepseek-*, minimax-*, glm-*, qwen* supported) |
17
+ | claude-code | `sonnet` / `opus` / `haiku` / `opusplan` / `Fable` / aliases |
18
+ | codex | pane `/model` or docs (`gpt-*`, `o*`) |
19
+ | grok | `grok models` |
20
+ | antigravity | `agy models` — set **base name only** (drop `(Medium)` / `(Thinking)` effort suffix) |
21
+ | opencode | `opencode models` |
24
22
 
25
- **Important:** Model names vary by backend. Always check the actual CLI output rather than guessing names.
26
-
27
- Example fleet.yaml:
28
23
  ```yaml
29
24
  defaults:
30
25
  backend: kiro-cli
31
26
  model: claude-sonnet-4-20250514
32
-
33
- instances:
34
- heavy-task:
35
- model: claude-opus-4-20250514
36
27
  ```
@@ -1,46 +1,27 @@
1
1
  ---
2
2
  name: session-management
3
- description: Where kiro-cli and claude-code store sessions, and how to fork one to a new instance
3
+ description: Session stores, forking, and auth-pause recovery
4
4
  ---
5
5
 
6
+ ## Auth failure (auto-pause)
7
+
8
+ When AgEnD sees `auth_error` it **pauses** that instance (`pausePending` sticky):
9
+ - One notification **per backend** (not per instance) — log in once, same-backend peers recover.
10
+ - Auth is **per-user global** across backends: claude-code, codex, kiro, grok, opencode, antigravity.
11
+ - Messages while paused stay in the **queue** — do not re-send.
12
+ - After the user re-auths: `wake` / normal wake clears `pausePending`.
13
+
6
14
  ## Where sessions live
7
15
 
8
16
  | | kiro-cli | claude-code |
9
17
  |---|---|---|
10
18
  | Store | `~/.kiro/sessions/cli/<uuid>.json` | `~/.claude/projects/<path-encoded>/*.jsonl` |
11
- | Keyed by | session uuid | project (working) directory |
12
- | Reload | `/chat load <file>` | none — `--continue` (latest for the dir) / `--resume <id>` |
13
- | Text export | — | `/export` (plain text, **not** reloadable) |
14
-
15
- `<path-encoded>` = the absolute working_directory with `/` → `-`
16
- (e.g. `/home/han/Projects/AgEnD` → `-home-han-Projects-AgEnD`).
17
-
18
- ## Fork a session to a new instance
19
-
20
- Confirm the source is **idle** first (`describe_instance` / `get_fleet_status`) — don't fork mid-task.
21
-
22
- **kiro-cli** — save, copy, load:
23
- 1. On the source, save: `/chat save <name>.json -f` (paste via tmux if needed).
24
- 2. `create_instance` (same backend).
25
- 3. `cp ~/.agend/workspaces/<source>/<name>.json ~/.agend/workspaces/<target>/`
26
- 4. Load on the target: `/chat load <name>.json`, or set `pre_task_command: "/chat load <name>.json"`.
27
-
28
- **claude-code** — copy the `.jsonl` (no save/load command):
29
- 1. Newest source session: `ls -lt ~/.claude/projects/<source-encoded>/*.jsonl | head`
30
- 2. `create_instance` (backend `claude-code`); note its working_directory.
31
- 3. Copy into the target's encoded project dir:
32
- ```bash
33
- TARGET_ENC="$(echo '<target-working-dir>' | sed 's#/#-#g')"
34
- mkdir -p ~/.claude/projects/$TARGET_ENC
35
- cp ~/.claude/projects/<source-encoded>/<session>.jsonl ~/.claude/projects/$TARGET_ENC/
36
- ```
37
- 4. Start the target — claude-code resumes the newest `.jsonl` via `--continue`.
19
+ | Reload | `/chat load <file>` | `--continue` / `--resume <id>` |
38
20
 
39
- **Caveats:** a claude-code session only truly makes sense under its original working_directory
40
- (paths inside the transcript refer to it). Pick the right `.jsonl` if several exist (newest by
41
- mtime; compaction/branches create new files). `/export` is text only, not reloadable.
21
+ `<path-encoded>` = absolute cwd with `/` `-`.
42
22
 
43
- ## Backup
23
+ ## Fork (source must be idle)
44
24
 
45
- Sessions are plain files back them up by copying the store paths above
46
- (e.g. `cp ~/.kiro/sessions/cli/*.json <dest>/`). Only copy while the instance is idle.
25
+ - **kiro:** `/chat save name.json -f` `create_instance` copy workspace file `/chat load name.json`
26
+ - **claude-code:** copy newest `*.jsonl` into target's encoded project dir start (uses `--continue`)
27
+ - Prefer `replace_instance` when the whole session is poisoned (see instance-lifecycle)
@@ -1,16 +1,22 @@
1
1
  import { EventEmitter } from "node:events";
2
+ /**
3
+ * Carries "this instance looks hung" from the daemon to the fleet manager.
4
+ *
5
+ * It is only an event bridge. Hang detection itself lives in the daemon's pane-state
6
+ * machine, which emits `hang` directly when a pane stops changing for the configured
7
+ * stuck timeout; `instance-lifecycle` subscribes to that and notifies.
8
+ *
9
+ * It used to also contain a silence-timer state machine — `start()`, `isHung()`,
10
+ * `hungEmitted`, and timestamps fed by `recordActivity` / `recordInbound` /
11
+ * `recordStatuslineUpdate`. None of it ran: `start()` was never called from
12
+ * anywhere, so `isHung()` was unreachable, the timestamps were written and never
13
+ * read, and the constructor's `timeoutMinutes` was ignored (the real stuck timeout
14
+ * is read separately by the pane monitor). Its tests exercised that dead logic —
15
+ * two had identical setup with contradictory expectations, which can only pass
16
+ * unnoticed when neither runs against anything real.
17
+ *
18
+ * Kept as a named class rather than a bare EventEmitter so the daemon → lifecycle
19
+ * wiring stays typed and greppable.
20
+ */
2
21
  export declare class HangDetector extends EventEmitter {
3
- private lastActivityTs;
4
- private lastStatuslineTs;
5
- private lastInboundTs;
6
- private hungEmitted;
7
- private checkTimer;
8
- private timeoutMs;
9
- constructor(timeoutMinutes: number);
10
- recordActivity(): void;
11
- recordInbound(): void;
12
- recordStatuslineUpdate(): void;
13
- isHung(): boolean;
14
- start(intervalMs?: number): void;
15
- stop(): void;
16
22
  }
@@ -1,53 +1,23 @@
1
1
  import { EventEmitter } from "node:events";
2
+ /**
3
+ * Carries "this instance looks hung" from the daemon to the fleet manager.
4
+ *
5
+ * It is only an event bridge. Hang detection itself lives in the daemon's pane-state
6
+ * machine, which emits `hang` directly when a pane stops changing for the configured
7
+ * stuck timeout; `instance-lifecycle` subscribes to that and notifies.
8
+ *
9
+ * It used to also contain a silence-timer state machine — `start()`, `isHung()`,
10
+ * `hungEmitted`, and timestamps fed by `recordActivity` / `recordInbound` /
11
+ * `recordStatuslineUpdate`. None of it ran: `start()` was never called from
12
+ * anywhere, so `isHung()` was unreachable, the timestamps were written and never
13
+ * read, and the constructor's `timeoutMinutes` was ignored (the real stuck timeout
14
+ * is read separately by the pane monitor). Its tests exercised that dead logic —
15
+ * two had identical setup with contradictory expectations, which can only pass
16
+ * unnoticed when neither runs against anything real.
17
+ *
18
+ * Kept as a named class rather than a bare EventEmitter so the daemon → lifecycle
19
+ * wiring stays typed and greppable.
20
+ */
2
21
  export class HangDetector extends EventEmitter {
3
- lastActivityTs = 0;
4
- lastStatuslineTs = 0;
5
- lastInboundTs = 0;
6
- hungEmitted = false;
7
- checkTimer = null;
8
- timeoutMs;
9
- constructor(timeoutMinutes) {
10
- super();
11
- this.timeoutMs = timeoutMinutes * 60 * 1000;
12
- }
13
- recordActivity() {
14
- this.lastActivityTs = Date.now();
15
- if (this.hungEmitted) {
16
- this.hungEmitted = false;
17
- }
18
- }
19
- recordInbound() {
20
- this.lastInboundTs = Date.now();
21
- }
22
- recordStatuslineUpdate() {
23
- this.lastStatuslineTs = Date.now();
24
- }
25
- isHung() {
26
- if (this.lastActivityTs === 0)
27
- return false;
28
- if (this.lastInboundTs === 0)
29
- return false;
30
- const now = Date.now();
31
- // Only flag as hung if:
32
- // 1. There's an inbound that hasn't been answered (no activity since inbound)
33
- // 2. Timeout has elapsed since that inbound
34
- const noActivitySinceInbound = this.lastActivityTs < this.lastInboundTs;
35
- const stale = now - this.lastInboundTs > this.timeoutMs;
36
- return stale && noActivitySinceInbound;
37
- }
38
- start(intervalMs = 60_000) {
39
- this.checkTimer = setInterval(() => {
40
- if (this.isHung() && !this.hungEmitted) {
41
- this.hungEmitted = true;
42
- this.emit("hang");
43
- }
44
- }, intervalMs);
45
- }
46
- stop() {
47
- if (this.checkTimer) {
48
- clearInterval(this.checkTimer);
49
- this.checkTimer = null;
50
- }
51
- }
52
22
  }
53
23
  //# sourceMappingURL=hang-detector.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hang-detector.js","sourceRoot":"","sources":["../src/hang-detector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,OAAO,YAAa,SAAQ,YAAY;IACpC,cAAc,GAAG,CAAC,CAAC;IACnB,gBAAgB,GAAG,CAAC,CAAC;IACrB,aAAa,GAAG,CAAC,CAAC;IAClB,WAAW,GAAG,KAAK,CAAC;IACpB,UAAU,GAA0C,IAAI,CAAC;IACzD,SAAS,CAAS;IAE1B,YAAY,cAAsB;QAChC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,SAAS,GAAG,cAAc,GAAG,EAAE,GAAG,IAAI,CAAC;IAC9C,CAAC;IAED,cAAc;QACZ,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACjC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,aAAa;QACX,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAClC,CAAC;IAED,sBAAsB;QACpB,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACrC,CAAC;IAED,MAAM;QACJ,IAAI,IAAI,CAAC,cAAc,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAC5C,IAAI,IAAI,CAAC,aAAa,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,wBAAwB;QACxB,8EAA8E;QAC9E,4CAA4C;QAC5C,MAAM,sBAAsB,GAAG,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,aAAa,CAAC;QACxE,MAAM,KAAK,GAAG,GAAG,GAAG,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC;QACxD,OAAO,KAAK,IAAI,sBAAsB,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,UAAU,GAAG,MAAM;QACvB,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE;YACjC,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;gBACvC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;gBACxB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACpB,CAAC;QACH,CAAC,EAAE,UAAU,CAAC,CAAC;IACjB,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC/B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"hang-detector.js","sourceRoot":"","sources":["../src/hang-detector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,YAAa,SAAQ,YAAY;CAAG"}
@@ -5,6 +5,7 @@ import type { IpcClient } from "./channel/ipc-bridge.js";
5
5
  import type { EventLog } from "./event-log.js";
6
6
  import type { TmuxControlClient } from "./tmux-control.js";
7
7
  import type { FleetInstructionsParams } from "./instructions.js";
8
+ export { isFleetStartCommandLine } from "./fleet-lock.js";
8
9
  export interface BackendInstallationInfo {
9
10
  binary: string;
10
11
  install: string;
@@ -13,8 +14,6 @@ export interface BackendInstallationInfo {
13
14
  export declare const BACKEND_INSTALLATION_INFO: Readonly<Record<string, BackendInstallationInfo>>;
14
15
  /** Check one executable using the same PATH visible to the fleet process. */
15
16
  export declare function checkBinaryInstalled(binary: string): boolean;
16
- /** Whether a process command line identifies the shared AgEnD fleet process. */
17
- export declare function isFleetStartCommandLine(commandLine: string): boolean;
18
17
  /**
19
18
  * Return why an instance daemon PID must not receive SIGTERM, or null when the
20
19
  * stale-process cleanup may proceed. daemon.pid historically stores the shared
@@ -50,6 +49,10 @@ export interface LifecycleContext {
50
49
  touchActivity(name: string): void;
51
50
  sendHangNotification(name: string, unchangedForMs?: number): Promise<void>;
52
51
  notifyInstanceTopic(name: string, text: string): void;
52
+ /** True for a dynamic ClassicBot channel instance (not a fleet topic). */
53
+ isClassicInstance?(name: string): boolean;
54
+ /** True while the fleet is stopping on purpose or an `agend update` is running. */
55
+ isPlannedRestart(): boolean;
53
56
  /** List claimed tasks for an instance (from task board). Returns empty array if unavailable. */
54
57
  listClaimedTasks(assignee: string): Array<{
55
58
  id: string;
@@ -64,6 +67,11 @@ export interface LifecycleContext {
64
67
  reactMessageStatus(instanceName: string, chatId: string, messageId: string, emoji: string): void;
65
68
  startPersistedPausedInstance(name: string): Promise<void>;
66
69
  }
70
+ /** What attachIncidentHandlers needs from a Daemon — the real one satisfies it. */
71
+ export interface IncidentEventSource {
72
+ on(event: string, handler: (...args: any[]) => void): unknown;
73
+ requestPauseWhenIdle(): void;
74
+ }
67
75
  /** Arguments accepted by handleCreate — mirrors CreateInstanceArgs in outbound-schemas.ts
68
76
  * plus internal-only fields forwarded by deploy_template (profile-derived). */
69
77
  export interface LifecycleCreateArgs {
@@ -99,7 +107,55 @@ export declare class InstanceLifecycle {
99
107
  private ctx;
100
108
  /** Active daemon processes: instanceName → Daemon */
101
109
  readonly daemons: Map<string, import("./daemon.js").Daemon>;
110
+ /** backend → last auth-error alert time, so one expiry sends one alert. */
111
+ private lastAuthAlertAt;
112
+ /**
113
+ * Minimum gap between MCP-revival auto-restarts of one instance. Kept here —
114
+ * not in the daemon — because each restart replaces the daemon object, which
115
+ * would take an in-daemon cooldown with it and allow a tight restart loop.
116
+ */
117
+ private static readonly MCP_AUTO_RESTART_COOLDOWN_MS;
118
+ /** instanceName → time of the last MCP-revival auto-restart. */
119
+ private mcpAutoRestartAt;
102
120
  constructor(ctx: LifecycleContext);
121
+ /**
122
+ * Report an incident to the user — unless the fleet is deliberately going
123
+ * down, in which case the "incident" is the shutdown doing its job.
124
+ *
125
+ * `agend update` stops instances, kills their MCP servers and restarts the
126
+ * daemon; every one of those looked like a crash to the alert path, so an
127
+ * upgrade produced a burst of ⚠️ for things that were working correctly. That
128
+ * burst is worse than noise: it teaches the operator to ignore the same alert
129
+ * that matters when a process really does die on its own.
130
+ *
131
+ * The event log and daemon.log entries still happen at the call sites — this
132
+ * suppresses the chat message, not the record. A crash outside a planned
133
+ * restart notifies exactly as before.
134
+ */
135
+ private notifyIncident;
136
+ /** Backend a running instance uses (config → fleet default). */
137
+ private backendOf;
138
+ /**
139
+ * One alert per backend per cooldown, naming every affected instance — a CLI's
140
+ * credentials are shared, so N instances failing is ONE problem with ONE fix
141
+ * (re-login once). The per-instance daemon cooldown can't dedupe across
142
+ * instances, so the fleet-level map does it here.
143
+ */
144
+ private notifyAuthErrorOnce;
145
+ /**
146
+ * System errors from a ClassicBot belong in the operator's General topic,
147
+ * never in the end user's chat channel. Fleet-topic instances retain their
148
+ * existing local notification target.
149
+ */
150
+ private ptyErrorNotificationTarget;
151
+ /**
152
+ * Handlers for the events that mean "something went wrong".
153
+ *
154
+ * Extracted from start() so the planned-restart suppression can be exercised
155
+ * against a plain event emitter — the alternative is a real Daemon, which
156
+ * means a real tmux window, which is why this rule went untested before.
157
+ */
158
+ attachIncidentHandlers(name: string, daemon: IncidentEventSource): void;
103
159
  start(name: string, config: InstanceConfig, topicMode: boolean, runtimeIdentity?: FleetInstructionsParams["runtimeIdentity"]): Promise<void>;
104
160
  isPaused(name: string): boolean;
105
161
  getLastPausedAt(name: string): number | null;