* Lots of work on ResyncController, issues with stale data, and
connection switching
* Add+Fix termination messages for shell controller
* Prune done/detached jobs on a timer
* Remove circular dependencies from wcore, use blockclose event in both
jobcontroller and blockcontroller instead of direct calls
* Fix concurrency bugs with job termination
* Send object update events when modifying the block datastructure in
jobcontroller
* Adds a Confirm on Quit dialog (and new config to disable it)
* New MacOS keybinding for Cmd:ArrowLeft/Cmd:ArrowRight to send Ctrl-A
and Ctrl-E respectively
* Fix Ctrl-V regression on windows to allow config setting to override
* Remove questionnaire in bug template
* Full featured tab indicators -- icon, color, priority, clear features.
Can be manipulated by new `wsh tabindicator` command
* Hook up BEL to new tab indicator system. BEL can now play a sound
and/or set an indicator (controlled by two new config options)
Lots of updates across all parts of the system to get this working. Big
changes to routing, streaming, connection management, etc.
* Persistent sessions behind a metadata flag for now
* New backlog queue in the router to prevent hanging
* Fix connection Close() issues that caused hangs when network was down
* Fix issue with random routeids (need to be generated fresh each time
the JWT is used and not fixed) so you can run multiple-wsh commands at
once
* Fix issue with domain sockets changing names across wave restarts
(added a symlink mechanism to resolve new names)
* ClientId caching in main server
* Quick reorder queue for input to prevent out of order delivery across
multiple hops
* Fix out-of-order event delivery in router (remove unnecessary go
routine creation)
* Environment testing and fix environment variables for remote jobs (get
from connserver, add to remote job starts)
* Add new ConnServerInit() remote method to call before marking
connection up
* TODO -- remote file transfer needs to be fixed to not create OOM
issues when transferring large files or directories
lots of stuff here.
introduces a streaming framework for the RPC system with flow control.
new authentication primitives for the RPC system. this is used to create
a persistent "job manager" process (via wsh) that can survive
disconnects. and then a jobcontroller in the main server that can
create, reconnect, and manage these new persistent jobs.
code is currently not actively hooked up to anything minus some new
debugging wsh commands, and a switch in the term block that lets me test
viewing the output.
after PRing this change the next steps are more testing and then
integrating this functionality into the product.
the PR spiraled and ended up being much larger than anticipated.
it is a refactor of wshrouter to have it track "links" as opposed to
just routes. this lets us simplify a lot of things when it comes to
multi-level routing.
* now the router can handle unauthenticated links directly, instead of a
weird limbo in wshproxy
* no more wshmultiproxy
* no more "authtoken" weirdness
* more straightforward handling in connserver (when using router option)
also adds more debugging, more logging, some windows fixes, other wsl
fixes
this fixes a rare case where a workspace tries to load a tab that
doesn't exist. this allows the UI to load even despite that condition.
in addition it fixes the "delete tab" path so, even if the tab doesn't
exist, it will get cleaned up correctly out of the workspace.
also updates ROADMAP.md, and fixes a node pruning bug on the FE, and
adds a new diff viewer that we can view the write_text_file and
edit_text_file diffs in. adds a backup file system that can be used to restore AI edited files back to their original states.
Documentation Updates (removing AI Widget Information / deprecation)
Hover effect on tool calls shows which widget is effected
Remove AI Widget from sidebar unless there is customized presets
Backend now provides blockid (if available) to frontend for tool calls
Don't allow tabs with active Wave AI sessions to get closed when we
close the last block. Have Cmd-W close Wave AI if it is focused (rather
than a random node). Also fixes some lurking bugs with the pinned tab
functionality (and adds some nice visual feedback when we try to close a
pinned tab).
Massive PR, over 13k LOC updated, 128 commits to implement the first pass at the new Wave AI panel. Two backend adapters (OpenAI and Anthropic), layout changes to support the panel, keyboard shortcuts, and a huge focus/layout change to integrate the panel seamlessly into the UI.
Also fixes some small issues found during the Wave AI journey (zoom fixes, documentation, more scss removal, circular dependency issues, settings, etc)
The current layout system uses a complex bidirectional atom architecture
that forces every layout change to round-trip through the backend
WaveObject, even though **the backend never reads this data** - it only
queues actions via `PendingBackendActions`. By switching to a "write
cache" pattern where local atoms are the source of truth and backend
writes are fire-and-forget, we can eliminate ~70% of the complexity
while maintaining full persistence.
----
Every layout change (split, close, focus, magnify) currently follows
this flow:
```
User action
↓
treeReducer() mutates layoutState
↓
layoutState.generation++ ← Only purpose: trigger the write
↓
Bidirectional atom setter (checks generation)
↓
Write to WaveObject {rootnode, focusednodeid, magnifiednodeid}
↓
WaveObject update notification
↓
Bidirectional atom getter runs
↓
ALL dependent atoms recalculate (every isFocused, etc.)
↓
React re-renders with updated state
```
---
## Proposed "Write Cache" Architecture
### Core Concept
```
User action
↓
Update LOCAL atom (immediate, synchronous)
↓
React re-renders (single tick, all atoms see new state)
↓
[async, fire-and-forget] Persist to WaveObject
```
### Key Principles
1. **Local atoms are source of truth** during runtime
2. **WaveObject is persistence layer** only (read on init, write async)
3. **Backend actions still work** via `PendingBackendActions`
4. **No generation tracking needed** (no need to trigger writes)
I did not mean to close the previous pr, anyway i tried to implement
what you suggested, the backend now does most of it
and DeleteWorkspace will return an unclaimed id and avoid closing the
window.
```go
const moveToNewWorkspace = await WorkspaceService.DeleteWorkspace(workspaceId)
console.log("delete-workspace done", workspaceId, ww?.waveWindowId);
if (ww?.workspaceId == workspaceId){
if ( workspaceList?.length > 1 ) {
await ww.switchWorkspace(moveToNewWorkspace)
} else {
console.log("delete-workspace closing window", workspaceId, ww?.waveWindowId);
ww.destroy();
}
}
});
```


