Changelog

All notable changes to Irrlicht, following Keep a Changelog conventions.

v0.5.8 (2026-07-15)

Mistral Vibe becomes a fully supported agent, the Activity Matrix lands on macOS, and a long sweep of session-lifecycle fixes closes the gaps where a session could silently stall in the wrong state.

Highlights

Mistral Vibe is now a supported agent

The Irrlicht dashboard listing three sessions in one project — a Codex session on gpt-5.6-terra, a Claude Code session on opus-4-8, and a Mistral Vibe session on mistral-medium-3.5, each with its own context bar and running cost

Irrlicht now watches Mistral Vibe sessions the same way it watches Claude Code, Codex, and the rest: working/waiting/ready state, the model chip, the context bar, running cost, task summaries, and backchannel control all work out of the box. No configuration — start vibe in a project and it shows up.

Why it matters: if you run Vibe alongside your other agents, it no longer sits invisible in a terminal you have to remember to check. (#921)

The Activity Matrix comes to the macOS app

The Activity Matrix in the macOS History panel — a grid of projects down the left and time buckets across the top, each cell a stacked working/waiting/ready mini bar, with Export CSV and Export JSON buttons

The Activity Matrix — a projects × time grid showing when your agents were working, waiting on you, or idle — shipped web-only in v0.5.7. It is now a top-level History tab in the macOS app too, zoomable across nine granularities from one minute to one year, with CSV and JSON export.

Why it matters: the menu bar app was the one place you could not see your own activity history, which is where most people actually live. (#1038, #1028, #1047, #1046)

Added

  • macOS: a “How is this calculated?” link on the CO2 chart (#1035, #1029) — the same link to the CO2 Methodology page the web dashboard already had, and it stays visible in the empty-data state.
  • A waiting-only summary mode replaces expand-all (#993, #985) — a strict two-state toggle instead of a noisy expand-all that flattened every session’s summary into a wall of text; sessions pop open and closed automatically as they enter and leave the waiting state.

Fixed

  • Codex subagent rollouts now link to their parent session (#1055, #1050) — instead of appearing as unrelated top-level sessions.
  • Codex accounts reporting only a single 7-day quota window (#1052) — no longer get a phantom 5-hour bucket invented alongside it, and the macOS and web copy no longer promises a fixed 5h/7d pair.
  • A parent session no longer flips to ready while a background subagent is still running (#1037, #1036) — Claude Code’s own pendingBackgroundAgentCount is now an independent hold condition alongside the file-based check it used to race against.
  • Non-Claude adapters no longer fall back to Claude’s model config (#1022, #1019) — mistral-vibe, aider, antigravity, gemini-cli, kiro-cli, and opencode read the operator’s ~/.claude/settings.json, which could surface an unrelated Claude Code session’s model on a Vibe session.
  • The waiting pill no longer truncates mid-question (#1004, #979) — the hard 70-character cut is gone, a scored sentence covers turns that end without a literal question, and the separate purple “intent” pill is removed on both platforms.
  • A session’s first turn is no longer swallowed (#1010, #1000, #999) — when the daemon discovers it late, or when it is a child session, the missing ready→working step is synthesized instead of collapsing the turn.
  • Backchannel state is re-keyed across presession reconciliation (#1007, #1001) — a session discovered before its process is identified keeps working control instead of silently losing it.
  • Cold-start session discovery is now bounded (#1011) — the filesystem watcher arms a root watch and scans newest-first instead of walking an unbounded history.
  • A tracked pre-session is not reaped until its PID is confirmed dead (#994).
  • History’s concurrency charts resolve each project once per scan (#1058, #1049) — and cache it, instead of invoking git per request.
  • A session whose only recorded transition is still active is counted in the concurrency series (#990, #983) — instead of vanishing from the chart.
  • Idle Mistral Vibe sessions retry cwd/project resolution (#1031) — instead of staying unattributed.
  • Homebrew: the cask uses the symbol form depends_on macos: :ventura (#1053) — silencing the deprecation warning Homebrew printed on every brew upgrade.

Changed / Docs / Distribution

  • Two macOS snapshot references drifted by a toolchain antialiasing change were regenerated (#1044).
  • ir:test-mac stamps the dev version string into the replace-mode Info.plist (#1006) — so a dev build is identifiable in the GUI.
  • onboarding-factory: teardown polls, daemon.log classification, and a known-seams document (#1025) — recording which gaps are deliberate.
  • onboarding-factory: shared TUI-dialog-dismiss poll extracted into _lib/drive (#1014, #1008) — and mistral-vibe’s tool-permission step waits on a condition instead of a fixed sleep.
  • CI: the star-history chart commit routes through a PR (#1015, #1013) — and its stargazer fetch uses a PAT instead of GITHUB_TOKEN.
  • /ir:exec docs and self-assign precondition (#1023, #991, #1030, #1027) — documents [mode] <N> invocation and auto mode, requires a feature to be implemented and verified across every frontend it touches, and makes Phase 4’s self-assign a verified precondition rather than a best-effort step.

Technical appendix

  • Mistral Vibe adapter (#921): a full agent column — Go adapter under core/adapters/inbound/agents/vibe/ plus the complete onboarding-factory matrix column (44 scenario cells, driver, recordings). Source is FilesUnderRoot on ~/.vibe/logs/session with SessionIDFromPath, since the transcript filename is the constant messages.jsonl and the session id is therefore the parent <session-id> directory. Verified against a real ~/.vibe transcript rather than the AI-generated handover doc, which was wrong on two points now corrected: tool calls use the OpenAI nested function.name shape (not a flat name), and vibe is a Python console-script whose comm is the interpreter — so an ExactName{"vibe"} process match would never fire and it needs a CommandPattern on the command line instead. cwd, model, context window, and token counts come from the sibling meta.json sidecar (config.active_model, config.auto_compact_threshold, stats.context_tokens), memoized by (mtime, size), since the JSONL itself carries no timestamp, cwd, model, or usage. SplitsQueuedFollowUpTurns opts into queued-turn boundary detection (#988) because vibe’s message_queue.py drains a mid-turn follow-up synchronously the instant the prior turn’s agent_running() clears — a genuinely distinct turn with no observable ready gap.
  • Activity Matrix on macOS (#1038, #1028): the daemon’s chart=state API was already correct and fully tested from v0.5.7; nothing on the macOS side ever consumed it, and the v0.5.7 changelog did not disclose the gap. Adds HistoryStateResponse (a sibling to HistoryYieldResponse/HistoryDoraResponse, matching the wire shape 1:1) and HistoryGranularity (the nine-step 1m..1y zoom that replaces range/start/end entirely for this chart, mirroring the daemon’s own chart=="state" special case). Activity is a top-level tab rather than nested under Metrics because it has no Range/Group at all. HistoryActivityContentView pins the project-name column beside a horizontally-scrollable grid — SwiftUI has no built-in two-axis sticky-header grid, so only the row-label column is pinned in this pass, a deliberate v1 scope reduction. Cells scale against the busiest cell in the whole grid, not per-row. Tooltips go through the app’s own .tooltip() rather than SwiftUI’s .help(), which does not render inside the NSPanel.
  • Activity Matrix cleanup (#1047, #1046): live QA of the new macOS tab surfaced four bugs in chart=state, two of them shared with its chart=agents sibling. A session with no recorded CWD was labeled “unknown” unconditionally inside the concurrency tracker, unlike every other chart, which only surfaces “unknown” past a 10%-of-window-total share — the substitution moved out to resolveUnknownConcurrencyProject/resolveUnknownStateProject. buildStateResponse never capped project rows, so every project with any historical activity appeared, including years-old one-off worktree sessions; capped to the busiest 8 (historyStateProjectLimit). concurrencyProject() did a raw filepath.Base(cwd) with no git-root resolution, so a session run inside .claude/worktrees/<N>-<slug>/ was keyed as its own project instead of folding into the real repo; now wired through git.Adapter.GetProjectName via a concurrencyProjectResolver interface, memoized per scan. HistoryActivityContentView was the one view in HistoryView.swift not constraining its width with .frame(maxWidth: .infinity) and overflowed the fixed 380pt panel.
  • pendingBackgroundAgentCount hold (#1037, #1036): the file-based hasActiveChildren check loses a short race when a child’s transcript finishes and is reclassified to ready (and cleaned up) moments before Claude Code delivers the task-notification that would give the parent a reason to keep working. Claude Code’s own turn_duration system event already reports pendingBackgroundAgentCount; it is now folded into holdParentForActiveChildren and reevaluateParent as an independent OR condition.
  • Non-Claude model fallback (#1022, #1019): getDefaultModelFromConfig’s switch only special-cased “pi” and “codex”, so every other adapter fell into a catch-all default that read the operator’s ~/.claude/settings.json. A mistral-vibe session whose meta.json sidecar had not been written yet (the window right after a /clear rotation) would surface an unrelated claude-code session’s model name instead of staying empty.
  • Waiting pill (#1004, #979): the summary fell back to the raw first prompt cut at a period, and the question box truncated an already-correctly-extracted question at a hard 70-rune cut, sometimes chopping off the actual question. The daemon’s safety bound rises to ~200 runes, a shared heuristic sentence-scorer covers the case where no literal question exists, and the pill passively upgrades from Claude Code’s own away_summary recap once it arrives. The separate purple “intent” pill is deleted on both platforms — the surviving orange/waiting pill is the only textual element and shows only while waiting, so a finished session shows no textual pill by design.
  • Waiting-only summary mode (#993, #985): a strict two-state toggle — collapsed (unchanged) and waiting (summary/question block shown only for sessions whose state === 'waiting'), computed live off session state so a session transitioning in or out of waiting pops open or closed automatically instead of relying on a stale snapshot. Web keeps its per-row manual chevron as a separate row-scoped concern layered on top of the global mode; macOS has no per-row toggle, so the mode alone drives summaryBlock visibility.
  • Backchannel re-keying (#1007, #1001): both BackchannelEngine’s edge state and the terminal-observer/session state were keyed on an identity that changes when a presession reconciles into a real session, so control silently detached at reconciliation; both are now re-keyed across the transition.
  • Codex rollout linkage (#1055, #1050): derives Codex thread identity and subagent parent linkage from rollout session metadata, and defers zero-byte child create events until their header is readable, preventing a child from flickering as a top-level session before its parent link is known.
  • Concurrency project caching (#1058, #1049): raw-CWD project resolution now persists on the daemon-lifetime concurrency tracker, with a cache-miss guard so concurrent history requests invoke git once per CWD rather than once per request.
  • Model alias map: re-synced against codeburn’s BUILTIN_ALIASES with no changes — the only upstream addition is a gpt-4.1 self-alias that is a no-op for us and stays deliberately omitted.

v0.5.7 (2026-07-13)

History gains three new lenses — agent activity over time, DORA metrics, and CO2 equivalents — alongside a major flaky-test cleanup, non-English waiting-cue detection, and a wave of accessibility and mechanical-quality fixes.

Added

  • Activity Matrix chart in History (chart=state) (#981, #986) — a projects × time-bucket grid of working/waiting/ready agent counts, zoomable across nine granularities from 1 minute to 1 year.
  • DORA metrics in History (#951, #959) — Deployment Frequency, Lead Time for Changes, Change Failure Rate, and MTTR as a new Metrics section on both macOS and web, computed on request from the selected project’s git repo.
  • CO2 equivalent reference lines on the CO2 chart (#952, #955) — a web search, a phone charge, a flight, a tree-year, and more, plus a “CO2 Methodology” docs page citing the source for every figure.
  • Non-English question and waiting-cue detection (#939) — sessions ending a turn in German, Spanish, French, or Portuguese now correctly register as waiting on you instead of only ASCII/English cues.
  • Cache-bloat badge shows percentage above baseline (#949) — not just an arrow.
  • macOS: a single “Enable notifications” master toggle (#947) — gates all three per-event notification rows instead of always showing them.
  • onboarding-factory: interrupt and reset_session driver actions (#970, #935, #972) — implemented for antigravity and kiro-cli.

Fixed

  • WCAG-AA contrast failures in summary/question pills (#984, #987) — on macOS and web, in both light and dark mode; some combinations measured as low as 2.03:1 against the 4.5:1 minimum.
  • CO2 chart reference-line labels overlapping each other (#980, #982) — the near-invisible methodology icon is now a clearly visible “How is this calculated?” link.
  • DORA project picker could overflow the History panel (#969, #962) — with a long project name.
  • History tab’s Range/Group controls closed a <fieldset> with a mismatched </div> (#968, #958) — HTML5 silently ignores the mismatch rather than closing, so later controls could end up nested inside the still-open fieldset.
  • Custom backchannel actions (e.g. /compact) never actually submitted (#966, #963) — they typed the command but never appended a submit sequence.
  • Contrast fix for the comparison-badges documentation page (#961, #960) — third attempt at satisfying an automated checker that appears to ignore alpha-compositing.
  • The ready→working “force-bounce” now logs a reason (#953, #957) — instead of silently flipping state with no visible cause in the events log.
  • A session starting a fresh background process right after settling to ready could bounce back to ready (#941, #937) — missing the new process’s liveness hold.
  • A recurring family of -race test flakes, closed out for good (#973, #976, #965, #967, #975) — root-caused to a session-repository aliasing bug (a shared *SessionState pointer handed out by Load()).
  • Swallowed exceptions in the web dashboard are now logged (#938) — instead of silently discarded, so private-mode storage failures and dropped websocket frames are at least visible in the console.

Changed / Docs / Distribution

  • macOS Settings and History panels now share one header/toggle/spacing/width chrome (#947) — the History Filters dropdown is removed in favor of the existing drilldown, and the Menu Bar Icon segmented control now fills its row edge-to-edge.
  • Internal: 13 functions taking 8+ positional parameters now take an options struct (#945).
  • ~50 mechanical SonarQube maintainability findings resolved (#932, #950, #960, #961) — across Go, Swift, JavaScript, CSS, and Dockerfiles, plus 79 already-fixed findings closed via the SonarQube API after being suppressed in code but never marked resolved upstream.

Technical appendix

  • Activity Matrix (#981, #986)ConcurrencyReader.StateSeries reconstructs per-project, per-state bucketed counts from lifecycle recordings; sessionTimeline.activeIntervals is refactored onto a shared stateReconstruction so the merged (agents) and per-state (state) views can never drift apart. chart=state resolves its window from a named ?granularity= step; month/6-month/year use an averaged bucket width rather than true calendar boundaries, a deliberate documented approximation. Found via live smoke-testing: a session still active at the query window’s end was spuriously counted as having gone “ready” — fixed with a regression test. A second, unrelated pre-existing edge case was found in the same session and filed separately as #983.
  • DORA metrics (#951, #959) — supersedes #919/#936’s wrong-shaped standalone-script attempt. core/domain/dora holds pure, unit-tested metric functions; core/adapters/outbound/git gains three read-only methods; new chart=dora follows the existing chart=yield/chart=agents pattern.
  • CO2 equivalents (#952, #955) — up to 3 red dotted reference lines at the height of a relatable everyday CO2e equivalent (17 entries spanning 0.2g to ~100 tonnes), ported natively to macOS SwiftUI Charts. Follow-up (#980, #982) densifies the table with 8 new entries, converts remaining imperial units to metric, and adds a pixel-level minimum-gap backstop.
  • Non-English waiting-cue detection (#939) — question/waiting-cue detection was ASCII/English-only. Five follow-up review rounds tightened the initial patch, dropping ambiguous German/Spanish/Portuguese possessive forms and English-word-collision prefixes, adding NFD-accent folding, and refactoring into named helpers per CodeScene/SonarCloud feedback; full 123-subtest session package suite passes unchanged throughout.
  • Cache-bloat percentage badge (#949, #946) — computes how far the session’s current median cache-creation per turn sits above the project’s p25 baseline.
  • Notifications master toggle + Settings/History chrome unification (#947) — a single toggle gates the three per-event rows with a one-time migration default; shared PanelHeader, IrrSpacing tokens, and EqualWidthSegmentedControl (an NSViewRepresentable bridge) so the Menu Bar Icon segmented control fills its row edge-to-edge. Removes History’s Filters dropdown entirely since drilldown already covers narrowing to one project/branch/model.
  • onboarding-factory driver actions (#970, #935, #972) — ports the interrupt step (Escape) into antigravity and kiro-cli, live-verified against real authenticated sessions. kiro-cli’s reset_session live-verifies (2.6.0) that /chat new rotates the session id in-process; antigravity’s agy has no reset command at all, documented as a genuine capability gap rather than a stub TODO.
  • WCAG-AA contrast (#984, #987) — pill text was a fixed hex on a 12%-alpha wash of itself; macOS adds Color.adaptive(light:dark:) and per-appearance tokens tuned against the measured wash colors; web adds light-mode overrides for the state-color tokens and their -dim companions.
  • DORA project picker overflow (#969, #962) — the picker used .fixedSize() with no upper bound.
  • History fieldset close tags (#968, #958) — a mismatched </div> against a “special” HTML5 element is ignored rather than closing it.
  • Backchannel Custom actions (#966, #963) — routed through SendCommand, which already owns the per-backend submit logic, instead of SendInput’s bare typing with no submit sequence.
  • Ready→working force-bounce logging (#953, #957)forceReadyToWorkingIfActive never called d.log.LogInfo like every sibling transition function.
  • Background-liveness probe ordering (#941, #937) — hoisted the force-bounce to run before the liveness probe, kept gated in lockstep with the NoSubstantiveActivity skip (#329).
  • mockRepo/memRepo aliasing race (#973, #976, #965, #967, #975) — root cause of a recurring whack-a-mole of “-race flakes” (#606, #942/#944, #956/#964); routed Load/Save/ListAll through a new deepCopySessionState helper closing the aliasing class at every call site, plus new regression-guard tests.
  • Swallowed exceptions logged (#938) — 21 empty/near-empty catch blocks now console.debug the error; extracted a shared collapsedSet.js factory to resolve a SonarCloud duplication-gate regression the fix triggered.
  • Options-struct refactor (#945) — 13 functions exceeding SonarQube’s 7-parameter limit now take a doc-commented Deps/Options struct; 61+ call sites updated, pure signature refactor.
  • SonarQube backlog (#901, #932, #950, #960, #961) — mechanical fixes, a cognitive-complexity extraction (resolveHistoryQuery out of handleGetHistory, complexity 20→under 15), a <dialog>-element conversion for Settings/Permissions modals, and 79 already-fixed findings closed via the SonarQube API.

v0.5.6 (2026-07-09)

Subscription quota lands in the menu bar icon; a full security & code-health sweep closes every open CodeQL/SonarQube finding.

Highlights

Subscription quota in the menu bar status icon

Settings panel showing the new menu bar icon style picker (Lights / Usage / Combined), a quota provider picker, and a quota shape picker (Bars / Circle)

The menu bar status icon can now render your 5-hour/weekly subscription-quota windows directly — as stacked bars or a compact ring — instead of only inside the popover. Pick Lights (today’s dots, unchanged default), Usage (quota only), or Combined (both side by side), and choose which provider’s quota to show if you have more than one subscription.

Why it matters: a glance at the menu bar now tells you how close you are to a quota reset, without opening the popover first. (#917, #909)

Added

  • /ir:release gained an automated security-scan gate (#890) — Dependabot, CodeQL, govulncheck, gosec, and npm audit all run before every release build, closing a gap where CodeQL’s default setup had silently lapsed with zero local scanning to catch it.
  • Model alias map synced with codeburn — added Xiaomi MiMo and Kwaipilot KAT-Coder aliases (currently price at $0 pending a LiteLLM entry for either model).

Fixed

  • Sessions with a still-active background child no longer flip to ready mid-turn (#889, #893, #897, #899) — covers both a parent whose turn ends on a waiting cue and a parent that already looked idle when a new Workflow-tool subagent appears.
  • A session’s transcript moving into or out of a git worktree is no longer mistaken for the session ending (#877, #878).
  • Long-running background-research subagents no longer falsely time out and get reaped (#881, #882) — the inter-write quiet window is raised to 90s to cover slow Web Search/Fetch-heavy gaps.
  • The CO2 estimate next to cost no longer wraps to a second line for kilogram-scale sessions (#920, #922).
  • install.sh no longer kills unrelated irrlichd daemons on the machine when restarting its own, and the release smoke test’s daemon-liveness check no longer flakes (#871, #876).
  • The README’s CodeScene badge was reading the hotspots-only score instead of the overall project score (#918).

Security

  • Closed every open CodeQL code-scanning alert and the remaining SonarQube security/reliability findings from the project-wide #901 sweep (#894, #896, #907, #908, #910, #912, #913) — path-injection guards across the daemon’s HTTP-facing file handlers, an SSRF check on the relay’s outbound WebSocket URL, prototype-pollution hardening in the dashboard, and path-traversal / innerHTML-injection hardening in the onboarding-factory viewer.

Changed / Docs / Distribution

  • README star-history chart is now self-hosted instead of depending on star-history.com’s shared, rate-limited API token (#903, #904); the SonarCloud quality-gate badge was replaced with the steadier security/reliability/maintainability rating badges (#902).
  • /ir:codescene-report replaced by /ir:sonarqube-report (#884) — concrete file:line findings with fix guidance instead of an aggregate hotspot score.
  • /ir:doc-review now fixes findings directly (#875) — instead of filing a GitHub issue for most of them; filing is the fallback only when a fix is genuinely ambiguous.

Technical appendix

  • Quota menu bar icon (#917, closes #909) — new @AppStorage-backed MenuBarStyle/QuotaVisualStyle/MenuBarQuotaProvider settings (default .lights preserves today’s icon); QuotaMenuBarRenderer renders stacked 5h/7d bars or a compact ring, sharing SessionListView’s pace-percent and color-ramp logic so the icon can’t disagree with the popover. A same-PR follow-up fixed real defects an independent second review caught: settings changes not triggering a repaint, a stale-snapshot mismatch with the popover, an absolute-only color ramp that could diverge from the popover’s pace-aware one, and hardcoded colors unreadable in light menu-bar appearance.
  • CO2 unit line-wrap fix (#922, fixes #920)formattedCO2 used two decimal digits for the kg branch while g/mg branches use 0-1, producing strings like “16.15kg” that overflow the fixed-width cost/CO2 column; matched the g branch’s precision and added lineLimit(1) as defense-in-depth.
  • CodeScene badge score source (#918) — the refresh workflow read current_score (hotspots-only) instead of code_health_weighted_average_current (the actual project score).
  • Cognitive-complexity sweep, part of #901 (#916) — refactored all 119 open go:S3776/javascript:S3776 findings down to Sonar’s threshold across 74 files via pure structural extraction, zero intended behavior change.
  • Flaky relay control test fixed (#915)TestRelayRoutesControlToDaemon now reads the client’s initial snapshot frame (which can only arrive post-registration) before dialing, establishing a real happens-before instead of relying on timing.
  • swift:S1075 false-positive suppression (#914, part of #901) — all 70 flagged lines are local filesystem/binary paths, fixed loopback-API routes, SwiftUI preview literals, or opaque test fixtures.
  • Remaining CodeQL alerts closed (#913) — a post-#910 scan found 4 more alerts, 3 new dataflow paths to already-guarded sinks through a different taint source, each closed with a direct taint-visible guard; the 4th (relay-URL request forgery) is left as documented, accepted risk since a user-configurable relay endpoint is incompatible with CodeQL’s fixed-allowlist sanitizer guidance.
  • All open CodeQL alerts resolved, part of #901 (#910, closes #907) — 37 alerts: 1 critical SSRF (relay WebSocket URL validation before DialContext), 35 high path-injection, 1 medium prototype pollution (irrlicht.js agent registry now Object.create(null)).
  • Remaining SonarQube security/reliability findings, part of #901 (#912) — an empty go.sum for a zero-dependency module, NOSONAR annotations on two already-documented intentional trade-offs, and a Dockerfile CMD rewritten to exec form.
  • swift:S100 naming-convention sweep (#911, part of #901) — mechanical rename of 98 underscore-named XCTest functions to camelCase across 12 test files, with matching snapshot reference images renamed alongside.
  • SonarQube findings phase 1, part of #901 (#908) — sanitized dashboard_url before logging, tightened a Keychain item to ThisDeviceOnly, charCodeAtcodePointAt on atob() output, safer float parsing. 17 additional findings confirmed false-positive/wontfix by direct code inspection.
  • README badge swaps (#902, #904, fixes #903) — self-hosted star-history SVG decouples the README from star-history.com’s shared, rate-limited token pool; SonarCloud’s quality-gate badge replaced with the security/reliability/maintainability rating badges.
  • Turn-done waiting hold, part of #901 sweep (#899, fixes #897) — a parent whose turn ends on a waiting cue collapsed straight to waiting even with a genuinely-running background Agent-tool child, because that branch never checked hasActiveChildren the way the ready-transition branch already did.
  • javascript:S6819 accessibility fix, part of #901 (#900, closes #898) — replaced a nested role="button" div/span pair in the design-system’s GroupHeader.jsx reference copy with two real sibling <button> elements.
  • SonarQube VULNERABILITY/BUG/CODE_SMELL cleanup, part of #901 (#896, fixes #895) — ~700 findings addressed across shell/JS/Go/Swift/CSS: absolute-path resolution for 16 shelled-out binaries, non-root Docker users, ARIA labels and contrast fixes, locale-safe sort comparators. One real bug fixed as a byproduct: state_machine.go’s TotalDurationMs() was missing its lock entirely.
  • Ready-parent hold for new Workflow-tool children (#893, fixes #889)holdParentWorkingForNewChild forces the parent back to working the instant a new child is discovered, surviving the periodic stale-session refresh instead of being silently undone.
  • Onboarding-factory archive-name and innerHTML hardening (#894, fixes #892) — a SafeArchiveName type is now the only path to archiveFilePath; manifestBox/driftNote innerHTML templates replaced with DOM construction.
  • ir:release security-scan gate (#890, fixes #885)tools/security-scan.sh checks open Dependabot/CodeQL alerts, govulncheck, gosec, and npm audit; wired into /ir:release Step 5.5 and tools/preflight.sh --only security. Re-enabled CodeQL default setup for Go and JavaScript/TypeScript.
  • dashboard_url iframe validation (#888, closes #886)resolveDashboardIframeUrl rejects anything but a same-origin http(s) URL instead of assigning server-provided input directly to iframe.src.
  • /ir:sonarqube-report replaces /ir:codescene-report (#884) — SonarQube Cloud’s issues API returns concrete file:line findings with rule keys and fix guidance, unlike CodeScene’s aggregate-only hotspot scores.
  • SubagentQuietWindow raised to 90s (#882, fixes #881) — 30s falsely tripped on a genuine 61-second inter-write gap from a WebSearch/WebFetch-heavy background subagent, causing the parent to surface ready for 67 seconds while real work continued.
  • Worktree-relocation transcript tracking (#877, #878)onRemoved now detects a transcript relocated into a sibling project-dir slug (e.g. entering/closing a git worktree) and re-points tracking at the surviving file instead of demoting the session to ready.
  • viewer.js decomposition (#873, #880) — extracted renderPlayback’s (660 lines, CodeScene’s worst score in the repo) timeline geometry, DOM painting, and replay-transport concerns into three sibling modules; DOM output byte-identical, locked by 41 new characterization tests.
  • irrlicht.js row-rendering decomposition (#872, #879) — split the ~270-line, ~37-branch updateSessionRow into per-column renderRow* helpers; pure mechanical extraction, DOM output unchanged.
  • Release-tooling fixes found while shipping v0.5.5 (#876, fixes #871)install.sh’s daemon kill is now scoped to whatever is bound to port 7837; the smoke test’s daemon-liveness check gained a retry loop; seed-demo-sessions now auto-creates its scenarios’ referenced cwd paths.
  • Agent landscape refresh (#874) — refreshed GitHub stars/metadata for tracked agents; marked Gemini CLI, Antigravity, and Kiro as irrlicht-supported; archived Void and Roo Code.
  • ir:doc-review fixes-by-default (#875) — every determinable finding is now fixed in place, with filing/closing a GitHub issue as the fallback only for genuinely ambiguous findings.
  • Model alias sync — added mimo-v2-flashxiaomi/mimo-v2-flash and kat-coder-pro-v1kwaipilot/kat-coder-pro from codeburn’s BUILTIN_ALIASES; neither canonical is in LiteLLM’s pricing table yet.
  • tools/security-scan.sh’s Dependabot/CodeQL gate was silently non-functional — missing an explicit --method GET, gh api defaulted every alert query to POST (since -f params force POST) and 404’d, misreported as an auth/scope failure. Running it for real surfaced CodeQL alert #54 (go/request-forgery, relay URL), already documented as accepted risk in #913 but never dismissed — dismissed now.

v0.5.5 (2026-07-04)

CO2 estimates next to cost, a self-explaining cache-regression badge, and reconnect reliability fixes.

Highlights

Estimated CO2 footprint alongside cost

A session row's cost column reading 7.1g CO2e instead of a dollar amount, with two more rows showing 9.3g CO2e and 2.0g CO2e

Click a session’s cost figure and it now cycles to an estimated CO2e footprint, with a hover tooltip disclosing how confident the number is — provider-disclosed coefficients where a provider publishes them (Gemini, Mistral), a cross-model average otherwise.

Why it matters: cost isn’t the only budget that matters. Now you can see a session’s environmental footprint with the same glance you’d check its price — no separate tool, no export. (#829, #831)

Cache-regression badge explains itself

A red badge under a session row reading claude-code 2.1.143 +14K cache tokens vs 2.1.98

The badge that flags a session whose prompt-cache reuse has regressed now always shows which agent version caused it (or a compact “cache ↑” fallback), with the full plain-language explanation on hover — instead of a bare icon you had to guess at.

Why it matters: when caching quietly breaks and a session starts costing more per turn, you immediately know why and which update triggered it, not just that something changed. (#813, #826, #827, #842)

Fixed

  • History → Usage tab: grouping by Model no longer buckets everything under “unknown” (#792, #793) — a wrong source field meant every session landed in one aggregate bucket regardless of which model was actually used.
  • History’s tab switcher moved into the header, controls collapsed to one row (#785, #788) — the Usage/Yield/Quota picker now lives in the header instead of a static label, and Range/Chart/Group/Forecast controls sit on one scrollable row instead of three stacked ones.
  • Menu bar app recovers when the daemon it’s connected to restarts (#843, #845) — local-daemon traffic now rides its own recyclable connection with a visible “daemon unreachable — retrying” state, instead of retrying forever against a wedged one.
  • Relay connections recover from the same wedged-restart failure mode (#846, #848) — mirrors the local-daemon fix for a restarted standalone irrlichtrelay.
  • Menu bar session count no longer double-counts relay-echoed sessions (#828, #830) — a local daemon that also publishes to a relay it subscribes to could overcount a project’s sessions in the menu bar dots/badge.
  • “Expand All Task Summaries” toggle now persists across restarts (#799, #800).
  • Antigravity sessions bound to a non-interactive background poller no longer show as phantom menu-bar circles (#784, #791) — a known third-party helper process is now recognized and excluded at admission time.
  • FocusMonitor’s remaining crash path on the macOS 26 SDK closed (#782, #790).
  • Subagents whose worktree/transcript disappeared mid-run are reaped (#850) — instead of lingering as phantom sessions.
  • Daemon route-registration startup race fixed (#794, #795, #798).

Changed / Docs / Distribution

  • Dropped a fragile SwiftPM resource-bundle dependency (#844, #847) — removes a crash mode where a dev build’s .app could outlive the worktree it was built from.
  • Doc-review sweep gained a --fix mode, wired into the release process itself, and the README’s CodeScene score badge now auto-refreshes on every push to main (#834, #835, #837).

Technical appendix

  • Estimated CO2 footprint (#829, #831)capacity.EstimateCO2Grams: a tiered CO2e estimation formula using provider-disclosed coefficients for Gemini and Mistral, falling back to a cross-model average (Epoch AI) for everything else, including Claude and GPT. Wired through the tailer’s existing cumulative-token computation into SessionMetrics.EstimatedCO2Grams + CO2Tier, mirroring EstimatedCostUSD’s plumbing. Ships as a click-to-cycle web dashboard surface first; CLI/macOS parity and historical/aggregate tracking are deferred follow-ups.
  • Cache-regression badge explanation (#813, #826, #827, #842) — an always-visible badge on both macOS and web showing the version attribution (or a compact cache ↑ fallback), composed once daemon-side into cache_bloat_explanation instead of each client re-deriving the copy independently.
  • History Group=Model fix (#792, #793)RecordSnapshot/RecordBaseline stamped each cost row’s Model field from SessionState.Model, which is never assigned in production code, so every row landed in the aggregate “unknown” bucket. Falls back to Metrics.ModelName, mirroring the session-list handler’s existing pattern.
  • History header/controls redesign (#785, #788) — moves the Usage/Yield/Quota tab picker into the header, flattens the controls onto one horizontally-scrollable row, and gives the popover a hard fixed height independent of width.
  • Local-daemon reconnect recovery (#843, #845) — fixes a reset-every-attempt backoff bug plus a URLSession.shared wedge against a restarted daemon, via a dedicated recyclable URLSession and a surfaced “daemon unreachable — retrying” state.
  • Relay reconnect recovery (#846, #848) — the same wedged-URLSession.shared fix, this time for a restarted standalone irrlichtrelay.
  • Relay-echo dedup in the flat sessions array (#828, #830) — mirrors the existing apiGroups name-based collapse into rebuildSessionsFromMap(), which only deduped by id and missed the echoed session’s drifted id.
  • Antigravity non-interactive host rejection (#784, #791) — a known third-party helper (CodexBar) keeps an Antigravity CLI process running in the background to poll quota; a synchronous ancestry check at PID-discovery time (PIDManager.AllowsSession) now excludes processes whose ancestry doesn’t resolve to a known terminal/IDE.
  • FocusMonitor KVC crash path (#782, #790)isFocusActive’s remaining value(forKey:) reads now use the same responds(to:)-guarded selector dispatch as the earlier singleton-lookup fix.
  • Subagent reaping on deleted transcript (#850) — adds isDeletedTranscript (confirmed-gone via os.IsNotExist) so a subagent whose parent PID was reused by an unrelated process is reaped instead of surviving forever.
  • Daemon route-registration startup race (#794, #795, #798) — plus a flaky CI test and preflight tooling fix bundled in the same PR.
  • SwiftPM resource-bundle removal (#844, #847)Bundle.module’s only use (AppIcon.icns) was already covered by a direct bundle copy, so the fragile SwiftPM mechanism is removed entirely.
  • ir:doc-review --fix mode (#834, #837) — auto-corrects doc claims directly contradicted by a code-derived fact; wired into /ir:release Step 4b so drift from any earlier release gets caught, not just this one’s diff. This is what caught README.md/site/index.html’s stale “no hooks” claim, corrected in this release.
  • CodeScene badge automation (#835, #802) — a scheduled workflow refreshes the README’s CodeScene badge on every push to main; a manual fetcher backs the /ir:codescene-report skill.
  • Hexagonal layering enforcement + ARS regression gate (#796, #803)core/architecture_test.go statically enforces domain/ports import direction; tools/ars-gate.sh flags an Agent Readiness Score regression as an advisory PR check.
  • Shared contract test for consent-gated adapter call sites (#797, #817)contracttesting.AssertPermissionGated behaviorally verifies a permission’s Apply/Remove wiring at runtime.
  • ir:test-mac componentized restart (#833, #838) — a TARGET=daemon|macos|full axis restarts just the changed component; MODE=replace now installs directly into /Applications/Irrlicht.app instead of a same-bundle-id parallel /tmp copy.
  • CodeScene hotspot cleanup sprint — eight no-behavior-change refactors splitting flagged hotspots: tailer.TailAndProcess/processParsedEvent (#822), domain/session.go by concern (#808, #816), irrlicht.js into modules (#805, #820), SessionListView.swift into per-concern views (#818), PIDManager’s liveness sweep/dedup deletion (#810, #819), irrlichd main() into named startup phases (#821), SessionManager god-object into extensions (#815), and session_detector_test.go by scenario group (#814, #824).
  • Dependency bumpgolang.org/x/net 0.52.0 → 0.55.0 in core (#801).
  • Misc test/tooling hygieneSessionRowSnapshotTests made hermetic against a live daemon (#841); pre-push hook no longer inherits a stray GIT_DIR/GIT_WORK_TREE (#812).
  • Docs — worktree shortcuts + Task Management tidy-up and a git-stash note in AGENTS.md (#789, #825), Karpathy Guidelines section removed (#787), ir:test-mac doc defaults to replace mode (#786), CodeScene/ARS README badges given their own line plus a workflow badge (#836, #823).

v0.5.4 (2026-06-29)

A full History view with spend analytics, a backchannel that acts on your agents, and plain-language session summaries.

Highlights

Spend & history analytics

Irrlicht's History view: a cumulative cost chart with a linear projection and a per-project breakdown with Export CSV / JSON buttons

A new History view turns every recorded session into cost analytics — a cumulative spend chart with a linear projection, breakdowns and drill-downs by project, branch/worktree, provider and model, a concurrent-agents timeline, and a productive-vs-reverted “yield ratio” per project. Facet any axis, cross-filter by token type, and export the whole thing to CSV or JSON. It ships in both the macOS app and the web dashboard.

Why it matters: you can finally see where your agent spend actually goes — which project, which model, and how much of it landed versus got reverted — without exporting anything by hand. (#752, #761, #771, #773, #778, #772)

Backchannel — act on your agents

The Backchannel Rules editor showing an Auto-compact rule: when Context (%) is at least 85 for any agent, Send the Compact command

Backchannel turns Irrlicht from a read-only monitor into something that can talk back: control discovered agents locally or remotely, across agents and terminal backends. Define event→action rules (e.g. when context crosses 85%, send the compact command), pick from agent-translated command presets, or write your own Custom command — and trigger them by a backchannel token.

Why it matters: routine babysitting — compacting a full context, answering a prompt, nudging a stuck session — can now happen automatically or with one click, from any machine. (#731, #733, #769)

Plain-language session summaries

A session row leading with a purple intent block 'Add OAuth login to the web dashboard' and an amber waiting block 'Should I run the migration?'

Session rows now lead with a human-readable headline instead of raw transcript text: a purple block summarizing what the agent is working on, and an amber block with the exact question it’s waiting on. Summaries are generated from lightweight markers with a pluggable compaction step.

Why it matters: a glance at the menu bar tells you what each agent is doing and which one actually needs you — no expanding rows to read the last message. (#765, #770, #743)

Added

  • ETA accuracy improvements, surfaced sooner (#768) — a replay-based estimator informs the task-completion estimate earlier in a turn.
  • Built-in diagnostics bundle (#742) — a /debug/bundle endpoint and an irrlichd --diagnose flag collect a redacted snapshot for bug reports.
  • Cache-creation regression detection (#749) — sessions that stop reusing prompt cache are flagged and attributed to the upstream agent version that changed.
  • Generic GUI-host fallback for click-to-focus (#741) — focusing a session works for more terminal/IDE hosts out of the box.
  • Agent-legible HTML snapshot artifacts for the session list, plus an enriched opt-in lifecycle trace that makes ghost sessions reconstructable (#764, #760).

Fixed

  • Detached background agents are now badged (#747) — instead of showing as phantom rows.
  • Sidebar no longer double-lists relay-echoed local projects (#748).
  • Ghost sessions age out / get reaped (#740, #734) — antigravity PID==0 ghosts age out, and sessions bound to a claude --bg-spare infra PID are reaped.
  • Settings panel no longer janks (#730) — the ~2fps / high-CPU redraw storm is gone.
  • Text-to-speech crash fixed on macOS (#781).
  • Launch crash fixed on the macOS 26 / Xcode 26 SDK (#782) — FocusMonitor resolves the Focus singleton via a guarded selector instead of KVC, which now raises NSUnknownKeyException.
  • Price non-Anthropic-frontend sessions correctly (#726) — synced the model-alias map with codeburn and added new aliases (OpenAI Codex gpt-5.5, Hermes lowercase glm-5.2).

Changed / Docs / Distribution

  • History and Settings panels decluttered on macOS (#781).
  • New /ir:exec skill (#758, #767) — an end-to-end issue loop (worktree → visual HTML plan → implement → PR → /review → /simplify); it now marks the issue in progress before implementing.
  • Antigravity 1.8 + 5.1 scenarios flipped to observed (#775, #776, #777) — by capturing and serving the conversations/<id>.db store in replay, with the context-window replay test pinned hermetic.
  • Snapshot coverage added for the unobservable session-row states (#762).
  • Dependency bumpform-data 4.0.5 → 4.0.6 in platforms/web (#739).

Technical appendix

  • History view — web (#752, #771, #778) and macOS (#761, #773) — a cost-analytics surface built entirely from on-disk recordings: Phase 1 lands the cumulative cost chart with a linear projection and Export CSV/JSON; Phase 2 adds branch/worktree and provider/model attribution with drill-downs; Phase 3 adds a concurrent-agents timeline computed from recording overlap; #778 adds faceted cross-filtering with token-type grouping. The macOS side reaches cost-analytics parity with the web dashboard.
  • Yield ratio per project (#772) — a productive-vs-reverted spend metric per project (spend that survived in the tree vs. spend on changes later reverted), surfaced in the History view.
  • Cache-creation regression detection (#749) — detects sessions whose prompt-cache reuse drops off and attributes the regression to the upstream agent version in play, so an agent update that breaks caching is visible rather than silently doubling cost.
  • Backchannel control (#731) — control discovered agents locally and remotely, across agents and terminal backends, reusing Focus’s Launcher targeting; gated behind a control permission so nothing is exercised while pending or denied.
  • Backchannel read-back (#733) — adds the terminal backend (tmux/kitty/iTerm) as a complementary observation source alongside the transcript tailer.
  • Backchannel command presets (#769) — agent-translated command presets plus a Custom option; #781 adds a backchannel token trigger.
  • Concise session summaries (#765, #770, #743) — session rows render a purple user-intent block and an amber waiting-question block from lightweight summary markers; #770 adds concise intent + waiting headlines with a pluggable compaction step; #765 also fixes a global collapse bug.
  • ETA estimator (#768) — replay-based research into the task-completion estimator improves accuracy and surfaces the ETA earlier in a turn.
  • Diagnostics bundle (#742) — a GET /debug/bundle endpoint and an irrlichd --diagnose flag assemble a redacted diagnostics snapshot for bug reports.
  • Click-to-focus GUI-host fallback (#741) — a generic outermost-top-level-.app host-detection fallback so click-to-focus resolves the right GUI host for more terminal/IDE embeddings.
  • Ghost-session lifecycle (#740, #734) — antigravity PID==0 ghost sessions age out via the no-substantive-activity gate; sessions bound to a long-lived claude --bg-spare infra PID are reaped via the infra-reaper self-healing sweep.
  • claudecode detached background agents (#747) — detached background agents that land in the claude daemon run pool are badged rather than rendered as phantom active rows.
  • Relay sidebar dedupe (#748) — the macOS sidebar de-duplicates local projects echoed back by the relay.
  • Settings panel jank (#730) — eliminated the ~2fps / high-CPU redraw storm in the Settings panel.
  • FocusMonitor SDK launch crash (#782)resolveFocusStatusCenter() resolved INFocusStatusCenter.default via KVC value(forKey:), which under the macOS 26 / Xcode 26 SDK raises NSUnknownKeyException (uncatchable from Swift) instead of returning nil — SIGABRTing every Developer-ID launch (DevID-gated, so ad-hoc/CI builds never hit it; only the release smoke test does). Now resolved with a responds(to:)-guarded selector perform.
  • Agent-legible observability (#760, #762, #764) — #760 enriches an opt-in daemon lifecycle trace (with replay --ghosts) so ghost sessions are reconstructable; #762 snapshots the unobservable session-row states; #764 emits agent-legible HTML snapshot artifacts for the session list.
  • Model-alias sync (#726 + this release) — re-synced core/pkg/capacity/aliases.go against codeburn’s BUILTIN_ALIASES; this release adds openai-codex:gpt-5.5gpt-5.5 (in LiteLLM) and the lowercase glm-5.2glm-5p1 Hermes spelling.
  • Antigravity replay observability (#775, #776, #777) — captures and serves antigravity’s sibling conversations/<id>.db store in replay (#775), flips the 1.8 + 5.1 cells to observed via golden-summary surfacing and a re-record (#776), and pins the LiteLLM cache so the replay context-window test is hermetic on cold CI (#777).
  • /ir:exec skill (#758, #767) — an end-to-end issue execution loop (worktree → visual HTML plan → implement → PR → /review → /simplify); #767 marks the issue in progress before implementing.
  • Dependenciesform-data 4.0.5 → 4.0.6 in platforms/web (#739).

v0.5.3 (2026-06-21)

Google Antigravity support, publish your sessions to a remote relay, and a clutch of daemon + adapter fixes.

Added

  • Google Antigravity adapter (#715) — one adapter covers both the agy CLI and the Antigravity IDE. Discovery is transcript-first, so PID-less IDE sessions are first-class.
  • Publish to relay (#718, #721, #722, #723) — a macOS toggle that streams this daemon’s sessions out to a standalone relay so you can watch them from another machine; pushing outbound means the daemon needs no inbound reachability (works behind NAT). The forwarder hot-reloads, so toggling Publish on/off or editing the URL/token applies to the running daemon with no relaunch and no interruption to session monitoring — whether the app spawned the daemon or adopted an already-running one.
  • Relay multi-tenant workspace isolation (#709, #713) — one relay can serve several daemons’ sessions kept apart per workspace.

Fixed

  • Antigravity context bar now renders (#719, #720) — tokens and the canonical model are read from the sibling conversation store rather than the transcript.
  • Claude Code sessions stay waiting across a daemon restart (#705, #706) — instead of flipping back to working.
  • Brand flame gradient legible on light backgrounds (#708).
  • Unified assistant-text truncation (#710) — every adapter truncates long assistant text the same way, via one tailer rule.

Changed / Docs

  • Exhaustive Source → watcher dispatch in buildAgentWatchers (#714) — a new source can’t silently land without a watcher.
  • Dashboard: extracted a collapsed-groups store (#712) and shared todo-snapshot reconciler (#711) — internal refactors.
  • Docs — onboarding-factory skill gains an overnight push-through mode (#717); the task-ETA figure was removed from the landing page (#704).

Technical appendix

  • Relay publish hot-reload (#722, #723) — a new relay.PublishController (core/adapters/outbound/relay/controller.go) owns the forwarder lifecycle: Apply(enabled, url, token) starts, stops, or reconfigures a single forwarder (cancel the ctx + start a fresh relay.NewForwarder), idempotent when the effective config is unchanged, mutex-guarded so concurrent PUTs serialize, blank URL counts as “off”. Always constructed and seeded once at startup from IRRLICHT_RELAY_URL / IRRLICHT_RELAY_TOKEN so headless/standalone daemons are unchanged. A loopback-only PUT /api/v1/relay/publish accepts {enabled,url,token}Apply → returns the resulting status (same shape as GET). On macOS, DaemonManager.publishSettingsDidChange() PUTs the config via a new PublishClient instead of relaunching, re-syncing on spawn and on adopt; the relay token moves from a spawn-time env var to the loopback PUT body (same 127.0.0.1 trust boundary as every other daemon endpoint). buildDaemonEnv strips inherited IRRLICHT_RELAY_URL / IRRLICHT_RELAY_TOKEN so an app-spawned daemon never self-seeds from a stale value, and the PUT retries so a single dropped request can’t strand the daemon.
  • Publish to relay (#718, #721) — the original macOS toggle (UserDefaults URL + Keychain token) with a PublishStatusMonitor poll surfacing the live link state (connecting / connected / auth_failed / disconnected) as a status dot.
  • Relay multi-tenant workspace isolation (#709, #713) — the relay partitions each daemon’s sessions per workspace so one relay hosts several daemons without cross-talk.
  • Antigravity adapter (#715) — one adapter onboards both the agy CLI and the Antigravity IDE; transcript-first discovery so PID=0 sessions are first-class, a multi-root source over the brain stores, and a path-based session id (constant transcript.jsonl).
  • Antigravity context bar (#719, #720) — tokens and the canonical model live in a sibling SQLite protobuf (conversations/<conv>.db, gen_metadata), not the transcript; read on turn_done.
  • Claude Code waiting-across-restart (#705, #706) — the waiting state is preserved across a daemon restart rather than recomputed back to working.
  • Adapter assistant-text truncation (#710) — collapsed into one shared tailer rule.
  • Daemon watcher dispatch (#714)buildAgentWatchers switched to an exhaustive Source dispatch so an unmapped source is an explicit gap, not a silent no-op.

v0.5.2 (2026-06-19)

Gemini CLI joins the lineup, the menu bar gets ~3× lighter under load, and context-pressure alerts become configurable.

Highlights

Gemini CLI is now a supported agent

Gemini CLI shown as a newly supported agent alongside Claude Code, Codex, Pi, Aider, OpenCode and Kiro CLI

Irrlicht now watches Gemini CLI sessions and reports them in the same working / waiting / ready vocabulary as every other agent. It reads Gemini’s JSONL transcripts under ~/.gemini/tmp, follows nested subagent chats, and tracks per-turn token deltas — no SDK, no config. The adapter ships at the alpha maturity stage with 44 recorded scenarios behind it.

Why it matters: if quota pushes you from Claude Code or Codex over to Gemini CLI, your monitoring follows you instead of going dark. (#668, #659, #679, #680, #681)

~3× less CPU when you’re running a lot of agents

Before/after CPU bar chart: ~100% of a core before, ~31% after, with 40 agents at 200 WebSocket pushes per second

With dozens of agents all ticking metrics at once, the macOS app used to redraw the whole session list on every single WebSocket message and saturate a CPU core. WebSocket-driven refreshes are now coalesced into one redraw per ~100ms window, and the per-row 1-second timers collapse into a single shared clock. State changes still flash through immediately, so the menu bar stays as responsive as ever.

Why it matters: running a fleet of agents no longer spins up your fan or drains your battery — measured ~100% → ~31% of a core under 40 agents. (#693, #690)

Context-pressure alerts you can tune

The Context pressure alert setting with a spoken-voice option and an Alert at 80% threshold field

The context-fill alert that warns you before a session runs out of room now has a threshold you set yourself — fire it at 80%, 90%, or wherever you like — instead of a fixed cutoff. The new alert lives in a tidied-up settings panel where less-common options collapse under an Advanced Settings group and in-progress features carry a Beta badge.

Why it matters: people who want an early heads-up and people who only want a last-second warning can each set the threshold that fits how they work. (#692, #689, #702, #694)

Added

  • Gemini CLI adapter (#668) — transcript-, process- and PID-aware monitoring for Gemini CLI sessions, at the alpha stage.
  • /ir:doc-review skill (#698, #691) — an objective, binary-criteria documentation audit that files one agent-ready GitHub issue per doc surface; supports a --report-only dry run.
  • Configurable context-fill alert threshold (#692, #689) — set the context-pressure alert as a percentage (default 80%) or an absolute token count; the former 95% critical tier folds into the single configurable threshold.
  • Settings: Advanced Settings group + Beta badges (#702, #694) — less-common controls collapse under a disclosure group, and in-progress features are marked Beta.
  • Full docs for every environment variable, hook, and focus/CLI endpoint (#699, #700, #701).

Fixed

  • Dead sessions now age out correctly (#684, #667) — a no-op refresh no longer bumps updated_at, so a crashed or abandoned agent stops looking alive.
  • Manual /compact shows the full lifecycle (#658, #656, #657) — working for the whole compaction window, back to ready when it finishes, via a Claude Code PreCompact hook and treating the manual compact_boundary as a turn boundary; existing installs pick up the hook on the next daemon restart, and an interrupted compaction is bounded by a timeout.
  • Installer downloads and verifies before removing the existing install (#654) — a failed download can’t leave you with no app.
  • gemini-cli: ESC-cancel and aborted-turn notices settle the turn (#659, #679, #680, #681) — plus a batch of consolidated detector edge cases.

Changed / Distribution

  • Gemini CLI moves from planned to alpha in the compatibility grids.
  • Release DMGs are codesigned before notarization (#652) — Gatekeeper’s primary-signature check now passes on the DMG file itself.
  • Dependency bumps (#686, #687, #688) — vite 8.0.14 → 8.0.16, form-data 4.0.5 → 4.0.6.

Technical appendix

  • gemini-cli adapter (#668) — new transcript + process + PID adapter at core/adapters/inbound/agents/geminicli/. Watches JSONL session files under ~/.gemini/tmp named session-<ts>-<first8hex> (session id is the filename stem, not the header UUID), with nested subagent chats at chats/<parent-uuid>/<child-uuid>.jsonl. Process discovery matches the bin/gemini command line (the OS process is node); cwd is read from the transcript body, forcing an EnrichNewSession pass off m.LastCWD. No explicit end-of-turn marker — a text-only assistant message maps to turn_done, benign info notices are ignored, and ESC-cancel / aborted-turn notices settle the turn (#659, #665). 44 scenarios recorded; consolidated detector fixes in #679 cover #660–#664, #676.
  • WebSocket refresh coalescing (#693, #690)session_updated pushes are batched into a single flushUIRefresh per ~100ms window that patches all dirty sessions in one map pass + one recompose (patchApiGroups(sessions:)) rather than O(K·N) per-message recomposes. Per-row 1Hz duration TimelineViews collapse into one shared DurationClock observed only by leaf labels. Context-pressure alerts ride rebuildSessionsFromMap and are deferred by at most one window; state-transition notifications still fire synchronously. Per-message debug print()s gated behind IRRLICHT_DEBUG. Added tools/wsload plus a deterministic coalescing regression test. Measured 40 sessions / 200 pushes/sec, release: ~100% → ~31% of a core.
  • Configurable context-fill alert threshold (#692, #689) — new ContextPressureThreshold value type (value + unit) as the single source of truth with a pure isExceeded(by:); SessionManager seeds the 80%/percent default, drives checkContextPressureAlerts off the configured value (fires once per session, re-arms on change), and adapts the notification title/body to the unit. The session-row badge reads the threshold via @AppStorage and collapses to one tier — the former 95% critical tier is removed. A token-count mode fires even when the model’s context window is unknown.
  • Settings reorganization (#702, #694) — less-common controls collapse under an Advanced Settings disclosure group; in-progress features carry a Beta badge.
  • Detector no-op refresh (#684, #667) — a refresh that produces no state change no longer advances updated_at, so the idle-sweep age-out timer isn’t reset on every poll and dead sessions transition out as designed.
  • claudecode manual /compact lifecycle (#658, #656, #657) — a PreCompact hook forces working during compaction and the manual compact_boundary releases to ready after; EnsureHooksInstalled adds the hook on daemon restart, and an interrupted compaction is bounded by a timeout.
  • Docs (#698, #691, #699, #700, #701) — the /ir:doc-review skill audits every doc surface against binary criteria and files one issue per surface; this release’s docs also gained full coverage of environment variables, hooks, focus endpoints, and the CLI tools.
  • Release tooling (#652, #653, #655)tools/build-release.sh codesigns the DMG between hdiutil create and notarytool submit so the stapled ticket covers the signed bytes; release-skill docs record the DMG-signature ordering and port-safe smoke/canary guidance.
  • Model aliases — codeburn BUILTIN_ALIASES sync ran at release time: no new entries needed (the upstream gpt-4.1 self-alias is a documented no-op); all eight LOCAL_OVERRIDE entries unchanged.

v0.5.1 (2026-06-07)

Ghost sessions and stuck states fixed: /compact strandings, Claude Code 2.1.168’s background daemon, and restart amnesia.

No new features this time — five fixes that together remove every known way a session row could lie to you: sessions stuck “working” after /compact, permanent ghost proc-… rows minted by Claude Code 2.1.168’s new background-daemon processes, a “?” agent icon from a startup race, and idle sessions resurrected as “working” by a daemon restart.

Fixed

  • Running /compact in an idle session no longer strands it in “working” (#641, #642) — the synthetic compact-summary transcript event is no longer mistaken for a real user turn.
  • Claude Code 2.1.168’s background-daemon processes no longer mint permanent ghost rows (#644, #648) — claude daemon run, --bg-pty-host PTY hosts, and --bg-spare spares are excluded by their command line, and ghosts persisted by earlier versions are retired automatically on the first scan after upgrading.
  • Ghost pre-session rows whose real session is bound to a sibling process are swept continuously (#645, #646) — not only at daemon startup, with a 90-second grace period so a freshly opened second agent in the same directory still gets its row.
  • Sessions created during a daemon-startup race no longer show “?” instead of the agent icon (#643, #647) — the adapter identity is backfilled on the next activity event, which also unblocks PID discovery and ghost cleanup for that session.
  • A daemon restart can no longer resurrect an idle session as permanently “working” (#649, #650) — the last event type is persisted in the metrics ledger (schema v4), older ledgers get a one-time full re-scan that also heals sessions stranded by the pre-fix parser, and dead background processes are purged from the ledger instead of resurrecting on every restart.

Docs

  • Corrected the documented permissions.json location (#640) — it lives in the daemon data dir, not Application Support.

Technical appendix

  • claudecode parser (#642)handleUserEvent now skips user events carrying isCompactSummary / isVisibleInTranscriptOnly (core/adapters/inbound/agents/claudecode/parser.go), so a manual /compact — which never starts a turn — can’t trip classifier rule 4 into working. Regression-covered by compaction_test.go against the recorded #641 transcript shape.
  • processlifecycle argv exclusion (#644, #648) — new ProcessObserver.ArgvOf(pid) port primitive (KERN_PROCARGS2 parse on darwin sharing the preamble decoder via procargs2ArgvOffset; /proc/<pid>/cmdline on linux; stub elsewhere) and a per-adapter agent.Process.ExcludeArgv predicate consulted by the scanner. claudecode declares IsInfraArgv: positional matches on daemon run / --bg-pty-host / --bg-spare argv elements — never substring scans, so prompts merely mentioning those tokens stay matched, and a nil argv is never excluded. Verdicts are cached per PID (argv is immutable for a process’s lifetime), nil reads are retried next poll, and the first excluded verdict emits a one-shot retirement removal that deletes pre-sessions persisted by pre-filter daemons.
  • detector sweep (#645, #646)sweepSupersededPreSessions now also runs on the CheckPIDLiveness tick instead of seed-only. findSupersedingSession stays the single matching predicate, extended to return the match kind: PID matches retire immediately; the CWD fallback only retires pre-sessions older than 90s whose superseding session has a live, distinct PID — guarding the #113 two-agents-one-cwd regression. Deletions route through deletedSessions so the scanner can’t flap-remint.
  • detector adapter backfill (#643, #647)processActivityLocked backfills state.Adapter from the watcher identity under WithSessionStateLock (#606 discipline), healing sessions created through the no-identity debounce/refresh fallback; since the PID-discovery retry passes state.Adapter, discovery and pre-session cleanup unblock on the same pass.
  • tailer ledger schema v4 (#649, #650)LastEventType is persisted in LedgerState and restored in SetLedgerState; LedgerSchemaVersion bumped to 4 with the load side aliasing the canonical const so write and validate can’t drift. Older-schema ledgers are discarded on load → one-time full re-scan under the current parser, which heals sessions already persisted as working over silent transcripts. The background-process liveness probe’s dead verdict now calls PurgeDeadBackgroundProcs, dropping phantom background_procs ledger entries that previously resurrected as background_process_count=1 on every restart.
  • claudecode permissions disclosure (#648) — the transcripts (observe) permission’s user-facing Touches/Detail text now discloses the process scanning it has always gated: reading the working directory and command line of running claude processes.
  • Model aliases — codeburn BUILTIN_ALIASES sync ran at release time: no new entries needed (the upstream gpt-4.1 self-alias is a documented no-op); all eight LOCAL_OVERRIDE entries unchanged.

v0.5.0 (2026-06-06)

Consent-first permissions, agent-authored task ETAs, and Kiro CLI joins the watch list.

Highlights

Nothing is read or modified until you grant it

Permission wizard listing Claude Code and Codex with per-permission toggles, what each permission touches, and an Apply button

Every read and modification irrlicht performs — transcript tailing, hook installs, statusline wraps, database polling — is now a declared per-agent permission behind explicit consent. When irrlicht detects a coding agent it hasn’t asked about, a wizard appears on whichever surface you’re looking at (macOS app or web dashboard), shows exactly what each permission touches, and exercises nothing until you hit Apply. Revoking actively undoes: hooks uninstall, watchers stop.

Why it matters: you can see — and veto — every way irrlicht touches your system, per agent, before it happens.

Heads-up on upgrade: existing installs see the wizard once after updating, and monitoring is paused until you answer it. Notifications are now opt-in by default too. (#570, #571, #579, #583)

Know when your agent will be done

Dashboard session list where a working session shows a ~9m-left task ETA chip beside its cost, with task progress 3/5 underneath

Working sessions now carry a task-completion ETA chip — “~9m left” — in the web dashboard and the macOS menu bar. The agent authors its own progress estimate and emits it as a hidden in-band marker; irrlicht parses it read-only, projects a completion time from the measured pace, and degrades honestly: a range while the rate is barely measurable, dimming when the last report goes stale, and a tasks-derived fallback when no marker arrives at all.

Why it matters: “is it nearly done or should I grab lunch?” is now answered at a glance, for every working session, without asking the agent. (#558, #567, #605, #621, #626)

Kiro CLI joins the watch list

A Kiro CLI session row showing a live context bar, task progress 1/2, and the model name

Irrlicht now watches AWS Kiro CLI sessions: live working/waiting/ready state, project + branch resolution, PID binding, task progress synthesized from Kiro’s todo lists, and model + context-window metrics read from Kiro’s session sidecar.

Why it matters: if Kiro is one of your agents, it now shows up beside Claude Code, Codex, and the rest — same row anatomy, same states, no special casing. (#280, #590, #603, #613)

Added

  • irrlicht-ls reaches dashboard parity and ships in the default install (#554, #580, #609) — hierarchical subagent display, project group headers, color-coded context utilization, cost and adapter columns, task-progress detail lines, --format json, and --id/--state/--project/--adapter filters; the PKG now symlinks it into /usr/local/bin.
  • Menu-bar attention icon for pending permission items (#607) — the macOS icon signals when an agent is waiting on a permission decision.
  • Gas Town: unified rig/role/cost display in the session list (#559, #560).

Fixed

  • Gas Town polling no longer spikes CPU (#557, #575) — event-driven polling replaces the hot loop.
  • Stale subagent badges (#601, #600, #637) — waiting-path cleanup, observable deletions, and push Seq-gap detection with immediate re-hydration keep parent badges truthful.
  • Sessions survive daemon restarts mid-run (#576, #584) — stale transcripts owned by a live process are rescued at backfill instead of being dropped.
  • Web: rows of a single collapsed group render (#564, #566).
  • macOS login item re-registers on every launch (#562, #563) — a self-heal for installs where the login item silently vanished.
  • macOS: premium voices whose names carry a quality suffix match again (#569).
  • First launch after install no longer races Gatekeeper (#553) — the installer pre-warms assessment so the app is ready before the daemon wait expires.
  • Ready-state icon stays inside its layout box (#596, #598).
  • Workflow-tool subagents link to their parent session (#565, #635) — and workflow run journals are no longer mistaken for transcripts.
  • Kiro: permission-gated edit-tool prompts classify case-insensitively (#588, #612).
  • Claude Code: task IDs are taken from TaskCreate results (#620) — and pruned task lists clear instead of lingering.

Changed

  • The eyed-flame mark lands everywhere (#587, #594) — app icon, menu bar, favicon, landing page, and docs all swap to the new mark; shape-only, the state-color system is unchanged.
  • Notifications are opt-in by default (#579) — part of the consent-first rollout; kitty click-to-focus asks before using remote control.

Docs

  • macOS setup guide on the site (#574).

Technical appendix

  • Consent-first permission architecture (#570, #571, #579, #583) — new core/domain/permission package: State/Kind/Set with absent-key-means-pending as the upgrade path. Adapters declare Permissions with Apply/Remove effect closures — claude-code: transcripts/hooks/statusline/instructions; codex + pi: transcripts; aider: history; opencode: database; gastown: state; launcher: env; kitty: remote-control. PermissionService exercises grants, undoes revokes, persists permissions.json, arbitrates wizard answers across surfaces (first answer wins), and runs a detection poller (pgrep / GT_ROOT stat probe, no session reads) only while something is pending in ask mode. GET /api/v1/permissions + POST /api/v1/permissions/answer; hook and statusline receivers drop payloads while ungranted. IRRLICHT_PERMISSION_MODE=grant-all auto-grants for demo/record/test daemons, strictly in-memory. --uninstall-hooks records hooks=denied so a restart cannot silently reinstall. The task-eta CLAUDE.md managed block became the instructions permission (#577, #583).
  • Task-completion ETA pipeline (#558, #567, #605, #621, #626, #619, #638, #620) — the agent emits a hidden irrlicht-eta marker in-band; the claudecode adapter scans full text blocks tolerantly (key drift accepted, absurd values rejected, latest valid wins). TaskEstimate mirrors through tailer → domain → metrics adapter; ForecastTaskCompletion projects from measured rate anchored at session start. Chip render rules: hidden unless working with reported progress, range below half completion, stale dimming past 3 min (web taskEtaPresentation, mirrored in SessionListView.swift). Hardening: tasks-derived fallback + hook marker carrier + 0/N chip (#605), pinned-high range between markers (#621), subagent-aware aggregation with marker > tasks > subagents precedence (#626), mandatory per-phase Bash-description carrier v3/v4 (#619, #638), authoritative TaskCreate IDs (#620).
  • Kiro CLI adapter (#280, #590, #603, #613, #612)FilesUnderRoot/JSONLineParser adapter watching ~/.kiro/sessions/cli/<uuid>.jsonl (verified against kiro-cli 2.5.1). No explicit end-of-turn marker: a text-only AssistantMessage maps to turn_done. The transcript carries no cwd — fallback to the <uuid>.json metadata sidecar via token-walk; PID discovery via pgrep -x kiro-cli. Sidecar MetricsReader surfaces model + context window live (#603); tasks synthesized from todo_list create/complete (#613); permission-gated edit-tool classification is case-insensitive (#612). Onboarded as a matrix column with an interactive tmux driver.
  • Session-detection internals — backfill rescues stale transcripts owned by a live process (#584); BuildAgentGroups no longer mutates its input (#573); PID assignment synchronized with the detector loop (#618) and pidmanager sweep paths brought under assignMu (#634); dead ContentChars pipeline removed, kiro tool-status semantics pinned (#595); Workflow-tool subagents link to their parent and run journals are excluded from discovery (#635).
  • Web client — single collapsed group renders rows (#566); push Seq-gap detection with immediate re-hydrate (#637); stale subagent state fixes: waiting-path cleanup, summary ordering, observable deletions, history leak (#601).
  • Gas Town — event-driven polling kills the CPU spike (#575); unified rig/role/cost block (#560); gt fetch timeouts logged instead of silent fallback (#633).
  • macOS app — attention icon for pending permission items (#607); ready-state icon clamp (#598); premium-voice quality-suffix matching (#569); login-item reconcile on launch (#563); swift test clean on main (#585); GroupView snapshots pinned to dark aqua (#610).
  • CLI + installirrlicht-ls dashboard parity, file-only, no daemon required (#580); ships in the bundle with a PKG postinstall symlink into /usr/local/bin (#609, #608); curl installer pre-warms Gatekeeper (#553).
  • Tooling / CI / tests — gofmt sweep + CI gofmt gate (#629, #632, #623); de-flakes: FSWatcher timeout (#630), ParentBadgeCleared polling (#624, #631), gastown replay settle window (#611), deterministic cursor-GC aging (#614), child-ready polling (#578, #582). Onboarding-factory (internal): recordings as single source of truth (#556), paginated coverage-matrix viewer (#591), kiro-cli and claudecode column recordings (#627, #636). Release-skill docs record build-release.sh as authoritative (#552).
  • Model aliases — codeburn BUILTIN_ALIASES sync ran at release time: no new entries; all eight LOCAL_OVERRIDE entries unchanged upstream.

v0.4.8 (2026-05-30)

Watch your agents from any machine — the irrlichtrelay ships, alongside a Linux daemon and per-provider quota tracking.

Highlights

Watch every machine from one place

Three irrlichd daemons pushing over authenticated wss to a standalone irrlichtrelay, which fans out to the macOS app and web dashboard

irrlichtrelay is a new standalone binary. Point each machine’s daemon at it and your macOS app and web dashboard show every session from every host in one aggregated list — your laptop, a Linux box, a VM — without being at any of them. The link is secured end-to-end: TLS/wss, bearer-token auth (with a token-minting CLI), an origin allowlist, and per-IP / per-connection caps.

Why it matters: you no longer have to sit at the machine running an agent to see what it’s doing — one relay, every host, from anywhere, over an authenticated connection. (#483, #547, #544, #548, #519)

Added

  • Linux daemon (#482, #478) — irrlichd now builds and runs on Linux (amd64 + arm64), daemon-only, behind a portable ProcessObserver observation layer; install via the same curl … | sh one-liner.
  • Per-provider windowed usage spend + subscription empty-state (#441, #386) — usage and quota are tracked per provider, with a clean empty-state before the first window of data lands.
  • OpenCode inherits OpenAI rate limits (#424) — OpenCode sessions pick up the OpenAI rate-limit window via the JWT account_id, so quota burndown is accurate for OpenCode→OpenAI users.
  • Background Bash processes hold a session working (#450, #452) — a claudecode session with a live backgrounded process stays working until it finishes, instead of falsely settling to ready.
  • Editable relay URL + token fields in macOS Settings (#550) — the relay endpoint and bearer token are now editable text fields in the app’s Settings.

Fixed

  • Surface claudecode tool-use permission prompts as waiting (#490) — a session paused on a permission prompt now flips to waiting instead of looking busy.
  • Codex: settle interrupted turns (#464) — a turn_aborted is treated as turn-end, so an interrupted Codex turn settles instead of hanging in working.
  • Price Warp / Cursor / Antigravity sessions correctly — added 22 new model aliases synced from codeburn (Warp auto-routers and codex display strings, Cursor dash-form reasoning tiers, Antigravity Gemini 3.5 Flash, and human-readable display-name forms), so these frontends price at real dollars instead of $0. Closes the alias-sync gap deferred in v0.4.7.
  • claudecode: strip trailing period from background-process output path (#501).

Changed / Distribution

  • Web dashboard split into three files (#418) — index.html + irrlicht.css + irrlicht.js, with Vitest unit tests; the daemon serves them from disk, no codegen.
  • Linux replay Dockerfile relocated to tools/ (#499).

Technical appendix

  • Relay (irrlichtrelay) — v0 round-trip daemon → relay → macOS + web (#483); v1·A secure exposure — TLS/wss, bearer-token auth + token-minting CLI, origin allowlist (#547); v1·B hardening — websocket read-limit + per-IP / per-connection caps (#544); v1 epic C–G — compound session keying across hosts, origin glyph, deploy artifacts, fade-on-disconnect, coding-factory demo (#548); macOS auto-connect on relay URL, connection-status dot, restored ⓘ + scroll (#519); editable relay URL + token Settings fields (#550); live cross-host round-trip testbed under examples/ (#486).
  • Cross-platform observation layer — new ports.ProcessObserver seam with build-tagged process_{darwin,linux,other}.go; the adapters are unchanged (internal seam, not DI), and a Linux daemon falls out — Windows is now “add one file” (#482, #478).
  • Quota / pricing — per-provider windowed usage spend + subscription empty-state (#441, #386); OpenCode→OpenAI rate-limit inheritance via JWT account_id (#424); 22 new frontend aliases in core/pkg/capacity/aliases.go synced from codeburn’s BUILTIN_ALIASES. Five Warp codex aliases resolve to gpt-5.3-codex, which LiteLLM does not yet price — they log on miss and are flagged for a follow-up sync rather than blocking the release.
  • Session detection — claudecode tool-use permission prompts surface as waiting (#490); backgrounded-Bash tracking keeps the session working and recognizes TaskOutput / <task-notification> completion in SDK-harnessed claude as well as BashOutput / KillShell in bare claude (#450, #452, #501); codex turn_aborted treated as turn-end (#464).
  • Onboarding factory (internal fixture tooling — no runtime impact) — eight-phase rewrite of the scenario × adapter coverage matrix: tools/agent-onboardingtools/onboarding-factory, with the of CLI as the sole writer of everything under replaydata/ (#522–#530). Schema cutover to per-scenario shards — one catalog (scenarios.json) plus per-cell metadata.json in id-prefixed folders, every recording moved under recordings/<name>/ (#510, #511, #514, #524). New 4-verb ir:onboarding-factory skill retiring ir:onboard-agent; of validate as a CI integrity gate. Per-adapter column recordings landed across claudecode, codex, opencode, aider, and pi (#515, #517, #518, #504, #489, #477, and others).
  • Tests / CI — per-worktree state isolation + headless daemon startup smoke test (#448); hermetic replay so byte-identity goldens reproduce (#447, #451); fswatcher flake fixes (#485, #487); three-dot diff in the replaydata deletion guard (#466); web dashboard Vitest suite (#418).

v0.4.7 (2026-05-22)

macOS distribution levels up: Sparkle auto-updates and a notarized DMG, plus OpenCode task progress reaches dashboard parity.

Added

  • Sparkle 2.x auto-update integration (#413) — the app now checks for updates on launch and offers a one-click upgrade; a manual “Check for Updates…” button lives in both the popover and the Settings panel. Signed appcast served from irrlicht.io/appcast.xml. First Sparkle-enabled release is v0.4.7; users on v0.4.6 must do one manual upgrade before auto-updates begin.
  • Web dashboard ports the macOS overlay’s provider quota chip (#417, closes #387) — popover and dashboard now show identical 5h/7d subscription bars (or cumulative usage spend) for the same /api/v1/sessions response. Settings modal gains a Provider-quota section with the same auto/subscription/usage controls as the macOS app.
  • OpenCode todowrite snapshots surface as task-progress dots (#410, closes #277) — parity with Claude Code’s TaskCreate/TaskUpdate pipeline. Content-keyed deltas survive OpenCode’s lack of stable todo IDs.

Fixed

  • OpenCode working→ready latency (#412, closes #278) — sessions flip from working to ready ~2.5 s faster.
  • Claude Code wrapped command preserves user statusLine output (#404).
  • Curl installer survives GitHub API rate limits (#401) — falls back to the redirect-based latest-release URL when the API returns 403.

Changed / Distribution

  • Developer-ID signed + Apple-notarized DMG (#406, #409, closes #233) — first launch through the curl installer or Homebrew cask no longer trips Gatekeeper; the quarantine-strip workarounds in site/install.sh and the cask postflight are gone.
  • com.apple.security.get-task-allow removed from production entitlements (#407, #415) — Apple notarization rejects binaries with the debug entitlement set to true. Build-time + canary-install guards both assert it is absent on the shipping artifact.

Docs

  • Kitty terminal-host docs (#402) — clarify that font/config changes require a full app restart, not just a reload.

Removed

  • tools/coverage-viewer and tools/find-flicker-sessions.sh deleted (#411, #414) — superseded by the unified agent-onboarding viewer at tools/agent-onboarding.

Internal

  • /ir:onboard-agent pipeline lands (#328, #408) — drives every adapter through a shared agent-agnostic scenario catalogue keyed by capabilities; records lifecycle fixtures and surfaces material drift vs. committed recordings. First-class opencode driver added in this release.

Deferred

  • Model alias map sync from codeburn skipped — 13 upstream additions remain unsynced; several target canonicals (e.g. gpt-5.3-codex) are not yet in LiteLLM’s pricing table, so adding them now would still resolve to zero-cost capacity. Will land in a follow-up once LiteLLM catches up.

Technical appendix

  • Sparkle 2.x integration (#413) — Sparkle 2.9.2 added via SwiftPM with a thin UpdateManager wrapping SPUStandardUpdaterController. EdDSA public key nKRcUPAmK6syLFEvp9O30FFvjhTIfGxYVv/6y8zpZI0= baked into both the tracked Info.plist and the tools/build-release.sh heredoc. tools/build-release.sh copies Sparkle.framework into Contents/Frameworks/, adds the @executable_path/../Frameworks rpath, and signs the nested helpers (Downloader.xpc, Installer.xpc, Updater.app, Autoupdate, framework binary) deepest-first before the outer bundle. New site/appcast.xml ships with one signed entry for v0.4.6 so the feed serves a valid response immediately. After installing v0.4.7, drag the app to /Applications/ — Sparkle refuses to self-update from a Gatekeeper-translocated ~/Downloads/ path.
  • Notarized + DevID signed DMG (#406, #409)tools/build-release.sh signs with the Developer ID cert + hardened runtime + entitlements when DEVELOPER_ID is set; notarizes and staples when NOTARYTOOL_KEYCHAIN_PROFILE is also set. The build exits 1 if DEVELOPER_ID is set without NOTARYTOOL_KEYCHAIN_PROFILE. FocusMonitor.swift detects the DevID signature at runtime and loads INFocusStatusCenter via NSClassFromString rather than statically linking Intents.framework.
  • get-task-allow removed from prod entitlements (#407, #415)Irrlicht-dev.entitlements still carries it for local Xcode debug. The verification uses value-aware XPath rather than key-grep so an explicit <false/> doesn’t false-fail; the canary install check re-runs the assertion against the actually-shipping bundle.
  • OpenCode todowrite (#410)opencode.Parser is stateful: each snapshot translates into the minimal TaskCreate/TaskUpdate delta sequence the tailer expects, content-keyed because OpenCode’s todos carry no stable IDs. A ~15-line accumulator in opencode/metrics.go populates metrics.Tasks on the SQLite metrics path. New TestComputeMetrics_TodowriteTasks drives querySessionMetrics with a synthetic SQLite DB.
  • OpenCode latency (#412) — eliminates a ~2.5 s tail where the SQLite metrics path waited on a debounce timer that no longer applied once the session went idle. New core/application/services/debounce_test.go pins the timing invariant.
  • /ir:onboard-agent pipeline (#328, #408) — unifies fixture refresh + adapter bootstrap + new-agent onboarding into one skill. Drives the real CLI through a shared agent-agnostic scenario catalogue keyed by requires: [capability]; adapters declare Capabilities and matrix cells fall out automatically. New drive-opencode-interactive.sh driver: each send step is an opencode run --session <id> subprocess; post-run SQLite export to the parts JSONL the parser expects.
  • Installer rate-limit hardening (#401)site/install.sh falls back to the /releases/latest redirect URL when the GitHub API returns 403.

v0.4.6 (2026-05-17)

Public roadmap page lands, with screenshots and concept tiles for every release row

Highlights

Public roadmap at /docs/roadmap.html

Roadmap page top — Horizon and v1.0 entries on a dashed future spine

A chronological newest-at-top timeline shows every shipped release with big milestones, italic notes for narrower changes, and a compact ALSO SHIPPED roll-call of every other issue/PR. Future versions (v0.5 → v1.0 plus horizon) are bold guesses against the Platform Rollout wiki, with concept tiles for the in-flight relay-v0 work, the planned VS Code panel, iOS / iPadOS, Android + Apple watch, and the v1.0 second-desktop + hosted relay launch.

Why it matters: where the project is going is now discoverable without spelunking GitHub issues. Linked from every docs sidebar, the landing footer, and the CHANGELOG.md preamble. Future releases will migrate items from above the today line down across it as they ship. (#395)

Added

  • Moonshot Kimi family in the alias mapkimi-auto, kimi-code, kimi-for-coding resolve to moonshot.kimi-k2-thinking in LiteLLM (codeburn sync). Sessions on Kimi-routed frontends now price at non-zero.

Changed

  • Brand: gradient flame refresh propagates across the remaining surfaces (#394) — picks up the docs sidebar dot, design-system preview tiles, and the few last places where the old wisp survived the v0.4.5 rollout.
  • Working-state icon: heartbeat halo → breathing solid dot (#393) — the SMIL halo animation was distracting; the solid dot with a gentle opacity breathe reads cleaner at 14px in the menu bar.
  • Web dashboard: subagent dot-matrix row dropped to match the macOS overlay (#397) — the 89/96-style dot strip was a web-only detail that no longer matched the unified row anatomy shipped in v0.4.5.

Fixed

  • Claude Code task list: prune entries absent from task_reminder snapshots (#396) — the in-memory task list is reconciled against the authoritative snapshot so dropped tasks no longer linger as in_progress indefinitely.

Docs / Tooling

  • Release-flow friction removed from CLAUDE.md (#392).
  • /ir:release release-notes template + Step 4a-roadmap mechanics — three-layer release notes (Headline → ≤ 3 Highlights with screenshots → Also → Technical appendix) at .claude/skills/ir:release/release-notes-template.md. Step 4a-roadmap codifies the roadmap-update protocol (Python recipe for ALSO SHIPPED, row-shape templates, today-line bump, pill rotation for minor releases).

Technical appendix

  • Public roadmap page (#395) — site/docs/roadmap.html is a single file with all CSS scoped inline (no docs.css edits). Pure-CSS git-branch via pseudo-elements; ALSO SHIPPED list extracted via git log <prev-tag>..<this-tag> with hex-color and natural-language noise filters. Linked from every docs/*.html sidebar (new "Project" section), the landing-page footer, and the CHANGELOG.md preamble.
  • Roadmap concept tiles — six future-section composites generated via Python + PIL + rsvg-convert using the new gradient flame brand. Tiles under assets/roadmap/v<ver>/; CSS .release-figure renders future tiles at 0.78 opacity and past at 0.92.
  • Release-notes template + Step 4a-roadmap expansion — three-layer template with worked example from v0.4.5. Step 2 / Step 4a in the skill updated to consume it; Step 4a-img enforces the highlight-image asset checklist; Step 4a-roadmap codifies a 5-substep protocol (extract refs, insert row, bump today, pill rotation, verify).
  • Brand refresh continuation (#394) — propagates the v0.4.5 single-path gradient flame to surfaces the original PR missed: docs sidebar .dot swapped for an inline gradient flame SVG, design-system preview tiles updated, AppIcon iconset regenerated via the reproducible tools/build-app-icon.sh builder.
  • Working-state breathing dot (#393) — svgIcons.working swaps the SMIL heartbeat halo for a solid circle with a CSS opacity breathe (no animation under prefers-reduced-motion). macOS SessionStateIcon updated to match.
  • Web subagent dot-matrix row removed (#397) — the 89/96 progress-dot strip below the session row no longer renders; macOS overlay never had it, this brings web in line.
  • Claude Code task-list pruning (#396) — the tailer treats task_reminder attachments as authoritative; any local in_progress missing from the snapshot is demoted to completed. Mirrors the v0.3.12 phantom-in_progress fix (#289) but for the absent-task case.
  • Model aliases — codeburn sync — adds kimi-auto, kimi-code, kimi-for-coding to core/pkg/capacity/aliases.go, all LOCAL_OVERRIDE to moonshot.kimi-k2-thinking since LiteLLM only ships the dotted-prefix key. Five changed entries in the codeburn diff are intentional LOCAL_OVERRIDEs — not updated.

v0.4.5 (2026-05-16)

Added

  • Pro / Max subscription burn-rate forecast in the macOS overlay (#309, #379) — Two ingestion paths cover the major subscription combos: Codex CLI's token_count events carry rate_limits in-band (parser extension), and Claude Code's statusline JSON is captured via a new statusLine.command that POSTs to /api/v1/hooks/claudecode/statusline. The statusline chain is wrapped in bash -c '...' so the bash-only tee >(...) process substitution survives Claude Code's POSIX-sh invocation; v1 wraps installed before the fix are migrated on the next daemon start without losing the user's original statusline command. Snapshots flow into SessionMetrics; a 5-sample rolling history feeds a linear-projection forecast; the menu-bar header shows a provider chip (Anthropic / OpenAI inferred from plan_type or adapter) with stacked 5h / 7d horizontal progress bars carrying percent + reset time inline, coloured via the existing pressure-level ramp. Version moves out of the header and into a small footer in Settings so the header slot is always reserved for quota data
  • Brand: new single-path gradient flame across design system + macOS app (#388) — Replaces the old "outer + inner-highlight + tear ellipse" wisp (viewBox 32) with a single-path silhouette designed at viewBox 1254, plus per-state gradient treatments (purple / orange / green) and a flat mono + black/white variant set. Six source SVGs land under assets/irrlicht_flame_*.svg; the design-system preview gains a "state colors" tile row and refreshed palette swatches per hue; OffFlameImage.swift is rescaled to viewBox 1254 with the single path and the unused core gradient dropped; AppIcon.icns is regenerated via a new reproducible builder tools/build-app-icon.sh (rsvg-convert with qlmanage fallback) that produces a byte-identical .icns on re-runs. The landing-page navbar swaps its 8×8 .sparkle dot for an inline 22×22 gradient flame SVG using the new mark, keeping the breathe animation
  • Unify per-row state icons across web and macOS (#382) — Replaces the dashed circle (web) and SF Symbol hammer/hourglass (macOS) with a shared visual vocabulary: working = heartbeat halo (purple), waiting = two-bar pause (orange), ready = unchanged checkmark. Web swaps svgIcons.working for an inline SMIL heartbeat-halo SVG, drops the now-unused .working spin rules, and hides the animated halo under prefers-reduced-motion. macOS adds a new SessionStateIcon SwiftUI view that renders the halo natively (repeatForever, honors accessibilityReduceMotion) and the pause bars as two RoundedRectangles. Snapshot fixtures impacted by the icon swap are re-recorded. Closes #380
  • /ir:triage assigns release milestones on ready-for-agent (#375) — Triage was applying labels but leaving milestone empty, so every ready-for-agent issue still needed a manual maintainer pass to land in a release bucket. The skill now picks a milestone from priority and creates one if the bucket doesn't exist: Priority-HighACTIVE (in-progress release), Priority-MediumNEXT, Priority-LowFUTURE. The three buckets are computed once per run from the latest published release tag (gh release view --json tagName), so the skill survives a version bump without an edit. Skipped on needs-info / wontfix and on issues already carrying a maintainer-set milestone. Bundled into the existing gh issue edit call. The brief template gains a **Milestone:** line

Fixed

  • Detect imperative waiting cues without a literal ? (#381, #383) — IsWaitingForUserInput now classifies turns ending with an imperative ask ("let me know if it's right", "verify locally and reply with the diff", "awaiting your go-ahead before I merge") as waiting instead of falling through to ready. Adds ExtractWaitingCue alongside the existing ExtractQuestionSnippet — both are OR'd in the public predicate, leaving snippet semantics unchanged. The cue regex set comes from issue #381's coverage matrix: five buckets (direct asks, approval framings, action gates, curated imperatives, trailing soft asks), all running against the trailing 1–2 sentences so earlier paragraph content can't trigger false positives. ExtractWaitingCue walks the tail newest-first so the more recent sentence is returned when both the last and second-to-last sentence match. State-classifier Rule 2a now reports turn ended with question or cue → waiting. Adds the agent-imperative-pending replay scenario and refreshes five existing claudecode goldens
  • Web: migrate sessions when project_name lands after the first push (#377) — The web dashboard's applySessionUpdate updated agent fields in place but never reconciled group membership when project_name changed. So if the first session_created WS push arrived before git-metadata enrichment had filled in project_name, the agent was placed in the "unknown" bucket and stayed there permanently. The macOS app dodges this because patchApiGroups schedules a full re-hydration on a missed session id; the web view had no equivalent path. Fix: on a session_updated for an existing top-level entry, detect when the new project_name points at a different group than where the agent currently lives, splice it out, find-or-create the target group, and clean up the source group if it goes empty. Children inherit their parent's group and have their sessionIndex entries re-anchored via indexChildren
  • Pricing: normalize frontend-rewritten model names — 63-entry alias map synced from codeburn (#371, #376) — Sessions running through frontends that rewrite the model name before the LLM call (Cursor's claude-4.6-opus-fast-mode, OMP's anthropic--claude-4.6-opus, Antigravity's gemini-3.1-pro-high) priced at $0 because CapacityManager.GetModelCapacity did an exact LiteLLM-key lookup with no normalization. Adds core/pkg/capacity/aliases.go (a 63-entry alias map ported from codeburn's BUILTIN_ALIASES) and resolves it inside GetModelCapacity before the existing lookup. Exact-match only; no prefix/fuzzy logic. Five aliases whose codeburn canonicals are missing from LiteLLM are marked with // LOCAL_OVERRIDE: <reason> and re-pointed to LiteLLM-present keys. Adds the /ir:refresh-aliases skill (standalone-PR + release-inline modes) that diffs against codeburn upstream, wired into /ir:release as Step 1.5 (fail-soft). Verified end-to-end via replay against claudecode/06-cost-calculation-07f5cca9: byte-identical $86.83 cost vs the canonical baseline

Docs

  • List VS Code extension as a planned platform (#370) — Adds "VS Code extension" between CLI (alpha) and Linux (planned) in both the landing-page Platforms column (site/index.html) and the README Platforms table; the README row links to the tracking issue (#350). Groups editor/UI surfaces (menu bar, web, CLI, VS Code) before OS-target planned items (Linux, Windows, iOS/iPadOS)

v0.4.4 (2026-05-16)

Added

  • Web dashboard reaches overlay parity (beta) (#354) — The dashboard served at http://127.0.0.1:7837 now reaches visual and functional parity with the macOS menu-bar overlay, in service of the upcoming VS Code panel (#350) and future relay-served clients. Row anatomy matches SessionListView.swift: state · num · subagent badge · branch · ctx-bar (with token overlay inside) · cost · model · adapter icon, with strict flex-wrap: nowrap, branch + bar growing into available slack, and a 22 px row height that survives the Context ↔ history mode toggle without shifting rows. Stacked below the row are the waiting question, task progress dots, the right-anchored 89/96-style subagent dot-matrix, and the pressure alert. The header carries the version chip, aggregated state icons (≤ 3 inline, else "N sessions"), a view-mode cycle (Context / 1 Min / 10 Min / 60 Min), a theme toggle (☀/☾), a settings cog (⚙), and a 3-state connection indicator (watching / reconnecting / disconnected) with a banner when the daemon connection drops. A new Settings modal mirrors SettingsView.swift minus the bits the web can't do — show-cost, debug mode (toggles row-id/row-elapsed/row-created chips), and three notification toggles (ready / waiting / context pressure) that fire new Notification(...) only when the dashboard isn't the focused tab. Themes pick icon_svg_light vs icon_svg_dark per resolved theme so the Codex icon is now visible on both. Group headers cycle day/week/month/year on click, persist their collapse state in localStorage, and Gas Town groups get a ⛽ glyph. A responsive cascade keeps the context bar — the load-bearing signal — visible at any width: cost drops below 420 px, model below 360, adapter icon below 320. On the daemon side this added exactly one route: GET /api/v1/version returning {"version": "..."}. The previous timeline-heatmap and Raw-JSON tab — both debug surfaces the overlay never had — are removed. Marked beta in README.md and site/docs/quickstart.html

Fixed

  • Codex split-event token_count priced at $0 (#361) — Codex sessions reported $0 cost because the parser emitted PerTurnContribution{Model: ""} on every token_count event — Codex splits the model name onto turn_context and the usage cursor onto token_count, so the tailer bucketed deltas under cumByModel[""], which has no pricing. In applyContribution, contributions with no model now fall back to t.metrics.ModelName (already maintained by applyModelMetadata from turn_context) so pricing and the UI's model display read the same field by construction. LedgerState persists ModelName so the fallback still works on the first token_count after a daemon restart. Six Codex replay goldens regenerated with the corrected pricing
  • Adapters honor agent-CLI env vars for relocated session dirs (#349) — Three coding-agent adapters now respect their upstream env-var convention for relocating session transcripts: Pi via PI_CODING_AGENT_SESSION_DIR (the absolute session dir itself), Claude Code via CLAUDE_CONFIG_DIR with /projects appended (transcripts only — the PID-metadata directory at ~/.claude/sessions/ remains hardcoded because that hunk is unverified), and Codex via CODEX_HOME with /sessions appended. fswatcher.New treats an absolute dir as-is and falls back to $HOME-relative for relative paths. Non-absolute env values (relative paths, unexpanded ~, trailing slashes) are filepath.Clean-ed and rejected with a log.Printf warning. Env vars are read once at Agent() construction, so a daemon restart is required after changing them. Default-install behavior is unchanged. Aider and OpenCode are intentionally untouched

Docs

  • Subtle mascot illustrations + sentence-level correctness audit (#365) — Adds 9 thematically-matched flame-mascot figures across site/docs/*.html, designed to read on both light and dark themes (640 px WebP @ q88, ~908 KB total, ~6% of the 14.4 MB PNG source). Floats reflow to centered blocks at ≤ 720 px. Three parallel review passes then cross-checked every factual claim in the docs against the current Go source and fixed 17 stale or wrong statements: context-pressure thresholds corrected to 0–60% / 60–80% / 80–90% / 90%+ to match core/pkg/tailer/tailer_metrics.go; menu-bar icon called a "sparkle" corrected to "flame"; six references to the deleted ./validate.sh script replaced with go test ./core/... -race -count=1 + tools/replay-fixtures.sh; processscanner/processlifecycle/ rename propagated; HTTP/WS diagram updated to /api/v1/sessions(/stream); adapter field now lists all 5 wired adapters; never-implemented --format json / --id <prefix> CLI flag docs removed; log filename corrected to events.log; lsof command updated to include the -Fn flag actually used

Distribution

  • Five release-skill guardrails against v0.4.3's shipping defect (#360) — v0.4.3 shipped a binary that crashed on every end-user install (#357 root-caused; #356 + #358 fixed). Five further guardrails prevent recurrence. (1) Drop NSFocusStatusUsageDescription from the Info.plist template — with Intents.framework statically linked, TCC preflights kTCCServiceListenEvent at process startup and SIGABRTs ad-hoc-signed builds regardless of the usage description. (2) Rewrite the coupling-rule prose with a per-key include/exclude table explaining the three-way relationship between AMFI-gated entitlements, TCC-gated NS*UsageDescription keys, and the linked frameworks themselves (the structural lever). (3) Add a framework-link audit between Swift build and signing: otool -L against a FORBIDDEN_FRAMEWORKS list aborts the release on any match. (4) Strengthen smoke-test failure semantics with explicit "DO NOT SHIP" framing, a tccutil reset prereq to clear poisoned cache, and a four-step debugging checklist. (5) Replace Step 9 verify with a real end-to-end install canary that runs curl ... | sh against the just-published release and validates via pgrep — the installer's "Launching... ✓" lies when AMFI kills the app immediately

v0.4.3 (2026-05-15)

Note: Assets re-cut later the same day. The original v0.4.3 binary statically linked Intents.framework via FocusMonitor.swift's direct INFocusStatusCenter calls, which made macOS TCC preflight kTCCServiceListenEvent at process startup and AMFI/TCC SIGABRT the ad-hoc-signed binary with launchd POSIX 153 on every end-user install. The re-cut moves FocusMonitor to dynamic dispatch (NSClassFromString + SecCodeCopySigningInformation gate) so Intents.framework is never loaded on ad-hoc builds. DND-aware notification silencing is paused until Developer-ID signing lands (#233); restoration tracked in #357. Release-skill regression fix that prevents recurrence: #356.

Added

  • macOS: autostart Irrlicht.app at login, on by default (#343) — On first launch the app registers itself as a login item via SMAppService.mainApp so the menu-bar overlay is up before you open your first terminal. A new toggle in Preferences flips the setting, and the choice is persisted to UserDefaults; a one-time gate (didApplyDefaultLoginItem) ensures the default never re-enables itself after you turn it off. The XPC call to launchd runs on a detached userInitiated task so the toggle animation stays smooth on slower Macs. The unsigned-then-signed-build dev edge case is called out inline in applyDefaultIfNeeded so future maintainers know why the gate exists

Fixed

  • macOS: focus VS Code windows on other Spaces (#344, #348) — When VS Code (or Cursor / Windsurf) was fullscreen on a different macOS Space, clicking a session row in the overlay was a silent no-op. AX's kAXWindowsAttribute omits cross-Space fullscreen windows for Electron hosts, so the title-matching activator never saw the target window. The fix enumerates the app's Window menu instead — that list is always complete — and AX-presses the title-matching item so macOS performs the Space switch and window raise atomically. Window-menu titles are recognized across the major macOS-supported locales (en/de/fr/es/it/pt/nl/sv/da/no/fi/pl/cs/ru/tr/ja/zh-Hans/zh-Hant/ko) so the path works for non-English users too. Hardening from /simplify review: drop the second-to-last-menu positional fallback (could trigger a destructive action in non-Cocoa-standard apps), unwrap menuBarRef before CFGetTypeID, and collapse the imperative lookup into first(where:) + compactMap
  • daemon: session disappears when a second Claude is opened in the same VS Code window (#345, #347) — Opening a second Claude Code session inside the same VS Code window briefly leaves the new process in the parent CWD before it cds into its worktree. The scanner minted a proc-<NEW> pre-session for that CWD; the claudecode adapter's CWD-based PID discovery — with no transcript yet, so the metadata-based filter is bypassed — then returned the neighbor process's PID, and HandlePIDAssigned's same-PID cleanup deleted the legitimate neighbor's session row. The row reappeared later via the activity-driven recovery path, which presented as a confusing flicker. Fix: pre-session IDs already encode the PID by construction (fmt.Sprintf("proc-%d", pid) in processlifecycle/scanner.go), so the daemon now short-circuits adapter-level discovery for them and calls HandlePIDAssigned directly with the parsed PID. The short-circuit sits above the ProcessWatcher == nil / discoverFn == nil guards so it's robust against future adapters that have a process matcher but no PIDForSession. Real sessions (UUID IDs with a transcript path) continue through the adapter unchanged. E2E regression test uses sync/atomic.Int32 for the discovery-call counter so a future regression races visibly under -race

Docs

  • Landscape page refresh against live GitHub data (#346) — site/landscape/index.html and site/landscape/compare/index.html regenerated from a fresh gh api sweep of 38 tracked agents (May 15, 2026 snapshot). Aider and OpenCode flip from planned to live in the landscape table to match their existing adapters under core/adapters/inbound/agents/. Two repo renames propagated: Pi badlogic/pi-monoearendil-works/pi (the v0.74 move) and Warp warpdotdev/Warpwarpdotdev/warp. Two plausibility-rule trips noted with explicit reasoning: Warp jumped 26.5k→58.5k stars after open-sourcing its codebase; Ruflo grew 33.1k→51.3k on viral promotion. The ir:agent-releases skill's tracked-releases.md adds 22 new versions across Claude Code (v2.1.120–v2.1.142), Codex (v0.125–v0.130 stables), Pi (v0.71–v0.74), and Gas Town (v1.0.1, v1.1.0)

v0.4.2 (2026-05-15)

Changed

  • macOS app: drop legacy file-polling, retire vaporware IRRLICHT_DISABLED env var (#337) — IRRLICHT_DISABLED was never wired into any code path, and IRRLICHT_USE_FILES gated a fallback path in SessionManager that the WebSocket transport has fully replaced; both are removed. ~165 lines of dead Swift in SessionManager.swift go with them (file watcher, debounce/periodic timers, loadExistingSessions, createInstancesDirectoryIfNeeded); init unconditionally uses WebSocket. Equivalent orphan reaping still happens daemon-side via PIDManager's syscall.Kill(pid, 0) sweep — no safety net was lost. The macOS app no longer reads from the daemon-owned instancesPath; that ordering dependency is now documented inline

Docs

  • configuration: document four real env vars with concrete recipes (#337) — site/docs/configuration.html drops the USE_FILES / DISABLED rows and adds rows for the four env vars that exist in code but were undocumented: IRRLICHT_UI_DIR, IRRLICHT_BIND_ADDR, IRRLICHT_MDNS, IRRLICHT_DEBUG. A "When to use these" section walks three real recipes — LAN phone access (with both shell-env and launchctl setenv flows so it works whether the daemon is shell-launched or auto-spawned by the macOS app), "Dashboard UI not found" recovery (showing the full four-place auto-detect order so the override slots in clearly), and a debug state dump. The original IRRLICHT_DEBUG=1 open -a Irrlicht example was broken on macOS — LaunchServices spawns GUI apps without inheriting shell env — and is replaced with three working alternatives (direct binary invocation, open --env, launchctl setenv)
  • architecture, SECURITY: drop vaporware kill-switch bullet, cross-link network-exposure docs (#337) — site/docs/architecture.html no longer mentions the IRRLICHT_DISABLED kill switch (which never existed). SECURITY.md cross-links the network-exposure paragraphs to configuration.html and to the planned hub-mode design

Distribution

  • Release skill: enforce long-line paragraphs in release notes / PR body (#335) — GitHub renders release-body markdown with the GFM "breaks" extension, so every soft line break inside a paragraph or bullet becomes <br>. The v0.4.0 and v0.4.1 release bodies were hand-wrapped at ~75 cols and shipped as a stack of short ragged lines on the release page (both since fixed via gh release edit --notes-file). Step 2 of /ir:release now carries an explicit line-wrap rule explaining the difference between GFM-with-breaks (release notes, PR body, issue body) and standard CommonMark (CHANGELOG.md); Step 8 switches the example from --notes to --notes-file pointing at a tempfile, so the body is reviewable, re-runnable, and the long lines survive shell escaping
  • assets: version reference screenshots (#336) — assets/session_limits.png and assets/straeter_light.png are now versioned alongside the other reference shots used in README drafts and social posts

v0.4.1 (2026-05-14)

Fixed

  • kitty: click-to-focus lands on the right window and tab (#326) — Three failures in the kitty click-to-focus path, fixed end-to-end. (1) When kitty is launched from a shell whose env contains TERM_PROGRAM=vscode (e.g. a VS Code integrated terminal), kitty inherits that value because kitty itself does not set TERM_PROGRAM (upstream kitty #4793); the daemon captured the inherited value and the click was routed to VS Code's activator. ReadLauncherEnv now overrides TermProgram to "kitty" whenever KITTY_WINDOW_ID is set and process ancestry confirms kitty.app is a parent. (2) With multiple kitty processes running, AppActivator.activate(bundleID:) always picked one — typically the oldest — and a post-kitten focus-window re-activate fired async, outside the menu-bar click context, racing macOS yield-focus rules and producing the "raises then drops back" symptom. The daemon now whitelists KITTY_PID; a new Launcher.KittyPID field is plumbed through to the Swift app; KittyActivator calls NSRunningApplication(processIdentifier:).activate(options: []) synchronously inside the click handler. (3) Apple-signed agents like pi (and /bin/zsh) hide their env from sysctl, so KITTY_* env vars never reached their sessions — every click hit bundle fallback. Three new darwin-only helpers in osutil_darwin.go derive these fields without reading the agent's env: kittyAncestryPID, kittyListenOnFor, kittyWindowIDForPID. backfillLauncher was extended so pre-existing sessions get all four kitty fields refreshed on daemon restart

Security

  • kitty: uid-check on /tmp/kitty-{PID} socket probe/tmp is world-writable, so kittyListenOnFor could be tricked into trusting a pre-planted Unix socket at the canonical path and sending kitten @ ls to a hostile listener. kittyListenOnFor now stats the candidate socket and skips any whose owner uid doesn't match os.Getuid() — kitty binds with its own credentials, so a foreign-owned socket at the canonical path is either stale or hostile. Test coverage in osutil_darwin_test.go exercises the current-uid, foreign-uid (root-gated), non-socket, missing-file, and zero-PID branches

Changed

  • kitty: cache ancestry walk in ReadLauncherEnv — The kitty-via-vscode pi worst-case path was walking the parent-process chain up to three times (each walk shells ps up to maxAncestry times). A new resolveHostFromAncestry returning (termProgram, hostPID) is memoized inside ReadLauncherEnv via a closure, so the chain is walked at most once. resolveTermProgramFromAncestry and kittyAncestryPID become thin wrappers; call-site behavior is unchanged

Docs

  • api-reference, contributing: catch v0.4.0 sweep gaps (#332) — site/docs/api-reference.html still referenced agentCfgs in the GET /api/v1/agents blurb; renamed to allAgents to match cmd/irrlichd/main.go. site/docs/contributing.html adapter-PR checklist still said "Implements the AgentWatcher interface"; replaced with the current Agent() / agent.Agent / allAgents contract

Distribution

  • Release skill hardening (#332) — Step 6 checksum recipe now includes irrlichd-darwin-universal.tar.gz (the curl --daemon-only path verifies it; omitting it shipped a release where the standalone daemon installer failed the integrity check). Step 7b drops --delete-branch from gh pr merge --squash so the release branch remains addressable post-squash. Step 4b trigger table gains rows for adapter-package edits and main.go slice/wiring renames so future Phase-A-style shape changes can't slip past doc sweeps

v0.4.0 (2026-05-14)

Added

  • macOS: per-event notification sound picker (#253) — Preferences gains a separate row per notification event (ready / waiting / context-pressure) with its own enable toggle, sound picker (Ping / Chime / Funk / Whoosh / Sosumi / None / Speak aloud / Custom), and preview button. Custom audio (aiff/wav/mp3/m4a/caf) is imported into ~/Library/Sounds/, transcoding mp3/m4a to LPCM-in-CAF via AVAudioFile so UNNotificationSound will play it. "Speak aloud" routes title+body through AVSpeechSynthesizer, pinned to en-US, and exposes three voice variants (Default / Zoe-Premium / Jamie-Premium); if a premium voice isn't installed, the row renders an inline "Install … in System Settings" button that deep-links to Accessibility → Spoken Content. Defaults: all three events enabled; Ready=Funk, Waiting=Ping, Context=Sosumi. Existing preferences preserved on upgrade

Fixed

  • Claude Code: don't bounce ready→working on post-turn away_summary (#329) — Claude Code writes a system/away_summary recap ~3 minutes after a turn ends. The parser correctly marked it Skip=true, but the fswatcher's mtime trigger still ran the full classification pipeline, and the force-bounce in processActivity saw the stale LastEventType from the prior turn_done and flipped the ready session back to working indefinitely. The tailer now surfaces a NoSubstantiveActivity signal when a pass consumed new content but produced no state-relevant change; the detector short-circuits the force-bounce / re-classify path on that signal while still refreshing LastEvent / EventCount / UpdatedAt and broadcasting so the UI's "last activity" stays current
  • Claude Code: detect AskUserQuestion / ExitPlanMode via PreToolUse hook (#307) — Claude Code can lag flushing the assistant tool_use block to JSONL for minutes after rendering an AskUserQuestion / ExitPlanMode overlay; the transcript-driven detector never saw the open tool call, so the session sat in working while the user stared at the prompt. A PreToolUse hook scoped to AskUserQuestion|ExitPlanMode fires synchronously when the model emits the tool_use and flips permissionPending; the existing PostToolUse matcher is widened to include both tools so the same edge clears the flag when the user answers. Legacy installs are migrated in place by upgradeStaleHookMatchers
  • Codex: treat <proposed_plan> as user-blocking like ExitPlanMode (#322) — Codex's Plan Mode ends a turn with a <proposed_plan>…</proposed_plan> block — semantically identical to Claude Code's ExitPlanMode. The block arrives as plain assistant text, so the classifier never saw an open user-blocking tool and fell through to ready, leaving the dashboard green while the agent was actually blocked. When an assistant message contains a fully-closed <proposed_plan> block, the codex parser now synthesizes a virtual ExitPlanMode tool-use — same user-blocking path as Claude Code; the existing ClearToolNames hook on user messages closes it when the user replies
  • Daemon: reject zombie sessions with missing cwd (#321) — A daemon restart within 2 minutes of claude --resume against a session whose worktree had been deleted re-admitted the session as a ghost: the transcript-mtime check treated the refreshed mtime as live, PID discovery failed, and the steady-state sweep only cleaned it up ~75s later. Admission now checks cwd existence alongside the stale-transcript guard at both onNewSession and seedAlivePIDs

Changed

  • #159 Phase A — Agent declaration replaces agents.Config — The legacy agents.Config struct + its five per-adapter Config() constructors and four map helpers are removed. Each adapter now exports a single Agent() constructor returning a sealed-sum declaration: Agent = Identity × Process × Source, where Process is ExactName | CommandPattern, Source is FilesUnderRoot | FilesUnderCWD | ProcessOwnedStore, and FileParser is JSONLineParser | RawLineParser. The daemon consumes []agent.Agent directly, with per-projection helpers in adapters/inbound/agents/maps.go. Variant-dispatched watcher wiring in cmd/irrlichd/wiring.go replaces the per-adapter loop. Phase A also lands an M0 contract-test layer (SessionState on-disk, 7 PushMessage shapes, GET /api/v1/agents) to guard the public surface
  • #159 Phase A — Watcher port replaces AgentWatcher; identity carried on the merge pipeline — The inbound watcher port gains an Identity() method and WithIdentity() builder; each per-watcher drain goroutine in SessionDetector.Run() captures identity once and wraps every event with it, so agent.Event.Adapter is removed. The old AgentWatcher interface is deleted. NewSessionDetector panics at construction when any watcher's Identity() is the zero value. metrics.New takes a single Registry struct instead of four positional maps

Docs

  • /ir:release skill — adapt to PR-required main + fix tap-publish race (#306) — main is now protected by a "Changes must be made through a pull request" repo rule. The release flow now stages on a short-lived release/v$NEW_VERSION branch, opens a PR with the drafted release notes, squash-merges, hard-resets local main to origin/main, and tags the merged commit. Step 6.5 patches the in-repo cask template via sed and leaves the sibling tap untouched until Step 8.5

v0.3.13 (2026-05-11)

Fixed

  • OpenCode: suppress ghost sessions when no opencode process is live — the OpenCode watcher's startup scan emitted EventNewSession for every non-archived row whose time_updated fell within maxAge, regardless of whether opencode was actually running. On every daemon restart, every historical session in the DB became a "live" row in the menu bar with no path that ever removed them. Now gates emission on a live opencode process owning the session's CWD via processlifecycle.LiveCWDs(processName). Sessions in the DB with no live process are tracked (cursor seeded so historical activity isn't back-filled if the process later starts) but not surfaced; a new emitted flag enables EventNewSession to fire on the dormant→live transition
  • OpenCode: clean up carryover ghost state on startup — users upgrading from v0.3.12 had ghost session JSON files (PID=0, DB-backed TranscriptPath) that neither syscall.Kill nor isStaleTranscript caught. A new branch in isStartupZombie deletes PID=0 sessions whose adapter has a registered process name iff no live process of that name owns the session's CWD. Only deletes when the lookup returns a definitive non-nil result

Changed

  • OpenCode: GC stale cursors, drop dead initialArchiveCleanupWindowgcExpiredCursors drops cursor entries whose lastObserved predates maxAge so the cursor map can't grow without bound for users who accumulate many sessions but rarely run the CLI. Tracked separately from cur.lastTS so a session whose time_updated bumps without new parts isn't wiped prematurely
  • OpenCode: consolidate DB-backed predicate, cache liveCWDs lookup — DRYs the "is this path DB-backed?" check and caches liveCWDs per adapter inside CleanupZombies so M ghost candidates sharing an adapter pay one pgrep fork, not M
  • Release: keep homebrew tap from silently lagging (#299) — the tap was stranded at v0.3.8 across four releases because Step 8.5 no-op'd silently when IRRLICHT_TAP_DIR was unset. update-cask.sh now auto-discovers a sibling ../homebrew-irrlicht clone before bailing and hard-fails on --push without a tap dir; the skill's Step 8.5 verifies the published cask version after publish and prints a loud WARNING on mismatch

Docs

  • README: supported platforms table (#301, #302, #303) — adds a Platforms table with CLI access references and links the macOS access cell to the releases page
  • Landing: mark OpenCode alpha; teach release skill the landing-page grid — the parallel grid on site/index.html was missed in v0.3.12 because Step 4b only enumerated site/docs/*.html. Extends the dynamic enumeration to scan site/*.html and adds explicit trigger-table rows for adapter maturity-stage changes and new platforms
  • README — note codeburn alongside other quota & cost trackers in the positioning section

v0.3.12 (2026-05-09)

Added

  • OpenCode adapter (#255) — first agent on the new SQLite-backed monitoring path. OpenCode stores all session data in a single WAL-mode database rather than JSONL files; the adapter ships an fsnotify WAL watcher polling session/part tables, a parser mapping step-finish / text / tool rows to normalized events, a MetricsProvider that bypasses the JSONL tailer for cost + token snapshots, CWD-based PID discovery, parent-child session linking via parent_id from the DB, and EventRemoved emission on session.time_archived. Closes #100
  • ir:triage skill (#283) — strictly diagnostic GitHub-issue triage skill that scores each issue against a 6-axis readiness rubric (Scope / Specification / Verifiability / Context / Independence / Reversibility) and lands it at ready-for-agent or needs-info with a one-line justification per label decision. Bulk sweep skips already-triaged issues; explicit /ir:triage #N always re-triages and edits the prior comment in place

Fixed

  • History bar right-anchors when states overflow bucketCount (#286) — HistoryBarView's anchor math only worked when states.count <= bucketCount. After #249 lowered bucketCount from 150 → 60, the 150-state test fixture started rendering an all-green bar because offset collapsed to 0 and the loop drew the oldest states inside the canvas while the newest tail was clipped past the right edge. Now takes states.suffix(bucketCount) and recomputes offset against the visible slice
  • Claude Code: reconcile phantom in_progress from task_reminder snapshots (#289) — Claude Code occasionally emits a TaskUpdate against a stale taskId and never sends a follow-up completed, so the UI hung at n / total forever. The tailer now treats the task_reminder attachment as authoritative — any local in_progress whose ID is missing from the snapshot is demoted to completed, and snapshot status wins on any present-with-divergent-status case. Closes #282
  • macOS: sync apiGroups on local session delete + reset (#287) — local deletes/resets only updated sessions (menu bar) and sessionMap and skipped apiGroups (list view), so a deleted session lingered in the list until rehydration and a reset row stayed working in the list while the menu bar already showed ready. Mirrors the WS handler and adds SessionState.withState(_:) so all 10 optional fields (children, role, subagents, adapter, launcher, …) survive the round-trip

Changed

  • Co-locate adapter display name + icons with Go adapters (#284) — adds DisplayName + IconSVGLight/IconSVGDark to agents.Config and a new GET /api/v1/agents endpoint serving them. Adapter is now the single source of truth for its own branding; adding a new adapter is a Go-only change. The macOS app and web dashboard look up name and icon from the registry — the five Swift <adapter>SVG functions and two switch statements in SessionState.swift are gone. Web dashboard renders adapter SVGs via <img src="data:image/svg+xml;base64,..."> so the browser image-loading sandbox blocks scripts even if the daemon binary is tampered with. AgentRegistry is @MainActor-isolated for Swift 6 strict-concurrency cleanliness. Closes #260

Docs

  • Adapter interfaces documented with exact Go signatures (#292) — site/docs/adapters.html gains an "Adapter Interfaces" section with the actual agents.Config, tailer.TranscriptParser (plus the optional RawLineParser, IdleFlusher, PendingContributor, ParserStateProvider hooks), agent.PIDDiscoverFunc, and agent.Event / inbound.AgentWatcher types, with file paths so readers can jump from doc to source
  • Release skill sweeps every docs page on each release (#293) — /ir:release Step 4b now enumerates site/docs/*.html and top-level READMEs dynamically (rather than from a hardcoded list) and walks each against the release diff so new pages cannot be silently missed

CI

  • Coverage workflow surfaces badge update failures (#281) — validates GIST_SECRET / COVERAGE_GIST_ID up front, captures the gist PATCH response so a non-2xx fails the job with the actual error body instead of dying silently inside curl -sf … > /dev/null, and adds connect/max-time + retry settings so transient 5xx and stalled handshakes don't hang the step

Tests

  • Replay: refresh stale opencode baseline-hello golden (#285) — golden was committed in #255 with a populated source_transcript field that the test zeros before comparison; regenerated via UPDATE_REPLAY_GOLDENS=1 to bring opencode in line with the other 4 adapters

v0.3.11 (2026-05-02)

Fixed

  • Serve stale LiteLLM cache instead of zeroing all costs (#275) — when the model-pricing cache was older than 24h, every cost calculation silently fell to zero (and omitempty dropped estimated_cost_usd from output entirely). Stale pricing is now served to non-daemon callers (replay tool, CLI, tests); IsCacheStale keeps its job of driving the daemon's background refresh
  • Aider: keep turn open across multiple > Tokens: lines (#274) — under --yes-always, aider auto-accepts file-add prompts and re-prompts the model within one user turn. The parser now treats > Tokens: as end-of-one-model-call (emitting assistant_message) and synthesizes the turn_done via an idle-flush hook, so sessions don't flip to ready mid-turn
  • Aider: emit turn_done on LLM-layer error (#273) — when aider prints a > litellm.BadRequestError: … blockquote without a > Tokens: line, the session no longer hangs in working forever
  • Tailer: drop bufio.Scanner cap so JSONL lines >2 MB don't wedge sessions (#271) — large transcript lines used to silently stop being processed once they exceeded the default 64 KB scanner buffer
  • Close 13 Code Scanning alerts (#266)
  • macOS: use brand off-flame for idle/empty state (#248)

Changed

  • Performance: shrink mobile payload, unblock render path (#272) — image optimization and CSS deferral for the marketing site; meaningful drop in mobile LCP/CLS
  • Daemon serves dashboard from disk, drops //go:embed (#267) — runtime walk-up search for platforms/web/index.html so the dashboard can be hot-edited in dev and shipped as a separate file in production bundles
  • Session history streams over WebSocket; bit-pack 60-bucket rings (#249) — replaces polling with live updates and a more compact wire format
  • Centralize per-adapter transcript extension (#251)

Distribution

  • onboard-agent covers claudecode/codex multi-turn + interrupted-turn (#269) — fixture matrix gains coverage for two real-world replay scenarios

Docs

  • Maturity-stage rubric and adapter onboarding section (#264)

Tests

  • Replay: zero source_transcript so goldens are worktree-portable (#250)

v0.3.10 (2026-04-27)

Fixed

  • Sweep zombie sessions on startup (#242) — Claude Code sessions no longer linger in the menu bar / UI after the underlying claude process has exited. The daemon now reaps stale entries on launch
  • Prune deleted sessions from apiGroups synchronously (#244) — when a session is removed, the overlay now updates immediately instead of showing a stale row until the debounced rehydrate lands. Walks agents, child subagents, and nested groups; drops project groups that become fully empty (gas-town excepted, since it renders even with no rigs)

Changed

  • Daemon: drop recycled-PID predicate from CleanupZombies — simpler, more reliable startup-cleanup path. Groundwork for #242

Tests

  • e2e regression test for the startup zombie sweep (#242)
  • 5 new SessionManagerApiGroupsTests cases covering top-level / child / parent removal, gas-town empty-rigs survival, and unknown-id no-op (#244)

v0.3.9 (2026-04-27)

Added

  • Aider adapter — first agent shipped through the new /ir:onboard-agent discovery flow. Includes parser, tmux-driven interactive driver, scenario fixtures, and a pinned trailing-? waiting-state contract. Aider sessions show alongside Claude Code, Codex, Pi, and Gas Town with the same three-state vocabulary
  • /ir:exec skill — issue-driven plan generation. Reads a GitHub issue and produces a structured implementation plan to start a fresh worktree
  • coverage-viewer dev webview (#222) — local web UI for the agent × scenario fixture matrix; shows which lifecycle events each adapter has recorded
  • tui capability + category taxonomy — adapters can declare tui as a discoverable capability so the canonical scenario matrix can target TUI-style agents
  • IRRLICHT_DEMO_MODE=1 — daemon flag that disables ProcessWatcher and per-adapter AgentWatchers so tools/seed-demo-sessions can stage screenshot scenarios without live processes leaking into the dropdown
  • Process discovery: CommandLineMatch + TranscriptFilename probes — wrapper-launched agents (e.g. invoked via pgrep -f) and per-CWD agents that write their transcript next to the project are now detected without a kqueue race
  • Transcript activity emission for CWD-resident transcripts — processlifecycle now emits transcript_activity events for agents whose transcript lives next to the working directory

Fixed

  • Tailer survives SendMessage tool across turn_done (#81) — Claude Code emits a turn_done between the assistant message and a follow-up SendMessage tool call; the tailer used to drop the second half. Sessions stay coherent across that boundary now
  • Mid-paragraph question detection + snippet trim (#236) — the waiting-state classifier used to require a question at the end of the assistant message. It now picks up questions mid-paragraph and trims the surfaced snippet for the menu bar block
  • Skip rhetorical Q&A pairs in question detection"Did X happen? Yes."-style self-answered questions no longer flip a session to waiting
  • Menu-bar button stays highlighted while panel is open (#224) — the NSPanel migration in 0.3.8 lost the button-pressed appearance; restored with explicit highlight-on-show / unhighlight-on-close
  • Tooltips restored after NSPanel migration (#218) — switched from SwiftUI .help() (silently dropped inside NSPanel) to an NSView-bridged tooltip modifier
  • History bars align with Context layout; modes renamed to "Min" with tooltips (#210)
  • Cost display drops cents at ≥$100 (#214, #215) — $132.41 was line-breaking the row; now renders $132
  • Stale session ledger files GC'd (#185) — the per-session ledger directory used to grow without bound; now cleaned alongside session expiry
  • Claude Code hook errors silenced when daemon is down (#221) — hooks no longer print noisy connection errors when irrlichd isn't running
  • Replay byte-identity test excludes bare events.jsonl — the bare events file is regenerated and shouldn't be part of the byte-identity check
  • Coverage-viewer rejects path-traversal in API + uses aider.Parser after stub removal

Changed

  • /ir:onboard-agent overhaul (#199, #200) — moved from a hardcoded scenario list to a features.json + replaydata layout with 3-subagent discovery, reasoned merge, and cross-agent feature widening. Adds Codex + Pi drivers and scenario columns; gastown gets its own orchestrator scenario axis. Onboarding aider through this flow validated the design end-to-end
  • Canonical scenario × adapter fixture matrix (#228, #231) — covers the 7 actionable scenario × adapter cells plus agent-question-pending for claudecode/codex/pi. Adds drive-pi-interactive.sh and two pi script-based fixtures
  • Dev scripts consolidated under tools/ — standalone tooling (HTTP viewers, fixture generators, homebrew-tap helper) now lives in top-level tools/ rather than core/cmd/
  • tools/homebrew-tap/update-cask.sh simplified — single source of truth for cask updates; the in-repo template and external tap repo are bumped from one script
  • Aider parser: single regex match per line; documented interface contract
  • e2e tests for processlifecycle crash, concurrent sessions, fswatcher (#205) — extracts IsCanonicalState and assertWatchersExited helpers

Distribution

  • Homebrew cask via own tap (#187) — brew tap ingo-eichhorst/irrlicht && brew install --cask irrlicht now resolves to the latest release. The cask is auto-bumped on each release via tools/homebrew-tap/update-cask.sh

Site

  • Landing page rewrite — restructured around a "first 30 seconds" pain → state → install flow, with stage-tag legend, install stats strip, and expanded "why" section. New menu-bar explainer screenshot and dark-forest backdrop
  • README restaged for first-30-seconds skim, adapters tagged by stage, explainer image promoted to hero banner
  • Design system reference added under tools/irrlicht-design-system/

v0.3.8 (2026-04-24)

Added

  • Menu bar rewrite: NSStatusItem + NSPanel — replaces SwiftUI's MenuBarExtra(.window) so content changes and panel resize land in the same runloop tick. Eliminates the one-frame background flash on group collapse/expand, and keeps the panel top pinned to the status item while height grows downward. Panel opens rightward with 10pt continuous-curve rounded corners; screen-edge clamp + notch fallback cover narrow right-edge displays
  • SessionListView column rebalance — panel 350 → 380pt; context bar 80 → 100pt; cost column 40 → 36pt. Branch column shrinks to 44pt when a subagent badge is present so the context bar's x-position is constant across rows. Two-pass FlowLayout aligns the 7pt task-progress circles with the taller "done/total" label

Fixed

  • Settings overlay background no longer transparent — the NSStatusItem + NSPanel rewrite uses a transparent panel so the rounded-corner clip works, which meant every SwiftUI branch had to paint its own background. SessionListView did, but the Settings pane didn't — the desktop wallpaper bled through. SettingsView now paints the same windowBackgroundColor, and a new SettingsViewTests pixel-opacity assertion samples the four corners + center to catch any future regression
  • "…" overflow indicator beyond 5 menu-bar groups — when more than five project groups are active, the menu bar icon shows a trailing "…" so you know the list is truncated rather than silently dropping the extras
  • Replay harness mirrors daemon parent-hold, permission-pending, and orphan promotion — sidecar-driven replay was skipping three pieces of daemon logic (parent-child hold when subagents are active, permission-pending overlay from PermissionRequest/PostToolUse hooks, and stale-sweep promotion of children whose transcripts go quiet). Extended-check now passes on the subagent and permission-hook fixtures without regenerating their sidecars

Changed

  • Core ARS composite 8.0 → 8.2 — large internal refactor: session_detector.go split into _activity/_helpers/_lifecycle/_subagent files; cmd/replay/main.go split into lifecycle/metrics/replay_sidecar/replay_transcript/extended_check/types/fixtures_test; cmd/irrlichd/main.go request handlers factored into handlers.go. Behavior unchanged
  • Unified agent registration via agents.Config — adding a new agent adapter is now one Config() constructor + one line in main.go's agentCfgs slice. PIDDiscoverFunc moved to domain/agent/ so Config can reference it without violating hexagonal layering; metrics adapter inverted so outbound no longer imports inbound
  • Shared constants between daemon and replayHookPermissionRequest/PostToolUse/PostToolUseFailure in the claudecode adapter, exported services.SubagentQuietWindow, and services.ForceReadyToWorkingReason — so hook names, the 30s stale-sweep window, and the force-ready reason string can't drift between the live classifier and the replay

Developer tooling

  • /ir:onboard-agent skill — produces a canonical scenario × adapter fixture matrix. Scenarios are defined once, agent-agnostically, with a requires: [capability] list; adapters declare Capabilities, and matrix cells fall out automatically. Unifies refresh, bootstrap, and new-agent-onboarding workflows
  • /ir:agent-landscape hardened against hallucinations — every agent in the landscape report is verified against the GitHub API before publishing; the skill refuses to emit entries it can't resolve

Site

  • Landing page: Terminals & IDEs column dropped — the click-to-focus host list grew past what fit cleanly in the features grid; replaced with a single "works with your terminal" sentence

v0.3.7 (2026-04-24)

Added

  • Agent history bar with 1s/10s/60s granularity — server-side pre-aggregates per-session state buckets (working/waiting/ready) under /api/v1/sessions/history. A single cycling mode button in the menubar switches between context display and the three history granularities
  • History persistence across daemon restarts — buffers saved to ~/.local/share/irrlicht/history.json every 60s and on shutdown, so the timeline survives a restart instead of resetting to empty
  • Waiting-state question block in the session row — when a session goes to waiting, the menubar row now shows the last assistant question (or the AskUserQuestion text) in an orange block beneath the row
  • Claude Code task list progressTaskCreate / TaskUpdate tool calls surface as a progress dot strip on the session card with a live "N/M" count
  • Click-to-focus across 17 terminal/IDE hosts — extending v0.3.6's launcher work to Zed, Rio, Tabby, WaveTerm, Alacritty, Nova, cmux, Kitty (socket-based) and the JetBrains family
  • Web UI timeline seeded from persisted daemon history — on page load the dashboard pulls /api/v1/sessions/history?granularity=1 and paints the last 60s immediately instead of starting empty

Fixed

  • Accurate per-model cost estimation across all adapters and restarts — the cost tracker now handles usage maps consistently for Claude Code, Codex, and the pi adapter, and survives daemon restarts without double-counting
  • Offline-at-startup: LiteLLM capacity fetch retries with backoff — the capacity table no longer stays empty when the laptop is offline at daemon boot
  • History timeline ticks flow right→left — new ticks land in the rightmost bucket and older ones shift left as time passes, in both the Swift HistoryBarView and the web dashboard canvas
  • Waiting-state detection survives long assistant messagesExtractAssistantText keeps the tail (with leading ellipsis) so a trailing question-mark still trips the waiting classifier; AskUserQuestion tool calls with no text block fall back to questions[0].question
  • Menubar popover: dynamic height + collapse state survives session refreshes — single ScrollView with .fixedSize(vertical:) lets the popover size to content up to 560pt; collapse state lifted onto SessionManager.collapsedGroupNames
  • Tasks: state resets on transcript rotation, stable across schema bump — in-memory task list cleared on file rotation; ledger schema bumped to v2 to force re-scan
  • Menubar tooltips work inside MenuBarExtra panels — switched from .help() to an NSView-bridged .tooltip(...) modifier
  • Launcher: ProcessRunner calls dispatched off the main thread — focus/open operations no longer stall the UI
  • Launcher: fullscreen Space handling — correctly raises windows on a fullscreen Space
  • Swift: Task model renamed to SessionTask so it stops shadowing Swift's built-in concurrency Task

Changed

  • Parsers split per-adapter — format-specific transcript parsers (Claude Code AskUserQuestion, TaskCreate/Update) live under the agent adapter packages instead of the shared tailer
  • Cost tracker usage-map extraction and ledger hot-path simplified; history granularity parsing cleaned up; $0 cost toggle stays visible so the timeframe cycle button remains reachable
  • Task parsing switch flattened; status constants lifted to one place

Tests

  • History persistence round-trip (save/load/missing-file/corrupt-file/version-mismatch) on HistoryTracker
  • AskUserQuestion text fallback (short message) and long-text tail storage on the Claude Code parser
  • Integration test for GET /api/v1/sessions/history (response shape + bad-granularity 400)
  • SessionMetrics.formattedCost two-decimal regression
  • New SessionRowView snapshot suite (waiting-question block + ContextBar token-count label); NSHostingView.appearance pinned to .darkAqua so snapshots are deterministic

v0.3.6 (2026-04-19)

Added

  • Jump to launching terminal or IDE on session click (#170) — Clicking a session row or delivered desktop notification brings the originating iTerm2 tab, Terminal.app window, or AXTitle-matched editor to the front. Host resolution walks the session's ppid chain; iTerm2 sessions are matched by UUID, Terminal tabs by tty, and generic apps by scoring window titles against the session CWD's deepest ancestor segment. The daemon captures the launcher env ($TERM_PROGRAM, $ITERM_SESSION_ID, tty) on first PID assignment so the lookup works even after the agent has been running for hours

Fixed

  • claude-code: gate PID negative filter on metadata mtime (#169) — After /clear, Claude leaves ~/.claude/sessions/<pid>.json pointing at the old session for up to two minutes. The detector no longer holds onto the dead session; once the new transcript's mtime exceeds the stale metadata, the PID is reassigned and the old session is cleaned up immediately
  • Web UI: render sessions on initial load (#167) — Initial-load handler now reads the bare-array response from GET /api/v1/sessions (previously expected a groups/orchestrator wrapper, so the dashboard stayed empty until the 30s rehydrator or a WebSocket delta arrived). Also removed three stale references to the removed /api/v1/orchestrators/gastown endpoint
  • macOS: restore project-group reorder chevrons (#172) — The up/down chevrons in top-level project group headers came back; ordering is derived from apiGroups directly so the UI state stays in sync with persisted order
  • CLI: irrlicht-ls runs from any subdirectory (#175) — go run now invoked from the repo root via --workspace

Changed

  • macOS Launcher split into per-host activators behind a HostActivator protocol — iTerm, Terminal.app, and an AXTitleMatchActivator for generic apps each live in their own file. Window raising uses the Accessibility API to raise a specific window rather than the frontmost one

Distribution / Dev

  • Persistent self-signed "Irrlicht Dev" identityir:test-mac now signs dev builds with a stable designated requirement so Accessibility and Automation TCC grants survive rebuilds. Run scripts/dev-sign-setup.sh once to install it

v0.3.5 (2026-04-19)

Added

  • Per-group cost display with switchable time frames (#83, #162) — Project group headers now surface day / week / month / year cost totals via a timeframe toggle instead of a single hard-coded window
  • curl | sh installer at irrlicht.io/install.sh — One-line install pulls the latest release zip, verifies the sha256, and registers the app with LaunchServices. Rerunning removes any previous install cleanly
  • Raw tab in the web UI — Inspect the /api/v1/sessions JSON payload live

Changed

  • Capacity: LiteLLM is now the single source of truth (#165) — The hand-maintained core/pkg/capacity/model-capacity.json table is removed; lookups go through a process-wide singleton that hot-reloads the LiteLLM cache when it's refreshed, so the daemon no longer needs a restart to see a new model. Fixes the 200K/1M flip on Claude Opus 4.7 and enables 1M context for Sonnet 4.6

Fixed

  • macOS: only notify on transitions from working (#161) — State-transition notifications fire on working → waiting and working → ready only. A waiting → ready transition no longer fires a redundant "ready" notification
  • Detector: synthetic waiting for collapsed user-blocking tools (#150, #160) — Sessions that ended on a user-blocking tool whose start was collapsed out of the transcript no longer linger as working
  • Replay: split sidecar timelines at process_exited boundaries (#144, #163) — /continue sessions with multiple process lifetimes no longer report spurious extra transitions
  • Detector: unstick subagents via parent task-notification (#134, #156)
  • Installer: preserve provenance and register with LaunchServices (#158) — First launch is no longer quarantined
  • Security: lock down irrlichd network exposure (#94, #155) — Binds to localhost only and rejects cross-origin WebSocket upgrades
  • Site: curl | sh install command wraps on narrow screens; hero spacing cleaned up
  • Tests: three stale SessionManagerTests updated (#166) — Match the current SessionState decoder and abbreviated RelativeDateTimeFormatter behavior

Distribution / CI

  • ARS badge workflow pinned to v0.0.9; GOPROXY=direct no longer required
  • ir:release skill guards against stale Swift binaries and missing SwiftPM resource bundles — the two root causes of the broken v0.3.4 bundle

v0.3.4 (2026-04-14)

Added

  • Gas Town full role support with recursive group nesting (#154) — First-class role registry surfaces all roles (mayor, deacon, witness, refinery, polecat, scribe, …) instead of treating them as ad-hoc strings, and codebase/rig groups can nest recursively for richer worker hierarchies
  • Desktop notifications on macOS state transitions (#147) — System notifications fire when sessions transition into waiting or ready, with a Settings toggle to opt out
  • Permission-pending state via Claude Code hooks (#108) — The daemon consumes PreToolUse / Notification hooks to detect modal permission prompts directly, instead of inferring them from transcript heuristics
  • Landscape page — 3-month growth trend lines, head-to-head comparison page, and alternative agent metrics

Fixed

  • Push: broadcast buffer increased (#152) — Bursty session updates no longer drop waiting transitions before clients can drain them
  • Tailer: preserve user-blocking tools across turn_done sweep (#148) — In-flight permission prompts (Bash, AskUserQuestion, …) are no longer cleared when the assistant briefly stops streaming
  • Daemon: prevent fswatcher event drops (#143) — Switched to a non-blocking event pump so user-blocking tool starts are never missed
  • macOS dev workflow builds a real .app bundle (#149) — Bundle identifier migrated to io.irrlicht.app so dev and release builds no longer share state
  • macOS: corrected dev fallback path for the bundled irrlichd binary — The menu bar app finds the daemon when running outside an installed bundle
  • Skill: ir:test-mac builds from the active worktree — When invoked from a worktree, builds from there instead of the main checkout

Changed

  • Unified replay tool (#141) — replay-session and replay-lifecycle consolidated into a single tool with subcommands, removing duplicated transcript-loading code

Docs

  • README problem section tightened to lead with concrete user pain rather than the architecture
  • CHANGELOG.md added at the repo root and wired into the ir:release skill so every release updates it

v0.3.3 (2026-04-11)

Fixed — Subagent lifecycle reliability

Several edge cases in parent/child session tracking caused subagents to linger in working after their parent's turn ended, or to disappear entirely when transcripts were still being written.

  • Subagent count unified at the adapter level (#132) — In-process and file-based counts are now reconciled in the adapter so the daemon sees a single authoritative value, eliminating drift between the two sources
  • Subagent quiet window bumped to 30s — API response gaps on long tool calls were tripping the previous window and prematurely marking subagents idle. 30s survives the observed gap distribution
  • Fast-forward orphaned subagents when parent turn is done — When the parent session finishes its turn but a child was left in an intermediate state, the child is now fast-forwarded to ready instead of hanging in working
  • Don't fast-forward subagents whose transcripts are still being written — Guards the fast-forward path so in-flight children aren't killed mid-stream
  • Re-evaluate parent when liveness sweep deletes last child — When the liveness sweep removes the final child session, the parent is immediately re-classified so its state reflects the new reality
  • FS watcher recursively watches newly-created subtrees — New project/session directories created after daemon startup are now observed without a restart

Added

  • Cost display toggle (#130) — The macOS menu bar cost readout can now be toggled in Settings and is off by default for users who prefer a lighter status line
  • Full session lifecycle recording & replay (#107, #138) — ir:test-mac runs can now record presession events, session events, and tail output into a sidecar file. The replay harness replays any recording byte-identically against the production tailer + classifier, and fails loudly on sidecar drift
  • Curated subagent fixtures — Real-world subagent transcripts (including an 11-background-agent fixture) are bundled with their lifecycle events so regressions in parent/child tracking are caught offline
  • Tailer FIFO replaced with id-keyed map (#117) — Open-tool tracking now uses a stable map keyed by tool use id instead of a FIFO, removing ordering assumptions that broke with parallel tool calls

Docs

  • README rewritten around real user pain and the competitive landscape of agent monitoring tools
  • GitHub-recognized community health files added: CODE_OF_CONDUCT.md, CONTRIBUTING.md, SECURITY.md, issue and PR templates

v0.3.2 (2026-04-07)

Fixed — Claude Code session state flicker (#102)

Four distinct bugs were causing long-running Claude Code sessions to bounce between working, waiting, and ready. All are fixed in this release.

  • Stale-tool timer disabled for Claude Code — The 15s heuristic tripped on every multi-second Bash invocation (builds, tests, find, network calls). Permission-pending modals and long-running Bash are indistinguishable in the JSONL stream, so the false-positive rate swamped the signal. Brings Claude Code in line with the Pi adapter's existing policy.
  • Tailer open-tool tracking collapsed to a single source of truth — Parallel integer counters and a name slice could desync on orphan tool_result events (from --continue resumes or compact replays), leaving metrics in an impossible has_open=false / open_tool_names=[Bash] state. Now derived solely from the name slice — orphans are idempotent no-ops.
  • ESC interrupts distinguished from benign tool errors — The classifier's cancellation rule fired on any tool_result.is_error=true (grep misses, failed builds, find on protected dirs), incorrectly flipping the session to ready. New LastWasUserInterrupt signal fires only on the literal [Request interrupted by user text marker.
  • stop_reason allow-list — Only null was treated as intermediate streaming; max_tokens and pause_turn passed through as terminal and tripped IsAgentDone() mid-turn. Flipped to an allow-list of end_turn/stop_sequence/refusal/tool_use — unknown future values default to "assume streaming".

Fixed

  • OpenCode agent registry corrected to anomalyco/opencode and marked as planned
  • Empty-state text updated from "Claude Code" to the generic "coding agent"

Added

  • Offline replay harness (core/cmd/replay) — Takes any Claude Code, Codex, or Pi transcript and feeds it through the production tailer + classifier using virtual time. A 500-hour session replays in under a second. Every transition is logged with reason, metric snapshot, and trigger cause
  • Regression fixtures under testdata/replay/<adapter>/ — Four Claude Code, one Codex, one Pi — all post-fix flicker-clean
  • Local session scanner (scripts/find-flicker-sessions.sh) — Ranks every transcript under ~/.claude/projects, ~/.codex/sessions, and ~/.pi/agent/sessions by flicker count, for harvesting new regressions
  • Adapter registry (core/adapters/inbound/agents/registry.go) — Single source of truth for adapter name → parser and adapter name → StatePolicy lookups, consumed by the replay harness

Docs

  • README updated with cost tracking, subagent visibility, and corrected state detection behavior
  • State machine docs: cancellation section rewritten to reflect the new LastWasUserInterrupt signal
  • API reference: session metrics schema updated (last_tool_result_was_error removed, last_was_user_interrupt added)

v0.3.1 (2026-04-06)

Added

  • Dynamic model capacity from LiteLLM — Context window sizes and pricing are now fetched from the LiteLLM API at daemon startup, removing hardcoded fallback assumptions
  • Token usage in debug mode — Debug mode now shows token usage metrics for all models, with percentage kept next to the context bar
  • Reorderable project groups — Project groups in the macOS popup and menu bar can now be reordered by the user

Changed

  • Removed client-side session expiry setting — Session expiry is now handled entirely by the daemon, simplifying the macOS app settings
  • Adapter-driven stale-tool waiting — Stale-tool timeout is now configured per adapter policy rather than globally

Fixed

  • Codex gpt-5.3 context window mapping corrected to 256K
  • Large appended transcript lines no longer skip events
  • Wrapped Codex transcript schema now parsed correctly
  • Pi compaction events treated as activity for working-state detection
  • Subagent tool tracking and debug row display preserved correctly
  • False ready state during tool calls and subagent work prevented
  • Session state flicker between tool calls — First streaming chunk of each new assistant message (new message ID, stop_reason=null) was misclassified as final, triggering false working→ready→working transitions on every tool call cycle
  • Stale-tool timeout increased from 5s to 15s to reduce false waiting transitions during long-running builds

Site

  • Agent landscape page with 63 tracked agents, 3-month trends, and deny list

v0.3.0 (2026-04-05)

Added

  • Permission-pending detection — Sessions with non-blocking tool calls awaiting user approval now transition to waiting state after a 5-second stale-tool timeout, correctly reflecting that the agent is blocked on permission
  • Last question display — When an agent asks a question and enters waiting state, the question text is captured and available via the API for display in the menu bar
  • Session state dots — Colored dots in the macOS menu bar and popover headers provide at-a-glance status for each session
  • Gas Town educational UI — Role hierarchy visualization and active tool display for Gas Town orchestrator sessions
  • Web dashboard redesign — Compact rows, DOM reconciliation for flicker-free updates, and timeline heatmap
  • Adapter-specific PID lifecycle — Codex and Pi sessions now have tailored PID discovery and process lifecycle handling alongside Claude Code
  • ir:agent-releases skill — Tracks upstream coding agent releases and reports changes that impact irrlicht monitoring

Changed

  • Per-adapter transcript parsers — Each agent adapter (Claude Code, Codex, Pi) now has its own transcript parser instead of a single shared parser, enabling format-specific handling and cleaner architecture
  • Shared CWD extraction logic extracted, dead code removed

Fixed

  • PID assignment race condition — Serialized PID assignment with state transitions to prevent concurrent sessions from claiming the same process
  • Orphan session cleanup after /clear — Sessions left behind when a user runs /clear in Claude Code are now properly cleaned up
  • Stuck working state from local commands — Local command events (shell escapes) no longer prevent sessions from transitioning out of working state
  • Daemon performance with many sessions — Resolved performance degradation when monitoring many concurrent sessions
  • Git root resolution for deleted worktree directories
  • Project name detection for non-git directories on activity refresh
  • Expanded click target for Settings and Quit buttons
  • URL-encoded dynamic badge URLs
  • Pi nested message parsing for role/stopReason/content fields
  • Pi PID discovery via command-line pattern matching
  • Include “assistant” in IsAgentDone fallback for turn detection

Site

  • Refined hero section — removed glow, shortened copy, cleaned up labels
  • Added counter.dev analytics to all pages

v0.2.5 (2026-04-05)

Added

  • Pi coding agent adapter — Monitor Pi sessions alongside Claude Code and Codex. Watches ~/.pi/agent/sessions/, parses JSONL v3 transcripts, detects state from stopReason, extracts tokens/cost/model, and supports Pi subagent linking via parentSession
  • Pi icon in macOS menu bar — Greek letter pi (π) icon distinguishes Pi sessions from other agents

v0.2.4 (2026-04-05)

Added

  • Will-o’-the-wisp app icon — Custom purple wisp flame icon for the macOS app bundle, replacing the default icon
  • Will-o’-the-wisp favicon — SVG and PNG favicons added to landing page and all documentation pages
  • SEO metadata — Open Graph, Twitter Card, and canonical URL tags across all site pages
  • CLAUDE.md development guide — Project conventions, build commands, and architecture overview for AI-assisted development

Fixed

  • Ready sessions no longer deleted while process is alive — Sessions in ready state are preserved as long as their Claude Code process is still running, preventing premature cleanup during idle periods

Distribution

  • ir:release skill — Automated release pipeline with DMG, PKG, universal binary builds, changelog updates, and GitHub release creation
  • DMG background asset for branded installer experience

v0.2.3 (2026-04-04)

Added

  • Subagent session lifecycle — Background and foreground subagents are detected as child sessions linked to their parent, with real-time state tracking and automatic cleanup when finished
  • Purple badge — Parent sessions display a live count of active subagents in a purple circle badge in the menu bar
  • Hierarchical dashboard APIGET /api/v1/sessions returns a DashboardResponse with Orchestrator → Group → Agent → Children hierarchy
  • PID discovery retry with backoff — Retries at 500ms, 1s, 2s intervals with CWD-based fallback when lsof fails
  • macOS app improvements — Debug mode (IRRLICHT_DEBUG=1), dev daemon support, clean shutdown, version display in UI, WebSocket keepalive with auto-reconnect
  • irrlicht-ls enhancements--format json output, --id prefix filter, hierarchical display with indented child sessions and agent count badges

Changed

  • Modular SessionDetector — Refactored into focused collaborators: StateClassifier (pure state transitions), MetadataEnricher (git/metrics), PIDManager (process lifecycle)
  • Unified processlifecycle package — Process scanner and process watcher merged into a single adapter
  • Cascade delete removes all child sessions when a parent session is deleted
  • Stale child sessions (transcript >2min old) cleaned up automatically in periodic sweep

Fixed

  • No false “waiting” state during tool execution — Only user-blocking tools (AskUserQuestion, ExitPlanMode) trigger waiting; Agent, Bash, Read etc. correctly show as working
  • Multi-instance session assignment — Running two Claude Code sessions in the same repo no longer causes PID conflicts; disambiguator ensures unique assignment
  • Subagent badge no longer persists after agent finishes
  • WebSocket connection state replaced isWatching flag with proper ConnectionState enum

v0.2.2 (2026-04-03)

Added

  • Embedded daemon in app bundle — Irrlicht.app bundles both SwiftUI UI and Go daemon; no separate services needed
  • DaemonManager — Auto-spawns, monitors, and restarts embedded daemon with exponential backoff
  • Session tooltips on hover in menu bar popover
  • DMG and PKG distribution artifacts

Fixed

  • Delete sessions immediately on process exit
  • Skip orphan transcript files on startup
  • Delete old session when /clear reuses same PID
  • Filter daemon self-PID from lsof
  • Ready-session TTL cleanup (30min default)

v0.2.0 (2026-04-03)

Added

  • OpenAI Codex adapter with recursive directory watching for sessions/YYYY/MM/DD/ structure
  • Per-model pricing data and EstimateCostUSD for cost tracking
  • Cost display in menu bar UI per session and per project group
  • Token breakdown tracking (input, output, cache read, cache creation)
  • Codex transcript event parsing (message, response_item, function_call, turn_context)
  • Codex model detection from ~/.codex/config.toml
  • Content character counting for token estimation when explicit counts unavailable
  • GPT-5.4 model capacity entry
  • Dark mode adaptive Codex SVG icon
  • Project name coloring by max context utilization (green/yellow/orange/red)
  • macOS .pkg installer bundling daemon + app + LaunchAgent

Changed

  • Filesystem watcher now recursively watches all subdirectories (supports deep nesting)
  • Git adapter resolves main repo root via --git-common-dir (worktree-aware)
  • Git adapter strips worktree- prefix from branch names
  • GetCWDFromTranscript now tail-reads last 32KB for latest CWD (supports mid-session worktree switches)
  • CWD/branch/project refreshed on every activity (not just on first detection)
  • Context utilization tests updated for 1M context windows
  • Token extraction refactored with shared extractUsage helper
  • Model config updated to v2.1.0 with pricing data for all models
  • build-release.sh now builds both daemon and Swift app, creates .pkg installer

Fixed

  • ESC cancellation detection using is_error flag on tool results
  • Parent session state adjustment based on sub-agent activity
  • Permission prompt detection as waiting state
  • Filesystem watcher race condition on directory creation

v0.1.0 (2026-03-20)

Added

  • Initial release
  • Claude Code session monitoring via filesystem watching
  • Three-state model: working, waiting, ready
  • macOS SwiftUI menu bar application
  • Go daemon (irrlichd) with HTTP API and WebSocket streaming
  • Context utilization tracking with pressure levels
  • Process exit detection via kqueue
  • Pre-session detection via process scanning
  • Git branch and project name resolution
  • mDNS/Bonjour service advertisement
  • Embedded web dashboard
  • irrlicht-ls CLI listing tool
  • Structured JSON logging with rotation
  • Gas Town orchestrator integration
  • Subagent detection and parent-child relationships

Format

This changelog follows the Keep a Changelog conventions. Versions use Semantic Versioning.

Types of changes:

  • Added — new features
  • Changed — changes in existing functionality
  • Deprecated — soon-to-be removed features
  • Removed — now removed features
  • Fixed — bug fixes
  • Security — vulnerability fixes