Open source AI, April 17 2026Stuck-tool dump on interruptacp-bridge/src/index.ts line 240

The April 17 2026 open source AI update nobody wrote up: the 15-line function that tells you which tool your agent was stuck on, and what it was trying to do

Every April 17 2026 roundup lists open source AI projects the same way: trending repo, star count, release tag. None of them covers what happens after git clone when your agent locks up mid-tool. Fazm shipped the missing half of the story the same afternoon: a 15-line function at acp-bridge/src/index.ts line 240 that, on user interrupt, dumps every in-flight tool with its exact Bash command, its Write or Edit target, its elapsed time, and which of the three parallel sessions owned it. Four commits, all on April 17, all in one MIT-licensed repo.

F
Fazm
14 min read
4.9from 200+
Every April 17 2026 claim tied to a commit SHA and a file:line
15-line logStuckToolsOnInterrupt at acp-bridge/src/index.ts line 240
MIT-licensed repo at github.com/mediar-ai/fazm
Google ADK 8.2K starsMeta Llama Stack 6.4KCodex CLI 0.121.0-alpha.4Block Goose 4.9KSmolAgents 4.1KLlama 4 Scout 10M ctxllama.cpp Vulkan flash-attnllama.cpp audio Gemma 4llama.cpp audio Qwen 3MemPalace 23K starsMicrosoft markitdownMCP 97M installsLinux Foundation MCP@playwright/mcp 0.0.68Claude Agent ACP 0.29.2eb1adda1 stuck-tool dumpd4f63904 queue fix5b31b3e0 unrouted logs17fa1513 gtimeout fallbackMIT license

Anchor fact, verifiable

15 lines at acp-bridge/src/index.ts:240 are the April 17 2026 story the roundups miss

The function is called logStuckToolsOnInterrupt(reason, sessionId?). It iterates a Map<string, InFlightTool> declared at line 179, calls summarizeToolInput (lines 181 to 228) to extract the relevant fields for Bash, Write, Edit, Read, or anything else, and writes one log line per stuck tool with elapsed seconds, lastStatus, and the bracketed summary. It is fired from two interrupt entry points: line 2635 for per-session stops, line 2647 for the legacy all-sessions stop. It landed in commit eb1adda1 on April 17 2026 at 13:02 PT, followed by 0d13b57a (13:04 PT) which added the in-progress input refresh and the elapsed-time delta to the completion log. That is the April 17 2026 open source AI change that does not appear in any star-count roundup.

0

Lines in logStuckToolsOnInterrupt at line 240

0

Commits to mediar-ai/fazm on April 17 2026

0

Interrupt entry points: line 2635 (per-session), line 2647 (all)

0

Line of the same-day && to || queue fix in AIResponseView.swift

What changes between a cold open source agent and Fazm's post-April-17 bridge

The before column is what a reader installing any of the top April 17 2026 projects gets by default. The after column is what Fazm's bridge emits to /tmp/fazm-dev.log the moment you hit stop.

Hitting stop on a 42-second Bash

User hits stop. The terminal spinner clears. No record of which tool was stuck. Next prompt is either eaten (because the queue flag was guarded by && instead of ||) or runs before the abort propagated.

  • No record of the stuck command
  • No elapsed time
  • Next prompt sometimes dropped
  • Unclear which session owned the tool

The Map that survives the interrupt

A stuck tool only stays debuggable if something outlives the event loop frame that raised the interrupt. Commit eb1adda1 added this declaration on April 17 2026 so the bridge has a snapshot to consult when stop is pressed.

acp-bridge/src/index.ts (lines 165 to 179)

Per-tool input extraction

The extractor has four typed branches (Bash, Write, Edit, Read) and a generic fallback for everything else. That is what keeps the stuck dump useful for any MCP server including user-added ones from ~/.fazm/mcp-servers.json.

acp-bridge/src/index.ts (lines 181 to 228)

What each stuck tool looks like in the log

Six card panel. One per summarizeToolInput branch plus the empty and fallback cases. Every card cites a line range in acp-bridge/src/index.ts.

Bash and terminal

Summary = `command=...` truncated to 300 chars + `description=...` truncated to 120. Lines 193 to 200 of acp-bridge/src/index.ts. This is the most common stuck tool in a Mac agent, and the most valuable to log verbatim.

