wta: refresh in session view reconciles session list against wt live panes#121
wta: refresh in session view reconciles session list against wt live panes#121DDKinger wants to merge 2 commits into
Conversation
F5 in the F2 sessions (/sessions) view now performs an on-demand
pane-liveness reconcile against WT, instead of just refetching
master's in-memory registry.
## Why
The push-based liveness path (helper-pipe disconnect →
`drop_sessions_for_helper` → `session_removed` broadcast)
misses cases where the agent CLI subprocess stays alive after its
WT pane closes — most visible with Gemini, which doesn't reliably
exit on stdin EOF when its pane is destroyed via Ctrl+Shift+W or
tab close. Without an active liveness check, the F2 row stays
stuck at Idle/Live until WT restarts.
A naive 'just refetch session/list' F5 (the previous commit on this
branch) doesn't help: master returns the same stale registry it
already has. The reconcile flow actually checks WT and demotes
rows whose pane is gone.
## Flow
`
Helper (F5 pressed)
→ enumerate WT panes via ShellManager (list_windows × tabs × panes)
→ send intellterm.wta/reconcile_liveness with the alive set +
enumerated_at_ms
Master
→ snapshot registry
→ for each row whose pane_session_id ∉ alive AND
last_activity_at_ms ≤ (enumerated_at_ms - 1s slack):
• remove from session_to_helper map
• remove from registry
• broadcast session_removed
→ broadcast sessions_changed (once, if any dropped)
→ return { dropped: u32 }
All helpers
→ session_removed → apply_master_session_ended → row → Ended
→ sessions_changed → schedule_agents_refetch → UI repaints
`
## Race guard
A session created concurrently with F5 enumeration would have a
pane that didn't exist when the helper snapshotted WT, so naively
master would falsely drop the brand-new row. The
`enumerated_at_ms` timestamp in the request lets master skip any
row whose `last_activity_at_ms` is newer than the snapshot
moment (with 1s slack for clock skew). Sessions with no activity
timestamp at all are also conservatively kept.
## All-or-nothing enumeration
If any wt_list_* call fails or returns malformed JSON, the helper
aborts without sending the request. A partial alive set would
falsely demote sessions whose pane lived in the unreachable subtree.
## Out of scope
- Master subscribing to WT `PaneClosed` events for push-based
liveness (the architectural root fix).
- Reconcile on the 5s tick (rejected: ongoing cost, WT UI thread
churn, masks the upstream bug).
## Code
- `session_registry.rs`: new INTELLTERM_METHOD_RECONCILE_LIVENESS
+ ReconcileLivenessParams / Response wire types + build/parse helpers.
- `master/mod.rs`: `handle_reconcile_liveness` (with
`RECONCILE_MIN_AGE_SLACK_MS` race guard), wired into ext_method.
- `protocol/acp/client.rs`: MasterExtRequest::ReconcileLiveness
variant, ShellManager threaded into dispatch_master_ext_request,
`enumerate_alive_panes` helper (all-or-nothing).
- `app.rs`: F5 handler now calls `schedule_reconcile_liveness`.
## Tests (10 new + 2 updated, 558 total)
- session_registry: 3 serde roundtrip tests
- master: 5 handler tests (drop stale, keep without pane binding,
race guard keeps fresh, keep no-ts, case-insensitive pane match)
- app: f5_in_agents_view_triggers_reconcile_liveness (replaces the
prior triggers_refetch test); f5_outside_agents_view_is_ignored
retained
Locale changes from prior commit preserved — 'F5 to refresh' hint
still accurately describes the F5 semantics (refresh, just smarter).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds an explicit, user-triggered liveness reconciliation path for the wta master session registry (F5 in the F2 sessions view), so stale “Idle/Live” rows can be demoted when the corresponding Windows Terminal pane has been closed but the agent CLI process remains alive.
Changes:
- Introduces a new ACP ext-method
intellterm.wta/reconcile_livenesswith request/response wire types. - Implements helper-side WT pane enumeration (window → tab → panes) and dispatches a reconcile request to master on F5 in Agents (F2) view; master drops stale pane-bound sessions with a race-guard timestamp check and broadcasts updates.
- Updates Agents view footer hint across locales to include “F5 to refresh”.
Reviewed changes
Copilot reviewed 93 out of 93 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/wta/src/session_registry.rs | Adds reconcile_liveness ext-method constant, wire types, request/response builders/parsers, and roundtrip tests. |
| tools/wta/src/protocol/acp/client.rs | Adds helper-side ReconcileLiveness request path and WT pane enumeration used by F5. |
| tools/wta/src/master/mod.rs | Adds master handler to reconcile registry against alive pane IDs with race guard + broadcasts. |
| tools/wta/src/app.rs | Wires F5 in Agents view to trigger ReconcileLiveness; adds app-level tests for key handling. |
| tools/wta/locales/af-ZA.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/am-ET.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/ar-SA.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/as-IN.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/az-Latn-AZ.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/bg-BG.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/bn-IN.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/bs-Latn-BA.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/ca-ES.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/ca-Es-VALENCIA.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/cs-CZ.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/cy-GB.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/da-DK.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/de-DE.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/el-GR.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/en-GB.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/en-US.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/es-ES.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/es-MX.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/et-EE.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/eu-ES.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/fa-IR.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/fi-FI.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/fil-PH.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/fr-CA.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/fr-FR.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/ga-IE.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/gd-gb.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/gl-ES.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/gu-IN.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/he-IL.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/hi-IN.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/hr-HR.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/hu-HU.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/hy-AM.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/id-ID.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/is-IS.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/it-IT.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/ja-JP.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/ka-GE.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/kk-KZ.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/km-KH.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/kn-IN.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/ko-KR.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/kok-IN.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/lb-LU.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/lo-LA.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/lt-LT.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/lv-LV.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/mi-NZ.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/mk-MK.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/ml-IN.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/mr-IN.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/ms-MY.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/mt-MT.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/nb-NO.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/ne-NP.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/nl-NL.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/nn-NO.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/or-IN.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/pa-IN.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/pl-PL.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/pt-BR.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/pt-PT.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/qps-ploc.yml | Adds F5 to Agents footer hint (pseudo-locale). |
| tools/wta/locales/qps-ploca.yml | Adds F5 to Agents footer hint (pseudo-locale). |
| tools/wta/locales/qps-plocm.yml | Adds F5 to Agents footer hint (pseudo-locale). |
| tools/wta/locales/quz-PE.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/ro-RO.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/ru-RU.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/sk-SK.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/sl-SI.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/sq-AL.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/sr-Cyrl-BA.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/sr-Cyrl-RS.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/sr-Latn-RS.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/sv-SE.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/ta-IN.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/te-IN.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/th-TH.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/tr-TR.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/tt-RU.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/ug-CN.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/uk-UA.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/ur-PK.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/uz-Latn-UZ.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/vi-VN.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/zh-CN.yml | Adds F5 to Agents footer hint. |
| tools/wta/locales/zh-TW.yml | Adds F5 to Agents footer hint. |
| let window_id = w | ||
| .get("window_id") | ||
| .and_then(|v| v.as_u64()) | ||
| .ok_or_else(|| anyhow::anyhow!("window missing window_id"))? | ||
| .to_string(); |
| let tab_id = t | ||
| .get("tab_id") | ||
| .and_then(|v| v.as_u64()) | ||
| .ok_or_else(|| anyhow::anyhow!("tab missing tab_id"))? | ||
| .to_string(); |
| for p in panes_arr { | ||
| if let Some(sid) = p.get("session_id").and_then(|v| v.as_str()) { | ||
| alive.push(sid.to_ascii_lowercase()); | ||
| } | ||
| } |
| # {Locked="Enter","Esc","↑","↓"} - key combinations, do not translate | ||
| agents.footer_hint: "[(↑ ↓ ţö ñåṽïğåţé • Enter ţö ĺåûñçĥ śéśśïöñ • Esc ţö éxïţ)!!!! !!!! !!!! !!!!]" | ||
| agents.footer_hint: "[(↑ ↓ ţö ñåṽïğåţé • Enter ţö ĺåûñçĥ śéśśïöñ • Esc ţö éxïţ • F5 ţö ŕéƒŕéśĥ)!!!! !!!! !!!! !!!! !!!!]" |
| # {Locked="Enter","Esc","↑","↓"} - key combinations, do not translate | ||
| agents.footer_hint: "[!!_(↑ ↓ to navigate • Enter to launch session • Esc to exit)_!!]" | ||
| agents.footer_hint: "[!!_(↑ ↓ to navigate • Enter to launch session • Esc to exit • F5 to refresh)_!!]" |
| # {Locked="Enter","Esc","↑","↓"} - key combinations, do not translate | ||
| agents.footer_hint: "[!! (↑_ ↓ to navigate • Enter to launch session • Esc to exit) !!]" | ||
| agents.footer_hint: "[!! (↑_ ↓ to navigate • Enter to launch session • Esc to exit • F5 to refresh) !!]" |
This comment has been minimized.
This comment has been minimized.
@check-spelling-bot Report
|
| ❌ Errors and Warnings | Count |
|---|---|
| 6 | |
| 54 | |
| ❌ forbidden-pattern | 13 |
| 1 | |
| 7 | |
| 1 |
See ❌ Event descriptions for more information.
These words are not needed and should be removed
Backgrounder Ccc cplusplus ctl Debian dotnet drv endptr EOFs evt Fullwidth gitlab hdr idl IME inbox intelligentterminal Ioctl KVM lbl lld lsb NONINFRINGEMENT notif oss outdir Podcast pri prioritization PSobject rcv segfault Signtool sourced SWP Tbl testname transitioning unk unparseable unregisters Virt VMs VTE webpage websites WTCLI xsiSome files were automatically ignored 🙈
These sample patterns would exclude them:
^\.dotnet\/\.dotnet\/TelemetryStorageService/
^\Q.dotnet/.dotnet/.workloadAdvertisingManifestSentinel10.0.200\E$
^\Q.dotnet/.dotnet/10.0.201.aspNetCertificateSentinel\E$
^\Q.dotnet/.dotnet/10.0.201.dotnetFirstUseSentinel\E$
^\Q.dotnet/.dotnet/10.0.201.toolpath.sentinel\E$
^\Qinstaller/bootstrap/target/.rustc_info.json\E$
^copilot-version\.err$
^copilot-version\.out$
You should consider excluding directory paths (e.g. (?:^|/)vendor/), filenames (e.g. (?:^|/)yarn\.lock$), or file extensions (e.g. \.gz$)
You should consider adding them to:
.github/actions/spelling/excludes.txt
File matching is via Perl regular expressions.
To check these files, more of their words need to be in the dictionary than not. You can use patterns.txt to exclude portions, add items to the dictionary (e.g. by adding them to allow.txt), or fix typos.
To update file exclusions and remove the previously acknowledged and now absent words, you could run the following commands
... in a clone of the git@github.com:microsoft/intelligent-terminal.git repository
on the dev/yuazha/f5-refresh-sessions branch (ℹ️ how do I use this?):
curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c/apply.pl' |
perl - 'https://github.com/microsoft/intelligent-terminal/actions/runs/26673780306/attempts/1' &&
git commit -m 'Update check-spelling metadata'Available 📚 dictionaries could cover words (expected and unrecognized) not in the 📘 dictionary
This includes both expected items (2063) from .github/actions/spelling/expect/alphabet.txt .github/actions/spelling/expect/expect.txt .github/actions/spelling/expect/web.txt
| Dictionary | Entries | Covers | Uniquely |
|---|---|---|---|
| cspell:csharp/csharp.txt | 32 | 2 | 2 |
| cspell:aws/aws.txt | 232 | 2 | 2 |
| cspell:fonts/fonts.txt | 536 | 1 | 1 |
Consider adding to the extra_dictionaries array (in the .github/actions/spelling/config.json file):
"cspell:csharp/csharp.txt",
"cspell:aws/aws.txt",
"cspell:fonts/fonts.txt",
To stop checking additional dictionaries, put (in the .github/actions/spelling/config.json file):
"check_extra_dictionaries": []Forbidden patterns 🙅 (8)
In order to address this, you could change the content to not match the forbidden patterns (comments before forbidden patterns may help explain why they're forbidden), add patterns for acceptable instances, or adjust the forbidden patterns themselves.
These forbidden patterns matched content:
Should be nonexistent
\b[Nn]o[nt][- ]existent\b
Should probably be Otherwise,
(?<=\. )Otherwise\s
Should be preexisting
[Pp]re[- ]existing
Complete sentences in parentheticals should not have a space before the period.
\s\.\)(?!.*\}\})
Should be ; otherwise or . Otherwise
https://study.com/learn/lesson/otherwise-in-a-sentence.html
, [Oo]therwise\b
Should be reentrant
[Rr]e[- ]entrant
Should be whether or not ...
(?i)\b(?:whe|ra)ther(?:\s\w+)+ or not\.
Should be WinGet
\bWinget\b
✏️ Contributor please read this
By default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later.
If the listed items are:
- ... misspelled, then please correct them instead of using the command.
- ... names, please add them to
.github/actions/spelling/allow/names.txt. - ... APIs, you can add them to a file in
.github/actions/spelling/allow/. - ... just things you're using, please add them to an appropriate file in
.github/actions/spelling/expect/. - ... tokens you only need in one place and shouldn't generally be used, you can add an item in an appropriate file in
.github/actions/spelling/patterns/.
See the README.md in each directory for more information.
🔬 You can test your commits without appending to a PR by creating a new branch with that extra change and pushing it to your fork. The check-spelling action will run in response to your push -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. 😉
If the flagged items are 🤯 false positives
If items relate to a ...
-
binary file (or some other file you wouldn't want to check at all).
Please add a file path to the
excludes.txtfile matching the containing file.File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.
^refers to the file's path from the root of the repository, so^README\.md$would exclude README.md (on whichever branch you're using). -
well-formed pattern.
If you can write a pattern that would match it,
try adding it to thepatterns.txtfile.Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.
Note that patterns can't match multiline strings.
Problem
The push-based liveness path (helper-pipe disconnect → master's
drop_sessions_for_helper→session_removedbroadcast) misses cases where the agent CLI subprocess stays alive after its WT pane closes — most visible with Gemini, which doesn''t reliably exit on stdin EOF when its pane is destroyed viaCtrl+Shift+Wor tab close. Without an active liveness check, the F2 (/sessions) row stays stuck at Idle/Live until WT restarts.A naive "F5 = refetch
session/list" approach doesn''t help: master returns the same stale registry it already has. This PR makes F5 actually check WT and demote rows whose pane is gone.Flow
Race guard
A session created concurrently with F5 enumeration would have a pane that didn''t exist when the helper snapshotted WT, so naively master would falsely drop the brand-new row. The
enumerated_at_mstimestamp in the request lets master skip any row whoselast_activity_at_msis newer than the snapshot moment (with 1s slack for clock skew). Sessions with no activity timestamp at all are also conservatively kept.All-or-nothing enumeration
If any
wt_list_*call fails or returns malformed JSON, the helper aborts without sending the request. A partial alive set would falsely demote sessions whose pane lived in the unreachable subtree.UX
Footer hint updated across all 89 locale files:
(↑ ↓ to navigate • Enter to launch session • Esc to exit • F5 to refresh)— preserves discoverability.Out of scope
PaneClosedevents for push-based liveness (the architectural root fix).Tests
558 total (+10 net new vs main):
session_registry: 3 serde roundtrip testsmaster: 5 handler tests (drop stale / keep without binding / race guard keeps fresh / keeps no-ts / case-insensitive pane match)app:f5_in_agents_view_triggers_reconcile_liveness+f5_outside_agents_view_is_ignored