@songsid/agend 2.1.4 → 2.1.5-beta.10

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 (82) hide show
  1. package/dist/backend/claude-code.js +2 -2
  2. package/dist/backend/claude-code.js.map +1 -1
  3. package/dist/backend/types.d.ts +22 -0
  4. package/dist/backend/types.js +23 -1
  5. package/dist/backend/types.js.map +1 -1
  6. package/dist/channel/adapters/discord.d.ts +2 -0
  7. package/dist/channel/adapters/discord.js +25 -3
  8. package/dist/channel/adapters/discord.js.map +1 -1
  9. package/dist/channel/adapters/telegram.d.ts +5 -0
  10. package/dist/channel/adapters/telegram.js +11 -0
  11. package/dist/channel/adapters/telegram.js.map +1 -1
  12. package/dist/channel/types.d.ts +7 -0
  13. package/dist/cli.js +37 -37
  14. package/dist/cli.js.map +1 -1
  15. package/dist/config.js +38 -0
  16. package/dist/config.js.map +1 -1
  17. package/dist/daemon.d.ts +38 -0
  18. package/dist/daemon.js +95 -43
  19. package/dist/daemon.js.map +1 -1
  20. package/dist/fleet-context.d.ts +5 -0
  21. package/dist/fleet-manager.d.ts +126 -0
  22. package/dist/fleet-manager.js +688 -95
  23. package/dist/fleet-manager.js.map +1 -1
  24. package/dist/full-restart.d.ts +20 -0
  25. package/dist/full-restart.js +39 -0
  26. package/dist/full-restart.js.map +1 -0
  27. package/dist/locale.js +72 -2
  28. package/dist/locale.js.map +1 -1
  29. package/dist/login-controller.d.ts +127 -0
  30. package/dist/login-controller.js +450 -0
  31. package/dist/login-controller.js.map +1 -0
  32. package/dist/login-flows.d.ts +41 -3
  33. package/dist/login-flows.js +37 -5
  34. package/dist/login-flows.js.map +1 -1
  35. package/dist/login-manager.d.ts +12 -1
  36. package/dist/login-manager.js +64 -7
  37. package/dist/login-manager.js.map +1 -1
  38. package/dist/login-window-lock.d.ts +26 -0
  39. package/dist/login-window-lock.js +64 -0
  40. package/dist/login-window-lock.js.map +1 -0
  41. package/dist/restart-progress.d.ts +35 -5
  42. package/dist/restart-progress.js +195 -34
  43. package/dist/restart-progress.js.map +1 -1
  44. package/dist/service-restart-selection.d.ts +27 -0
  45. package/dist/service-restart-selection.js +25 -0
  46. package/dist/service-restart-selection.js.map +1 -0
  47. package/dist/tmux-manager.d.ts +19 -0
  48. package/dist/tmux-manager.js +79 -12
  49. package/dist/tmux-manager.js.map +1 -1
  50. package/dist/topic-commands.js +23 -7
  51. package/dist/topic-commands.js.map +1 -1
  52. package/dist/types.d.ts +18 -0
  53. package/dist/ui/web-terminal/terminal.css +17 -0
  54. package/dist/ui/web-terminal/terminal.html +44 -0
  55. package/dist/ui/web-terminal/terminal.js +148 -0
  56. package/dist/ui/web-terminal/vendor/LICENSE.addon-fit +19 -0
  57. package/dist/ui/web-terminal/vendor/LICENSE.addon-web-links +19 -0
  58. package/dist/ui/web-terminal/vendor/LICENSE.xterm +21 -0
  59. package/dist/ui/web-terminal/vendor/VENDORED.md +22 -0
  60. package/dist/ui/web-terminal/vendor/addon-fit.js +1 -0
  61. package/dist/ui/web-terminal/vendor/addon-web-links.js +1 -0
  62. package/dist/ui/web-terminal/vendor/xterm.css +285 -0
  63. package/dist/ui/web-terminal/vendor/xterm.js +1 -0
  64. package/dist/update-marker.d.ts +11 -1
  65. package/dist/update-marker.js +19 -4
  66. package/dist/update-marker.js.map +1 -1
  67. package/dist/update-progress.d.ts +1 -1
  68. package/dist/update-progress.js +12 -0
  69. package/dist/update-progress.js.map +1 -1
  70. package/dist/usage/providers.d.ts +39 -0
  71. package/dist/usage/providers.js +69 -3
  72. package/dist/usage/providers.js.map +1 -1
  73. package/dist/web-terminal-http.d.ts +61 -0
  74. package/dist/web-terminal-http.js +422 -0
  75. package/dist/web-terminal-http.js.map +1 -0
  76. package/dist/web-terminal.d.ts +336 -0
  77. package/dist/web-terminal.js +888 -0
  78. package/dist/web-terminal.js.map +1 -0
  79. package/dist/ws-server.d.ts +69 -0
  80. package/dist/ws-server.js +392 -0
  81. package/dist/ws-server.js.map +1 -0
  82. package/package.json +1 -1