Write

Summary = `file_path=...` + `content=<N>chars`. Lines 201 to 208. The content itself is not logged, only its length, so a 50 KB file write prints as content=50000chars, which is the signal you need without the payload.

Edit

Summary = `file_path=...` + `old=...` truncated to 80 chars of old_string. Lines 209 to 216. 80 chars is enough to identify the block an Edit was targeting without bloating the log.

Read

Summary = `file_path=...`. Lines 217 to 220. Single field. A stuck Read is rare, but when it happens (e.g. large PDF) you want to see exactly which path.

Everything else (MCP, Task, TodoWrite, etc)

Generic fallback at lines 221 to 228. Object.entries(rawInput).slice(0, 3) stringified with each value truncated to 120 chars. This is what makes the dump useful for mcp__playwright__browser_navigate, mcp__whatsapp__whatsapp_send_message, mcp__google-workspace__gmail_send, user MCPs from ~/.fazm/mcp-servers.json, and any other tool Claude might invoke.

Empty rawInput

If the tool hung before any rawInput chunks arrived, summarizeToolInput returns "" and the log line ends with the literal `[no input captured]`. That distinguishes a stuck tool with unknown input from one whose input had already streamed.

Five kinds of tools, one Map, five kinds of log lines

Every tool call the bridge sees on the wire flows through the same tracker. When interrupt fires, the tracker fans out into typed log lines the reader can grep in /tmp/fazm-dev.log.

Tool call to stuck-tool log

Bash tool
Write tool
Edit tool
Read tool
mcp__playwright__* tool
inFlightTools Map + summarizeToolInput
Tool STUCK [command=...]
Tool STUCK [file_path=...]
Tool STUCK [old=...]
Tool STUCK [file_path=...]
Tool STUCK [url=...] via fallback

The 15-line dump itself

The entire function. One loop, one filter on sessionId, one logErr call with summary or the literal [no input captured] if the tool hung before its arguments finished streaming.

acp-bridge/src/index.ts (lines 240 to 254)

Where the dump is actually called

Both interrupt branches of the bridge's interrupt handler call logStuckToolsOnInterrupt before tearing down the session. The per-session path scopes to ctx.sessionId so the log only contains that session's stuck tools.

acp-bridge/src/index.ts (lines 2630 to 2656)

Eight-step lifecycle, from tool_use to post-interrupt resume

1

Tool call arrives on the wire

The Claude Agent SDK streams a tool_use event over the ACP transport. acp-bridge/src/index.ts handleSessionUpdate (around line 2126) receives a tool_call with status=pending and rawInput still empty (the SDK streams arguments incrementally).

2

Bridge captures a snapshot in inFlightTools

Line 2192 (commit eb1adda1) calls inFlightTools.set(toolCallId, { title, kind, sessionId, sessionKey, startedAt: Date.now(), rawInput, lastStatus: status, lastLoggedInputFingerprint }). This is the object that will survive any future interrupt.

3

Input streams in, tracker refreshes

Commit 0d13b57a added a pending/in_progress refresh block at lines 2222 to 2245. Each time rawInput's fingerprint changes, the tracker updates tracked.rawInput and logs a single Tool input: line so the Bash command shows up in /tmp/fazm-dev.log even if the tool later hangs.

4

Tool completes, tracker deletes the entry

Line 2349 calls inFlightTools.delete(toolCallId). The log line Tool completed: ... status=... output=... elapsed=... [summary] replaces the running tracker snapshot. From this point the tool cannot be flagged as stuck.

5

User hits stop

The Fazm UI sends an interrupt message over the WebSocket. The bridge's interrupt handler decides, based on targetKey, whether this is a per-session or all-sessions interrupt.

6

Per-session dump (line 2635)

logStuckToolsOnInterrupt(`user interrupt (key=${targetKey})`, ctx.sessionId) iterates inFlightTools, skips any entry whose tool.sessionId does not match, and writes one Tool STUCK log line per matching entry before the session is aborted.

7

All-sessions dump (line 2647)

Legacy interrupt calls without a sessionKey take the all-sessions branch. logStuckToolsOnInterrupt("user interrupt (all sessions)") dumps every in-flight tool across main, floating, and observer, then the loop aborts each active query individually.

8

Next prompt resumes cleanly

