fix: event-loop freezes, plugin-install boot crash, tmux attach/scrollbar, Codex transcripts

- GET /api/workflows: one LEAD() window pass in a read worker (read-pool.js,
  workflow-stats.js); idx_events_session restored, idx_events_tool added
- compat-sqlite gains function/pluck/readonly so a plugin install without a
  better-sqlite3 build boots again; antigravity-usage uses db.js's Database
- install-hooks adds no checkout hooks while the agenthub plugin is enabled
  (events were doubled)
- terminal attach runs the live tmux server's own binary; the pane scrollbar
  gets a visible 2-column style, and claude panes start with
  CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN=1 so tmux keeps history to scroll
- Codex sessions' conversation in GET /api/sessions/:id/transcript
- Codex/Antigravity sessions claimed by their workspace without hooks
- UI-audit follow-ups, docs and OpenAPI
This commit is contained in:
2026-10-05 17:01:05 +07:00
parent 442f133930
commit 198fbb7980
78 changed files with 2716 additions and 873 deletions
+28
View File
@@ -98,6 +98,14 @@ describe("trimEventData", () => {
const junk = big(STR_CAP + 10, "{");
assert.equal(trimEventData(junk), junk);
});
it("returns a long payload it has nothing to trim byte for byte (no number rounding)", () => {
// 12345678901234567890 does not survive a JSON.parse/stringify round trip.
const json = `{"tool_name":"Bash","id":12345678901234567890,"tool_input":{"a":"${big(
STR_CAP - 10
)}","b":"${big(STR_CAP - 10)}"}}`;
assert.equal(trimEventData(json), json);
});
});
describe("event inserts", () => {
@@ -125,4 +133,24 @@ describe("event inserts", () => {
const again = await sweepEventPayloads(db);
assert.equal(again.rows, 0);
});
it("the sweep commits once a chunk's bytes pass the budget, and still trims every row", async () => {
db.prepare("INSERT OR IGNORE INTO sessions (id) VALUES ('s-sweep')").run();
const ins = db.prepare(
"INSERT INTO events (session_id, event_type, data, created_at) VALUES ('s-sweep', 'PostToolUse', ?, '2026-01-01T00:00:00Z')"
);
for (let i = 0; i < 5; i++) ins.run(payload);
let txns = 0;
const counting = {
prepare: (sql) => db.prepare(sql),
transaction: (fn) => {
const t = db.transaction(fn);
return (...a) => (txns++, t(...a));
},
};
// Each payload is ~32 KB; a 1-byte budget closes the transaction after every row.
const r = await sweepEventPayloads(counting, { chunk: 200, maxBytes: 1 });
assert.equal(r.rows, 5);
assert.ok(txns >= 5, `expected a transaction per row, got ${txns}`);
});
});