@songsid/agend 2.1.0 → 2.1.1-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 (65) hide show
  1. package/README.md +1 -0
  2. package/README.zh-TW.md +1 -0
  3. package/dist/agent-cli-instructions.md +9 -0
  4. package/dist/backend/antigravity.d.ts +4 -1
  5. package/dist/backend/antigravity.js +15 -2
  6. package/dist/backend/antigravity.js.map +1 -1
  7. package/dist/backend/codex.d.ts +1 -0
  8. package/dist/backend/codex.js +30 -2
  9. package/dist/backend/codex.js.map +1 -1
  10. package/dist/backend/grok.d.ts +1 -0
  11. package/dist/backend/grok.js +8 -1
  12. package/dist/backend/grok.js.map +1 -1
  13. package/dist/backend/kiro.d.ts +7 -0
  14. package/dist/backend/kiro.js +15 -6
  15. package/dist/backend/kiro.js.map +1 -1
  16. package/dist/backend/types.d.ts +11 -6
  17. package/dist/backend/types.js +48 -4
  18. package/dist/backend/types.js.map +1 -1
  19. package/dist/cli.js +16 -8
  20. package/dist/cli.js.map +1 -1
  21. package/dist/config-validator.js +18 -0
  22. package/dist/config-validator.js.map +1 -1
  23. package/dist/config.js +5 -0
  24. package/dist/config.js.map +1 -1
  25. package/dist/daemon.d.ts +11 -0
  26. package/dist/daemon.js +56 -32
  27. package/dist/daemon.js.map +1 -1
  28. package/dist/fleet-context.d.ts +2 -0
  29. package/dist/fleet-manager.d.ts +25 -2
  30. package/dist/fleet-manager.js +127 -22
  31. package/dist/fleet-manager.js.map +1 -1
  32. package/dist/general-knowledge/skills/tui-effort/SKILL.md +115 -0
  33. package/dist/instance-lifecycle.d.ts +8 -0
  34. package/dist/instance-lifecycle.js +57 -4
  35. package/dist/instance-lifecycle.js.map +1 -1
  36. package/dist/instructions.js +10 -3
  37. package/dist/instructions.js.map +1 -1
  38. package/dist/locale.js +2 -0
  39. package/dist/locale.js.map +1 -1
  40. package/dist/sd-notify.d.ts +18 -0
  41. package/dist/sd-notify.js +41 -2
  42. package/dist/sd-notify.js.map +1 -1
  43. package/dist/service-installer.d.ts +7 -0
  44. package/dist/service-installer.js +43 -3
  45. package/dist/service-installer.js.map +1 -1
  46. package/dist/tmux-control.js +7 -1
  47. package/dist/tmux-control.js.map +1 -1
  48. package/dist/tmux-manager.d.ts +16 -1
  49. package/dist/tmux-manager.js +69 -3
  50. package/dist/tmux-manager.js.map +1 -1
  51. package/dist/topic-commands.js +6 -2
  52. package/dist/topic-commands.js.map +1 -1
  53. package/dist/types.d.ts +13 -0
  54. package/dist/ui/view.html +170 -33
  55. package/dist/update-check.d.ts +22 -0
  56. package/dist/update-check.js +44 -0
  57. package/dist/update-check.js.map +1 -0
  58. package/dist/view-api.d.ts +7 -0
  59. package/dist/view-api.js +56 -12
  60. package/dist/view-api.js.map +1 -1
  61. package/package.json +1 -1
  62. package/templates/systemd.service.ejs +9 -1
  63. package/dist/fleet-system-prompt.d.ts +0 -11
  64. package/dist/fleet-system-prompt.js +0 -61
  65. package/dist/fleet-system-prompt.js.map +0 -1
