You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: live activity push watcher, new session command, resume rename (#359)
* feat: live activity push watcher, new session command, resume rename
- Add fsnotify-based EventWatcher for push session updates (replaces polling)
- Add SessionTracker for PID tracking of launched sessions
- Add focus window support (Win32 SetForegroundWindow)
- Add new session launcher with + keybinding
- Add W keybinding to focus selected session terminal window
- Rename custom_command to resume_session_command with v1->v2 migration
- Add new_session_command config field and settings panel entry
- Add blinking dot and highlighted row for waiting sessions
- Show last event type and timestamp in session detail pane
- Update all docs (README, SECURITY, config.astro, features.astro)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 37403417-09bc-4600-8056-fbb5a2fb18bb
* feat: default new_session_command to 'copilot' and resume to 'copilot --resume'
- Change defaultNewSessionCommand from 'gh copilot' to 'copilot'
- Flip FindCLIBinary() to prefer 'copilot' over 'ghcs'
- Update README, spec doc, web config page with new defaults
- Add new_session_command to web config.astro options table
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 37403417-09bc-4600-8056-fbb5a2fb18bb
* fix: reject non-positive PIDs and make EventWatcher.Stop synchronous
Two cross-platform bugs surfaced by the new SessionTracker tests on Linux.
IsProcessAlive treated PID 0 as alive on Unix: syscall.Kill(0, 0) targets the
caller's own process group, so it returns nil. Both implementations now reject
non-positive PIDs up front. The existing coverage lived in a windows-only test
file, which is why CI never caught it; the tests move to process_test.go so
they run on every platform.
EventWatcher used one time.AfterFunc per session for debouncing, and Stop only
called Timer.Stop() without waiting. A timer that had already fired could
invoke onChange after the TUI closed eventWatchCh, panicking with "send on
closed channel" (reproduced in 3 of 4 internal/tui runs). Debouncing now uses a
dirty set drained by a single goroutine, and Stop waits on a WaitGroup, so no
callback can be in flight once Stop returns.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: dac50159-d2c8-4ade-920a-333a51fc9ce3
* fix: satisfy staticcheck and errcheck in live-activity code
LastSessionEvent built SessionEvent field-by-field from an identical struct
(staticcheck S1016); use a direct conversion instead. Also explicitly discard
the return values of the Win32 syscall wrappers in focus_windows.go, which
errcheck flags when linting on Windows.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: dac50159-d2c8-4ade-920a-333a51fc9ce3
* refactor: drop unused SessionTracker
SessionTracker was never populated outside its own tests. LaunchNewSession
always returns PID 0 because wt.exe spawns a detached child whose PID it
cannot recover, and the TUI discarded that value anyway, so HasLive was
always false and the focus path always fell through to the lock-file lookup.
Remove the type and its tests, and collapse handleFocusWindowKey onto
FindSessionPID, which is the mechanism that was actually doing the work. The
test plan is updated to map the focus cases onto the platform PID tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: dac50159-d2c8-4ade-920a-333a51fc9ce3
* fix: restore trailing debounce in EventWatcher
The rewritten debounce fired a fixed 50ms after the first change rather than
after changes stopped arriving, turning it into a throttle. A sustained burst
of appends therefore produced one callback per 50ms window instead of one per
quiet period, which is what TestEventWatcher_DebounceCollapses caught under
the race detector.
Reset the window on every new change, as the original per-session timers did,
and cap the total deferral at 500ms so a continuously active session still
refreshes instead of having its window extended forever.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: dac50159-d2c8-4ade-920a-333a51fc9ce3
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 37403417-09bc-4600-8056-fbb5a2fb18bb
Copilot-Session: dac50159-d2c8-4ade-920a-333a51fc9ce3
Copy file name to clipboardExpand all lines: README.md
+16-6Lines changed: 16 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -41,7 +41,7 @@ Dispatch reads your local Copilot CLI session store and presents every past sess
41
41
-**Session favorites** (`*`) — star sessions as favorites. Filter to show only favorites via the `!` status picker
42
42
-**Session tags** (`#`) — attach comma-separated tags to sessions and filter to a tag with the `tag:` search token
43
43
-**Session aliases** (`A`) — give a session a short, memorable alias and resume it from the CLI with `dispatch open <alias>` instead of the full session ID
44
-
-**Settings panel** (`,`) — 19 fields: Yolo Mode, Agent, Model, Launch Mode, Pane Direction, Terminal, Shell, Custom Command, Theme, Crash Recovery, Preview Position, Redact Secrets, Excluded Words, Auto Refresh, Notify On Waiting, and column toggles for Repo, Folder, Turns, and Host
44
+
-**Settings panel** (`,`) — 20 fields: Yolo Mode, Agent, Model, Launch Mode, Pane Direction, Terminal, Shell, Resume Session Command, New Session Command, Theme, Crash Recovery, Preview Position, Redact Secrets, Excluded Words, Auto Refresh, Notify On Waiting, and column toggles for Repo, Folder, Turns, and Host
45
45
-**Configurable list columns** (`,` settings) — choose which optional columns (repo, folder, turns, host) appear in the session list. Defaults show every column, and the session name and attention indicator are always visible
46
46
-**Shell picker** — auto-detects installed shells, modal picker when multiple available
47
47
-**5 built-in themes** — Dispatch Dark, Dispatch Light, Campbell, One Half Dark, One Half Light + custom via Windows Terminal JSON
|`model`| string |`""`| Pass `--model <name>` to Copilot CLI |
707
707
|`launch_mode`| string |`"tab"`| How to open sessions: `in-place`, `tab`, `window`, `pane`|
708
708
|`pane_direction`| string |`"auto"`| Split direction for pane mode: `auto`, `right`, `down`, `left`, `up` (see note below) |
709
-
|`custom_command`| string |`""`| Custom launch command (`{sessionId}` is replaced) |
709
+
|`resume_session_command`| string |`""`| Custom resume command (`{sessionId}` is replaced). Defaults to `copilot --resume`|
710
+
|`new_session_command`| string |`""`| Command to launch new sessions (`{cwd}` is replaced). Defaults to `copilot`|
710
711
|`excluded_dirs`| array |`[]`| Directory paths to hide from session list |
711
712
|`excluded_words`| array |`[]`| Comma-separated words; sessions containing any word are hidden |
712
713
|`attention_threshold`| string |`"15m"`| Duration after which an inactive running session is marked stale |
@@ -767,7 +768,8 @@ The split starts in the session's working directory (`-c`) and runs the resume c
767
768
"model": "",
768
769
"launch_mode": "tab",
769
770
"pane_direction": "auto",
770
-
"custom_command": "",
771
+
"resume_session_command": "",
772
+
"new_session_command": "",
771
773
"excluded_dirs": [],
772
774
"theme": "auto",
773
775
"workspace_recovery": true,
@@ -779,12 +781,20 @@ The split starts in the session's working directory (`-c`) and runs the resume c
779
781
}
780
782
```
781
783
782
-
### Custom Command
784
+
### Resume Session Command
783
785
784
-
Set `custom_command` to replace the default Copilot CLI launch entirely. Use `{sessionId}` as the placeholder. When set, Agent, Model, and Yolo Mode fields are ignored.
786
+
Set `resume_session_command` to replace the default Copilot CLI resume command. Use `{sessionId}` as the placeholder for the session to resume. When set, Agent, Model, and Yolo Mode fields are ignored.
Set `new_session_command` to customize the command used when launching a brand new session from dispatch (the `+` keybinding). Use `{cwd}` as the placeholder for the working directory. When empty, defaults to `copilot`.
<!-- Pipeline tracking (auto-managed, not part of product spec) -->
7
+
<!-- ## Pipeline Status -->
8
+
<!-- Phase: VERIFYING -->
9
+
10
+
# Live Activity Monitor
11
+
12
+
## Problem
13
+
14
+
Dispatch polls session attention status every 30 seconds (`attentionRefreshInterval`). This means state transitions (a session finishing AI work and waiting for user input) can take up to 30 seconds to appear. Users working across multiple sessions need instant feedback about which sessions need their attention, the ability to launch new sessions from dispatch (to track them), and a one-key action to switch focus to a running session's terminal.
15
+
16
+
## Solution
17
+
18
+
Four integrated capabilities:
19
+
20
+
1.**Event Watcher (push-based)**: Replace 30-second polling with filesystem notifications. Watch `~/.copilot/session-state/` for changes to `events.jsonl` and lock files. When a file changes, re-classify only that session and push the update to the TUI instantly.
21
+
22
+
2.**New Session Launch**: A configurable keybinding that starts a brand new Copilot CLI session in the selected session/folder's working directory. The launch command is configurable via `new_session_command` in settings.
23
+
24
+
3.**Session Process Tracking**: When dispatch launches (or resumes) a session, record the child process PID. This tracking persists so dispatch knows which running sessions it owns.
25
+
26
+
4.**Focus Session**: A keybinding that brings the terminal window of a tracked running session to the foreground. Uses the tracked PID to locate the owning window.
A new `EventWatcher` struct using `fsnotify` to monitor `~/.copilot/session-state/`. On file write events for `events.jsonl` or `inuse.*.lock`, it debounces (50ms), re-classifies the affected session, and fires a callback with the session ID and new status. The existing `attentionRefreshInterval` polling remains as a fallback (bumped to 120s) for edge cases where fsnotify misses events.
33
+
34
+
### New Session Launch
35
+
36
+
Config adds `NewSessionCommand string` (default: `"copilot"`). Template variables: `{cwd}`. Keybinding: `N` (uppercase, mnemonic: "New"). When triggered, dispatch runs the configured command in a new WT tab/pane at the selected folder's cwd.
37
+
38
+
### Session Process Tracking (`internal/data/sessiontrack.go`)
39
+
40
+
An in-memory map `sessionID -> TrackedSession{PID int, LaunchTime time.Time}` with periodic cleanup of dead PIDs. Not persisted to disk (PIDs are ephemeral; stale after reboot). Populated when `LaunchSession` or the new-session launch completes.
Given a PID, walk the process tree upward to find the terminal host window, then call `SetForegroundWindow`. Keybinding: `F` (uppercase, mnemonic: "Focus"). Only active when the selected session has a tracked live PID.
45
+
46
+
## Acceptance Criteria
47
+
48
+
1. When a session's `events.jsonl` changes, the attention dot updates within 200ms (not 30s)
49
+
2. Pressing `N` on a selected session/folder launches a new Copilot CLI session in that cwd
50
+
3. The `new_session_command` config setting controls what command is run for new sessions
51
+
4. Pressing `F` on a session with a tracked live process brings its terminal window to the foreground
52
+
5. The 30-second polling fallback still works when fsnotify is unavailable or misses events
53
+
6. No increase in idle CPU usage (fsnotify is kernel-driven, not busy-wait)
54
+
55
+
## Non-Goals
56
+
57
+
- Tab-level granularity within Windows Terminal (WT doesn't expose tab-focus APIs externally)
0 commit comments