From 11e21bcf185ca5a6db3f3418becc93bc817f7159 Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Thu, 22 Oct 2026 22:14:19 -0700 Subject: [PATCH] Stop showing the held tool call as running during a pause --- internal/agents/antigravity.go | 4 ++-- internal/agents/codex.go | 2 +- internal/agents/pause.go | 24 +++++++++++++++++++----- internal/agents/snapshots.go | 2 +- mac/Sources/Byre/Chat.swift | 7 +++++-- mac/Sources/Byre/TranscriptBuilder.swift | 15 ++++++++++++++- 6 files changed, 42 insertions(+), 12 deletions(-) diff --git a/internal/agents/antigravity.go b/internal/agents/antigravity.go index f4361a0..b0a70af 100644 --- a/internal/agents/antigravity.go +++ b/internal/agents/antigravity.go @@ -205,7 +205,7 @@ func (m *Manager) agyBeforeModel(l *live) hookcmd.Func { if in.InvocationNum == 0 || l.reviewID != "" { return map[string]any{}, nil } - m.holdWhilePaused(ctx, l) + m.holdWhilePaused(ctx, l, "") mt := m.deliverMidTurn(ctx, l) if mt.text == "" { return map[string]any{}, nil @@ -216,7 +216,7 @@ func (m *Manager) agyBeforeModel(l *live) hookcmd.Func { func (m *Manager) agyBeforeTool(l *live) hookcmd.Func { return func(ctx context.Context, input json.RawMessage) (map[string]any, error) { - if !m.heldBeforeTool(ctx, l) || !m.midTurnWaiting(ctx, l) { + if !m.heldBeforeTool(ctx, l, "") || !m.midTurnWaiting(ctx, l) { return map[string]any{"decision": "allow"}, nil } return map[string]any{"decision": "deny", "reason": pausedCallNotice}, nil diff --git a/internal/agents/codex.go b/internal/agents/codex.go index fb4e7b4..88f923a 100644 --- a/internal/agents/codex.go +++ b/internal/agents/codex.go @@ -209,7 +209,7 @@ func (m *Manager) steerMidTurn(ctx context.Context, l *live, s *codex.Session) { func (m *Manager) codexAfterTool(l *live) hookcmd.Func { return func(ctx context.Context, input json.RawMessage) (map[string]any, error) { - if m.holdWhilePaused(ctx, l) && ctx.Err() == nil { + if m.holdWhilePaused(ctx, l, "") && ctx.Err() == nil { m.steerMidTurn(context.WithoutCancel(ctx), l, l.sess.(codexProcess).s) } return map[string]any{}, nil diff --git a/internal/agents/pause.go b/internal/agents/pause.go index c1be8ca..0f425ac 100644 --- a/internal/agents/pause.go +++ b/internal/agents/pause.go @@ -131,7 +131,15 @@ func (m *Manager) isPaused(sessionID string) bool { return m.paused[sessionID] != nil } -func (m *Manager) holdWhilePaused(ctx context.Context, l *live) bool { +func hookToolUseID(input json.RawMessage) string { + in := struct { + ToolUseID string `json:"tool_use_id"` + }{} + json.Unmarshal(input, &in) + return in.ToolUseID +} + +func (m *Manager) holdWhilePaused(ctx context.Context, l *live, toolUseID string) bool { held := false for { m.mu.Lock() @@ -141,7 +149,13 @@ func (m *Manager) holdWhilePaused(ctx context.Context, l *live) bool { return held } if !held { - slog.Info("agent held by pause", "session", l.id) + slog.Info("agent held by pause", "session", l.id, "tool_use_id", toolUseID) + if toolUseID != "" { + err := m.emit(context.WithoutCancel(ctx), l, agentActor, "agent.held", map[string]any{"tool_use_id": toolUseID}) + if err != nil { + slog.Error("record held tool call", "session", l.id, "err", err) + } + } } held = true select { @@ -152,13 +166,13 @@ func (m *Manager) holdWhilePaused(ctx context.Context, l *live) bool { } } -func (m *Manager) heldBeforeTool(ctx context.Context, l *live) bool { - return m.holdWhilePaused(ctx, l) && ctx.Err() == nil +func (m *Manager) heldBeforeTool(ctx context.Context, l *live, toolUseID string) bool { + return m.holdWhilePaused(ctx, l, toolUseID) && ctx.Err() == nil } func (m *Manager) beforeTool(l *live) hookcmd.Func { return func(ctx context.Context, input json.RawMessage) (map[string]any, error) { - if !m.heldBeforeTool(ctx, l) { + if !m.heldBeforeTool(ctx, l, hookToolUseID(input)) { return map[string]any{}, nil } mt := m.deliverMidTurn(ctx, l) diff --git a/internal/agents/snapshots.go b/internal/agents/snapshots.go index 7922de9..2f4a8be 100644 --- a/internal/agents/snapshots.go +++ b/internal/agents/snapshots.go @@ -291,7 +291,7 @@ func (m *Manager) afterTool(l *live) hookcmd.Func { if err != nil { return nil, err } - m.holdWhilePaused(ctx, l) + m.holdWhilePaused(ctx, l, hookToolUseID(input)) mt := m.deliverMidTurn(ctx, l) if mt.text == "" { return map[string]any{}, nil diff --git a/mac/Sources/Byre/Chat.swift b/mac/Sources/Byre/Chat.swift index 2e5b108..94287f0 100644 --- a/mac/Sources/Byre/Chat.swift +++ b/mac/Sources/Byre/Chat.swift @@ -1334,7 +1334,7 @@ struct RowView: View { } content: { VStack(alignment: .leading, spacing: 6) { PreBox(text: input) - ToolResult(result: row.result, unfinished: row.unfinished, started: row.started) + ToolResult(result: row.result, unfinished: row.unfinished, held: row.held, started: row.started) } .padding(.vertical, 6) } @@ -1350,7 +1350,7 @@ struct RowView: View { ForEach(row.body!.rows) { child in RowView(row: child, model: model, nested: true) } - ToolResult(result: row.result, unfinished: row.unfinished, started: row.started) + ToolResult(result: row.result, unfinished: row.unfinished, held: row.held, started: row.started) } } @@ -1703,11 +1703,14 @@ private struct DiscussItem: View { private struct ToolResult: View { let result: String? let unfinished: Bool + let held: Bool let started: Date? var body: some View { if let result { PreBox(text: result) + } else if held { + label("held by pause") } else if unfinished || started == nil { label(unfinished ? "no result" : "running…") } else { diff --git a/mac/Sources/Byre/TranscriptBuilder.swift b/mac/Sources/Byre/TranscriptBuilder.swift index 4284c7f..a7061dc 100644 --- a/mac/Sources/Byre/TranscriptBuilder.swift +++ b/mac/Sources/Byre/TranscriptBuilder.swift @@ -78,6 +78,7 @@ final class Row: Identifiable { var tag = false var result: String? var unfinished = false + var held = false var started: Date? var status = "starting…" var statusState = SubagentState.running @@ -97,7 +98,7 @@ final class Row: Identifiable { var pending: Bool { switch kind { case .tool, .subagent: - return result == nil && !unfinished + return result == nil && !unfinished && !held case .group: return body!.rows.contains { $0.pending } default: @@ -590,6 +591,18 @@ final class TranscriptBuilder { ctx.questions[item.id]?.question = item continue } + if ev.kind == "agent.held" { + ctx.tools[jsString(p["tool_use_id"])]?.held = true + continue + } + if ev.kind == "session.paused" { + if !bool(p, "paused") { + for tool in ctx.tools.values { + tool.held = false + } + } + continue + } let sub = ctx.subagents[parent] if ev.kind == "agent.tool_call" && isSubagentCall(p) { let row = renderSubagent(p, parent)