package/dist/daemon.d.ts CHANGED
@@ -198,12 +198,23 @@ export declare class Daemon extends EventEmitter {
198
198
  private startErrorMonitor;
199
199
  /** Evaluate one pane snapshot. Kept synchronous so state-machine edges are unit-testable. */
200
200
  private evaluateErrorPatterns;
201
+ /**
202
+ * Notification text for a detected pattern. `formatMessage` patterns build it
203
+ * from the match so the user gets the specifics (e.g. which period and what
204
+ * percentage remains) instead of a generic "running low".
205
+ *
206
+ * Uses the LAST match in the pane: an older warning may still be scrolled up
207
+ * (10% earlier, 5% now), and the newest one is the one worth reporting.
208
+ */
209
+ private resolveErrorMessage;
201
210
  /**
202
211
  * Interrupt the CLI's current generation (cancel button / `/cancel`).
203
212
  * Direct tmux key event (not a paste) so it registers as the interrupt key.
204
213
  * kiro-cli interrupts on Ctrl+C; the others (claude-code, codex, …) on Escape.
205
214
  */
206
215
  sendEscape(): Promise<void>;
216
+ /** Send the backend-specific graceful quit command/key sequence. */
217
+ private sendQuitSequence;
207
218
  stop(): Promise<void>;
208
219
  getHangDetector(): HangDetector | null;
209
220
  getInstanceState(): InstanceState | "paused";
package/dist/daemon.js CHANGED
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
4
4
  import { createHash, randomBytes } from "node:crypto";
5
5
  import { EventEmitter } from "node:events";
6
6
  import { clearPausedMarker, writePausedMarker } from "./pause-marker.js";
7
- import { TmuxManager } from "./tmux-manager.js";
7
+ import { TmuxManager, resolveTmuxLogicalSize } from "./tmux-manager.js";
8
8
  import { TranscriptMonitor } from "./transcript-monitor.js";
9
9
  import { ContextGuardian } from "./context-guardian.js";
10
10
  import { IpcServer } from "./channel/ipc-bridge.js";
@@ -543,7 +543,7 @@ export class Daemon extends EventEmitter {
543
543
  });
544
544
  // 2. Tmux — ensure session, create window if not alive
545
545
  await TmuxManager.ensureSession(this.tmuxSessionName);
546
- this.tmux = new TmuxManager(this.tmuxSessionName, "");
546
+ this.tmux = new TmuxManager(this.tmuxSessionName, "", resolveTmuxLogicalSize(this.config.terminal));
547
547
  // Strategy A: always start fresh Claude window (MCP server has no reconnection)
548
548
  // Kill any existing window from previous run
549
549
  const windowIdFile = join(this.instanceDir, "window-id");
@@ -1101,11 +1101,35 @@ export class Daemon extends EventEmitter {
1101
1101
  this.lastErrorNotifiedAt.set(key, now);
1102
1102
  if (ep.action === "failover")
1103
1103
  this.lastFailoverAt = now;
1104
- this.logger.warn({ errorType: ep.type, action: ep.action }, `PTY error detected: ${ep.message}`);
1105
- this.emit("pty_error", { name: this.name, ...ep });
1104
+ const message = this.resolveErrorMessage(pane, ep);
1105
+ this.logger.warn({ errorType: ep.type, action: ep.action }, `PTY error detected: ${message}`);
1106
+ this.emit("pty_error", { name: this.name, ...ep, message });
1106
1107
  break; // Only handle first unsuppressed new error per scan
1107
1108
  }
1108
1109
  }