Because the interrupt preserved the queue state (thanks to the same-day d4f63904 fix at AIResponseView.swift line 977), the user's follow-up text is enqueued instead of flushed. They keep typing, the stuck-tool log line tells them which repo or URL hung, and the next run starts from a known state.

Twelve messages, five actors, one STUCK line

A prompt-to-stop trace. The red Stop at message six is the moment interrupt key=main arrives. Message nine is the call to logStuckToolsOnInterrupt at line 2635. Message ten is the log line itself.

prompt to STUCK log line

UserFloating barACP bridgeinFlightToolsMCP childprompt: build the reposession/prompttool_use: Bash (status=pending)inFlightTools.set (line 2192)rawInput streams in: command=npm run build...refresh + fingerprint check (line 2232)Stopinterrupt key=mainlogStuckToolsOnInterrupt (line 2635)Tool STUCK ... [command=..., elapsed=42.3s]session/cancel + abortinterrupted (session paused, state preserved)

Verifying every April 17 2026 claim, live

Clone github.com/mediar-ai/fazm and run these four commands. The last one is the real log line the bridge emits when you hit stop on a 42-second Bash.

Verifying the April 17 2026 stuck-tool dump
15 lines

Tool STUCK on user interrupt (key=main): Bash ... elapsed=42.3s, lastStatus=in_progress [command=sleep 600 && echo done, ...]

acp-bridge/src/index.ts lines 247 to 253, verbatim log output shape

The one-operator fix that shipped 11 minutes after the dump

Commit d4f63904 at 13:13 PT changed a single && to || in AIResponseView.swift line 977 so follow-up prompts typed during the interrupted streaming window get queued instead of flushed. The inline comment that landed with the change is the clearest explanation of why the old guard never fired.

Follow-up message queue guard

// Desktop/Sources/FloatingControlBar/AIResponseView.swift
// BEFORE commit d4f63904 (April 17 2026)

if isLoading && isThisSessionStreaming {
    // THIS window is actively streaming a response - queue the message (text only)
    onEnqueueMessage?(trimmed)
} else {
    // Window is idle (or another window is busy) - always render the user
    ...
}
-9% shifted logic

Eight April 17 2026 open source projects, re-ranked from the stuck-tool-log seat

The flat SERP gives each project one line. Here is what each one looks like when the question is: does its call into the Fazm bridge show up readable in /tmp/fazm-dev.log when stop is pressed?

@playwright/mcp 0.0.68

Named in every April 17 roundup. Actually bundled in Fazm at acp-bridge/package.json line 13. When a stuck browser_navigate shows up in /tmp/fazm-dev.log with [url=...], the generic fallback at summarizeToolInput lines 221 to 228 is what made that readable.

@agentclientprotocol/claude-agent-acp 0.29.2

The Zed-originated open source Agent Client Protocol. Pinned in Fazm at acp-bridge/package.json line 15. The handleSessionUpdate function that feeds inFlightTools is reading ACP tool_call messages, exactly as the spec defines.

Google Agent Development Kit (8.2K stars)

Trending in the April 17 feed. A framework for building multi-agent systems. Different layer from Fazm: ADK builds agents, Fazm ships one to non-developers and watches when it gets stuck.

Meta Llama Stack + Llama 4 Scout 10M ctx

April 17 roundup headliner. Relevant to Fazm's stuck-tool dump because a 10M-context model is also a 10M-context-payload model: an Edit tool_call with a huge old_string could easily stall the stdio pipe. summarizeToolInput caps old at 80 chars so the log stays readable.

OpenAI Codex CLI 0.121.0-alpha.4

Realtime V2 background agent streaming. Named across every April 17 roundup. Has no user-visible equivalent of the stuck-tool dump in its changelog as of this date.

Block Goose (4.9K stars)

Open source local-first agent framework with MCP support. CLI, not a signed Mac app. Stuck Bash debugging there is whatever the shell environment gives you.

MemPalace (23K+ stars)

96.6% on LongMemEval, MIT licensed, April 17 headline. A memory project, not an interrupt project. Orthogonal to the stuck-tool angle but worth naming for readers arriving from the April 17 SERP.

llama.cpp Vulkan + audio Gemma 4 + Qwen 3