---------
Co-authored-by: Evan Simkowitz <esimkowitz@users.noreply.github.com>
Fixes a bug where if you deleted all but one character in the workspace
name, you couldn't delete the final character. To fix this, I have made
the workspace editor save a separate entry from the backend. The backend
will also only update its DB value and notify the frontend if something
was actually edited. If you delete all the characters in the name and
don't put anything new in, though, the name will be whatever the last
character you had was, since the name of a saved workspace cannot be
empty.
Also moves icon and color definitions to the backend and makes it so the
new workspaces get created with a different icon color for each index.
If an ephemeral window has more than one tab, always create a new window
when switching workspaces
Also fixes workspace accelerators on macOS
Only create new tab in `CheckAndFixWindow` if no tabs or pinned tabs
exist
Update `resolvers.resolveTabNum` to account for pinned tabs
Remove obsolete and unused `wstore.DeleteTab`
Only show accelerators for first 9 workspaces in workspace app menu to
be consistent with other keybindings
Fix tabbar spacing to remove min size for drag right spacer, account for
workspace switcher button size
Fix updatebanner size calculations
Moves the wlayout package contents to wcore to prevent import cycles.
Moves the layout calls to other wcore functions instead of being handled
by the services. Removes redundant CreateTab in EnsureInitialData and
adds a isInitialLaunch flag to the CreateTab and CreateWorkspace
functions to ensure that the initial tab is pinned and does not have the
initial tab layout (since the starter layout gets applied later)
Adds a new app menu for creating a new workspace or switching to an
existing one. This required adding a new WPS event any time a workspace
gets updated, since the Electron app menus are static.
This also fixes a bug where closing a workspace could delete it if it
didn't have both a pinned and an unpinned tab.
implements `wsh run` command. lots of fixes (and new options) for command blocks. cleans up the UX/UI for command blocks. lots of bug fixes for blockcontrollers. other minor bug fixes.
also makes editor:* vars into settings override atoms.

This functions very similarly to VSCode's pinned tab feature. To pin a
tab, you can right-click on it and select "Pin tab" from the context
menu. Once pinned, a tab will be fixed to the left-most edge of the tab
bar, in order of pinning. Pinned tabs can be dragged around like any
others. If you drag an unpinned tab into the pinned tabs section (any
index less than the highest-index pinned tab), it will be pinned. If you
drag a pinned tab out of the pinned tab section, it will be unpinned.
Pinned tabs' close button is replaced with a persistent pin button,
which can be clicked to unpin them. This adds an extra barrier to
accidentally closing a pinned tab. They can still be closed from the
context menu.
When a tab was being deleted, for instance, the last block that got
deleted would cascade to delete its parent tab, even though the
`DeleteTab` function was already going to do this. This would produce DB
collisions that would put the app into a bad state. Now we pass a
`recursive` flag that is used to determine whether to perform the
recursive close of the parents.
Also updates `DeleteWorkspace` so that named workspaces will always be
deleted if they're empty, even if `force` is false
Updates `DeleteBlock` to close its parent tab if the tab has no more
blocks. This will also cascade to close the workspace if it no longer
has any tabs, same for window.
I had to move some block-related functionality around on the backend.
This fixes a bug where closing the active tab would clean up the closed
tab view before switching to an un-closed tab view, putting the window
into an unrecoverable state. This also moves around some of the CloseTab
logic so that it's more standardized and reduces unnecessary
frontend-backend comms and DB writes
There was a bug in main-server where we were returning Window from
EnsureInitialData and then overwriting the layout on the active tab. The
return values from this function are unclear. Some of the initial data
was being set in the wcore.EnsureInitialData and some was being set in
main, so I've moved all the logic into EnsureInitialData and now it just
returns error.