1110
+ /**
1111
+ * Notification text for a detected pattern. `formatMessage` patterns build it
1112
+ * from the match so the user gets the specifics (e.g. which period and what
1113
+ * percentage remains) instead of a generic "running low".
1114
+ *
1115
+ * Uses the LAST match in the pane: an older warning may still be scrolled up
1116
+ * (10% earlier, 5% now), and the newest one is the one worth reporting.
1117
+ */
1118
+ resolveErrorMessage(pane, ep) {
1119
+ if (!ep.formatMessage)
1120
+ return ep.message;
1121
+ try {
1122
+ const flags = ep.pattern.flags.includes("g") ? ep.pattern.flags : ep.pattern.flags + "g";
1123
+ const matches = [...pane.matchAll(new RegExp(ep.pattern.source, flags))];
1124
+ const last = matches[matches.length - 1];
1125
+ return last ? ep.formatMessage(last) : ep.message;
1126
+ }
1127
+ catch (err) {
1128
+ // A bad formatter must not swallow the notification entirely.
1129
+ this.logger.debug({ err, errorType: ep.type }, "formatMessage failed — using static message");
1130
+ return ep.message;
1131
+ }
1132
+ }
1109
1133
  /**
1110
1134
  * Interrupt the CLI's current generation (cancel button / `/cancel`).
1111
1135
  * Direct tmux key event (not a paste) so it registers as the interrupt key.
@@ -1115,6 +1139,30 @@ export class Daemon extends EventEmitter {
1115
1139
  const cancelKey = this.backend?.getCancelKey() ?? "Escape";
1116
1140
  await this.tmux?.sendSpecialKey(cancelKey);
1117
1141
  }
1142
+ /** Send the backend-specific graceful quit command/key sequence. */
1143
+ async sendQuitSequence() {
1144
+ if (!this.tmux || !this.backend)
1145
+ return false;
1146
+ const quitCmd = this.backend.getQuitCommand();
1147
+ const quitKey = this.backend.getQuitKey?.();
1148
+ if (quitCmd) {
1149
+ await this.tmux.sendKeys(quitCmd);
1150
+ // Delay before Enter to prevent tmux server races when instances stop in
1151
+ // parallel (same pattern as pasteText).
1152
+ await new Promise(r => setTimeout(r, 150));
1153
+ await this.tmux.sendSpecialKey("Enter");
1154
+ return true;
1155
+ }
1156
+ if (!quitKey)
1157
+ return false;
1158
+ const presses = Math.max(1, Math.floor(this.backend.getQuitKeyPresses?.() ?? 1));
1159
+ for (let i = 0; i < presses; i++) {
1160
+ await this.tmux.sendSpecialKey(quitKey);
1161
+ if (i + 1 < presses)
1162
+ await new Promise(r => setTimeout(r, 250));
1163
+ }
1164
+ return true;
1165
+ }
1118
1166
  async stop() {
1119
1167
  this.logger.info("Stopping daemon instance");
1120
1168
  this.freezeRuntimeMonitors();
@@ -1134,20 +1182,8 @@ export class Daemon extends EventEmitter {
1134
1182
  this.saveSessionId();
1135
1183
  this.healthCheckPaused = true;
1136
1184
  let killed = false;
1137
- const quitCmd = this.backend?.getQuitCommand();
1138
- const quitKey = this.backend?.getQuitKey?.();
1139
- if (quitCmd) {
1140
- await this.tmux.sendKeys(quitCmd);
1141
- // Delay before Enter to prevent tmux server race when multiple
1142
- // instances stop in parallel (same pattern as pasteText).
1143
- await new Promise(r => setTimeout(r, 150));
1144
- await this.tmux.sendSpecialKey("Enter");
1145
- }
1146
- else if (quitKey) {
1147
- // Some CLIs quit via a key chord (e.g. grok Ctrl+Q), not a typed command.
1148
- await this.tmux.sendSpecialKey(quitKey);
1149
- }
1150
- if (quitCmd || quitKey) {
1185
+ const quitSent = await this.sendQuitSequence();
1186
+ if (quitSent) {
1151
1187
  // Wait up to 3s for graceful exit, polling every 200ms. A healthy CLI
1152
1188
  // exits within ~1s; a longer wait just delays the force-kill fallback.
1153
1189
  for (let i = 0; i < 15; i++) {
@@ -1223,19 +1259,7 @@ export class Daemon extends EventEmitter {
1223
1259
  const transition = (async () => {
1224
1260
  try {
1225
1261
  this.saveSessionId();
1226
- const quitCmd = this.backend?.getQuitCommand();
1227
- const quitKey = this.backend?.getQuitKey?.();
1228
- if (this.tmux) {
1229
- if (quitCmd) {
1230
- await this.tmux.sendKeys(quitCmd);
1231
- await new Promise(r => setTimeout(r, 150));
1232
- await this.tmux.sendSpecialKey("Enter");
1233
- }
1234
- else if (quitKey) {
1235
- // Key-chord quit (e.g. grok Ctrl+Q) — no typed command to send.
1236
- await this.tmux.sendSpecialKey(quitKey);
1237
- }
1238
- }
1262
+ await this.sendQuitSequence();
1239
1263
  let exited = false;
1240
1264
  for (let i = 0; i < 15; i++) {
1241
1265
  await new Promise(r => setTimeout(r, 200));
@@ -1818,7 +1842,7 @@ export class Daemon extends EventEmitter {
1818
1842
  const match = windows.find(w => w.name === this.name);
1819
1843
  if (!match)
1820
1844
  return undefined;
1821
- this.tmux = new TmuxManager(this.tmuxSessionName, match.id);
1845
+ this.tmux = new TmuxManager(this.tmuxSessionName, match.id, resolveTmuxLogicalSize(this.config.terminal));
1822
1846
  writeFileSync(join(this.instanceDir, "window-id"), match.id);
1823
1847
  await this.controlClient?.registerWindow(match.id);
1824
1848
  this.bindInstanceStateOutputListener(match.id);