feat: pet chat window replaces the Sagi panel; Windows pane attach no longer crashes the server

- Sagi: clicking the pet opens its chat window directly (PetChat + SagiInfo
  status strip, Needs you, Mute / Clear alerts); SagiPanel removed.
- Chat view answers Claude's unnumbered folder-trust prompt (parseBareMenu).
- pty-attach: resize/write after a Windows attach exited, and node-pty's pipe
  errors, no longer throw out of the WebSocket handler.
- restart-helper: the server started by an update logs to
  data/agenthub-server.log instead of discarding its output.
This commit is contained in:
2026-10-16 10:27:22 +07:00
parent a58e95a957
commit f460fff406
16 changed files with 734 additions and 555 deletions
+24 -3
View File
@@ -3,14 +3,20 @@
* @file Detached helper spawned by `server/lib/self-restart.js`. Polls the
* old dashboard PID until it exits (server/index.js's own SIGTERM handler
* has a 5s force-exit backstop, so this never waits forever), then starts a
* fresh server from the same entry point and exits itself.
* fresh server from the same entry point and exits itself. The new server's
* stdout/stderr are appended to the log file it is given (the checkout's
* `data/agenthub-server.log`, the one `agenthub start` writes), so a crash
* after an update still leaves Node's stack trace behind.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
require("../server/lib/windows-hide").install(); // before child_process is read
const fs = require("fs");
const path = require("path");
const { spawn } = require("child_process");
const { rotateIfLarge } = require("./log-rotate");
const [, , pidArg, entry] = process.argv;
const [, , pidArg, entry, logArg] = process.argv;
const pid = parseInt(pidArg, 10);
// ponytail: fixed poll cadence/cap, not configurable — this script has one caller.
@@ -26,10 +32,25 @@ function isAlive(p) {
}
}
/** Append fd for the new server's output, or "ignore" when there is none. */
function openLog(file) {
if (!file) return "ignore";
try {
fs.mkdirSync(path.dirname(file), { recursive: true });
rotateIfLarge(file);
const fd = fs.openSync(file, "a");
fs.writeSync(fd, `[${new Date().toISOString()}] restart-helper: old server ${pid} gone, starting ${entry}\n`);
return fd;
} catch {
return "ignore"; // a log must never stop the restart
}
}
function restart() {
const out = openLog(logArg);
const child = spawn(process.execPath, [entry], {
detached: true,
stdio: "ignore",
stdio: ["ignore", out, out],
env: process.env,
});
child.unref();