diff --git a/docs/operations.md b/docs/operations.md index 232b921..9f3d6b2 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -53,7 +53,9 @@ Running turns are capped globally and per vendor (`DefaultLimits` in `internal/a When Claude Code reports a rate-limit status of `rejected` and the turn then fails, the inbox gets a `rate_limited` item carrying the vendor's status (including when the limit resets) instead of a generic error. `GET /api/admin/status` reports running and queued turns and the latest rate-limit status each vendor sent. -Agent activity becomes events with `agent_session_id` set: `user.message`, `agent.message`, `agent.thinking`, `agent.tool_call`, `agent.tool_result`, `agent.report`, `turn.ended`, and `session.state`. Anything else Claude Code emits (rate-limit status, task progress, state changes) is recorded unchanged as `agent.vendor`. +Agent activity becomes events with `agent_session_id` set: `user.message`, `agent.message`, `agent.thinking`, `agent.tool_call`, `agent.tool_result`, `agent.report`, `turn.ended`, `session.state`, and `session.context`. Anything else Claude Code emits (rate-limit status, task progress, state changes) is recorded unchanged as `agent.vendor`. An `agent.report` event carries `inbox_item_id`, the inbox item filed for the same report. `session.context` records the context size whenever it changes: `used` is the last assistant message's input, cache-read, cache-creation and output tokens, and `window` is the model's context window from the turn's `result` (`modelUsage[model].contextWindow`), so it is 0 until the session's first turn after a server start ends. The task header shows the latest one as a percentage of the window, coloured green, amber from 70%, red from 90%. + +Messages byre itself sends an agent (actor `byre`, such as the restart notice) show as a muted note, not a user bubble. The transcript view has an accent border while the session is `running`, and a braille spinner on its last line. The spinner is two GIFs in `web/img/`, one per colour scheme, rendered by `web/img/spinner.go` during `go generate ./web`; its colours are copied from the palette in `web/style.css`, so a palette change means updating them there too. Consecutive tool calls (with any thinking between them) collapse into one group whose summary counts calls per tool and file, and file paths show relative to the worktree. A report card that is at least half visible in the transcript while the page is visible dismisses its inbox item, as the viewing user. In the composer, Enter sends and Shift-Enter adds a newline; unsent text is kept per task in memory, so it survives switching tabs or tasks but not a page reload. ## Inbox diff --git a/internal/agents/inbox.go b/internal/agents/inbox.go index abb8235..f2ccce8 100644 --- a/internal/agents/inbox.go +++ b/internal/agents/inbox.go @@ -170,12 +170,14 @@ func (m *Manager) validateAsk(ctx context.Context, l *live, args askArgs) error return nil } -func (m *Manager) fileReport(l *live, r report.Report) { +func (m *Manager) fileReport(l *live, r report.Report) string { id := store.NewID() err := m.store.CreateInboxItem(context.Background(), m.newItem(l, id, id, api.Report, api.Fyi, false, store.ToPayload(r))) if err != nil { slog.Error("file report", "session", l.id, "err", err) + return "" } + return id } func (m *Manager) fileError(l *live, msg string) { diff --git a/internal/agents/manager.go b/internal/agents/manager.go index 24072bb..7d802ad 100644 --- a/internal/agents/manager.go +++ b/internal/agents/manager.go @@ -59,6 +59,10 @@ type live struct { stopping bool interrupted bool rateLimited map[string]any + + contextModel string + contextUsed int + contextWindow int } func New(st *store.Store, limits Limits) *Manager { @@ -455,11 +459,12 @@ func (m *Manager) ensureLive(ctx context.Context, sess api.AgentSession, task ap l = &live{id: sess.Id, vendor: sess.Vendor, taskID: task.Id, projectID: task.ProjectId} l.gate = &report.Gate{OnReport: func(r report.Report) { - err := m.emit(context.Background(), l, agentActor, "agent.report", store.ToPayload(r)) + payload := store.ToPayload(r) + payload["inbox_item_id"] = m.fileReport(l, r) + err := m.emit(context.Background(), l, agentActor, "agent.report", payload) if err != nil { slog.Error("record report", "session", l.id, "err", err) } - m.fileReport(l, r) }} opts := claude.Options{ @@ -549,7 +554,15 @@ func (m *Manager) handle(ctx context.Context, l *live, ev claude.Event) error { return err } } - return nil + usage, err := ev.ContextUsage() + if err != nil { + return err + } + if usage.Tokens == 0 { + return nil + } + l.contextModel = usage.Model + return m.updateContext(ctx, l, usage.Tokens, l.contextWindow) case "user": blocks, err := ev.Blocks() @@ -585,6 +598,14 @@ func (m *Manager) handle(ctx context.Context, l *live, ev claude.Event) error { return err } + window := res.ModelUsage[l.contextModel].ContextWindow + if window > 0 { + err = m.updateContext(ctx, l, l.contextUsed, window) + if err != nil { + return err + } + } + m.mu.Lock() interrupted := l.interrupted rateLimited := l.rateLimited @@ -644,6 +665,15 @@ func (m *Manager) handle(ctx context.Context, l *live, ev claude.Event) error { return m.emit(ctx, l, agentActor, "agent.vendor", raw) } +func (m *Manager) updateContext(ctx context.Context, l *live, used, window int) error { + if used == l.contextUsed && window == l.contextWindow { + return nil + } + l.contextUsed = used + l.contextWindow = window + return m.emit(ctx, l, agentActor, "session.context", map[string]any{"model": l.contextModel, "used": used, "window": window}) +} + func (m *Manager) handleAssistantBlock(ctx context.Context, l *live, b claude.Block) error { switch b.Type { case "text": diff --git a/internal/agents/manager_test.go b/internal/agents/manager_test.go index d31fc42..1af5c2e 100644 --- a/internal/agents/manager_test.go +++ b/internal/agents/manager_test.go @@ -156,12 +156,22 @@ func TestTurnAndFollowUp(t *testing.T) { } events := e.waitIdle() - for _, kind := range []string{"session.created", "user.message", "agent.tool_call", "agent.tool_result", "agent.report", "turn.ended"} { + for _, kind := range []string{"session.created", "user.message", "agent.tool_call", "agent.tool_result", "agent.report", "turn.ended", "session.context"} { if !has(events, kind) { t.Fatalf("missing %s in %v", kind, kinds(events)) } } + window := 0 + for _, ev := range events { + if ev.Kind == "session.context" { + window = ev.Payload["window"].(int) + } + } + if window <= 0 { + t.Fatalf("no context window reported after the turn ended") + } + log := gitOut(t, e.task.WorktreePath, "log", "-1", "--format=%an <%ae>") if log != gitName+" <"+gitEmail+">" { t.Fatalf("agent committed as %q", log) diff --git a/internal/claude/session.go b/internal/claude/session.go index a52e78f..21bf7b9 100644 --- a/internal/claude/session.go +++ b/internal/claude/session.go @@ -58,12 +58,45 @@ type Block struct { } type Result struct { - Subtype string `json:"subtype"` - IsError bool `json:"is_error"` - Result string `json:"result"` - DurationMS int `json:"duration_ms"` - NumTurns int `json:"num_turns"` - TotalCostUSD float64 `json:"total_cost_usd"` + Subtype string `json:"subtype"` + IsError bool `json:"is_error"` + Result string `json:"result"` + DurationMS int `json:"duration_ms"` + NumTurns int `json:"num_turns"` + TotalCostUSD float64 `json:"total_cost_usd"` + ModelUsage map[string]ModelUsage `json:"modelUsage"` +} + +type ModelUsage struct { + ContextWindow int `json:"contextWindow"` +} + +type ContextUsage struct { + Model string + Tokens int +} + +func (e Event) ContextUsage() (ContextUsage, error) { + msg := struct { + Message struct { + Model string `json:"model"` + Usage struct { + InputTokens int `json:"input_tokens"` + CacheCreationInputTokens int `json:"cache_creation_input_tokens"` + CacheReadInputTokens int `json:"cache_read_input_tokens"` + OutputTokens int `json:"output_tokens"` + } `json:"usage"` + } `json:"message"` + }{} + err := json.Unmarshal(e.Raw, &msg) + if err != nil { + return ContextUsage{}, err + } + u := msg.Message.Usage + return ContextUsage{ + Model: msg.Message.Model, + Tokens: u.InputTokens + u.CacheCreationInputTokens + u.CacheReadInputTokens + u.OutputTokens, + }, nil } func (e Event) Blocks() ([]Block, error) { diff --git a/web/dist/panes.js b/web/dist/panes.js index 25f85a2..795b71d 100644 --- a/web/dist/panes.js +++ b/web/dist/panes.js @@ -33,7 +33,7 @@ export class Pane { b.onclick = () => this.show(v); return b; })); - const root = h("div", { class: `view ${view}` }); + const root = h("div", { class: `view view-${view}` }); replace(this.body, root); this.dispose = views[view].mount(root); } diff --git a/web/dist/panes.js.map b/web/dist/panes.js.map index 46a6cee..36f1c42 100644 --- a/web/dist/panes.js.map +++ b/web/dist/panes.js.map @@ -1 +1 @@ -{"version":3,"file":"panes.js","sourceRoot":"","sources":["../src/panes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAIxC,MAAM,KAAK,GAA8E;IACvF,UAAU,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE;IAC3D,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE;IAC5C,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE;CAC7C,CAAC;AAEF,MAAM,KAAK,GAAW,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAEvD,MAAM,OAAO,IAAI;IAOc;IANpB,EAAE,CAAc;IACR,IAAI,CAAc;IAClB,IAAI,CAAc;IAC3B,IAAI,CAAO;IACX,OAAO,GAAe,GAAG,EAAE,GAAE,CAAC,CAAC;IAEvC,YAA6B,GAAW,EAAE,QAAc;QAA3B,QAAG,GAAH,GAAG,CAAQ;QACtC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAEhE,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,CAAgB,CAAC;QACrE,IAAI,CAAC,IAAI,GAAG,KAAK,IAAI,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;QACvD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED,IAAI,CAAC,IAAU;QACb,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC,CAAC;QAE9C,OAAO,CACL,IAAI,CAAC,IAAI,EACT,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACjB,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YAC7E,CAAC,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC/B,OAAO,CAAC,CAAC;QACX,CAAC,CAAC,CACH,CAAC;QAEF,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,QAAQ,IAAI,EAAE,EAAE,CAAC,CAAC;QACjD,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAEO,UAAU;QAChB,OAAO,aAAa,IAAI,CAAC,GAAG,EAAE,CAAC;IACjC,CAAC;CACF"} \ No newline at end of file +{"version":3,"file":"panes.js","sourceRoot":"","sources":["../src/panes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAIxC,MAAM,KAAK,GAA8E;IACvF,UAAU,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE;IAC3D,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE;IAC5C,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE;CAC7C,CAAC;AAEF,MAAM,KAAK,GAAW,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAEvD,MAAM,OAAO,IAAI;IAOc;IANpB,EAAE,CAAc;IACR,IAAI,CAAc;IAClB,IAAI,CAAc;IAC3B,IAAI,CAAO;IACX,OAAO,GAAe,GAAG,EAAE,GAAE,CAAC,CAAC;IAEvC,YAA6B,GAAW,EAAE,QAAc;QAA3B,QAAG,GAAH,GAAG,CAAQ;QACtC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAEhE,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,CAAgB,CAAC;QACrE,IAAI,CAAC,IAAI,GAAG,KAAK,IAAI,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;QACvD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED,IAAI,CAAC,IAAU;QACb,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC,CAAC;QAE9C,OAAO,CACL,IAAI,CAAC,IAAI,EACT,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACjB,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YAC7E,CAAC,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC/B,OAAO,CAAC,CAAC;QACX,CAAC,CAAC,CACH,CAAC;QAEF,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,aAAa,IAAI,EAAE,EAAE,CAAC,CAAC;QACtD,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAEO,UAAU;QAChB,OAAO,aAAa,IAAI,CAAC,GAAG,EAAE,CAAC;IACjC,CAAC;CACF"} \ No newline at end of file diff --git a/web/dist/state.js b/web/dist/state.js index 7b82822..ed8eb52 100644 --- a/web/dist/state.js +++ b/web/dist/state.js @@ -71,24 +71,30 @@ export async function loadInbox() { state.inbox = await get("/api/inbox"); notify("inbox"); } +let liveDuringLoad; export async function selectTask(taskId) { state.selectedTaskId = taskId; state.transcript = []; state.lastTranscriptSeq = 0; + liveDuringLoad = undefined; notify("selection"); if (!taskId) { return; } + const held = []; + liveDuringLoad = held; for (;;) { const page = await get("/api/tasks/{taskId}/events", { path: { taskId }, query: { after: state.lastTranscriptSeq, limit: 1000 } }); - if (state.selectedTaskId !== taskId) { + if (state.selectedTaskId !== taskId || liveDuringLoad !== held) { return; } appendTranscript(page); if (page.length < 1000) { - return; + break; } } + liveDuringLoad = undefined; + appendTranscript(held); } function appendTranscript(events) { const fresh = events.filter((ev) => ev.seq > state.lastTranscriptSeq); @@ -101,7 +107,12 @@ function appendTranscript(events) { } export function handleEvent(ev) { if (ev.task_id && ev.task_id === state.selectedTaskId) { - appendTranscript([ev]); + if (liveDuringLoad) { + liveDuringLoad.push(ev); + } + else { + appendTranscript([ev]); + } } switch (ev.kind) { case "user.updated": diff --git a/web/dist/state.js.map b/web/dist/state.js.map index 730112c..d93e7ec 100644 --- a/web/dist/state.js.map +++ b/web/dist/state.js.map @@ -1 +1 @@ -{"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/web/dist/task.js b/web/dist/task.js index 271f9cf..081f21f 100644 --- a/web/dist/task.js +++ b/web/dist/task.js @@ -7,9 +7,78 @@ export function mountTranscript(root) { const head = h("header", { class: "task-header" }); const transcript = h("div", { class: "transcript" }); const composer = h("div", { class: "composer" }); - let tools = new Map(); + const visibleReports = new Map(); + const acked = new Set(); + const ackVisible = () => { + if (document.visibilityState !== "visible") { + return; + } + const ids = [...new Set(visibleReports.values())].filter((id) => !acked.has(id) && state.inbox.some((i) => i.id === id)); + if (ids.length === 0) { + return; + } + for (const id of ids) { + acked.add(id); + } + post("/api/inbox/responses", { body: { responses: ids.map((id) => ({ item_id: id, type: "dismiss" })) } }).catch((err) => { + for (const id of ids) { + acked.delete(id); + } + console.error("acknowledge reports", err); + }); + }; + const observer = new IntersectionObserver((entries) => { + for (const e of entries) { + const id = e.target.dataset["inboxItem"]; + if (!id) { + continue; + } + if (e.isIntersecting) { + visibleReports.set(e.target, id); + } + else { + visibleReports.delete(e.target); + } + } + ackVisible(); + }, { root: transcript, threshold: 0.5 }); + const newCtx = () => { + const task = state.selectedTaskId ? findTask(state.selectedTaskId) : undefined; + return { + tools: new Map(), + run: undefined, + root: task?.worktree_path ?? "", + watchReport: (el, itemId) => { + el.dataset["inboxItem"] = itemId; + observer.observe(el); + }, + }; + }; + let ctx = newCtx(); + let context = new Map(); + const spinner = h("div", { class: "spinner", hidden: true }); + const setRunning = (running) => { + transcript.classList.toggle("running", running); + if (running === !spinner.hidden) { + return; + } + const pinned = transcript.scrollHeight - transcript.scrollTop - transcript.clientHeight < 80; + spinner.hidden = !running; + if (pinned) { + transcript.scrollTop = transcript.scrollHeight; + } + }; + const put = (el) => { + if (spinner.parentNode === transcript) { + transcript.insertBefore(el, spinner); + } + else { + transcript.append(el); + } + }; const renderHead = () => { const task = state.selectedTaskId ? findTask(state.selectedTaskId) : undefined; + setRunning(task !== undefined && currentSession(task.id)?.state === "running"); if (!task) { replace(head); return; @@ -28,7 +97,7 @@ export function mountTranscript(root) { alert(errorText(err)); } }; - replace(head, h("div", { class: "meta" }, h("code", {}, task.branch), h("code", { class: "path" }, task.worktree_path)), h("div", { class: "head-actions" }, sess && h("span", { class: `state ${sess.state}` }, sess.state), complete)); + replace(head, h("div", { class: "meta" }, h("code", {}, task.branch)), h("div", { class: "head-actions" }, sess && contextMeter(context.get(sess.id)), complete)); }; const renderComposer = () => { const task = state.selectedTaskId ? findTask(state.selectedTaskId) : undefined; @@ -41,29 +110,78 @@ export function mountTranscript(root) { replace(composer, startForm(task.id)); return; } - replace(composer, messageForm(sess.id, sess.state)); + replace(composer, messageForm(task.id, sess.id, sess.state)); + }; + const addToRun = (el, call) => { + const run = ctx.run; + if (!run) { + ctx.run = { first: el, group: undefined, calls: call ? [call] : [] }; + put(el); + return; + } + if (call) { + run.calls.push(call); + } + if (!run.group) { + const summary = h("summary"); + const body = h("div", { class: "tool-group-body" }); + const group = h("details", { class: "tool-group" }, summary, body); + run.first.replaceWith(group); + body.append(run.first); + if (run.first.classList.contains("errored")) { + group.classList.add("errored"); + } + run.group = { el: group, summary, body }; + } + run.group.body.append(el); + replace(run.group.summary, runSummary(run.calls)); }; const appendEvents = (events) => { const pinned = transcript.scrollHeight - transcript.scrollTop - transcript.clientHeight < 80; + let contextChanged = false; for (const ev of events) { - const el = renderEvent(ev, tools); - if (el) { - transcript.append(el); + const p = ev.payload; + if (ev.kind === "session.context" && ev.agent_session_id) { + const prev = context.get(ev.agent_session_id); + const window = Number(p["window"]) || prev?.window || 0; + context.set(ev.agent_session_id, { used: Number(p["used"]), window, model: String(p["model"] ?? "") }); + contextChanged = true; + continue; } + if (ev.kind === "agent.tool_call") { + addToRun(renderToolCall(p, ctx), { name: String(p["name"]), target: toolTarget(p["input"], ctx.root) }); + continue; + } + const el = renderEvent(ev, ctx); + if (!el) { + continue; + } + if (ev.kind === "agent.thinking") { + addToRun(el, undefined); + continue; + } + ctx.run = undefined; + put(el); + } + if (contextChanged) { + renderHead(); } if (pinned) { transcript.scrollTop = transcript.scrollHeight; } }; const renderAll = () => { - tools = new Map(); + observer.disconnect(); + visibleReports.clear(); + ctx = newCtx(); + context = new Map(); renderHead(); renderComposer(); if (!state.selectedTaskId) { replace(transcript, h("div", { class: "empty" }, "No task selected. Pick one with ⌘K or from the Tasks tab.")); return; } - replace(transcript); + replace(transcript, spinner); appendEvents(state.transcript); }; const offs = [ @@ -79,33 +197,53 @@ export function mountTranscript(root) { } }), onEach(["transcript"], (fresh) => appendEvents(fresh)), + onEach(["inbox"], ackVisible), ]; + document.addEventListener("visibilitychange", ackVisible); replace(root, h("div", { class: "transcript-view" }, head, transcript, composer)); renderAll(); return () => { for (const off of offs) { off(); } + document.removeEventListener("visibilitychange", ackVisible); + observer.disconnect(); }; } +const composerDrafts = new Map(); +function keepDraft(input, key) { + input.value = composerDrafts.get(key) ?? ""; + input.addEventListener("input", () => { + if (input.value) { + composerDrafts.set(key, input.value); + } + else { + composerDrafts.delete(key); + } + }); + return () => composerDrafts.delete(key); +} function startForm(taskId) { const prompt = h("textarea", { placeholder: "What should the agent do?", rows: 4, required: true }); + const clearDraft = keepDraft(prompt, `start:${taskId}`); const model = h("select", {}, ...models.map((m) => h("option", { value: m }, m || "default model"))); const form = h("form", { class: "compose" }, prompt, h("div", { class: "compose-actions" }, model, h("button", { type: "submit" }, "Start agent"))); form.onsubmit = async (e) => { e.preventDefault(); try { await post("/api/tasks/{taskId}/sessions", { path: { taskId }, body: { prompt: prompt.value, model: model.value || undefined } }); + clearDraft(); } catch (err) { alert(errorText(err)); } }; - submitOnModEnter(prompt, form); + submitOnEnter(prompt, form); return form; } -function messageForm(sessionId, sessionState) { +function messageForm(taskId, sessionId, sessionState) { const text = h("textarea", { placeholder: sessionState === "idle" ? "Message the agent" : "Wait for the turn to end, or interrupt it", rows: 3 }); + const clearDraft = keepDraft(text, `message:${taskId}`); const send = h("button", { type: "submit", disabled: sessionState !== "idle" }, "Send"); const interrupt = h("button", { type: "button", class: "danger", disabled: sessionState !== "running" && sessionState !== "queued" }, sessionState === "queued" ? "Cancel queued turn" : "Interrupt"); interrupt.onclick = async () => { @@ -125,39 +263,76 @@ function messageForm(sessionId, sessionState) { try { await post("/api/sessions/{sessionId}/messages", { path: { sessionId }, body: { text: text.value } }); text.value = ""; + clearDraft(); } catch (err) { alert(errorText(err)); } }; - submitOnModEnter(text, form); + submitOnEnter(text, form); return form; } -function submitOnModEnter(input, form) { +function submitOnEnter(input, form) { input.onkeydown = (e) => { - if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { + if (e.key === "Enter" && !e.shiftKey && !e.isComposing) { e.preventDefault(); form.requestSubmit(); } }; } -function renderEvent(ev, tools) { +function contextMeter(c) { + if (!c || c.window <= 0) { + return null; + } + const pct = Math.min(100, (c.used / c.window) * 100); + const level = pct >= 90 ? "danger" : pct >= 70 ? "warn" : "ok"; + const title = `context: ${c.used.toLocaleString()} of ${c.window.toLocaleString()} tokens${c.model ? ` (${c.model})` : ""}`; + return h("span", { class: `context-meter ${level}`, title }, `${Math.round(pct)}%`); +} +function renderToolCall(p, ctx) { + const result = h("div", { class: "tool-result pending" }, "running…"); + ctx.tools.set(String(p["id"]), result); + return h("details", { class: "tool" }, h("summary", {}, h("span", { class: "tool-name" }, String(p["name"])), " ", h("span", { class: "tool-brief" }, toolBrief(p["input"], ctx.root))), h("pre", { class: "tool-input" }, JSON.stringify(p["input"], null, 2)), result); +} +function runSummary(calls) { + const counts = new Map(); + for (const c of calls) { + const key = `${c.name}\0${c.target}`; + const entry = counts.get(key); + if (entry) { + entry.n++; + } + else { + counts.set(key, { call: c, n: 1 }); + } + } + if (counts.size === 0) { + return h("span", { class: "tool-brief" }, "thinking"); + } + const parts = []; + for (const { call, n } of counts.values()) { + if (parts.length > 0) { + parts.push(", "); + } + parts.push(h("span", {}, h("span", { class: "tool-name" }, call.name), call.target ? h("span", { class: "tool-brief" }, " ", call.target) : null, n > 1 ? h("span", { class: "tool-count" }, ` ×${n}`) : null)); + } + return h("span", {}, ...parts); +} +function renderEvent(ev, ctx) { const p = ev.payload; const when = h("time", { dateTime: ev.created_at, title: ev.created_at }, ago(ev.created_at)); switch (ev.kind) { case "user.message": + if (ev.actor === "byre") { + return h("div", { class: "note" }, String(p["text"]), " ", when); + } return h("div", { class: "msg user" }, h("div", { class: "who" }, ev.actor, " ", when), markdown(String(p["text"]), "text md")); case "agent.message": return h("div", { class: "msg agent" }, markdown(String(p["text"]), "text md")); case "agent.thinking": return h("details", { class: "thinking" }, h("summary", {}, "thinking"), markdown(String(p["text"]), "text md")); - case "agent.tool_call": { - const result = h("div", { class: "tool-result pending" }, "running…"); - tools.set(String(p["id"]), result); - return h("details", { class: "tool" }, h("summary", {}, h("span", { class: "tool-name" }, String(p["name"])), " ", h("span", { class: "tool-brief" }, toolBrief(p["input"]))), h("pre", { class: "tool-input" }, JSON.stringify(p["input"], null, 2)), result); - } case "agent.tool_result": { - const target = tools.get(String(p["tool_use_id"])); + const target = ctx.tools.get(String(p["tool_use_id"])); const body = h("pre", {}, resultText(p["content"])); if (!target) { return h("div", { class: "tool-result orphan" }, body); @@ -165,12 +340,18 @@ function renderEvent(ev, tools) { target.className = p["is_error"] ? "tool-result error" : "tool-result"; replace(target, body); if (p["is_error"]) { - target.closest("details")?.classList.add("errored"); + target.closest("details.tool")?.classList.add("errored"); + target.closest("details.tool-group")?.classList.add("errored"); } return null; } - case "agent.report": - return reportCard(p); + case "agent.report": { + const card = reportCard(p); + if (typeof p["inbox_item_id"] === "string" && p["inbox_item_id"]) { + ctx.watchReport(card, p["inbox_item_id"]); + } + return card; + } case "turn.ended": return h("div", { class: `turn-end ${p["is_error"] ? "error" : ""}` }, p["is_error"] ? `turn ended: ${String(p["subtype"]).replaceAll("_", " ")}` : ""); case "session.created": @@ -209,15 +390,25 @@ export function reportCard(p) { return h("div", { class: `report-section ${key}` }, h("h4", {}, label), h("ul", {}, ...items.map((i) => h("li", {}, inlineMarkdown(i))))); })); } -function toolBrief(input) { +function toolBrief(input, root) { if (typeof input !== "object" || input === null) { return ""; } const i = input; const brief = i["command"] ?? i["file_path"] ?? i["pattern"] ?? i["url"] ?? i["question"] ?? i["description"] ?? ""; - const text = String(brief); + const text = relative(String(brief), root); return text.length > 120 ? `${text.slice(0, 120)}…` : text; } +function toolTarget(input, root) { + if (typeof input !== "object" || input === null) { + return ""; + } + const path = input["file_path"]; + return typeof path === "string" ? relative(path, root) : ""; +} +function relative(path, root) { + return root && path.startsWith(`${root}/`) ? path.slice(root.length + 1) : path; +} function resultText(content) { if (typeof content === "string") { return content; diff --git a/web/dist/task.js.map b/web/dist/task.js.map index b00664d..8b0d0c8 100644 --- a/web/dist/task.js.map +++ b/web/dist/task.js.map @@ -1 +1 @@ -{"version":3,"file":"task.js","sourceRoot":"","sources":["../src/task.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"task.js","sourceRoot":"","sources":["../src/task.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/web/embed.go b/web/embed.go index 9b6fae0..a688bcd 100644 --- a/web/embed.go +++ b/web/embed.go @@ -4,6 +4,7 @@ import "embed" //go:generate npm ci --silent //go:generate npm run --silent generate +//go:generate go run ./img/spinner.go -//go:embed index.html style.css dist src fonts +//go:embed index.html style.css dist src fonts img/*.gif var FS embed.FS diff --git a/web/img/spinner-dark.gif b/web/img/spinner-dark.gif new file mode 100644 index 0000000..fe154be Binary files /dev/null and b/web/img/spinner-dark.gif differ diff --git a/web/img/spinner-light.gif b/web/img/spinner-light.gif new file mode 100644 index 0000000..dd18daa Binary files /dev/null and b/web/img/spinner-light.gif differ diff --git a/web/img/spinner.go b/web/img/spinner.go new file mode 100644 index 0000000..ade8f37 --- /dev/null +++ b/web/img/spinner.go @@ -0,0 +1,89 @@ +//go:build ignore + +package main + +import ( + "image" + "image/color" + "image/gif" + "log" + "math" + "os" +) + +const ( + width = 16 + height = 32 + radius = 2.6 + levels = 16 + samples = 4 + delay = 8 +) + +var frames = []rune("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏") + +var dots = [6][2]float64{{4.5, 8}, {4.5, 16}, {4.5, 24}, {11.5, 8}, {11.5, 16}, {11.5, 24}} + +func main() { + render("img/spinner-light.gif", color.RGBA{0x1a, 0x45, 0xbd, 0xff}, color.RGBA{0xfb, 0xfb, 0xfa, 0xff}) + render("img/spinner-dark.gif", color.RGBA{0x8a, 0xb4, 0xff, 0xff}, color.RGBA{0x15, 0x17, 0x1a, 0xff}) +} + +func render(path string, fg, bg color.RGBA) { + palette := color.Palette{color.RGBA{}} + for i := 1; i <= levels; i++ { + palette = append(palette, blend(bg, fg, float64(i)/levels)) + } + + anim := &gif.GIF{} + for _, r := range frames { + bits := int(r - 0x2800) + img := image.NewPaletted(image.Rect(0, 0, width, height), palette) + for y := 0; y < height; y++ { + for x := 0; x < width; x++ { + level := int(math.Round(coverage(bits, x, y) * levels)) + img.SetColorIndex(x, y, uint8(level)) + } + } + anim.Image = append(anim.Image, img) + anim.Delay = append(anim.Delay, delay) + anim.Disposal = append(anim.Disposal, gif.DisposalBackground) + } + + f, err := os.Create(path) + if err != nil { + log.Fatal(err) + } + defer f.Close() + err = gif.EncodeAll(f, anim) + if err != nil { + log.Fatal(err) + } +} + +func coverage(bits, x, y int) float64 { + hit := 0 + for sy := 0; sy < samples; sy++ { + for sx := 0; sx < samples; sx++ { + px := float64(x) + (float64(sx)+0.5)/samples + py := float64(y) + (float64(sy)+0.5)/samples + for i, d := range dots { + if bits&(1< { notify("inbox"); } +let liveDuringLoad: ByreEvent[] | undefined; + export async function selectTask(taskId: string | undefined): Promise { state.selectedTaskId = taskId; state.transcript = []; state.lastTranscriptSeq = 0; + liveDuringLoad = undefined; notify("selection"); if (!taskId) { return; } + const held: ByreEvent[] = []; + liveDuringLoad = held; for (;;) { const page = await get("/api/tasks/{taskId}/events", { path: { taskId }, query: { after: state.lastTranscriptSeq, limit: 1000 } }); - if (state.selectedTaskId !== taskId) { + if (state.selectedTaskId !== taskId || liveDuringLoad !== held) { return; } appendTranscript(page); if (page.length < 1000) { - return; + break; } } + liveDuringLoad = undefined; + appendTranscript(held); } function appendTranscript(events: ByreEvent[]): void { @@ -121,7 +128,11 @@ function appendTranscript(events: ByreEvent[]): void { export function handleEvent(ev: ByreEvent): void { if (ev.task_id && ev.task_id === state.selectedTaskId) { - appendTranscript([ev]); + if (liveDuringLoad) { + liveDuringLoad.push(ev); + } else { + appendTranscript([ev]); + } } switch (ev.kind) { diff --git a/web/src/task.ts b/web/src/task.ts index 9ae148c..d30ff4e 100644 --- a/web/src/task.ts +++ b/web/src/task.ts @@ -5,14 +5,102 @@ import { currentSession, findTask, onEach, state } from "./state.js"; const models = ["", "opus", "sonnet", "haiku"]; +type Call = { name: string; target: string }; + +type Run = { + first: HTMLElement; + group: { el: HTMLDetailsElement; summary: HTMLElement; body: HTMLElement } | undefined; + calls: Call[]; +}; + +type Ctx = { + tools: Map; + run: Run | undefined; + root: string; + watchReport: (el: HTMLElement, itemId: string) => void; +}; + export function mountTranscript(root: HTMLElement): () => void { const head = h("header", { class: "task-header" }); const transcript = h("div", { class: "transcript" }); const composer = h("div", { class: "composer" }); - let tools = new Map(); + + const visibleReports = new Map(); + const acked = new Set(); + const ackVisible = () => { + if (document.visibilityState !== "visible") { + return; + } + const ids = [...new Set(visibleReports.values())].filter((id) => !acked.has(id) && state.inbox.some((i) => i.id === id)); + if (ids.length === 0) { + return; + } + for (const id of ids) { + acked.add(id); + } + post("/api/inbox/responses", { body: { responses: ids.map((id) => ({ item_id: id, type: "dismiss" as const })) } }).catch((err) => { + for (const id of ids) { + acked.delete(id); + } + console.error("acknowledge reports", err); + }); + }; + const observer = new IntersectionObserver( + (entries) => { + for (const e of entries) { + const id = (e.target as HTMLElement).dataset["inboxItem"]; + if (!id) { + continue; + } + if (e.isIntersecting) { + visibleReports.set(e.target, id); + } else { + visibleReports.delete(e.target); + } + } + ackVisible(); + }, + { root: transcript, threshold: 0.5 }, + ); + + const newCtx = (): Ctx => { + const task = state.selectedTaskId ? findTask(state.selectedTaskId) : undefined; + return { + tools: new Map(), + run: undefined, + root: task?.worktree_path ?? "", + watchReport: (el, itemId) => { + el.dataset["inboxItem"] = itemId; + observer.observe(el); + }, + }; + }; + let ctx = newCtx(); + let context = new Map(); + + const spinner = h("div", { class: "spinner", hidden: true }); + const setRunning = (running: boolean) => { + transcript.classList.toggle("running", running); + if (running === !spinner.hidden) { + return; + } + const pinned = transcript.scrollHeight - transcript.scrollTop - transcript.clientHeight < 80; + spinner.hidden = !running; + if (pinned) { + transcript.scrollTop = transcript.scrollHeight; + } + }; + const put = (el: HTMLElement) => { + if (spinner.parentNode === transcript) { + transcript.insertBefore(el, spinner); + } else { + transcript.append(el); + } + }; const renderHead = () => { const task = state.selectedTaskId ? findTask(state.selectedTaskId) : undefined; + setRunning(task !== undefined && currentSession(task.id)?.state === "running"); if (!task) { replace(head); return; @@ -32,8 +120,8 @@ export function mountTranscript(root: HTMLElement): () => void { }; replace( head, - h("div", { class: "meta" }, h("code", {}, task.branch), h("code", { class: "path" }, task.worktree_path)), - h("div", { class: "head-actions" }, sess && h("span", { class: `state ${sess.state}` }, sess.state), complete), + h("div", { class: "meta" }, h("code", {}, task.branch)), + h("div", { class: "head-actions" }, sess && contextMeter(context.get(sess.id)), complete), ); }; @@ -48,16 +136,63 @@ export function mountTranscript(root: HTMLElement): () => void { replace(composer, startForm(task.id)); return; } - replace(composer, messageForm(sess.id, sess.state)); + replace(composer, messageForm(task.id, sess.id, sess.state)); + }; + + const addToRun = (el: HTMLElement, call: Call | undefined) => { + const run = ctx.run; + if (!run) { + ctx.run = { first: el, group: undefined, calls: call ? [call] : [] }; + put(el); + return; + } + if (call) { + run.calls.push(call); + } + if (!run.group) { + const summary = h("summary"); + const body = h("div", { class: "tool-group-body" }); + const group = h("details", { class: "tool-group" }, summary, body); + run.first.replaceWith(group); + body.append(run.first); + if (run.first.classList.contains("errored")) { + group.classList.add("errored"); + } + run.group = { el: group, summary, body }; + } + run.group.body.append(el); + replace(run.group.summary, runSummary(run.calls)); }; const appendEvents = (events: ByreEvent[]) => { const pinned = transcript.scrollHeight - transcript.scrollTop - transcript.clientHeight < 80; + let contextChanged = false; for (const ev of events) { - const el = renderEvent(ev, tools); - if (el) { - transcript.append(el); + const p = ev.payload as Record; + if (ev.kind === "session.context" && ev.agent_session_id) { + const prev = context.get(ev.agent_session_id); + const window = Number(p["window"]) || prev?.window || 0; + context.set(ev.agent_session_id, { used: Number(p["used"]), window, model: String(p["model"] ?? "") }); + contextChanged = true; + continue; } + if (ev.kind === "agent.tool_call") { + addToRun(renderToolCall(p, ctx), { name: String(p["name"]), target: toolTarget(p["input"], ctx.root) }); + continue; + } + const el = renderEvent(ev, ctx); + if (!el) { + continue; + } + if (ev.kind === "agent.thinking") { + addToRun(el, undefined); + continue; + } + ctx.run = undefined; + put(el); + } + if (contextChanged) { + renderHead(); } if (pinned) { transcript.scrollTop = transcript.scrollHeight; @@ -65,14 +200,17 @@ export function mountTranscript(root: HTMLElement): () => void { }; const renderAll = () => { - tools = new Map(); + observer.disconnect(); + visibleReports.clear(); + ctx = newCtx(); + context = new Map(); renderHead(); renderComposer(); if (!state.selectedTaskId) { replace(transcript, h("div", { class: "empty" }, "No task selected. Pick one with ⌘K or from the Tasks tab.")); return; } - replace(transcript); + replace(transcript, spinner); appendEvents(state.transcript); }; @@ -89,7 +227,9 @@ export function mountTranscript(root: HTMLElement): () => void { } }), onEach(["transcript"], (fresh) => appendEvents(fresh as ByreEvent[])), + onEach(["inbox"], ackVisible), ]; + document.addEventListener("visibilitychange", ackVisible); replace(root, h("div", { class: "transcript-view" }, head, transcript, composer)); renderAll(); @@ -97,27 +237,46 @@ export function mountTranscript(root: HTMLElement): () => void { for (const off of offs) { off(); } + document.removeEventListener("visibilitychange", ackVisible); + observer.disconnect(); }; } +const composerDrafts = new Map(); + +function keepDraft(input: HTMLTextAreaElement, key: string): () => void { + input.value = composerDrafts.get(key) ?? ""; + input.addEventListener("input", () => { + if (input.value) { + composerDrafts.set(key, input.value); + } else { + composerDrafts.delete(key); + } + }); + return () => composerDrafts.delete(key); +} + function startForm(taskId: string): HTMLElement { const prompt = h("textarea", { placeholder: "What should the agent do?", rows: 4, required: true }); + const clearDraft = keepDraft(prompt, `start:${taskId}`); const model = h("select", {}, ...models.map((m) => h("option", { value: m }, m || "default model"))); const form = h("form", { class: "compose" }, prompt, h("div", { class: "compose-actions" }, model, h("button", { type: "submit" }, "Start agent"))); form.onsubmit = async (e) => { e.preventDefault(); try { await post("/api/tasks/{taskId}/sessions", { path: { taskId }, body: { prompt: prompt.value, model: model.value || undefined } }); + clearDraft(); } catch (err) { alert(errorText(err)); } }; - submitOnModEnter(prompt, form); + submitOnEnter(prompt, form); return form; } -function messageForm(sessionId: string, sessionState: string): HTMLElement { +function messageForm(taskId: string, sessionId: string, sessionState: string): HTMLElement { const text = h("textarea", { placeholder: sessionState === "idle" ? "Message the agent" : "Wait for the turn to end, or interrupt it", rows: 3 }); + const clearDraft = keepDraft(text, `message:${taskId}`); const send = h("button", { type: "submit", disabled: sessionState !== "idle" }, "Send"); const interrupt = h( "button", @@ -141,29 +300,81 @@ function messageForm(sessionId: string, sessionState: string): HTMLElement { try { await post("/api/sessions/{sessionId}/messages", { path: { sessionId }, body: { text: text.value } }); text.value = ""; + clearDraft(); } catch (err) { alert(errorText(err)); } }; - submitOnModEnter(text, form); + submitOnEnter(text, form); return form; } -function submitOnModEnter(input: HTMLTextAreaElement, form: HTMLFormElement): void { +function submitOnEnter(input: HTMLTextAreaElement, form: HTMLFormElement): void { input.onkeydown = (e) => { - if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { + if (e.key === "Enter" && !e.shiftKey && !e.isComposing) { e.preventDefault(); form.requestSubmit(); } }; } -function renderEvent(ev: ByreEvent, tools: Map): HTMLElement | null { +function contextMeter(c: { used: number; window: number; model: string } | undefined): HTMLElement | null { + if (!c || c.window <= 0) { + return null; + } + const pct = Math.min(100, (c.used / c.window) * 100); + const level = pct >= 90 ? "danger" : pct >= 70 ? "warn" : "ok"; + const title = `context: ${c.used.toLocaleString()} of ${c.window.toLocaleString()} tokens${c.model ? ` (${c.model})` : ""}`; + return h("span", { class: `context-meter ${level}`, title }, `${Math.round(pct)}%`); +} + +function renderToolCall(p: Record, ctx: Ctx): HTMLElement { + const result = h("div", { class: "tool-result pending" }, "running…"); + ctx.tools.set(String(p["id"]), result); + return h( + "details", + { class: "tool" }, + h("summary", {}, h("span", { class: "tool-name" }, String(p["name"])), " ", h("span", { class: "tool-brief" }, toolBrief(p["input"], ctx.root))), + h("pre", { class: "tool-input" }, JSON.stringify(p["input"], null, 2)), + result, + ); +} + +function runSummary(calls: Call[]): HTMLElement { + const counts = new Map(); + for (const c of calls) { + const key = `${c.name}\0${c.target}`; + const entry = counts.get(key); + if (entry) { + entry.n++; + } else { + counts.set(key, { call: c, n: 1 }); + } + } + if (counts.size === 0) { + return h("span", { class: "tool-brief" }, "thinking"); + } + const parts: (HTMLElement | string)[] = []; + for (const { call, n } of counts.values()) { + if (parts.length > 0) { + parts.push(", "); + } + parts.push( + h("span", {}, h("span", { class: "tool-name" }, call.name), call.target ? h("span", { class: "tool-brief" }, " ", call.target) : null, n > 1 ? h("span", { class: "tool-count" }, ` ×${n}`) : null), + ); + } + return h("span", {}, ...parts); +} + +function renderEvent(ev: ByreEvent, ctx: Ctx): HTMLElement | null { const p = ev.payload as Record; const when = h("time", { dateTime: ev.created_at, title: ev.created_at }, ago(ev.created_at)); switch (ev.kind) { case "user.message": + if (ev.actor === "byre") { + return h("div", { class: "note" }, String(p["text"]), " ", when); + } return h("div", { class: "msg user" }, h("div", { class: "who" }, ev.actor, " ", when), markdown(String(p["text"]), "text md")); case "agent.message": @@ -172,20 +383,8 @@ function renderEvent(ev: ByreEvent, tools: Map): HTMLElemen case "agent.thinking": return h("details", { class: "thinking" }, h("summary", {}, "thinking"), markdown(String(p["text"]), "text md")); - case "agent.tool_call": { - const result = h("div", { class: "tool-result pending" }, "running…"); - tools.set(String(p["id"]), result); - return h( - "details", - { class: "tool" }, - h("summary", {}, h("span", { class: "tool-name" }, String(p["name"])), " ", h("span", { class: "tool-brief" }, toolBrief(p["input"]))), - h("pre", { class: "tool-input" }, JSON.stringify(p["input"], null, 2)), - result, - ); - } - case "agent.tool_result": { - const target = tools.get(String(p["tool_use_id"])); + const target = ctx.tools.get(String(p["tool_use_id"])); const body = h("pre", {}, resultText(p["content"])); if (!target) { return h("div", { class: "tool-result orphan" }, body); @@ -193,13 +392,19 @@ function renderEvent(ev: ByreEvent, tools: Map): HTMLElemen target.className = p["is_error"] ? "tool-result error" : "tool-result"; replace(target, body); if (p["is_error"]) { - target.closest("details")?.classList.add("errored"); + target.closest("details.tool")?.classList.add("errored"); + target.closest("details.tool-group")?.classList.add("errored"); } return null; } - case "agent.report": - return reportCard(p); + case "agent.report": { + const card = reportCard(p); + if (typeof p["inbox_item_id"] === "string" && p["inbox_item_id"]) { + ctx.watchReport(card, p["inbox_item_id"]); + } + return card; + } case "turn.ended": return h("div", { class: `turn-end ${p["is_error"] ? "error" : ""}` }, p["is_error"] ? `turn ended: ${String(p["subtype"]).replaceAll("_", " ")}` : ""); @@ -250,16 +455,28 @@ export function reportCard(p: Record): HTMLElement { ); } -function toolBrief(input: unknown): string { +function toolBrief(input: unknown, root: string): string { if (typeof input !== "object" || input === null) { return ""; } const i = input as Record; const brief = i["command"] ?? i["file_path"] ?? i["pattern"] ?? i["url"] ?? i["question"] ?? i["description"] ?? ""; - const text = String(brief); + const text = relative(String(brief), root); return text.length > 120 ? `${text.slice(0, 120)}…` : text; } +function toolTarget(input: unknown, root: string): string { + if (typeof input !== "object" || input === null) { + return ""; + } + const path = (input as Record)["file_path"]; + return typeof path === "string" ? relative(path, root) : ""; +} + +function relative(path: string, root: string): string { + return root && path.startsWith(`${root}/`) ? path.slice(root.length + 1) : path; +} + function resultText(content: unknown): string { if (typeof content === "string") { return content; diff --git a/web/style.css b/web/style.css index fe0bb33..a3dab9e 100644 --- a/web/style.css +++ b/web/style.css @@ -449,23 +449,23 @@ textarea { flex-shrink: 0; } -.state { +.context-meter { font-size: 12px; - padding: 1px 8px; - border-radius: 10px; - background: var(--surface); - color: var(--muted); } -.state.running { +.context-meter.ok { color: var(--ok); } -.state.queued { - background: var(--warn-tint); +.context-meter.warn { color: var(--warn); } +.context-meter.danger { + color: var(--danger); + font-weight: 700; +} + .transcript { flex: 1; overflow-y: auto; @@ -473,6 +473,29 @@ textarea { display: flex; flex-direction: column; gap: 12px; + border: 1px solid transparent; + transition: border-color 0.3s; +} + +.transcript.running { + border-color: var(--accent); +} + +.spinner { + flex-shrink: 0; + width: 8px; + height: 16px; + background: url(/img/spinner-light.gif) no-repeat 0 0 / 8px 16px; +} + +.spinner[hidden] { + display: none; +} + +@media (prefers-color-scheme: dark) { + .spinner { + background-image: url(/img/spinner-dark.gif); + } } .empty { @@ -591,7 +614,8 @@ textarea { } .thinking, -.tool { +.tool, +.tool-group { border: 1px solid var(--border); border-radius: 8px; padding: 4px 10px; @@ -599,10 +623,31 @@ textarea { } .thinking summary, -.tool summary { +.tool summary, +.tool-group summary { cursor: pointer; } +.tool-group-body { + display: flex; + flex-direction: column; + gap: 6px; + padding: 6px 0 4px; +} + +.tool-group-body .thinking, +.tool-group-body .tool { + background: var(--raised); +} + +.tool-group.errored { + border-color: var(--danger); +} + +.tool-count { + color: var(--muted); +} + .thinking summary { color: var(--muted); }