@@ -1,11 +1,31 @@
1
1
  import { t } from "./locale.js";
2
2
  import { updateElapsedSeconds } from "./update-progress.js";
3
+ const TERMINAL_DELIVERY_RETRY_MS = 1_000;
4
+ export const RESTART_PROGRESS_TERMINAL_TIMEOUT_MS = 30_000;
3
5
  function formatElapsed(ms) {
4
6
  const seconds = Math.max(0, Math.floor(ms / 1000));
5
7
  const minutes = Math.floor(seconds / 60);
6
8
  const remainder = seconds % 60;
7
9
  return minutes > 0 ? `${minutes}m ${remainder}s` : `${seconds}s`;
8
10
  }
11
+ /** One terminal text shared by the in-place edit and FleetManager fallback. */
12
+ export function formatRestartProgressCompletion(mode, summary, startedAt, now = Date.now()) {
13
+ const elapsed = formatElapsed(now - startedAt);
14
+ const lines = mode === "update"
15
+ ? [t("update.progress.complete", summary.version, summary.running, summary.total, updateElapsedSeconds(startedAt, now))]
16
+ : mode === "reload"
17
+ ? [t("restart.progress.complete_summary", summary.version, summary.running, summary.total, updateElapsedSeconds(startedAt, now))]
18
+ : [`✅ Fleet ready — ${summary.running}/${summary.total} instances running (${elapsed}) · v${summary.version}`];
19
+ if (summary.pausedNames.length) {
20
+ lines.push(`⏸ Paused (${summary.pausedNames.length}): ${summary.pausedNames.join(", ")}`);
21
+ }
22
+ if (summary.failedNames?.length) {
23
+ lines.push(`⚠️ Failed (${summary.failedNames.length}): ${summary.failedNames.join(", ")}`);
24
+ }
25
+ if (mode === "update" && summary.tipText)
26
+ lines.push("", summary.tipText);
27
+ return lines.join("\n");
28
+ }
9
29
  /** One General-topic message that is edited throughout a fleet startup. */
