This release includes 3 breaking changes for platform teams planning a safe upgrade.
✓ No known CVEs patched in this version
Topics
+12 more
Affected surfaces
Summary
AI summaryUpdates Deferred, Migration Notes, and payload across a mixed release.
Full changelog
[2.8.4] — 2026-05-12 — Agent Notification Pipeline Restoration
사용자가 보고한 "Claude 가 작업을 끝내도 사이드바 dot, unread 배지, OS 토스트 — 3가지 신호 전부 안 뜬다" 결함을 root-cause 수준에서 복구. main 의 감지 레이어 (PTYBridge, AgentDetector, ActivityMonitor) 가 emit 하는 신호를 renderer UI 까지 연결하는 wiring 이 4 군데 끊겨 있었고, wmux production 인 daemon mode 에서는 PTYBridge 가 아예 우회되어 본 fix 가 0 효과 라는 더 큰 결함도 포함. 메인은 PR #30 (4 commits, +1579/-141, 29 files) 이고, 같은 릴리즈에 두 개의 다른 PR — #28 (@dev-minggyu, workspace drag reorder 복구 — 외부 기여 첫 컨트리뷰션) 과 #29 (multiview sticky group + MiniSidebar feature parity) — 도 함께 ship 됐다.
Fixed
- Workspace 드래그 정렬이 동작하지 않던 결함 (#28, @dev-minggyu — 외부 기여 첫 컨트리뷰션) — 좌측 사이드바의 전역 파일-드롭 핸들러가 내부 워크스페이스 드래그 이벤트까지 OS 파일 드롭처럼 처리하면서
move드래그가 충돌해 정렬이 막혀 있었다. 신규src/shared/dragDrop.ts헬퍼가DataTransfer가 실제 OS 파일 드래그인지 판별, 전역 드롭 핸들러와 오버레이가 파일 드래그에만 반응하도록 제한. 내부text/plain드래그 회귀 테스트 21 라인 추가. - Multiview sticky group + MiniSidebar feature parity (#29) — 사용자가 보고한 multiview 3개 결함을 묶어 수정. (a) Ctrl-click 순서 무시되고 grid 가 항상 workspace 배열 순서로 렌더되던 결함 →
AppLayout이multiviewIds자체를 iterate 해서 Ctrl-click 순서 보존. (b) 그룹 밖 workspace 를 plain-click 하면 그룹이 통째로 사라지던 결함 →setActiveWorkspace가multiviewIdsclear 안 함 +activeWorkspaceId ∈ multiviewIds일 때만 grid 렌더 (그룹 외부 클릭 시엔 단일 view, 멤버 재클릭 시 grid 복구). (c) 접힌 사이드바 (MiniSidebar) 가 multiview indicator / drag-reorder / W1·W2 라벨 / unread 배지 / agent dot 전부 없던 결함 → 펼친 사이드바와 동일 기능 부여,AGENT_STATUS_ICON을Sidebar/agentStatusIcon.ts로 추출해 두 사이드바 lockstep. Codex review 가 잡은 reseed 결함 (stale 그룹에서 새 multiview 시작 시 Ctrl-click 무반응) 도 함께 수정. +5 multiview 회귀 테스트. - AgentDetector status event 가 아무에게도 listen 되지 않던 결함 —
src/main/pty/PTYBridge.ts:207가agentDetector.onCritical만 구독하고onEvent는 dead code. Claude/Codex/Aider 의 "esc to interrupt" / "shift+tab to cycle" / "Applied edit to" 같은 정확한 prompt 패턴은 감지되어 emit 되었지만 호출되는 콜백이 0 개라 사이드바 dot 이 영영 켜지지 않았다. PTYBridge 가onEvent도 구독하도록 추가,IPC.METADATA_UPDATE로agentStatus/agentNamebroadcast +sendNotification호출. IPC.NOTIFICATIONpayload shape 가 sender 마다 달라서 외부 RPC 알림이 깨지던 결함 —PTYBridge는(channel, ptyId, notification)3-arg,notify.rpc.ts는(channel, { title, body, type })1-arg. preloadnotification.onNew는 3-arg signature 라 RPC path 의 첫 인자가 ptyId 자리로 들어가 payload 가 silent 하게 깨졌다. 새sendNotificationutility (src/main/notification/sendNotification.ts) 가 단일(window, ptyId|null, payload)contract 로 통일.IPC.METADATA_UPDATE가 두 sender 사이에 shape 불일치였던 결함 —metadata.handler는(ptyId, data)2-arg,meta.rpc는(payload)1-arg 로 같은 채널에 송신. 한 path 가 정상 동작하는 동안 다른 path 가 silent 하게 깨졌다.MetadataUpdatePayload(src/shared/types.ts) 를 단일 discriminated payload 로 정의,broadcastMetadataUpdateutility 로 모든 sender 통일. meta.rpc 의{kind: 'status'|'progress'}discriminator 폐기, workspace-level field 로 직접 매핑.- WorkspaceMetadata.agentStatus 가 자동으로 'idle' 로 복귀하지 않던 결함 —
'waiting'/'complete'/'running'이 한 번 set 되면 lifecycle reset 없음. 사용자 입력 후 agent 가 다시 실행되어도 dot 은'waiting', PTY 가 죽어도 dot 은'running'으로 남는 거짓말 발생. ActivityMonitor 의 새onActive콜백이 burst 진입 시점에'running'설정,PTYBridge.onExit가'idle'broadcast,cleanupInstance도 dispose path 에서 동일하게 broadcast (idempotent). renderer 의AppLayout가 session restore 직후 모든 workspace 의 stale agentStatus 를 sanitize. - Daemon mode 에서 알림 wiring 이 통째로 빠져 있던 결함 (production blocker) — wmux 의 production normal 은 daemon mode. PTY output 은
DaemonPTYBridge를 통과하고PTYBridge는 우회된다.DaemonPTYBridge가 이미'agent'/'critical'/'idle'event 를 emit 하고 있었지만DaemonSessionManager는'idle'만 forward,daemon/index.ts는'activity.idle'만 broadcast,DaemonClient는'session.died'만 specific emit. 즉 local mode fix 만으로는 사용자 환경에서 0 효과. 신규DaemonNotificationRouter(src/main/notification/DaemonNotificationRouter.ts) 가 daemon broadcast event 5 종 (session:agent/active/critical/idle/died/destroyed) 을 listen 해서 PTYBridge 와 동일한 로직 실행.DaemonEventtype 에'activity.active'+'session.destroyed'추가,daemon/index.ts가 신규 type 모두 broadcast,DaemonClient가 specific emit. daemon 측AgentDetector의 dedup state 도 onActive burst 시점에 in-process 로 reset (main 에서 daemon process 의 detector 에 접근 불가하기 때문). - PTY echo / SIGWINCH redraw 가 false-positive idle 알림을 유발하던 결함 (사용자 발견) — 7-round review pipeline (CEO + Eng + Codex × 4 + Claude subagent) 가 catch 못 한 케이스. ActivityMonitor 는 byte count 휴리스틱이라 "agent task ending" 과 "외부 상태 변화로 인한 PTY redraw" 를 구분 못 함. (a) 사용자 keystroke 가 PTY echo 로 돌아와 active threshold 를 넘기고 잠시 멈추면 "Task may have finished" 가 사용자 입력 중에 발화. (b) workspace 전환 시
FitAddon.fit()→IPC.PTY_RESIZE→ SIGWINCH → TUI agent 의 full-screen redraw 가 active 진입 → 5s 후 idle timer 발화. 신규idleSuppression모듈 (src/main/notification/idleSuppression.ts) 이lastResizeAt/lastUserWriteAt을 per-ptyId 로 추적, 30 s window 내면 activity-fallback 알림 suppress. AgentDetector 의 precise event 는 gate 안 함 (정확한 신호이므로).pty.handler.ts의 4 path (write × 2 + resize × 2) 가markResize/markUserWrite호출. 사용자가 보고한 "타자 치는 중 알람" + "워크스페이스만 눌렀다가 다른 곳 가면 +1" 두 시나리오 모두 해결. - 사용자가 보고 있는 surface 에도 알림이 누적되던 결함 —
useNotificationListener가 active workspace 의 active surface 일치 여부 체크 없이 무조건addNotification+pushToast호출. 사용자가 직접 보고 있는 곳은 알림 의미 0 인데 unread 배지가 계속 올라갔다. 알림 발생 직전isActivePtySurface체크 → 일치하면 in-app surface (addNotification+pushToast) skip. OS toast 는ToastManager가 자체 focus gate 가지고 있어 변경 없음. - workspace 전환만으로는 unread 가 read 처리 되지 않던 결함 — 사용자 보고: "워크스페이스만 눌러서 들렀다가 다른 곳 가면 unread 가 +1." Pane click 만이 markRead 트리거였고 sidebar 의 workspace 타일 click 은 read 영향 0.
workspaceSlice.setActiveWorkspaceaction 이 해당 workspace 의 모든 unread 를 read 로 자동 처리하도록 변경.Array.isArray(state.notifications)가드로 workspaceSlice 단독 테스트 호환. - pushToast 가 사용자 toast 설정 무시하던 결함 —
useNotificationListener가 settings 의toastEnabled무시하고 매번 in-app overlay 띄움. 사용자가 "Toast notifications" 끄면 OS toast 만 suppress, in-app 은 그대로 표시되던 결함.state.toastEnabledgate 추가 (sound playback 패턴과 동일). - AgentDetector 의 Claude
esc to interrupt가 false-positive 'waiting' — 실제로는 "지금 response 가 진행 중, ESC 로 중단 가능" 힌트이지 idle 신호가 아니다. 패턴 제거. mid-turn 에 잘못된 알림 fire 차단. - AgentDetector enum 명명 불일치 —
AgentEvent.status: 'completed'vsWorkspaceMetadata.agentStatus: 'complete'.AgentStatusenum 으로 통일 (Aider 패턴'completed'→'complete'텍스트 변경 포함). 외부 consumer 없어 안전. - AgentDetector dedup 이 turn N+1 의 같은 prompt 를 영영 차단하던 결함 —
lastEmittedKey가 single global string 이라 한 번 emit 한 prompt 는 다시 emit 안 됨 → 사용자가 추가 입력해도 사이드바 dot 갱신 0.lastEmittedForMap 으로 per-(agent:status) 분리 +resetEmissionState()method 추가, ActivityMonitor 의 새 active burst 시점에 reset (turn boundary). local mode 는 PTYBridge 가 직접 호출, daemon mode 는DaemonPTYBridge.onActive콜백이 in-process 에서 호출. - AgentDetector 의 ANSI strip 이 private-mode prefix 를 못 잡던 결함 —
\x1b[?25h같은 cursor visibility 시퀀스 (?포함) 가[0-9;]*[a-zA-Z]regex 와 안 맞아clean에 잔존, gate 매칭 실패 가능.[0-9;?<=>]*[a-zA-Z@]로 확장. - AgentDetector 가 lone
\rredraw 를 한 라인으로 처리하던 결함 — Claude/Codex TUI footer 는 CR 단독으로 redraw.split(/\r?\n/)가 통째로 묶어 line-anchored regex 가 매칭 실패.split(/\r?\n|\r(?!\n)/)로 확장. - AgentDetector.onEvent/onCritical 이 unsubscribe 안 돌려주던 결함 —
void반환이라 PTY recycle 시마다 listener 누적. v2.7.2 의 PlaywrightEngine CDP 세션 누수와 동일 카테고리. unsubscribe 함수 반환으로 변경, PTYBridgecleanupInstance+ DaemonPTYBridgecleanup에서 호출. ActivityMonitor 의onActiveToIdle/onActive도 같은 패턴. - AgentDetector callback 내부 throw 가 후속 라인 감지를 죽이던 결함 — PTYBridge middleware 패턴과 일치시켜 onEvent/onActive 콜백 본문에 try/catch 가드 추가. 한 callback 의 실패가 PTY stream 전체를 죽이지 않게 격리.
AGENT_EVENT_SUPPRESSION_MS로 ActivityMonitor 의 fallback 알림 dedup — AgentDetector 가 precise event emit 직후 ActivityMonitor 가 또 idle 발화하면 같은 turn 에 알림 2 회. PTYBridge / DaemonNotificationRouter 가lastAgentEventAt추적, 10 s 이내면 fallback skip.notifyRPC 가 workspaceId 없이는 깨지던 결함 — preload signature 가ptyId: string강제,addNotification이surfaceId강제. RPC path 는 ptyId 가 없어 silent drop 되거나 type error. workspaceId optional 로 변경 (CLIwmux notifybackward compat 유지),Notification.surfaceIdoptional, useNotificationListener 가nullptyId 면 workspaceId 로 active surface resolve (or active workspace fallback).
Added
sendNotificationutility (src/main/notification/sendNotification.ts) — 모든IPC.NOTIFICATION송신의 단일 entry point. window null/destroyed 가드 +(ptyId | null, payload)시그니처 통일. PTYBridge 4 호출 지점 + notify.rpc + DaemonNotificationRouter 모두 import.broadcastMetadataUpdateutility (src/main/ipc/handlers/metadata.handler.ts) — 모든IPC.METADATA_UPDATE송신의 단일 entry point. MetadataUpdatePayload 단일 shape.idleSuppression모듈 (src/main/notification/idleSuppression.ts) — per-PTY resize/user-write 시점 추적. 30 s suppression window 로 ActivityMonitor 의 byte-count heuristic false-positive 차단.DaemonNotificationRouter(src/main/notification/DaemonNotificationRouter.ts) — daemon mode 에서 PTYBridge 의 알림 라우팅 역할 대체.DaemonClientevent 5 종 listen →sendNotification+broadcastMetadataUpdate+ toast.- AgentDetector 의 in-process API 확장 —
getActiveAgents()/getLastAgent()/resetEmissionState()public method 추가. PTYBridge 가 lastAgent name 을 onActive metadata 에 채워 넣을 수 있게. - 37 신규 unit test —
AgentDetector.test.ts(18, enum/unsubscribe/dedup/\rsplit/ANSI strip/getters/critical),ActivityMonitor.test.ts(+4, onActive cycle dedup),sendNotification.test.ts(4, null/destroyed/ptyId 분기),PTYBridge.notify.test.ts(5, METADATA_UPDATE + NOTIFICATION + try/catch + cleanup unsub),notify.rpc.test.ts(6, workspaceId optional + MCP path + type fallback + toast). IRON RULE 7 regression 중 6 cover, R7 (pushToast in renderer) 는 jsdom 필요해 manual.
Migration Notes
- 자동. 사용자 액션 불필요.
Notification.surfaceId를 optional 로 변경 —Pane.tsx의surfaceIds.has(n.surfaceId)에 undefined guard 추가됨. 다른 consumer 없음.AgentEvent.statusenum 변경 ('completed'→'complete') — wmux 내부에서 PTYBridgeonCritical만 consume 했고 onEvent 는 dead code 였으므로 외부 영향 없음.IPC.METADATA_UPDATEpayload shape 통일 — preloadmetadata.onUpdate시그니처가(payload)단일 인자로 변경. renderer 의useNotificationListener가 호환 처리. 외부 MCP / CLI consumer 영향 없음.notifyRPC 의workspaceId는 optional 신규 param. CLIwmux notify --title X --body Y는 그대로 동작. MCP 클라이언트가mcp.claimWorkspace의 workspaceId 를 함께 보내면 precise routing (active surface auto-select).
Deferred (follow-up issues)
DaemonNotificationRouterregression test suite — manual verification 으로 cover, daemon IPty pipeline mock 은 별도 작업.- session-restore sanitize regression test — session fixture builder 필요.
onExitelapsed=0 cosmetic (cleanupInstance 가 ptyCreatedAt 먼저 wipe 하는 path) — purely message-text, behavioural 영향 0.DaemonClient.removeAllListenerson disconnect — pre-existing, 본 PR 범위 외.TODOS.md에 cherry-picked deferral 추가: E3 (transient dot flash animation, P3), E4 (per-workspace notification mute, P2), E5 (tray icon unread badge — cross-platform, P2), Phase 2 Eureka (Claude Code stop-hook → OSC 9 BEL emit, P3).
Review Trail
| Pass | Reviewer | Findings | Status |
|---|---|---|---|
| Plan 1 | /plan-ceo-review | 5 proposals | SELECTIVE_EXPANSION, 2 accepted |
| Plan 1 | Codex round 1 | 10 | all addressed |
| Plan 1 | /plan-eng-review | 11, 1 critical | all addressed |
| Plan 1 | Codex round 2 | 8 | all addressed (daemon mode wiring 6 파일 추가) |
| Code 2 | Codex round 3 | 2 (P1+P2) | all addressed in 5aee27f |
| Code 3 | Codex round 4 | 3 (P2+P2+P3) | all addressed in cddd3bd |
| Code 3 | Claude subagent | 7 (P2+P2+P3×5) | 2 addressed, 5 deferred |
| Code 4 | 사용자 manual test | 2 (resize/typing FP) | addressed in 42f5bd3 |
7-round review pipeline 의 한계: AI review 가 PTY echo / SIGWINCH redraw 같은 runtime 동작 은 코드만 보고 모델링하기 어렵다. 사용자 manual test 가 마지막 안전망이 됐다는 점이 기록 가치 있음.
Breaking Changes
- `IPC.NOTIFICATION` payload shape change to a single unified contract (window, ptyId|null, payload).
- `IPC.METADATA_UPDATE` payload shape unified across senders via `MetadataUpdatePayload`.
- `Notification.surfaceId` field made optional; consumers must handle undefined values.
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
About Wmux
All releases →Related context
Beta — feedback welcome: [email protected]