Files
byre/mac/Sources/Byre/TranscriptBuilder.swift
T
2026-10-19 09:59:03 -07:00

898 lines
31 KiB
Swift

import AppKit
import Foundation
import Observation
import OpenAPIRuntime
let quietReply = "NO_REPLY"
struct Usage {
var used: Int
var window: Int
var model: String
}
struct Call {
let name: String
let target: String
}
enum NotePart {
case text(String)
case task(String)
case url(String)
}
enum NoteStyle {
case plain, error
}
struct Note {
var parts: [NotePart]
var when: Date?
var style: NoteStyle = .plain
var cancelMergeAfterReview: String?
}
enum SubagentState {
case running, done, error
}
enum RowKind {
case user(actor: String, when: Date, images: [String], md: [MDBlock]?)
case byrePrompt(brief: String, when: Date, md: [MDBlock])
case agent(md: [MDBlock])
case subagentMessage(when: Date, md: [MDBlock])
case stream
case thinking(md: [MDBlock])
case tool(name: String, brief: String, input: String)
case subagent(title: String, description: String, prompt: [MDBlock])
case group
case orphan(String)
case turnEnd(error: String?)
case note(Note)
case review(note: Note, md: [MDBlock])
case finding(index: Int)
case findingResponse(snapshot: Int, index: Int, response: String, reason: String)
case ready(when: Date, md: [MDBlock])
case handoff
case snapshot(Snapshot)
case question
}
@MainActor
private var rowIds = 0
@MainActor
@Observable
final class RowList {
var rows: [Row] = []
}
@MainActor
@Observable
final class Row: Identifiable {
let id: Int
let kind: RowKind
let taskId: String?
var hidden = false
var tag = false
var result: String?
var unfinished = false
var started: Date?
var status = "starting…"
var statusState = SubagentState.running
var active = false
weak var owner: Row?
var calls: [Call] = []
var summary: Note?
var review: Review?
var reviewOf: Int?
var question: InboxItem?
var text = ""
var md: [MDBlock] = []
var summarySeq: Int64?
let body: RowList?
var pending: Bool {
switch kind {
case .tool, .subagent:
return result == nil && !unfinished
case .group:
return body!.rows.contains { $0.pending }
default:
return false
}
}
var inActive: Bool {
var o = owner
while let r = o {
if r.active {
return true
}
o = r.owner
}
return false
}
func within(_ ancestor: Row) -> Bool {
var o = owner
while let r = o {
if r === ancestor {
return true
}
o = r.owner
}
return false
}
init(_ kind: RowKind, taskId: String?, body: Bool = false) {
rowIds += 1
id = rowIds
self.kind = kind
self.taskId = taskId
self.body = body ? RowList() : nil
}
}
struct PendingSend: Identifiable {
let id: Int
let text: String
let images: [PendingImage]
let imageIds: [String]
let local: Bool
let queued: Bool
let md: [MDBlock]
var inputId: Int? = nil
}
@MainActor
private final class Run {
let first: Row
let container: RowList
var group: Row?
var calls: [Call]
let parent: String
init(first: Row, container: RowList, calls: [Call], parent: String) {
self.first = first
self.container = container
self.calls = calls
self.parent = parent
}
}
@MainActor
private final class Subagent {
let row: Row
var run: Run?
init(row: Row) {
self.row = row
}
}
@MainActor
private final class Ctx {
var tools: [String: Row] = [:]
var subagents: [String: Subagent] = [:]
var reviewers: Set<String> = []
var reviewButtons: [Int: Row] = [:]
var reviews: [Int: Review] = [:]
var reviewRows: [String: [Row]] = [:]
var questions: [String: Row] = [:]
var run: Run?
var held: RowList?
var open = false
var drawn: [Row]?
var quiet = false
var handoff: Row?
var reply: Row?
}
@MainActor
@Observable
final class TranscriptBuilder {
let root = RowList()
private(set) var pending: [PendingSend] = []
private(set) var context: [String: Usage] = [:]
private(set) var open = false
private(set) var lastMergeAfterReview: Int?
private(set) var lastReady: Int?
@ObservationIgnored private var ctx = Ctx()
@ObservationIgnored private var sink: RowList?
@ObservationIgnored private var stream: Row?
@ObservationIgnored private var streamTimer: Task<Void, Never>?
@ObservationIgnored private let taskId: String?
@ObservationIgnored private let worktree: () -> String
@ObservationIgnored private let running: () -> Bool
@ObservationIgnored private var pendingIds = 0
init(taskId: String?, worktree: @escaping () -> String, running: @escaping () -> Bool) {
self.taskId = taskId
self.worktree = worktree
self.running = running
}
func stop() {
dropStream()
}
func append(_ events: [ByreEvent]) -> Bool {
let changed = render(events)
open = ctx.open
return changed
}
func prepend(_ events: [ByreEvent]) {
let live = ctx
let box = RowList()
ctx = Ctx()
sink = box
_ = render(events)
sink = nil
ctx = live
root.rows.insert(contentsOf: box.rows, at: 0)
}
func showPending(_ text: String, _ images: [PendingImage]) -> () -> Void {
pendingIds += 1
let entry = PendingSend(id: pendingIds, text: text, images: images, imageIds: [], local: true, queued: running(), md: trimmed(text).isEmpty ? [] : parseMarkdown(text))
pending.append(entry)
ctx.run = nil
return { [weak self] in
self?.pending.removeAll { $0.id == entry.id }
}
}
func withdrawn(_ inputId: Int) {
pending.removeAll { $0.inputId == inputId }
}
private func confirmPending(_ text: String, queued: Bool, local: Bool) {
let i = queued ? pending.firstIndex { !$0.local && $0.text == text } : nil
if let i = i ?? (local ? pending.firstIndex { $0.local && $0.text == text } : nil) {
pending.remove(at: i)
}
}
private func release() {
guard let buffered = ctx.held else {
return
}
ctx.held = nil
ctx.open = true
(sink ?? root).rows.append(contentsOf: buffered.rows)
}
@discardableResult
private func put(_ row: Row, keepHeld: Bool = false) -> RowList {
if let handoff = ctx.handoff {
handoff.body!.rows.append(row)
return handoff.body!
}
if !keepHeld {
release()
}
ctx.drawn?.append(row)
if let held = ctx.held {
held.rows.append(row)
return held
}
let list = sink ?? root
list.rows.append(row)
return list
}
private func fold() {
let drawn = ctx.drawn ?? []
(sink ?? root).rows.removeAll { r in drawn.contains { $0 === r } }
ctx.drawn = nil
ctx.held = nil
ctx.open = false
ctx.quiet = false
}
private func subagentTag(_ parent: String) -> Bool {
!parent.isEmpty && ctx.subagents[parent] == nil
}
private func addToRun(_ row: Row, _ call: Call, _ parent: String) {
if let sub = ctx.subagents[parent] {
sub.run = extendRun(sub.run, row, call, parent) { r in
sub.row.body!.rows.append(r)
return sub.row.body!
}
return
}
ctx.run = extendRun(ctx.run, row, call, parent) { r in
self.put(r, keepHeld: true)
}
}
private func extendRun(_ run: Run?, _ row: Row, _ call: Call, _ parent: String, _ place: (Row) -> RowList) -> Run {
guard let run, run.parent == parent else {
let list = place(row)
return Run(first: row, container: list, calls: [call], parent: parent)
}
run.calls.append(call)
if run.group == nil {
let group = Row(.group, taskId: taskId, body: true)
group.owner = ctx.subagents[parent]?.row
for list in [run.container, sink ?? root] {
if let i = list.rows.firstIndex(where: { $0 === run.first }) {
list.rows[i] = group
break
}
}
ctx.drawn?.append(group)
group.body!.rows.append(run.first)
run.group = group
}
run.group!.body!.rows.append(row)
run.group!.calls = run.calls
run.group!.tag = subagentTag(run.parent)
return run
}
private func dropStream() {
streamTimer?.cancel()
streamTimer = nil
guard let stream else {
return
}
root.rows.removeAll { $0 === stream }
self.stream = nil
}
func appendDelta(_ ev: ByreEvent) {
if ctx.handoff != nil {
return
}
if let sid = ev.agentSessionId, ctx.reviewers.contains(sid) {
return
}
let row: Row
if let stream {
row = stream
} else {
row = Row(.stream, taskId: taskId)
row.hidden = true
stream = row
ctx.run = nil
put(row, keepHeld: true)
}
row.text += str(ev.payload.value, "text") ?? ""
if streamTimer != nil {
return
}
streamTimer = Task { [weak self] in
try? await Task.sleep(for: .milliseconds(100))
guard let self, !Task.isCancelled, let stream = self.stream else {
return
}
self.streamTimer = nil
stream.hidden = self.ctx.held != nil || quietReply.hasPrefix(trimmed(stream.text))
stream.md = parseMarkdown(stream.text)
}
}
private func openHandoff() {
let row = Row(.handoff, taskId: taskId, body: true)
row.summary = Note(parts: [.text("writing a handoff…")])
put(row)
ctx.handoff = row
}
private func closeHandoff(_ end: Row?) {
guard let handoff = ctx.handoff else {
return
}
ctx.handoff = nil
if let end, case .note(let note) = end.kind {
handoff.summary = note
} else {
handoff.summary = Note(parts: [.text("handoff not written")])
}
}
private func render(_ events: [ByreEvent]) -> Bool {
var contextChanged = false
for ev in events {
let p = ev.payload.value
let parent = parentOf(p)
if ev.kind == "session.created" && str(p, "role") == "reviewer", let sid = ev.agentSessionId, !sid.isEmpty {
ctx.reviewers.insert(sid)
continue
}
if let sid = ev.agentSessionId, ctx.reviewers.contains(sid) {
continue
}
if ev.kind == "agent.vendor" {
claudeTaskStatus(p)
continue
}
if ev.kind == "session.subagents" {
if int(p, "subagents") == 0 {
for sub in ctx.subagents.values where sub.row.active {
endSubagent(sub.row, .error, "ended with its process")
}
}
continue
}
if sink == nil && ((ev.kind == "agent.message" && parent.isEmpty) || ev.kind == "turn.ended" || ev.kind == "user.message") {
dropStream()
}
if sink == nil && ev.kind == "user.message" {
confirmPending(str(p, "text") ?? "", queued: true, local: ev.actor != "byre")
}
if sink == nil && (ev.kind == "task.merged" || ev.kind == "task.discarded") {
pending.removeAll { !$0.local }
}
if ev.kind == "user.queued" {
if sink == nil {
let text = str(p, "text") ?? ""
confirmPending(text, queued: false, local: true)
pendingIds += 1
let images = (p["images"] as? [(any Sendable)?] ?? []).map { jsString($0) }
pending.append(PendingSend(id: pendingIds, text: text, images: [], imageIds: images, local: false, queued: true, md: trimmed(text).isEmpty ? [] : parseMarkdown(text), inputId: int(p, "id")))
}
continue
}
if ev.kind == "user.unqueued" {
if sink == nil, let id = int(p, "id") {
withdrawn(id)
}
continue
}
if ev.kind == "user.message" && bool(p, "bounce") {
if let reply = ctx.reply {
(sink ?? root).rows.removeAll { $0 === reply }
ctx.held?.rows.removeAll { $0 === reply }
}
continue
}
if ev.kind == "session.context" && sink != nil {
continue
}
if ev.kind == "session.context", let sid = ev.agentSessionId, !sid.isEmpty {
let prev = context[sid]
let window = int(p, "window").flatMap { $0 == 0 ? nil : $0 } ?? prev?.window ?? 0
context[sid] = Usage(used: int(p, "used") ?? 0, window: window, model: str(p, "model") ?? "")
contextChanged = true
continue
}
if ev.kind == "snapshot.created" {
ctx.run = nil
let snap = try! decodePayload(ev.payload, as: Snapshot.self)
let row = Row(.snapshot(snap), taskId: taskId)
ctx.reviewButtons[snap.n] = row
if snap.trigger != .answer && sink == nil {
lastReady = nil
}
put(row)
continue
}
if ev.kind == "review.started" || ev.kind == "review.finished" || ev.kind == "review.finding" {
let r = try! decodePayload(ev.payload, as: Review.self)
ctx.reviewButtons[r.snapshotN]?.review = r
ctx.reviews[r.snapshotN] = r
for row in ctx.reviewRows[r.id] ?? [] {
row.review = r
}
if ev.kind == "review.finished" {
ctx.run = nil
for row in renderReview(r, ev.createdAt) {
put(row)
}
continue
}
}
if ev.kind == "inbox.updated" {
let item = try! decodePayload(ev.payload, as: InboxItem.self)
ctx.questions[item.id]?.question = item
continue
}
let sub = ctx.subagents[parent]
if ev.kind == "agent.tool_call" && isSubagentCall(p) {
let row = renderSubagent(p)
row.started = ev.createdAt
if let sub {
sub.run = nil
sub.row.body!.rows.append(row)
continue
}
ctx.run = nil
put(row, keepHeld: true)
continue
}
if ev.kind == "agent.tool_call" && sub == nil && str(p, "name") == "mcp__byre__respond_to_finding" {
let input = dict(p, "input") ?? [:]
let n = int(input, "snapshot") ?? 0
let row = Row(.findingResponse(snapshot: n, index: (int(input, "finding") ?? 0) - 1, response: str(input, "response") ?? "", reason: str(input, "reason") ?? ""), taskId: taskId)
row.started = ev.createdAt
ctx.tools[jsString(p["id"])] = row
if let r = ctx.reviews[n], r.state == .done {
row.review = r
ctx.reviewRows[r.id, default: []].append(row)
}
ctx.run = nil
put(row, keepHeld: true)
continue
}
if ev.kind == "agent.tool_call" {
let name = toolLabel(jsString(p["name"]))
let row = renderToolCall(p)
row.started = ev.createdAt
addToRun(row, Call(name: name, target: toolTarget(p["input"] ?? nil, worktree())), parent)
continue
}
guard let row = renderEvent(ev) else {
continue
}
if let sub {
sub.run = nil
sub.row.body!.rows.append(row)
continue
}
ctx.run = nil
if ev.kind == "agent.message" && parent.isEmpty {
ctx.drawn = nil
ctx.reply = row
}
if ev.kind == "turn.ended" {
ctx.open = false
}
if ev.kind == "turn.ended" && bool(p, "handoff") && ctx.handoff != nil {
closeHandoff(row)
continue
}
if ev.kind == "turn.ended" || (ev.kind == "user.message" && !bool(p, "mid_turn")) {
closeHandoff(nil)
}
if ev.kind == "user.message" && bool(p, "handoff") {
openHandoff()
}
if ev.kind == "turn.ended" && ctx.quiet && ctx.drawn != nil {
fold()
continue
}
if ev.kind == "turn.ended" || ev.kind == "user.message" {
ctx.drawn = nil
}
if ev.kind == "user.message" && ev.actor == "byre" && !(ev.owner ?? "").isEmpty && !bool(p, "mid_turn") && !bool(p, "handoff") {
ctx.held = RowList()
ctx.open = false
ctx.drawn = []
continue
}
if ev.kind == "user.message" && !bool(p, "mid_turn") {
ctx.open = true
}
if ev.kind == "turn.ended" && bool(p, "summary") && taskId != nil {
row.summarySeq = ev.seq
}
if ev.kind == "task.merge_after_review" && sink == nil {
lastMergeAfterReview = row.id
}
if ev.kind == "task.ready" && sink == nil {
lastReady = row.id
}
if ev.kind == "inbox.created" && sink == nil {
lastReady = nil
}
put(row, keepHeld: ev.kind == "agent.thinking")
}
return contextChanged
}
private func claudeTaskStatus(_ p: JSONDict) {
guard str(p, "type") == "system", let sub = ctx.subagents[jsString(p["tool_use_id"])] else {
return
}
switch str(p, "subtype") {
case "task_started":
sub.row.active = true
sub.row.status = bool(p, "is_backgrounded") ? "running in the background" : "running"
case "task_progress":
sub.row.status = str(p, "description") ?? "running"
case "task_notification":
endSubagent(sub.row, str(p, "status") == "completed" ? .done : .error, jsString(p["status"]))
default:
break
}
}
private func endSubagent(_ row: Row, _ state: SubagentState, _ status: String) {
row.active = false
row.statusState = state
row.status = status
for tool in ctx.tools.values where tool.result == nil && tool.within(row) {
tool.unfinished = true
}
}
private func endTools() {
for row in ctx.tools.values where row.result == nil && !(row.active || row.inActive) {
row.unfinished = true
}
}
private func renderToolCall(_ p: JSONDict) -> Row {
let row = Row(.tool(name: toolLabel(jsString(p["name"])), brief: toolBrief(p["input"] ?? nil, worktree()), input: prettyJSON(p["input"] ?? nil)), taskId: taskId)
row.tag = subagentTag(parentOf(p))
row.owner = ctx.subagents[parentOf(p)]?.row
ctx.tools[jsString(p["id"])] = row
return row
}
private func renderSubagent(_ p: JSONDict) -> Row {
let input = dict(p, "input") ?? [:]
let id = jsString(p["id"])
let type = str(input, "subagent_type") ?? "subagent"
let row = Row(.subagent(title: "\(jsString(p["name"])) \(type)", description: str(input, "description") ?? "", prompt: parseMarkdown(str(input, "prompt") ?? "")), taskId: taskId, body: true)
row.tag = subagentTag(parentOf(p))
row.owner = ctx.subagents[parentOf(p)]?.row
ctx.tools[id] = row
ctx.subagents[id] = Subagent(row: row)
return row
}
private func renderReview(_ r: Review, _ when: Date) -> [Row] {
let outcome = r.state == .failed ? "failed: \(r.error)" : r.state == .skipped ? "skipped: no changes" : reviewCounts(r)
let parts: [NotePart] = [.text("review of snapshot #\(r.snapshotN): \(outcome)")]
let fs = findings(r)
let head = fs.isEmpty
? Row(.note(Note(parts: parts, when: when, style: r.state == .failed ? .error : .plain)), taskId: taskId)
: Row(.review(note: Note(parts: parts, when: when), md: parseMarkdown(str(r.result?.value ?? [:], "summary") ?? "")), taskId: taskId)
let rows = [head] + fs.indices.map { Row(.finding(index: $0), taskId: taskId) }
for row in rows {
row.review = r
row.reviewOf = r.snapshotN
}
ctx.reviewRows[r.id, default: []] += rows
return rows
}
private func renderEvent(_ ev: ByreEvent) -> Row? {
let p = ev.payload.value
let when = ev.createdAt
func note(_ parts: [NotePart], _ style: NoteStyle = .plain, time: Bool = true) -> Row {
Row(.note(Note(parts: parts, when: time ? when : nil, style: style)), taskId: taskId)
}
switch ev.kind {
case "user.message":
ctx.quiet = false
if !bool(p, "mid_turn") {
endTools()
}
let text = str(p, "text") ?? ""
if ev.actor == "byre" {
let first = text.components(separatedBy: "\n").first ?? ""
let brief = first.count > 120 ? "\(first.prefix(120))…" : first
let row = Row(.byrePrompt(brief: brief, when: when, md: parseMarkdown(text)), taskId: taskId)
row.hidden = bool(p, "continuation")
return row
}
let images = (p["images"] as? [(any Sendable)?] ?? []).map { jsString($0) }
return Row(.user(actor: ev.actor, when: when, images: images, md: trimmed(text).isEmpty ? nil : parseMarkdown(text)), taskId: taskId)
case "agent.message":
let text = jsString(p["text"])
if !parentOf(p).isEmpty {
let row = Row(.subagentMessage(when: when, md: parseMarkdown(text)), taskId: taskId)
row.tag = subagentTag(parentOf(p))
return row
}
if trimmed(trimmed(text).components(separatedBy: "\n").last ?? "") == quietReply {
ctx.quiet = true
return nil
}
ctx.quiet = false
return Row(.agent(md: parseMarkdown(text)), taskId: taskId)
case "agent.thinking":
let row = Row(.thinking(md: parseMarkdown(jsString(p["text"]))), taskId: taskId)
row.tag = subagentTag(parentOf(p))
return row
case "agent.tool_result":
let body = resultText(p["content"] ?? nil)
guard let target = ctx.tools[jsString(p["tool_use_id"])] else {
return Row(.orphan(body), taskId: taskId)
}
target.result = body
if case .findingResponse = target.kind, bool(p, "is_error") {
target.hidden = true
}
return nil
case "turn.ended":
endTools()
if bool(p, "handoff") {
return note([.text("handoff written; continuing in a new session with a fresh context")])
}
return Row(.turnEnd(error: bool(p, "is_error") ? "turn ended: \(jsString(p["subtype"]).replacingOccurrences(of: "_", with: " "))" : nil), taskId: taskId)
case "session.created":
let model = str(p, "model") ?? ""
return note([.text("\(ev.actor) started a \(jsString(p["vendor"])) session\(model.isEmpty ? "" : " (\(model))")")])
case "session.model":
return note([.text("\(ev.actor) switched the model to \(jsString(p["model"]))")])
case "session.state":
guard let error = str(p, "error"), !error.isEmpty else {
return nil
}
return note([.text("session \(jsString(p["state"])): \(error)")], .error, time: false)
case "inbox.created":
let row = Row(.question, taskId: taskId)
let item = try! decodePayload(ev.payload, as: InboxItem.self)
row.question = item
ctx.questions[item.id] = row
return row
case "session.error":
return note([.text(jsString(p["error"]))], .error)
case "session.rate_limited":
let limit = dict(p, "rate_limit") ?? [:]
let resets = (limit["resetsAt"] as? Double ?? (limit["resetsAt"] as? Int).map(Double.init)).map { Date(timeIntervalSince1970: $0).formatted(date: .numeric, time: .standard) } ?? "unknown"
return note([.text("\(jsString(p["error"])) (\(jsString(p["vendor"])) \(limit["rateLimitType"].map { jsString($0) } ?? "") limit resets \(resets))")], .error)
case "task.created":
if let from = str(p, "forked_from") {
return note([.text("\(ev.actor) forked this task from "), .task(from), .text(" at snapshot #\(jsString(p["forked_at"])); its agent continues that task's conversation as it stood there")])
}
if let parentTask = str(p, "parent_task_id") {
return note([.text("subtask of "), .task(parentTask), .text(", created by its agent")])
}
return nil
case "task.parent_merge_started":
return note([.text("\(ev.actor) asked "), .task(jsString(p["parent_task_id"])), .text("'s agent to merge this subtask into its branch")])
case "task.parent_merge_stopped":
return note([.text("the merge into the parent task didn't start")], .error)
case "task.merge_title":
return note([.text("\(ev.actor) set the merge commit title to \"\(jsString(p["merge_title"]))\"")])
case "task.merge_queued":
return note([.text("\(ev.actor) asked to merge; held until the merge in progress in this project is done")])
case "task.merge_stopped":
return note([.text("\(ev.actor) stopped the merge for review: \(jsString(p["reason"]))")])
case "task.merge_after_review":
var n = Note(parts: [.text("\(ev.actor) asked to merge once the review of snapshot #\(jsString(p["snapshot_n"])) finishes with no findings")], when: when)
n.cancelMergeAfterReview = jsString(p["id"])
return Row(.note(n), taskId: taskId)
case "task.merge_after_review_cancelled":
return note([.text("\(ev.actor) cancelled the merge after review")])
case "task.merge_after_review_dropped":
return note([.text("didn't merge after the review: \(jsString(p["reason"]))")])
case "preview.exposed":
return note([.text("\(ev.actor) exposed \"\(jsString(p["name"]))\" at "), .url(String(int(p, "port") ?? 0))])
case "task.merged":
let commit = str(p, "commit") ?? ""
if let parentTask = str(p, "parent_task_id") {
return note([.text("\(ev.actor) merged the task into "), .task(parentTask), .text(", now at \(commit.prefix(7))")])
}
let pushed = str(p, "pushed") ?? ""
return note([.text("\(ev.actor) merged the task\(commit.isEmpty ? "" : " as \(commit.prefix(7))")\(pushed.isEmpty ? "" : " and pushed it to \(pushed)")")])
case "task.merged_to_main":
return note([.text("\(ev.actor) merged the work so far as \(jsString(p["commit"]).prefix(7)), \"\(jsString(p["merge_title"]))\", pushed it to \(jsString(p["pushed"])) and kept the task open")])
case "task.summary":
return note([.text("\(ev.actor) set the task summary")])
case "task.ready":
return Row(.ready(when: when, md: parseMarkdown(jsString(p["overview"]))), taskId: taskId)
case "task.discard_started":
return note([.text("\(ev.actor) asked to discard the task; the agent writes the task summary first")])
case "task.discarded":
return note([.text("\(ev.actor) discarded the task")])
default:
return nil
}
}
}
private let severities = ["critical", "high", "medium", "low", "info"]
func isReviewEvent(_ ev: ByreEvent) -> Bool {
ev.kind.hasPrefix("review.")
}
func findings(_ r: Review) -> [JSONDict] {
(r.result?.value["findings"] as? [(any Sendable)?] ?? []).map { $0 as? JSONDict ?? [:] }
}
func findingCount(_ r: Review) -> Int {
r.state == .done ? findings(r).indices.filter { findingOpen(r, $0) }.count : 0
}
func latestReview(_ events: [ByreEvent]) -> Review? {
var best: Review?
for ev in events where isReviewEvent(ev) {
let r = try! decodePayload(ev.payload, as: Review.self)
if best == nil || r.snapshotN >= best!.snapshotN {
best = r
}
}
return best
}
func reviewCounts(_ r: Review) -> String {
severityCounts(findings(r))
}
func severityCounts(_ fs: [JSONDict]) -> String {
let parts = severities.map { s in (s, fs.filter { str($0, "severity") == s }.count) }.filter { $0.1 > 0 }
return parts.isEmpty ? "no findings" : parts.map { "\($0.1) \($0.0)" }.joined(separator: ", ")
}
private func parentOf(_ p: JSONDict) -> String {
str(p, "parent_tool_use_id") ?? ""
}
private func isSubagentCall(_ p: JSONDict) -> Bool {
let name = str(p, "name")
return name == "Agent" || name == "Task"
}
func toolLabel(_ name: String) -> String {
let prefix = "mcp__byre__"
if !name.hasPrefix(prefix) {
return name
}
return name.dropFirst(prefix.count).split(separator: "_", omittingEmptySubsequences: false).map { $0.prefix(1).uppercased() + $0.dropFirst() }.joined(separator: " ")
}
private func toolBrief(_ input: (any Sendable)?, _ root: String) -> String {
guard let i = input as? JSONDict else {
return ""
}
let brief = ["command", "file_path", "pattern", "url", "question", "description"].lazy.compactMap { i[$0] ?? nil }.first
let text = relative(brief.map { jsString($0) } ?? "", root)
return text.count > 120 ? "\(text.prefix(120))…" : text
}
private func toolTarget(_ input: (any Sendable)?, _ root: String) -> String {
guard let path = (input as? JSONDict)?["file_path"] as? String else {
return ""
}
return relative(path, root)
}
private func relative(_ path: String, _ root: String) -> String {
!root.isEmpty && path.hasPrefix("\(root)/") ? String(path.dropFirst(root.count + 1)) : path
}
private func resultText(_ content: (any Sendable)?) -> String {
if let s = content as? String {
return s
}
if let list = content as? [(any Sendable)?] {
return list.map { c in
if let d = c as? JSONDict, let text = d["text"] {
return jsString(text)
}
return compactJSON(c)
}.joined(separator: "\n")
}
return prettyJSON(content)
}