10
30
  export class RestartProgress {
11
31
  total;
@@ -17,7 +37,10 @@ export class RestartProgress {
17
37
  messageId = null;
18
38
  lastReportedReady = 0;
19
39
  updateTimer = null;
20
- editChain = Promise.resolve();
40
+ /** At most one provider edit is active and one newest progress frame waits
41
+ * behind it. This prevents a slow adapter from accumulating 1 Hz history. */
42
+ progressEditWorker = null;
43
+ pendingProgressText = null;
21
44
  finished = false;
22
45
  mode;
23
46
  constructor(total, startedAt, logger, options = {}) {
@@ -25,7 +48,7 @@ export class RestartProgress {
25
48
  this.startedAt = startedAt;
26
49
  this.logger = logger;
27
50
  this.mode = options.mode ?? "restart";
28
- this.enabled = this.mode === "update" || total > 5;
51
+ this.enabled = this.mode === "update" || this.mode === "reload" || total > 5;
29
52
  }
30
53
  /** May be called before the channel adapter is ready; progress is retained. */
31
54
  markReady() {
@@ -40,9 +63,12 @@ export class RestartProgress {
40
63
  async start(target) {
41
64
  if (!this.enabled || !target || this.messageId || this.finished)
42
65
  return false;
66
+ const adapter = this.resolveAdapter(target);
67
+ if (!adapter)
68
+ return false;
43
69
  this.target = target;
44
70
  try {
45
- const sent = await target.adapter.sendText(target.chatId, `🔄 Fleet restarting — ${this.total} instances starting...`, { threadId: target.threadId });
71
+ const sent = await adapter.sendText(target.chatId, `🔄 Fleet restarting — ${this.total} instances starting...`, { threadId: target.threadId });
46
72
  this.messageId = sent.messageId;
47
73
  if (this.ready >= 5)
48
74
  this.queueProgressEdit();
@@ -58,45 +84,41 @@ export class RestartProgress {
58
84
  }
59
85
  /** Adopt the pre-update message after the new fleet process reconnects. */
60
86
  async resume(target, messageId) {
61
- if (!this.enabled || this.mode !== "update" || !target || !messageId || this.finished)
87
+ if (!this.enabled || this.mode === "restart" || !target || !messageId || this.finished)
88
+ return false;
89
+ if (!target.adapter && !target.resolveAdapter)
62
90
  return false;
63
91
  this.target = target;
64
92
  this.messageId = messageId;
65
- this.enqueueEdit(t("update.progress.starting", updateElapsedSeconds(this.startedAt)));
66
- await this.editChain;
93
+ // Do not let a stuck provider call hold fleet startup. The coalescing worker
94
+ // owns this best-effort progress frame; finish() has its own hard deadline.
95
+ this.scheduleProgressEdit(this.mode === "reload"
96
+ ? t("restart.progress.starting", updateElapsedSeconds(this.startedAt))
97
+ : t("update.progress.starting", updateElapsedSeconds(this.startedAt)));
67
98
  this.updateTimer = setInterval(() => this.queueProgressEdit(), 1_000);
68
99
  this.updateTimer.unref?.();
69
100
  return true;
70
101
  }
71
- /** Edit the original message to its terminal state. Returns true if it existed. */
102
+ /** Deliver the terminal state. Returns true only after an edit or fresh fallback succeeds. */
72
103
  async finish(summary) {
73
104
  if (!this.enabled)
74
105
  return false;
106
+ // Capture the current worker before preventing it from starting any newer
107
+ // pending frame. The terminal path waits for this exact edit so the final
108
+ // edit cannot overtake an ordinary, healthy provider round trip.
109
+ const progressEditWorker = this.progressEditWorker;
75
110
  this.finished = true;
111
+ this.pendingProgressText = null;
76
112
  if (this.updateTimer) {
77
113
  clearInterval(this.updateTimer);
78
114
  this.updateTimer = null;
79
115
  }
80
116
  if (!this.target || !this.messageId)
81
117
  return false;
82
- const elapsed = formatElapsed(Date.now() - this.startedAt);
83
- const lines = this.mode === "update" && summary
84
- ? [t("update.progress.complete", summary.version, summary.running, summary.total, updateElapsedSeconds(this.startedAt))]
85
- : summary
86
- ? [`✅ Fleet ready — ${summary.running}/${summary.total} instances running (${elapsed}) · v${summary.version}`]
87
- : [`✅ Fleet ready — ${this.ready}/${this.total} instances started (${elapsed})`];
88
- if (summary?.pausedNames.length) {
89
- lines.push(`⏸ Paused (${summary.pausedNames.length}): ${summary.pausedNames.join(", ")}`);
90
- }
91
- if (summary?.failedNames?.length) {
92
- lines.push(`⚠️ Failed (${summary.failedNames.length}): ${summary.failedNames.join(", ")}`);
93
- }
94
- if (this.mode === "update" && summary?.tipText) {
95
- lines.push("", summary.tipText);
96
- }
97
- this.enqueueEdit(lines.join("\n"));
98
- await this.editChain;
99
- return true;
118
+ const text = summary
119
+ ? formatRestartProgressCompletion(this.mode, summary, this.startedAt)
120
+ : `✅ Fleet ready — ${this.ready}/${this.total} instances started (${formatElapsed(Date.now() - this.startedAt)})`;
121
+ return this.deliverTerminal(text, progressEditWorker);
100
122
  }
101
123
  get readyCount() { return this.ready; }
102
124
  queueProgressEdit() {
@@ -105,17 +127,156 @@ export class RestartProgress {
105
127
  this.lastReportedReady = this.ready;
106
128
  const text = this.mode === "update"
107
129
  ? t("update.progress.instances", this.ready, this.total, updateElapsedSeconds(this.startedAt))
108
- : `🔄 Fleet restarting — ${this.ready}/${this.total} ready...`;
109
- this.enqueueEdit(text);
130
+ : this.mode === "reload"
131
+ ? t("restart.progress.instances", this.ready, this.total, updateElapsedSeconds(this.startedAt))
132
+ : `🔄 Fleet restarting — ${this.ready}/${this.total} ready...`;
133
+ this.scheduleProgressEdit(text);
110
134
  }
111
- enqueueEdit(text) {
112
- const target = this.target;
113
- const messageId = this.messageId;
114
- if (!target || !messageId)
135
+ resolveAdapter(target = this.target) {
136
+ if (!target)
137
+ return undefined;
138
+ return target.resolveAdapter ? target.resolveAdapter() : target.adapter;
139
+ }
140
+ adapterReady(adapter) {
141
+ const health = adapter.getHealthSnapshot?.();
142
+ return !health || health.isReady;
143
+ }
144
+ /** Tail-only coalescing for non-terminal progress. */
145
+ scheduleProgressEdit(text) {
146
+ if (this.finished || !this.target || !this.messageId)
115
147
  return;
116
- this.editChain = this.editChain
117
- .then(() => target.adapter.editMessage(target.chatId, messageId, text, target.threadId))
118
- .catch(err => this.logger.warn({ err }, "Failed to edit fleet restart progress"));
148
+ this.pendingProgressText = text;
149
+ if (this.progressEditWorker)
150
+ return;
151
+ const worker = this.drainProgressEdits();
152
+ this.progressEditWorker = worker;
153
+ const complete = () => {
154
+ if (this.progressEditWorker === worker)
155
+ this.progressEditWorker = null;
156
+ if (!this.finished && this.pendingProgressText != null)
157
+ this.scheduleProgressEdit(this.pendingProgressText);
158
+ };
159
+ void worker.then(complete, err => {
160
+ this.logger.warn({ err }, "Fleet restart progress worker failed");
161
+ complete();
162
+ });
163
+ }
164
+ async drainProgressEdits() {
165
+ while (!this.finished) {
166
+ const text = this.pendingProgressText;
167
+ if (text == null)
168
+ return;
169
+ this.pendingProgressText = null;
170
+ const target = this.target;
171
+ const messageId = this.messageId;
172
+ const adapter = this.resolveAdapter(target);
173
+ if (!target || !messageId || !adapter || !this.adapterReady(adapter))
174
+ continue;
175
+ try {
176
+ await adapter.editMessage(target.chatId, messageId, text, target.threadId);
177
+ }
178
+ catch (err) {
179
+ this.logger.warn({ err }, "Failed to edit fleet restart progress");
180
+ }
181
+ }
182
+ }
183
+ /**
184
+ * A terminal state must mean "delivered", not merely "had a message id".
185
+ * Wait through a bounded adapter-recovery window, re-resolving replacement
186
+ * adapter objects each time. If editing the adopted message fails, post a
187
+ * fresh completion message so the user is never left at the last X/N update.
188
+ */
189
+ async deliverTerminal(text, progressEditWorker) {
190
+ const target = this.target;
191
+ if (!target || !this.messageId)
192
+ return false;
193
+ const deadline = Date.now() + RESTART_PROGRESS_TERMINAL_TIMEOUT_MS;
194
+ if (progressEditWorker) {
195
+ const drained = await this.beforeDeadline(() => progressEditWorker, deadline);
196
+ if (drained.status === "timeout") {
197
+ this.logger.warn({}, "Timed out waiting for the in-flight fleet progress edit before terminal delivery");
198
+ return this.terminalDeliveryFailed();
199
+ }
200
+ if (drained.status === "rejected") {
201
+ this.logger.warn({ err: drained.reason }, "Fleet progress edit worker rejected before terminal delivery");
202
+ }
203
+ }
204
+ let adapter = await this.waitForReadyAdapter(deadline);
205
+ if (!adapter)
206
+ return this.terminalDeliveryFailed();
207
+ const editAdapter = adapter;
208
+ const edit = await this.beforeDeadline(() => editAdapter.editMessage(target.chatId, this.messageId, text, target.threadId), deadline);
209
+ if (edit.status === "fulfilled")
210
+ return true;
211
+ if (edit.status === "rejected") {
212
+ this.logger.warn({ err: edit.reason }, "Failed to edit fleet restart progress terminal state");
213
+ }
214
+ else {
215
+ this.logger.warn({}, "Timed out editing fleet restart progress terminal state");
216
+ }
217
+ // The edit may have failed because the gateway dropped between the first
218
+ // readiness probe and the write. Re-check and, if necessary, wait for the
219
+ // replacement generation before the one and only fresh-send attempt.
220
+ adapter = await this.waitForReadyAdapter(deadline);
221
+ if (!adapter)
222
+ return this.terminalDeliveryFailed();
223
+ const sendAdapter = adapter;
224
+ const sent = await this.beforeDeadline(() => sendAdapter.sendText(target.chatId, text, { threadId: target.threadId }), deadline);
225
+ if (sent.status === "fulfilled") {
226
+ this.logger.warn({}, "Posted fresh fleet completion after terminal progress edit was unavailable");
227
+ return true;
228
+ }
229
+ if (sent.status === "rejected") {
230
+ this.logger.warn({ err: sent.reason }, "Failed to post fresh fleet completion after edit failure");
231
+ }
232
+ else {
233
+ this.logger.warn({}, "Timed out posting fresh fleet completion after edit failure");
234
+ }
235
+ return this.terminalDeliveryFailed();
236
+ }
237
+ async waitForReadyAdapter(deadline) {
238
+ while (true) {
239
+ const remaining = deadline - Date.now();
240
+ if (remaining <= 0)
241
+ return undefined;
242
+ const adapter = this.resolveAdapter();
243
+ if (adapter && this.adapterReady(adapter))
244
+ return adapter;
245
+ await new Promise(resolve => {
246
+ const timer = setTimeout(resolve, Math.min(TERMINAL_DELIVERY_RETRY_MS, remaining));
247
+ timer.unref?.();
248
+ });
249
+ }
250
+ }
251
+ async beforeDeadline(start, deadline) {
252
+ const remaining = deadline - Date.now();
253
+ if (remaining <= 0)
254
+ return { status: "timeout" };
255
+ let work;
256
+ try {
257
+ work = start();
258
+ }
259
+ catch (reason) {
260
+ return { status: "rejected", reason };
261
+ }
262
+ let timer;
263
+ const timeout = new Promise(resolve => {
264
+ timer = setTimeout(() => resolve({ status: "timeout" }), remaining);
265
+ timer.unref?.();
266
+ });
267
+ const settled = work.then(value => ({ status: "fulfilled", value }), reason => ({ status: "rejected", reason }));
268
+ const result = await Promise.race([settled, timeout]);
269
+ if (timer)
270
+ clearTimeout(timer);
271
+ return result;
272
+ }
273
+ terminalDeliveryFailed() {
274
+ const data = { timeout_ms: RESTART_PROGRESS_TERMINAL_TIMEOUT_MS };
275
+ if (this.logger.error)
276
+ this.logger.error(data, "Fleet completion could not be delivered after adapter recovery wait");
277
+ else
278
+ this.logger.warn(data, "Fleet completion could not be delivered after adapter recovery wait");
279
+ return false;
119
280
  }
120
281
  }
121
282
  //# sourceMappingURL=restart-progress.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"restart-progress.js","sourceRoot":"","sources":["../src/restart-progress.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"restart-progress.js","sourceRoot":"","sources":["../src/restart-progress.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { ServiceState } from "./service-installer.js";
2
+ /**
3
+ * `restartSystemdService()` returns false for both a definitive command error
4
+ * and its five-minute Type=notify timeout. The latter can still finish and
5
+ * start a healthy replacement, so callers must leave the cross-process marker
6
+ * pending for that replacement instead of declaring failure.
7
+ */
8
+ export declare const SYSTEMD_RESTART_INDETERMINATE_EXIT_CODE = 0;
9
+ export interface SystemdRestartSelectionInput {
10
+ platform: "macos" | "linux";
11
+ systemServiceInstalled: boolean;
12
+ systemState: ServiceState;
13
+ userServiceInstalled: boolean;
14
+ userState: ServiceState;
15
+ }
16
+ export interface SystemdRestartTarget {
17
+ unit: "agend" | "com.agend.fleet";
18
+ user: boolean;
19
+ state: ServiceState;
20
+ }
21
+ /**
22
+ * Select the authoritative systemd unit before considering launchd or a
23
+ * detached PID. This pure boundary is deliberately injectable in tests: a
24
+ * chat-triggered full restart must never bypass an installed systemd unit and
25
+ * fall back to a SIGUSR1/in-process replacement.
26
+ */
27
+ export declare function selectSystemdRestartTarget(input: SystemdRestartSelectionInput): SystemdRestartTarget | null;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * `restartSystemdService()` returns false for both a definitive command error
3
+ * and its five-minute Type=notify timeout. The latter can still finish and
4
+ * start a healthy replacement, so callers must leave the cross-process marker
5
+ * pending for that replacement instead of declaring failure.
6
+ */
7
+ export const SYSTEMD_RESTART_INDETERMINATE_EXIT_CODE = 0;
8
+ /**
9
+ * Select the authoritative systemd unit before considering launchd or a
10
+ * detached PID. This pure boundary is deliberately injectable in tests: a
11
+ * chat-triggered full restart must never bypass an installed systemd unit and
12
+ * fall back to a SIGUSR1/in-process replacement.
13
+ */
14
+ export function selectSystemdRestartTarget(input) {
15
+ if (input.platform === "macos")
16
+ return null;
17
+ if (input.systemServiceInstalled) {
18
+ return { unit: "agend", user: false, state: input.systemState };
19
+ }
20
+ if (input.userServiceInstalled) {
21
+ return { unit: "com.agend.fleet", user: true, state: input.userState };
22
+ }
23
+ return null;
24
+ }
25
+ //# sourceMappingURL=service-restart-selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-restart-selection.js","sourceRoot":"","sources":["../src/service-restart-selection.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uCAAuC,GAAG,CAAC,CAAC;AAgBzD;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CACxC,KAAmC;IAEnC,IAAI,KAAK,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IAC5C,IAAI,KAAK,CAAC,sBAAsB,EAAE,CAAC;QACjC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;IAClE,CAAC;IACD,IAAI,KAAK,CAAC,oBAAoB,EAAE,CAAC;QAC/B,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC;IACzE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -24,12 +24,25 @@ export declare class TmuxManager {
24
24
  /** PID identifies a concrete tmux server generation. */
25
25
  static getServerPid(sessionName: string): Promise<number | null>;
26
26
  static sessionExists(name: string): Promise<boolean>;
27
+ /**
28
+ * Bounded, tri-state-safe variant for the login/install path: true = exists,
29
+ * false = tmux positively says the session is absent, anything else
30
+ * (timeout, spawn failure, unexpected exit) REJECTS — never "absent".
31
+ */
32
+ static sessionExistsStrict(name: string): Promise<boolean>;
27
33
  static killSession(name: string): Promise<void>;
28
34
  static listWindows(sessionName: string): Promise<Array<{
29
35
  id: string;
30
36
  name: string;
31
37
  }>>;
38
+ /** Bounded, failing variant for the login/install path: a timeout or tmux error REJECTS instead of reading as "no windows". */
39
+ static listWindowsStrict(sessionName: string): Promise<Array<{
40
+ id: string;
41
+ name: string;
42
+ }>>;
32
43
  static getPanePid(sessionName: string, windowId: string): Promise<number | null>;
44
+ /** Window name handed to createWindow(), kept so a cleanup can find a window whose id we never learned. */
45
+ private pendingWindowName;
33
46
  createWindow(command: string, cwd: string, windowName?: string): Promise<string>;
34
47
  /** Restart the process inside the existing window, preserving its window id. */
35
48
  respawnWindow(command: string, cwd: string): Promise<void>;
@@ -49,6 +62,12 @@ export declare class TmuxManager {
49
62
  */
50
63
  private applyLogicalSize;
51
64
  killWindow(): Promise<void>;
65
+ /**
66
+ * killWindow() then verify by listing: true only when the window is
67
+ * positively absent. A tmux error while listing is "unknown" → false, so
68
+ * callers never report a cleanup as done on a guess.
69
+ */
70
+ killWindowConfirmed(): Promise<boolean>;
52
71
  /**
53
72
  * Check if the tmux window still exists in the session.
54
73
  * Note: with remain-on-exit enabled, a window with a dead pane still
@@ -43,6 +43,8 @@ export function resolveTmuxLogicalSize(config) {
43
43
  rows: config?.rows ?? DEFAULT_TMUX_LOGICAL_SIZE.rows,
44
44
  };
45
45
  }
46
+ /** Hard bound for tmux operations on the remote login/install path: a wedged tmux must never hold a teardown open. */
47
+ const LOGIN_TMUX_OP_TIMEOUT_MS = 10_000;
46
48
  export class TmuxManager {
47
49
  sessionName;
48
50
  logicalSize;
@@ -74,9 +76,11 @@ export class TmuxManager {
74
76
  if (existing)
75
77
  return existing;
76
78
  const operation = (async () => {
77
- if (!(await TmuxManager.sessionExists(name))) {
79
+ // Strict: only a POSITIVE "no such session" leads to creating one. A
80
+ // timeout or spawn failure rejects instead of being read as "absent".
81
+ if (!(await TmuxManager.sessionExistsStrict(name))) {
78
82
  try {
79
- await exec("tmux", TmuxManager.tmuxArgs(["new-session", "-d", "-s", name]));
83
+ await exec("tmux", TmuxManager.tmuxArgs(["new-session", "-d", "-s", name]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS });
80
84
  }
81
85
  catch (err) {
82
86
  if (!String(err).includes("duplicate session"))
@@ -87,7 +91,7 @@ export class TmuxManager {
87
91
  // global default would also change tmux sessions that AgEnD does not own.
88
92
  await exec("tmux", TmuxManager.tmuxArgs([
89
93
  "set-option", "-t", name, "mouse", "on",
90
- ]));
94
+ ]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS });
91
95
  })().finally(() => {
92
96
  if (TmuxManager.ensureSessionInFlight.get(name) === operation) {
93
97
  TmuxManager.ensureSessionInFlight.delete(name);
@@ -118,6 +122,24 @@ export class TmuxManager {
118
122
  return false;
119
123
  }
120
124
  }
125
+ /**
126
+ * Bounded, tri-state-safe variant for the login/install path: true = exists,
127
+ * false = tmux positively says the session is absent, anything else
128
+ * (timeout, spawn failure, unexpected exit) REJECTS — never "absent".
129
+ */
130
+ static async sessionExistsStrict(name) {
131
+ try {
132
+ await exec("tmux", TmuxManager.tmuxArgs(["has-session", "-t", name]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS });
133
+ return true;
134
+ }
135
+ catch (err) {
136
+ const e = err;
137
+ const text = String(e.stderr ?? "");
138
+ if (e.code === 1 && !e.killed && /can't find session|no server running|error connecting to .*No such file/.test(text))
139
+ return false;
140
+ throw new Error(`tmux has-session could not be determined for ${name}`);
141
+ }
142
+ }
121
143
  static async killSession(name) {
122
144
  try {
123
145
  await exec("tmux", TmuxManager.tmuxArgs(["kill-session", "-t", name]));
@@ -140,6 +162,16 @@ export class TmuxManager {
140
162
  return [];
141
163
  }
142
164
  }
165
+ /** Bounded, failing variant for the login/install path: a timeout or tmux error REJECTS instead of reading as "no windows". */
166
+ static async listWindowsStrict(sessionName) {
167
+ const { stdout } = await exec("tmux", TmuxManager.tmuxArgs([
168
+ "list-windows", "-t", sessionName, "-F", "#{window_id}|||#{window_name}"
169
+ ]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS });
170
+ return stdout.trim().split("\n").filter(Boolean).map(line => {
171
+ const [id, name] = line.split("|||");
172
+ return { id, name };
173
+ });
174
+ }
143
175
  static async getPanePid(sessionName, windowId) {
144
176
  try {
145
177
  const { stdout } = await exec("tmux", TmuxManager.tmuxArgs([
@@ -153,9 +185,14 @@ export class TmuxManager {
153
185
  }
154
186
  }
155
187
  // === Instance window methods ===
188
+ /** Window name handed to createWindow(), kept so a cleanup can find a window whose id we never learned. */
189
+ pendingWindowName = null;
156
190
  async createWindow(command, cwd, windowName) {
191
+ this.pendingWindowName = windowName ?? null;
157
192
  if (windowName) {
158
- const sameName = (await TmuxManager.listWindows(this.sessionName))
193
+ // Strict listing: a tmux that cannot answer must not be read as "no
194
+ // duplicates" — that would create a second window (fail-closed).
195
+ const sameName = (await TmuxManager.listWindowsStrict(this.sessionName))
159
196
  .filter(window => window.name === windowName);
160
197
  if (sameName.length > 0) {
161
198
  // Reuse one exact window id instead of creating another window with the
@@ -174,7 +211,7 @@ export class TmuxManager {
174
211
  await exec("tmux", TmuxManager.tmuxArgs([
175
212
  "set-window-option", "-t", `${this.sessionName}:${this.windowId}`,
176
213
  "allow-rename", "off",
177
- ])).catch(() => { });
214
+ ]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS }).catch(() => { });
178
215
  return this.windowId;
179
216
  }
180
217
  }
@@ -182,7 +219,8 @@ export class TmuxManager {
182
219
  if (windowName)
183
220
  args.push("-n", windowName);
184
221
  args.push("-P", "-F", "#{window_id}", command);
185
- const { stdout } = await exec("tmux", TmuxManager.tmuxArgs(args));
222
+ // Hard bound: a wedged tmux must not hold a login/install teardown open forever.
223
+ const { stdout } = await exec("tmux", TmuxManager.tmuxArgs(args), { timeout: 15_000 });
186
224
  this.windowId = stdout.trim();
187
225
  try {
188
226
  // Apply the configured geometry and hand sizing to real terminals. The
@@ -190,7 +228,7 @@ export class TmuxManager {
190
228
  // what makes `window-size latest` safe here (see applyLogicalSize).
191
229
  await this.applyLogicalSize();
192
230
  if (windowName) {
193
- await exec("tmux", TmuxManager.tmuxArgs(["set-window-option", "-t", `${this.sessionName}:${this.windowId}`, "allow-rename", "off"])).catch(() => { });
231
+ await exec("tmux", TmuxManager.tmuxArgs(["set-window-option", "-t", `${this.sessionName}:${this.windowId}`, "allow-rename", "off"]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS }).catch(() => { });
194
232
  }
195
233
  }
196
234
  catch (err) {
@@ -210,7 +248,7 @@ export class TmuxManager {
210
248
  await exec("tmux", TmuxManager.tmuxArgs([
211
249
  "respawn-window", "-k", "-t", `${this.sessionName}:${this.windowId}`,
212
250
  "-c", cwd, command,
213
- ]));
251
+ ]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS });
214
252
  }
215
253
  /**
216
254
  * Give the window its configured geometry, then hand sizing authority to real
@@ -234,21 +272,50 @@ export class TmuxManager {
234
272
  "resize-window", "-t", target,
235
273
  "-x", String(this.logicalSize.columns),
236
274
  "-y", String(this.logicalSize.rows),
237
- ]));
275
+ ]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS });
238
276
  await exec("tmux", TmuxManager.tmuxArgs([
239
277
  "set-window-option", "-t", target, "window-size", "latest",
240
- ]));
278
+ ]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS });
241
279
  }
242
280
  async killWindow() {
243
281
  if (!this.windowId)
244
282
  return;
245
283
  try {
246
- await exec("tmux", TmuxManager.tmuxArgs(["kill-window", "-t", `${this.sessionName}:${this.windowId}`]));
284
+ await exec("tmux", TmuxManager.tmuxArgs(["kill-window", "-t", `${this.sessionName}:${this.windowId}`]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS });
247
285
  }
248
286
  catch {
249
287
  // Expected if window already exited
250
288
  }
251
289
  }
290
+ /**
291
+ * killWindow() then verify by listing: true only when the window is
292
+ * positively absent. A tmux error while listing is "unknown" → false, so
293
+ * callers never report a cleanup as done on a guess.
294
+ */
295
+ async killWindowConfirmed() {
296
+ const name = this.pendingWindowName;
297
+ if (!this.windowId && !name)
298
+ return true; // nothing was ever asked for
299
+ const mine = (w) => (this.windowId ? w.id === this.windowId : w.name === name);
300
+ const list = async () => {
301
+ const { stdout } = await exec("tmux", TmuxManager.tmuxArgs(["list-windows", "-t", this.sessionName, "-F", "#{window_id}|||#{window_name}"]), { timeout: 10_000 });
302
+ return stdout.trim().split("\n").filter(Boolean).map(line => { const [id, n] = line.split("|||"); return { id, name: n ?? "" }; });
303
+ };
304
+ try {
305
+ // Kill by id when known; otherwise every window carrying the name we
306
+ // asked for (a createWindow that timed out may still have made one).
307
+ // Kills run in PARALLEL so the total stays bounded regardless of how
308
+ // many duplicates exist: list (10 s) + kills (10 s) + list (10 s) ≤ 30 s.
309
+ const targets = (await list()).filter(mine);
310
+ await Promise.all(targets.map(w => exec("tmux", TmuxManager.tmuxArgs(["kill-window", "-t", `${this.sessionName}:${w.id}`]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS }).catch(() => { })));
311
+ return !(await list()).some(mine);
312
+ }
313
+ catch (err) {
314
+ // No such session at all ⇒ no window either; anything else is unknown → not confirmed.
315
+ const text = String(err?.stderr ?? err.message ?? "");
316
+ return /can't find session|no server running/.test(text);
317
+ }
318
+ }
252
319
  /**
253
320
  * Check if the tmux window still exists in the session.
254
321
  * Note: with remain-on-exit enabled, a window with a dead pane still
@@ -270,7 +337,7 @@ export class TmuxManager {
270
337
  await exec("tmux", TmuxManager.tmuxArgs([
271
338
  "set-option", "-t", `${this.sessionName}:${this.windowId}`,
272
339
  "remain-on-exit", "on",
273
- ]));
340
+ ]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS });
274
341
  }
275
342
  /**
276
343
  * Get pane status. Returns null if the window doesn't exist.