April 17 commits add Vulkan flash-attention, audio Gemma 4 support, and Qwen 3 audio support. Relevant to any on-device MCP tool Fazm might spawn locally. None of these runtimes changes what the stuck-tool dump needs to capture: the call's raw input.

Roundup framing vs. Fazm's post-April-17 debug surface

Nine dimensions on which a typical April 17 2026 roundup and Fazm's actual source tree diverge. Every cell on the right is tied to a file:line anchor the reader can grep.

FeatureTypical April 17 2026 roundupFazm (MIT, github.com/mediar-ai/fazm)
Release coverageTrending repos, star counts, release tagsFour commits on April 17, 13:02 to 13:13 PT
Debug surfaceNot addressedlogStuckToolsOnInterrupt at acp-bridge/src/index.ts line 240
Per-tool input captureNot addressedsummarizeToolInput lines 181 to 228 (Bash/Write/Edit/Read + fallback)
Elapsed time on stuckNot exposedelapsed=(Date.now()-startedAt)/1000 formatted .1f at line 247
Per-session vs all-sessionsFlat process killTwo entry points: line 2635 (per-key) and line 2647 (all)
Queue behavior on interruptNext prompt often eatenAIResponseView.swift line 977: if (isLoading || isThisSessionStreaming)
MCP coverageCover each MCP server as a standalone repoAny MCP server's stuck rawInput is summarized via the generic fallback (lines 221 to 228)
Log destinationstdout terminal, not persisted/tmp/fazm-dev.log (dev) and /tmp/fazm.log (prod), tailable live
LicenseMixed across projectsMIT, one repo at github.com/mediar-ai/fazm

Ten independently grep-verifiable claims

Every bullet here is a single grep away in the public Fazm repo. If a claim ever stops being true, the grep will say so.

April 17 2026 grep-verifiable facts

  • acp-bridge/src/index.ts line 169 declares interface InFlightTool with eight fields
  • acp-bridge/src/index.ts line 179 declares const inFlightTools = new Map<string, InFlightTool>()
  • acp-bridge/src/index.ts lines 181 to 228 define summarizeToolInput with branches for Bash, Write, Edit, Read plus a generic 3-field fallback
  • acp-bridge/src/index.ts line 240 defines function logStuckToolsOnInterrupt(reason, sessionId?)
  • acp-bridge/src/index.ts line 2192 calls inFlightTools.set(toolCallId, ...) on tool start
  • acp-bridge/src/index.ts line 2349 calls inFlightTools.delete(toolCallId) on tool completion
  • acp-bridge/src/index.ts line 2635 calls logStuckToolsOnInterrupt on per-session interrupt
  • acp-bridge/src/index.ts line 2647 calls logStuckToolsOnInterrupt("user interrupt (all sessions)") on legacy interrupt
  • Desktop/Sources/FloatingControlBar/AIResponseView.swift line 977 reads: if isLoading || isThisSessionStreaming
  • git log --since="2026-04-17" --until="2026-04-18" --oneline lists 18 commits including eb1adda1, 0d13b57a, 5b31b3e0, d4f63904, 17fa1513

Watch the stuck-tool dump fire live on a 42-second Bash

Twenty-minute walkthrough: we open acp-bridge/src/index.ts line 240, tail /tmp/fazm-dev.log, trigger a stuck Bash, hit stop, and read the STUCK line together. Then we wire your MCP server into the same bridge.

Book a call

FAQ, on the April 17 2026 stuck-tool dump

What were the most talked-about open source AI projects and updates on April 17, 2026?

Every public roundup names roughly the same shortlist: Google Agent Development Kit (8.2K stars), Meta Llama Stack (6.4K stars) deploying Llama 4 Scout with its 10M-context window, OpenAI Codex CLI 0.121.0-alpha.4 with Realtime V2 background agent streaming, Block Goose (4.9K stars), Hugging Face SmolAgents (4.1K stars), llama.cpp shipping Vulkan flash-attention plus audio Gemma 4 and Qwen 3 support, MemPalace (23K+ stars) hitting 96.6% on LongMemEval, Microsoft markitdown for PDF/DOCX/PPTX to Markdown conversion, and MCP crossing 97M installs in March with Linux Foundation governance. What those roundups never cover is what the consumer does after git clone when their agent locks up mid-tool-call.

What specifically did Fazm ship on April 17 2026 that no roundup mentions?

