feat: right-click menus everywhere, workspace/session cost, long-term DB safety

Right-click menus: one portal shell (components/ContextMenu.tsx) for every
item with actions - sidebar/Recents/archived session rows (SessionMenu),
console tabs, pane headers, the Changes panel's files/tree/commits, the
workspace home, sessions outside a workspace (SessionRowMenu: Sessions
table, Dashboard, Activity, Workflow runs), prompt images, every Settings
list and every Claude Code/Codex config list (FileItemMenu). Buttons stay;
a delete keeps its button's confirm; the shell stops portal clicks from
reaching the card that opened it.

Status bar: Workspace $ and Session $ on the Workspace page, from
?lane= on GET /api/pricing/cost and the focused/replayed session's cost.

Database, no data dropped: synchronous=NORMAL, journal_size_limit 64 MB,
hourly PASSIVE checkpoint + PRAGMA optimize, idx_events_session_created
(replacing the redundant idx_events_session), watchdog reads
sessions.transcript_path, transcript parsed before the hook's write
transaction, GET /api/sessions/:id?events=0 for the web page (reloads only
on its own session, debounced), streamed export, transactional clear-data
and chunked cleanup, subagent import reads tool_use_ids once, runtime logs
rotated past 5 MB.
This commit is contained in:
2026-10-05 09:37:48 +07:00
parent 65fe680d38
commit ad6e80192d
80 changed files with 2914 additions and 489 deletions
+17 -5
View File
@@ -1029,16 +1029,27 @@ function importSubagentFromJsonl(dbModule, sessionId, mainAgentId, subData) {
// Per-tool-call events under the subagent's own agent_id so the UI can attribute
// them to the subagent. Idempotent by (agent_id, event_type, tool_use_id).
if (Array.isArray(subData.toolEvents) && subData.toolEvents.length > 0) {
const eventExists = db.prepare(
"SELECT 1 FROM events WHERE agent_id = ? AND event_type = ? AND data LIKE ? LIMIT 1"
// Every tool_use_id this agent already has, read ONCE: a `data LIKE` per
// tool event re-scanned the agent's payloads for each one, and this runs
// on every SubagentStop — quadratic in a long subagent's tool calls.
const seen = new Set(
db
.prepare(
`SELECT event_type, CASE WHEN json_valid(data) THEN json_extract(data, '$.tool_use_id') END AS id
FROM events WHERE agent_id = ? AND event_type IN ('PreToolUse', 'PostToolUse')`
)
.all(targetAgentId)
.filter((r) => r.id)
.map((r) => `${r.event_type}\u0001${r.id}`)
);
const has = (type, id) => seen.has(`${type}\u0001${id}`);
for (const tev of subData.toolEvents) {
if (!tev.tool_use_id) continue;
const useIdMarker = `%"tool_use_id":${JSON.stringify(tev.tool_use_id)}%`;
const ts = tev.pre_timestamp || subData.startedAt;
const truncatedInput = truncateForEvent(tev.tool_input);
if (!eventExists.get(targetAgentId, "PreToolUse", useIdMarker)) {
if (!has("PreToolUse", tev.tool_use_id)) {
seen.add(`PreToolUse\u0001${tev.tool_use_id}`);
insertEvent.run(
sessionId,
targetAgentId,
@@ -1057,7 +1068,8 @@ function importSubagentFromJsonl(dbModule, sessionId, mainAgentId, subData) {
created++;
}
if (tev.post_timestamp && !eventExists.get(targetAgentId, "PostToolUse", useIdMarker)) {
if (tev.post_timestamp && !has("PostToolUse", tev.tool_use_id)) {
seen.add(`PostToolUse\u0001${tev.tool_use_id}`);
insertEvent.run(
sessionId,
targetAgentId,