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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user