An in-flight tool diagnostic tracker and a stuck-tool dump on interrupt. The full set of April 17 2026 commits to github.com/mediar-ai/fazm: eb1adda1 at 13:02 PT added the inFlightTools Map and logStuckToolsOnInterrupt function to acp-bridge/src/index.ts, 0d13b57a at 13:04 PT updated tool logging to include in-progress rawInput and elapsed time, 5b31b3e0 at 13:06 PT added logging for unrouted notifications and tool call events, d4f63904 at 13:13 PT fixed the follow-up message queue condition in AIResponseView.swift, baf1dff5 at 13:23 PT added a changelog entry for that fix, 2858771b + a40713cd + 261785e0 + 5c6f171e + 83c143a7 + 8c5a7b03 + a3bb8fac + 7f75da0b at 12:43 to 12:46 PT added the mcp_servers_available broadcast wire, e15b06e3 + e92666a9 + 16a0ad9f + d3f77160 at 16:57 to 16:58 PT normalized model IDs and the available-models UI, and 17fa1513 at 20:07 PT added a GNU timeout fallback to run.sh for macOS shells without coreutils. Eighteen commits on April 17 alone. The anchor of this guide is the first four.

Where is the stuck-tool dump code in the Fazm repo?

acp-bridge/src/index.ts. The InFlightTool interface is declared at line 169, the live Map<string, InFlightTool> called inFlightTools at line 179, the summarizeToolInput extractor at lines 181 to 228 (Bash command capped 300 chars + description 120, Write file_path + content length, Edit file_path + old_string capped 80 chars, Read file_path, then a generic first-three-fields fallback capped 120 chars), the fingerprintInput helper at line 230, and logStuckToolsOnInterrupt itself at line 240. The Map is populated at line 2192 (Tool started), refreshed at line 2232 (Tool input change via in-progress rawInput), and deleted at line 2349 (Tool completed). The two interrupt call sites are line 2635 (per-session) and line 2647 (all sessions).

What fields does the stuck-tool log line actually contain?

For every entry in inFlightTools at the moment of interrupt, logStuckToolsOnInterrupt writes: reason (user interrupt key=<sessionKey> or user interrupt all sessions), tool title, toolCallId, kind, sessionKey or sessionId, elapsed seconds formatted to one decimal, lastStatus (pending or in_progress), and a bracketed summary that depends on the tool. For Bash, command=... (up to 300 chars) and description=... (up to 120). For Write, file_path=... plus content=<N>chars. For Edit, file_path=... plus old=... (up to 80 chars of old_string). For Read, file_path=.... For everything else, the first three keys of rawInput with each value truncated to 120 chars. If rawInput is empty at interrupt time, the line reads [no input captured].

Why did the follow-up message queue bug fix on the same day matter?

Desktop/Sources/FloatingControlBar/AIResponseView.swift line 977. The original condition was `if (isLoading && isThisSessionStreaming)` which only enqueued follow-up text while the streaming flag was true. The commit d4f63904 changed it to `if (isLoading || isThisSessionStreaming)` with an inline comment on line 978 that reads verbatim: `isLoading flips to false as soon as the first delta arrives, so && would never fire during streaming.` That is, during the most common real failure mode, the follow-up text was being flushed into the UI instead of queued, corrupting the next prompt. Paired with the stuck-tool dump from the same afternoon, the reader gets both halves of a real debug loop: the log line tells them which tool was hung, and the queue fix ensures their next attempt does not eat the prompt.

What is the exact log line a reader would see if their agent hung on a Bash command?

Tool STUCK on user interrupt (key=main): Bash (id=toolu_01ABC123, kind=execute, session=main, elapsed=42.3s, lastStatus=in_progress) [command=npm run build && npm test --watch, description=run the project build then start the watch tests]. The exact format is produced by the logErr call at lines 247 to 253 of acp-bridge/src/index.ts: reason + tool.title + toolCallId + tool.kind + sessionKey fallback to sessionId + elapsedMs/1000 to one decimal + lastStatus, then summarizeToolInput output appended in brackets. Everything prints to /tmp/fazm-dev.log on development builds and /tmp/fazm.log on production.

Why is per-session interrupt important for an open source MCP-based agent?

