* feat(monitor): 优化磁盘IO展示与系统分区告警 [4030]
* fix(monitor): 修复磁盘监控评审问题 [4030]
* fix(monitor): align Partitions bars with Memory; hide bare / label
Label-above-bar for partition list; single-root `/` hides the label and
centers the bar like Memory/CPU (unchanged). Multi-disk keeps top align +
system alerts. Disk IO adds a spacer row to track usage labels.
Review crops under .github/review/ for PR discussion (drop before merge OK).
* fix(monitor): keep bare-/ align; multi-disk labels stay left
Address Chris density feedback: keep the single root label hidden and vertically centered with Memory, and restore side-by-side labels for multiple disks. Remove the Disk I/O spacer and update the review crops.
* chore(monitor): drop PR #4031 discussion review crops
---------
Co-authored-by: wendeng.dong <wendeng.dong@sailuntire.com>
Co-authored-by: 陈大猫 <16399091+binaricat@users.noreply.github.com>
Move infrequent Tool Access / MCP / privacy / HTTP setup into focused dialogs; keep status and clear actions on the main page. Rebased onto current main after locale/settings conflicts.
Codex CLEAN (gpt-6.1-terra). CI lint-and-test green. Focused privacy/MCP/guidance tests passed on rebase tip. No release.
Continuation of #3969 by @hexart (Joseph Hau). Preserves original commit recognition and adds spaces / %#→# coverage so idle macOS zsh terminals accept AI commands.
Codex CLEAN (gpt-6.1-terra) vs main. CI green. No release.
Co-authored-by: Joseph Hau <hexart@gmail.com>
Gate ModelSelector discovery on settled error; preserve explicit Refresh and config resets. Component regressions included.
Codex CLEAN (gpt-6.1-terra) vs main. CI green on tip. No release.
* fix(ai): keep dropped AI chats recoverable with dangling marking and /rebind
Closing a member terminal or dissolving a workspace used to clear the
chat's active pointer, making it invisible (scope rank 0) and
unrecoverable from the UI even though the record survived in storage.
- orphan cleanup now marks dropped chats `scopeDangling: true` and keeps
record + active pointer intact; the flag clears when the target
returns or the chat is rebound
- `/chats` lists dangling chats with a numbered inline affordance and
hint line
- new `/rebind <n>` panel action rewrites `scope` to the panel's current
scope, clears the marker and restores the active pointer
- i18n keys added for en / zh-CN / zh-TW / es / ru
Stacked on #4018. Refs #4015 (R2).
* fix(workspace): reuse existing workspace id when merging tabs with a workspace
createWorkspaceFromSessions silently no-opped when either merged tab
already belonged to a workspace (e.g. dragging a workspace pane onto a
standalone terminal), and the older dissolve->recreate behavior
regenerated the id that workspace-scoped AI chats bind to.
When one of the merged tabs has a workspace, the orphan tab now joins
that existing workspace (pane inserted next to the anchor session, tab
slot collapsed), so workspace-scoped chat bindings survive. Merging two
standalone tabs still creates a fresh workspace.
Stacked on #4018. Refs #4015 (R3).
* fix(ai): persist cleaned active map on dangling chat rebind
rebindSessionScope was updating React state to drop stale old-scope
pointers, then rebuilding localStorage from the pre-rebind snapshot and
resurrecting them. Persist the same cleaned map via rebindActiveSessionMap.
* fix(ai): persist rebind map inside setState; wire backup actions on terminal panel
- Write cleaned active-session map from inside the rebind updater so a
deferred React setState cannot skip snapshot/localStorage persistence.
- Pass exportSessionsBackup/importSessionsBackup through TerminalLayerSupport
and include them in AI_CHAT_SIDE_PANEL_AI_STATE_KEYS so history-drawer
backup buttons work on the normal terminal path.
* fix(ai): guard backup restore races and use app-wide live targets for /chats
- Skip restore when in-memory sessions already exist; apply snapshot,
storage write, and AI state emit atomically inside the empty-state path
so peer useAIState instances see the restore.
- Thread validAIScopeTargetIds into AIChatSidePanel as liveScopeTargetIds
so /chats does not mark chats bound to other open terminals as dangling.
* fix(ai): open rebound chat in panel view and refresh /chats listing
- rebindSessionScope also sets the panel view to the rebound session so an
existing draft/other chat does not keep hiding the recovery.
- After /rebind, rewrite the stored listing instead of rebuilding from the
stale render so the chat is no longer offered as dangling.
* fix(workspace): invert join split when dragged pane owns workspace; refresh /chats titles
- When merging a workspace-owned dragged pane onto a standalone tab, the
orphan is inserted — invert the drop position so the previewed side matches.
- Compare workspaces in AIChatSidePanel memo so /chats picks up renames.
* fix(ai): validate live sessions array on boot; mark imported orphans dangling
- Reject parseable non-array store values (e.g. {}) via readLiveSessionsArray
so boot restore is not blocked by a corrupt-but-truthy value.
- After manual import, re-run orphan cleanup with the last known live target
set so chats bound to closed scopes become dangling and /rebind-able.
* fix(ai): merge imported sessions before orphan cleanup
Apply import merge to the snapshot/ref synchronously, then run dangling
marking, so a deferred setState cannot let cleanup rewrite over the import.
* fix(ai): mark restored/imported orphans dangling even with no live targets
Restore and import do not change live terminals/workspaces, so the orphan
cleanup effect will not re-run. Always call cleanup against the last known
live target set after those paths — an empty set is a valid observed state
(nothing mounted), not an unavailable snapshot — so chats bound to missing
scopes become dangling and /chats can offer /rebind.
* fix(ai): apply backup restore synchronously before dangling cleanup
Do not gate restore + orphan marking on a setState updater having run.
A deferred updater left didRestore false, skipped cleanup, and could leave
restored chats with missing scopes unmarked and unrebindable from /chats.
* fix(ai): complete /rebind from a partial slash picker selection
Selecting Rebind after typing `/re` left the composer unchanged, so a
following number became `/re 1` and could be sent to the agent. Replace the
slash query with `/rebind ` the same way Rename does.
* fix(ai): replace hostIds on rebind; skip non-array backup generations
- Rebind builds a fresh destination scope (including hostIds from the
panel) instead of spreading the old scope, so history matching follows
the new terminal/workspace host.
- readLatestGeneration treats valid JSON that is not a session array as
unusable and continues to older generations, matching corrupt skips.
* fix(ai): revalidate dangling chat before /rebind from stale /chats listing
Skip rebind when the cached entry is deleted or already bound to a live
scope; drop stale listing entries so a second panel cannot steal the chat.
* fix(ai): reject malformed /rebind numbers
Require the entire trimmed argument to be digits so /rebind 1foo and
/rebind 1.5 show usage instead of silently targeting index 1.
---------
Co-authored-by: binaricat <binaricat@users.noreply.github.com>
Co-authored-by: 陈大猫 <16399091+binaricat@users.noreply.github.com>
* feat(ai): add /rename and /chats slash commands plus a session durability layer
Panel slash commands (#4015 feedback):
- /rename <title> renames the current chat via updateSessionTitle; the
command is intercepted in ChatInput's system-command branch and never
reaches the agent. Empty title shows a usage hint. The history drawer
gains an inline rename affordance.
- /chats renders a dismissible system-style note listing the panel's
visible chats (title / agent backend / workspace-or-terminal scope with
a dangling marker / relative last-active time; current chat marked).
Deliberately not named /resume: session.resume already means
flow-recovery-after-cancel. Listing logic is pure (aiChatsListing.ts)
so a future rebind action can attach per-entry actions.
- i18n keys added for en/es/ru/zh-CN/zh-TW.
Durability layer (additive; cleanup/eviction semantics untouched):
- Every successful store write schedules a debounced (~2s) mirror of the
exact persisted JSON to {userData}/ai-sessions-backup/ through a new
aiSessionsBackupBridge: atomic temp+rename writes, 5 rolling
generations, timestamped names.
- On boot, a missing/empty live store is restored from the newest
parseable backup generation. The asymmetry is deliberate: empty ->
restore, non-empty -> leave alone, so restored data can never
resurrect explicitly deleted chats.
- Manual export/import in the history drawer via real file dialogs
(fixed-path fallback where dialogs are unavailable); import merges by
id with newer updatedAt winning.
Tests: bridge rotation/corruption/rejection, restore decisions, merge
semantics, debounce throttle, mirror-on-write, slash interception and
/chats listing seams. eslint clean; tsc unchanged vs baseline.
* fix(ai): repair sessions backup IPC and restore/import guards
- Forward write/export payloads through preload unchanged so the main
bridge receives a string json (was double-wrapped → invalid-json).
- Restore from disk only when the live store is missing or corrupt;
treat a valid [] as an intentional clear. Flush the debounced mirror
on beforeunload so deletions land before quit.
- Require id + messages[] + finite updatedAt when parsing backup/
import entries so malformed objects cannot reach pruneSessionsForStorage.
Co-authored-by: Netcatty Maintainer <binaricat@users.noreply.github.com>
* fix(ai): complete /rename from partial slash and harden import shape
- Selecting Rename from the slash picker replaces the active slash token
with `/rename ` so a following title is intercepted, not sent to the agent.
- parseBackupSessions also requires title, agentId, and a scope object with
a string type before accepting an imported/backup entry.
Co-authored-by: Netcatty Maintainer <binaricat@users.noreply.github.com>
* fix(ai): validate backup messages and skip prune on synced restore
- Drop backup/import sessions whose messages lack id/role/timestamp.
- When cloud AI-session sync is enabled, restore parsed sessions without
pruneSessionsForStorage so local retention cannot truncate synced history.
Co-authored-by: Netcatty Maintainer <binaricat@users.noreply.github.com>
* fix(ai): export prepareRestoredSessions helper
Missed in the previous commit; required by useAIState boot restore and tests.
Co-authored-by: Netcatty Maintainer <binaricat@users.noreply.github.com>
* fix(ai): require message content on backup import; raise disk cap to 64MB
* fix(ai): normalize corrupt sessions store to [] before boot restore
* fix(ai): skip non-array backup generations when reading latest
* fix(ai): emit sessions change on backup restore; skip unusable gens
* fix(ai): treat empty backup gen as wipe; align gen usability with parser
* fix(ai): seed backup on boot for live store; report durable import count
* fix(ai): emit sessions change after backup import
* docs(ai): clarify sessions backup recovers in-profile store only
---------
Co-authored-by: binaricat <binaricat@users.noreply.github.com>
Merge global Agent host runtime + sandbox Chat sample on top of #4011.
Electron plugin smoke PASS at fb691318c (S0–S4; S5 BLOCKED-FIXTURE). Codex CLEAN. No release.
Empty commit only. Prior tip 57e682dc lint-and-test was cancelled by
runner shutdown mid-suite (no failing assertions). Re-runs via Actions
API are blocked for the fine-grained PAT; push retriggers PR checks.
* fix(sync): block convergent AI session shrink after merge
Related #4017 — convergent smartMerge could drop many AI sessions
post-merge (e.g. 46→7) and still report success; classic syncAll
already guarded this. Gate upload/apply with detectSuspiciousShrink.
* fix(sync): drop explicit any in convergent shrink override test
* Fix blocked chat shrink advancing sync baseline
Reuse the selection-token snapshot comparison for list and install, including a rejected stale or withdrawn selection. Preserve synchronous unavailable-source validation before the asynchronous list operation. The loaded-row regression rejects installation without downloading or executing a package.
Apply the exact six-file patch from implement_plugin_row_layout, based on 0c05a96fa1. Patch SHA-256: 5c58150fc3a7f72169b1a23ad1410cf37c51051b29bd6600bf30e848a9f366d6. Native visual acceptance remains pending.
A peer FIN can leave readyState closed while destroyed is still false.
Concurrent tool calls wrote into that socket, hit writeAfterFIN, and
failed with "TCP connection lost" instead of sharing one reconnect.
Treat that socket as down, and make later callers join the in-flight re-auth.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>