Skip to content

Commit a19cb6f

Browse files
authored
Add Write File Tools to WaveAI (#2492)
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.
1 parent 5d4fa5b commit a19cb6f

43 files changed

Lines changed: 1781 additions & 213 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.roo/rules/rules.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ It has a TypeScript/React frontend and a Go backend. They talk together over `ws
3535
- Import the "cn" function from "@/util/util" to do classname / clsx class merge (it uses twMerge underneath)
3636
- For element variants use class-variance-authority
3737
- Do NOT create private fields in classes (they are impossible to inspect)
38+
- Use PascalCase for global consts at the top of files
3839
- **Component Practices**:
3940
- Make sure to add cursor-pointer to buttons/links and clickable items
4041
- NEVER use cursor-help (it looks terrible)
@@ -48,6 +49,12 @@ It has a TypeScript/React frontend and a Go backend. They talk together over `ws
4849
- _never_ use cursor-help, or cursor-not-allowed (it looks terrible)
4950
- We have custom CSS setup as well, so it is a hybrid system. For new code we prefer tailwind, and are working to migrate code to all use tailwind.
5051

52+
### RPC System
53+
54+
To define a new RPC call, add the new definition to `pkg/wshrpc/wshrpctypes.go` including any input/output data that is required. After modifying wshrpctypes.go run `task generate` to generate the client APIs.
55+
56+
For normal "server" RPCs (where a frontend client is calling the main server) you should implement the RPC call in `pkg/wshrpc/wshserver.go`.
57+
5158
### Code Generation
5259

5360
- **TypeScript Types**: TypeScript types are automatically generated from Go types. After modifying Go types in `pkg/wshrpc/wshrpctypes.go`, run `task generate` to update the TypeScript type definitions in `frontend/types/gotypes.d.ts`.

ROADMAP.md

Lines changed: 78 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -6,59 +6,82 @@ Want input on the roadmap? Join the discussion on [Discord](https://discord.gg/X
66

77
Legend: ✅ Done | 🔧 In Progress | 🔷 Planned | 🤞 Stretch Goal
88

9-
## v0.11.0
10-
11-
Released on 1/25/25
12-
13-
- ✅ File/Directory Preview improvements
14-
- ✅ Reworked fileshare layer running over RPC
15-
- ✅ Expanded URI types supported by `wsh file ...`
16-
- ✅ EC-TIME timeout when transferring large files
17-
- ✅ Fixes for reducing 2FA requests on connect
18-
- ✅ WebLinks in the terminal working again
19-
- ✅ Search in Web Views
20-
- ✅ Search in the Terminal
21-
- ✅ Custom init files for widgets and terminal blocks
22-
- ✅ Multi-Input between terminal blocks on the same tab
23-
- ✅ Gemini AI support
24-
- ✅ Various Connection Bugs + Improvements
25-
- ✅ More Connection Config Options
26-
27-
## v0.11.1
28-
29-
Targeting 1/31/25
30-
31-
- 🔧 Reduce main-line 2FA requests to 1 per connection
32-
- 🔧 Remote S3 bucket browsing (directory + files)
33-
- 🔷 Drag & drop between preview blocks
34-
- 🔷 Drag into/out of a preview block from native file explorer
35-
- 🔷 Wave Apps (Go SDK)
36-
- 🔷 JSON schema support (basic)
37-
- 🤞 Frontend Only Widgets, React + Babel Transpiling in an iframe/webview
38-
39-
## v0.12
40-
41-
Targeting mid-February.
42-
43-
- 🔷 Import/Export Tab Layouts and Widgets
44-
- 🔷 log viewer
45-
- 🔷 binary viewer
46-
- 🔷 New layout actions (splitting, replacing blocks)
47-
- 🔷 Rewrite of window/tab system
48-
- 🔷 Minimized / Non-Visible blocks
49-
- 🔷 Custom keybindings to quickly switch / invoke built-in and custom widgets
50-
- 🔷 More Drag & Drop support of files/URLs to create blocks
51-
- 🔷 Tab Templates
52-
53-
## Planned (Unscheduled)
54-
55-
- 🔷 Customizable Keybindings
56-
- 🔷 Launch widgets with custom keybindings
57-
- 🔷 Re-assign system keybindings
9+
## Current AI Capabilities
10+
11+
Wave Terminal's AI assistant is already powerful and continues to evolve. Here's what works today:
12+
13+
### AI Provider Support
14+
15+
- ✅ OpenAI (including gpt-5 and gpt-5-mini models)
16+
17+
### Context & Input
18+
19+
- ✅ Widget context integration - AI sees your open terminals, web views, and other widgets
20+
- ✅ Image and document upload - Attach images and files to conversations
21+
- ✅ Local file reading - Read text files and directory listings on local machine
22+
- ✅ Web search - Native web search capability for current information
23+
- ✅ Shell integration awareness - AI understands terminal state (shell, version, OS, etc.)
24+
25+
### Widget Interaction Tools
26+
27+
- ✅ Widget screenshots - Capture visual state of any widget
28+
- ✅ Terminal scrollback access - Read terminal history and output
29+
- ✅ Web navigation - Control browser widgets
30+
31+
## ROADMAP Enhanced AI Capabilities
32+
33+
### AI Configuration & Flexibility
34+
35+
- 🔷 BYOK (Bring Your Own Key) - Use your own API keys for any supported provider
36+
- 🔧 Enhanced provider configuration options
37+
- 🔷 Context (add markdown files to give persistent system context)
38+
39+
### Expanded Provider Support
40+
41+
Top priorities are Claude (for better coding support), and the OpenAI Completions API which will allow us to interface with
42+
many more local/open models.
43+
44+
- 🔷 Anthropic Claude - Full integration with extended thinking and tool use
45+
- 🔷 OpenAI Completions API - Support for older model formats
46+
- 🤞 Google Gemini - Complete integration
47+
- 🤞 Local AI agents - Run AI models locally on your machine
48+
49+
### Advanced AI Tools
50+
51+
#### File Operations
52+
53+
- 🔧 AI file writing with intelligent diff previews
54+
- 🔧 Rollback support for AI-made changes
55+
- 🔷 Multi-file editing workflows
56+
- 🔷 Safe file modification patterns
57+
58+
#### Terminal Command Execution
59+
60+
- 🔧 Execute commands directly from AI
61+
- 🔧 Intelligent terminal state detection
62+
- 🔧 Command result capture and parsing
63+
64+
### Remote & Advanced Capabilities
65+
66+
- 🔷 Remote file operations - Read and write files on SSH connections
67+
- 🔷 Custom AI-powered widgets (Tsunami framework)
68+
- 🔷 AI Can spawn Wave Blocks
69+
- 🔷 Drag&Drop from Preview Widgets to Wave AI
70+
71+
### Wave AI Widget Builder
72+
73+
- 🔷 Visual builder for creating custom AI-powered widgets
74+
- 🔷 Template library for common AI workflows
75+
- 🔷 Rapid prototyping and iteration tools
76+
77+
## Other Platform & UX Improvements (Non AI)
78+
79+
- 🔷 Import/Export tab layouts and widgets
80+
- 🔧 Enhanced layout actions (splitting, replacing blocks)
81+
- 🔷 Extended drag & drop for files/URLs
82+
- 🔷 Tab templates for quick workspace setup
83+
- 🔷 Advanced keybinding customization
84+
- 🔷 Widget launch shortcuts
85+
- 🔷 System keybinding reassignment
5886
- 🔷 Command Palette
59-
- 🔷 AI Context
60-
- 🔷 Monaco Theming
61-
- 🔷 File system watching for Preview
62-
- 🔷 File system watching for drag and drop
63-
- 🤞 Explore VSCode Extension Compatibility with standalone Monaco Editor (language servers)
64-
- 🤞 VSCode File Icons in Preview
87+
- 🔷 Monaco Editor theming

cmd/server/main-server.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/wavetermdev/waveterm/pkg/authkey"
1919
"github.com/wavetermdev/waveterm/pkg/blockcontroller"
2020
"github.com/wavetermdev/waveterm/pkg/blocklogger"
21+
"github.com/wavetermdev/waveterm/pkg/filebackup"
2122
"github.com/wavetermdev/waveterm/pkg/filestore"
2223
"github.com/wavetermdev/waveterm/pkg/panichandler"
2324
"github.com/wavetermdev/waveterm/pkg/remote/conncontroller"
@@ -55,6 +56,8 @@ const TelemetryTick = 2 * time.Minute
5556
const TelemetryInterval = 4 * time.Hour
5657
const TelemetryInitialCountsWait = 5 * time.Second
5758
const TelemetryCountsInterval = 1 * time.Hour
59+
const BackupCleanupTick = 2 * time.Minute
60+
const BackupCleanupInterval = 4 * time.Hour
5861

5962
var shutdownOnce sync.Once
6063

@@ -118,6 +121,23 @@ func telemetryLoop() {
118121
}
119122
}
120123

124+
func backupCleanupLoop() {
125+
defer func() {
126+
panichandler.PanicHandler("backupCleanupLoop", recover())
127+
}()
128+
var nextCleanup int64
129+
for {
130+
if time.Now().Unix() > nextCleanup {
131+
nextCleanup = time.Now().Add(BackupCleanupInterval).Unix()
132+
err := filebackup.CleanupOldBackups()
133+
if err != nil {
134+
log.Printf("error cleaning up old backups: %v\n", err)
135+
}
136+
}
137+
time.Sleep(BackupCleanupTick)
138+
}
139+
}
140+
121141
func panicTelemetryHandler(panicName string) {
122142
activity := wshrpc.ActivityUpdate{NumPanics: 1}
123143
err := telemetry.UpdateActivity(context.Background(), activity)
@@ -446,6 +466,7 @@ func main() {
446466
go stdinReadWatch()
447467
go telemetryLoop()
448468
go updateTelemetryCountsLoop()
469+
go backupCleanupLoop()
449470
go startupActivityUpdate(firstLaunch) // must be after startConfigWatcher()
450471
blocklogger.InitBlockLogger()
451472
go wavebase.GetSystemSummary() // get this cached (used in AI)

docs/docs/config.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ wsh editconfig
6464
| editor:stickyscrollenabled | bool | enables monaco editor's stickyScroll feature (pinning headers of current context, e.g. class names, method names, etc.), defaults to false |
6565
| editor:wordwrap | bool | set to true to enable word wrapping in the editor (defaults to false) |
6666
| editor:fontsize | float64 | set the font size for the editor (defaults to 12px) |
67+
| editor:inlinediff | bool | set to true to show diffs inline instead of side-by-side, false for side-by-side (defaults to undefined which uses Monaco's responsive behavior) |
6768
| preview:showhiddenfiles | bool | set to false to disable showing hidden files in the directory preview (defaults to true) |
6869
| markdown:fontsize | float64 | font size for the normal text when rendering markdown in preview. headers are scaled up from this size, (default 14px) |
6970
| markdown:fixedfontsize | float64 | font size for the code blocks when rendering markdown in preview (default is 12px) |

frontend/app/aipanel/aimessage.tsx

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,15 @@ import { WaveUIMessage, WaveUIMessagePart } from "./aitypes";
1111
import { WaveAIModel } from "./waveai-model";
1212

1313
const AIThinking = memo(
14-
({ message = "AI is thinking...", reasoningText }: { message?: string; reasoningText?: string }) => {
14+
({
15+
message = "AI is thinking...",
16+
reasoningText,
17+
isWaitingApproval = false,
18+
}: {
19+
message?: string;
20+
reasoningText?: string;
21+
isWaitingApproval?: boolean;
22+
}) => {
1523
const scrollRef = useRef<HTMLDivElement>(null);
1624

1725
useEffect(() => {
@@ -30,17 +38,21 @@ const AIThinking = memo(
3038
return (
3139
<div className="flex flex-col gap-1">
3240
<div className="flex items-center gap-2">
33-
<div className="animate-pulse flex items-center">
34-
<i className="fa fa-circle text-[10px]"></i>
35-
<i className="fa fa-circle text-[10px] mx-1"></i>
36-
<i className="fa fa-circle text-[10px]"></i>
37-
</div>
41+
{isWaitingApproval ? (
42+
<i className="fa fa-clock text-base text-yellow-500"></i>
43+
) : (
44+
<div className="animate-pulse flex items-center">
45+
<i className="fa fa-circle text-[10px]"></i>
46+
<i className="fa fa-circle text-[10px] mx-1"></i>
47+
<i className="fa fa-circle text-[10px]"></i>
48+
</div>
49+
)}
3850
{message && <span className="text-sm text-gray-400">{message}</span>}
3951
</div>
4052
{displayText && (
4153
<div
4254
ref={scrollRef}
43-
className="text-sm text-gray-500 overflow-y-auto max-h-[2lh] max-w-[600px] pl-9"
55+
className="text-sm text-gray-500 overflow-y-auto max-h-[3lh] max-w-[600px] pl-9"
4456
>
4557
{displayText}
4658
</div>
@@ -172,7 +184,7 @@ const getThinkingMessage = (
172184
parts: WaveUIMessagePart[],
173185
isStreaming: boolean,
174186
role: string
175-
): { message: string; reasoningText?: string } | null => {
187+
): { message: string; reasoningText?: string; isWaitingApproval?: boolean } | null => {
176188
if (!isStreaming || role !== "assistant") {
177189
return null;
178190
}
@@ -182,7 +194,7 @@ const getThinkingMessage = (
182194
);
183195

184196
if (hasPendingApprovals) {
185-
return { message: "Waiting for Tool Approvals..." };
197+
return { message: "Waiting for Tool Approvals...", isWaitingApproval: true };
186198
}
187199

188200
const lastPart = parts[parts.length - 1];
@@ -214,7 +226,9 @@ export const AIMessage = memo(({ message, isStreaming }: AIMessageProps) => {
214226
<div
215227
className={cn(
216228
"px-2 rounded-lg [&>*:first-child]:!mt-0",
217-
message.role === "user" ? "py-2 bg-accent-800 text-white max-w-[calc(100%-20px)]" : null
229+
message.role === "user"
230+
? "py-2 bg-accent-800 text-white max-w-[calc(100%-20px)]"
231+
: "min-w-[min(100%,500px)]"
218232
)}
219233
>
220234
{displayParts.length === 0 && !isStreaming && !thinkingData ? (
@@ -232,7 +246,11 @@ export const AIMessage = memo(({ message, isStreaming }: AIMessageProps) => {
232246
)}
233247
{thinkingData != null && (
234248
<div className="mt-2">
235-
<AIThinking message={thinkingData.message} reasoningText={thinkingData.reasoningText} />
249+
<AIThinking
250+
message={thinkingData.message}
251+
reasoningText={thinkingData.reasoningText}
252+
isWaitingApproval={thinkingData.isWaitingApproval}
253+
/>
236254
</div>
237255
)}
238256
</>

0 commit comments

Comments
 (0)