Fazm runs three parallel ACP sessions (main, floating, observer). Before April 17, a generic interrupt either ignored per-session context or bailed the entire process. The April 17 dump at line 2635 takes a sessionId argument and filters inFlightTools by `tool.sessionId !== sessionId`, so a user who hits stop on the floating bar only sees the floating-session Bash command dumped, not the observer session's unrelated save_observer_card call. Line 2647, the all-sessions variant called by legacy interrupt messages with no sessionKey, dumps every in-flight tool across the three sessions. Neither variant kills the bridge process. Tool state survives the interrupt, which is what makes the next prompt resumable.

How does this differ from Cursor or Claude Desktop or the reference Claude Agent SDK?

Those tools either use a single-threaded event loop (stuck tool freezes the whole UI) or a per-tool timeout (stuck tool silently fails with no record of what it was trying to do). Fazm's approach preserves the raw input of every tool call as it streams in, so at the moment of interrupt the log still has the exact Bash command (not just Bash), the exact file_path for Write or Edit, and the exact elapsed time. This is the diagnostic fidelity that makes a consumer-grade open source Mac agent debuggable by its user, not just by the developer who built it.

Which open source projects from the April 17 2026 SERP feed does Fazm actually bundle?

Two of the named projects are direct runtime deps: @playwright/mcp at acp-bridge/package.json line 13 (the same @playwright/mcp that every roundup mentions as a trending MCP server) and @agentclientprotocol/claude-agent-acp at line 15 (Zed's open source Agent Client Protocol, which is how the ACP bridge talks to Claude). Llama Stack, Codex CLI, Goose, SmolAgents, ADK, Archon, MemPalace, and markitdown are named in the April 17 roundups but are not bundled by Fazm. The stuck-tool dump applies to any MCP tool served over the bridge, including user-added MCP servers from ~/.fazm/mcp-servers.json.

What does the summarizeToolInput fallback do for MCP tools that are not Bash, Write, Edit, or Read?

Lines 221 to 228 of acp-bridge/src/index.ts. For any tool not matched by the Bash/Write/Edit/Read branches, the function iterates Object.entries(rawInput).slice(0, 3), stringifies each value, and truncates any string longer than 120 chars with a Unicode ellipsis. That generic fallback is what keeps the dump useful for mcp__playwright__browser_navigate (url=...), mcp__whatsapp__whatsapp_send_message (contact=..., message=...), mcp__google-workspace__gmail_send, or any user MCP tool. A stuck Playwright navigation on a 10s hanging URL therefore shows up as: Tool STUCK on user interrupt: mcp__playwright__browser_navigate (id=..., elapsed=10.4s, lastStatus=in_progress) [url=https://some-slow-site.example.com/long/path?...] rather than a generic timeout.

How can I verify these April 17 2026 claims myself?

Clone github.com/mediar-ai/fazm (MIT license). Run `git log --since="2026-04-17 00:00" --until="2026-04-18 00:00" --oneline` to see all eighteen commits. Open acp-bridge/src/index.ts and grep for: line 169 InFlightTool interface, line 179 inFlightTools Map, lines 181 to 228 summarizeToolInput, line 240 logStuckToolsOnInterrupt, line 2192 inFlightTools.set on tool start, line 2349 inFlightTools.delete on tool complete, line 2635 and line 2647 the two interrupt call sites. Then open Desktop/Sources/FloatingControlBar/AIResponseView.swift line 977 to see the `if (isLoading || isThisSessionStreaming)` fix with the inline explanation on line 978. Tail /tmp/fazm-dev.log while using the app, hit stop on a Bash tool mid-run, and the STUCK log line will print to that file.

Why is this angle different from every other April 17 2026 open source AI roundup?

A roundup is a shopping list. It tells the reader what exists. This guide is a post-install debug surface. It tells the reader what happens when those projects get wired into a consumer Mac app and one of them stalls. The anchor fact is four specific commits that all landed on April 17 2026, all in one repo, all adding observability to the moment when the agent's forward progress stops. That is the angle the roundups structurally cannot include, because a roundup points outward at projects, and the stuck-tool dump lives inward, in the bridge process between the UI and the open source tools the UI spawns.

fazm.AI Computer Agent for macOS
© 2026 fazm. All rights reserved.

How did this page land for you?

React to reveal totals

Comments ()

Leave a comment to see what others are saying.

Public and anonymous. No signup.