Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 78 additions & 55 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,59 +6,82 @@ Want input on the roadmap? Join the discussion on [Discord](https://discord.gg/X

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

## v0.11.0

Released on 1/25/25

- ✅ File/Directory Preview improvements
- ✅ Reworked fileshare layer running over RPC
- ✅ Expanded URI types supported by `wsh file ...`
- ✅ EC-TIME timeout when transferring large files
- ✅ Fixes for reducing 2FA requests on connect
- ✅ WebLinks in the terminal working again
- ✅ Search in Web Views
- ✅ Search in the Terminal
- ✅ Custom init files for widgets and terminal blocks
- ✅ Multi-Input between terminal blocks on the same tab
- ✅ Gemini AI support
- ✅ Various Connection Bugs + Improvements
- ✅ More Connection Config Options

## v0.11.1

Targeting 1/31/25

- 🔧 Reduce main-line 2FA requests to 1 per connection
- 🔧 Remote S3 bucket browsing (directory + files)
- 🔷 Drag & drop between preview blocks
- 🔷 Drag into/out of a preview block from native file explorer
- 🔷 Wave Apps (Go SDK)
- 🔷 JSON schema support (basic)
- 🤞 Frontend Only Widgets, React + Babel Transpiling in an iframe/webview

## v0.12

Targeting mid-February.

- 🔷 Import/Export Tab Layouts and Widgets
- 🔷 log viewer
- 🔷 binary viewer
- 🔷 New layout actions (splitting, replacing blocks)
- 🔷 Rewrite of window/tab system
- 🔷 Minimized / Non-Visible blocks
- 🔷 Custom keybindings to quickly switch / invoke built-in and custom widgets
- 🔷 More Drag & Drop support of files/URLs to create blocks
- 🔷 Tab Templates

## Planned (Unscheduled)

- 🔷 Customizable Keybindings
- 🔷 Launch widgets with custom keybindings
- 🔷 Re-assign system keybindings
## Current AI Capabilities

Wave Terminal's AI assistant is already powerful and continues to evolve. Here's what works today:

### AI Provider Support

- ✅ OpenAI (including gpt-5 and gpt-5-mini models)

### Context & Input

- ✅ Widget context integration - AI sees your open terminals, web views, and other widgets
- ✅ Image and document upload - Attach images and files to conversations
- ✅ Local file reading - Read text files and directory listings on local machine
- ✅ Web search - Native web search capability for current information
- ✅ Shell integration awareness - AI understands terminal state (shell, version, OS, etc.)

### Widget Interaction Tools

- ✅ Widget screenshots - Capture visual state of any widget
- ✅ Terminal scrollback access - Read terminal history and output
- ✅ Web navigation - Control browser widgets

## ROADMAP Enhanced AI Capabilities

### AI Configuration & Flexibility

- 🔷 BYOK (Bring Your Own Key) - Use your own API keys for any supported provider
- 🔧 Enhanced provider configuration options
- 🔷 Context (add markdown files to give persistent system context)

### Expanded Provider Support

Top priorities are Claude (for better coding support), and the OpenAI Completions API which will allow us to interface with
many more local/open models.

- 🔷 Anthropic Claude - Full integration with extended thinking and tool use
- 🔷 OpenAI Completions API - Support for older model formats
- 🤞 Google Gemini - Complete integration
- 🤞 Local AI agents - Run AI models locally on your machine

### Advanced AI Tools

#### File Operations

- 🔧 AI file writing with intelligent diff previews
- 🔧 Rollback support for AI-made changes
- 🔷 Multi-file editing workflows
- 🔷 Safe file modification patterns

#### Terminal Command Execution

- 🔧 Execute commands directly from AI
- 🔧 Intelligent terminal state detection
- 🔧 Command result capture and parsing

### Remote & Advanced Capabilities

- 🔷 Remote file operations - Read and write files on SSH connections
- 🔷 Custom AI-powered widgets (Tsunami framework)
- 🔷 AI Can spawn Wave Blocks
- 🔷 Drag&Drop from Preview Widgets to Wave AI

### Wave AI Widget Builder

- 🔷 Visual builder for creating custom AI-powered widgets
- 🔷 Template library for common AI workflows
- 🔷 Rapid prototyping and iteration tools

## Other Platform & UX Improvements (Non AI)

- 🔷 Import/Export tab layouts and widgets
- 🔧 Enhanced layout actions (splitting, replacing blocks)
- 🔷 Extended drag & drop for files/URLs
- 🔷 Tab templates for quick workspace setup
- 🔷 Advanced keybinding customization
- 🔷 Widget launch shortcuts
- 🔷 System keybinding reassignment
- 🔷 Command Palette
- 🔷 AI Context
- 🔷 Monaco Theming
- 🔷 File system watching for Preview
- 🔷 File system watching for drag and drop
- 🤞 Explore VSCode Extension Compatibility with standalone Monaco Editor (language servers)
- 🤞 VSCode File Icons in Preview
- 🔷 Monaco Editor theming
21 changes: 21 additions & 0 deletions cmd/server/main-server.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/wavetermdev/waveterm/pkg/authkey"
"github.com/wavetermdev/waveterm/pkg/blockcontroller"
"github.com/wavetermdev/waveterm/pkg/blocklogger"
"github.com/wavetermdev/waveterm/pkg/filebackup"
"github.com/wavetermdev/waveterm/pkg/filestore"
"github.com/wavetermdev/waveterm/pkg/panichandler"
"github.com/wavetermdev/waveterm/pkg/remote/conncontroller"
Expand Down Expand Up @@ -51,6 +52,8 @@ const TelemetryTick = 2 * time.Minute
const TelemetryInterval = 4 * time.Hour
const TelemetryInitialCountsWait = 5 * time.Second
const TelemetryCountsInterval = 1 * time.Hour
const BackupCleanupTick = 2 * time.Minute
const BackupCleanupInterval = 4 * time.Hour

var shutdownOnce sync.Once

Expand Down Expand Up @@ -114,6 +117,23 @@ func telemetryLoop() {
}
}

func backupCleanupLoop() {
defer func() {
panichandler.PanicHandler("backupCleanupLoop", recover())
}()
var nextCleanup int64
for {
if time.Now().Unix() > nextCleanup {
nextCleanup = time.Now().Add(BackupCleanupInterval).Unix()
err := filebackup.CleanupOldBackups()
if err != nil {
log.Printf("error cleaning up old backups: %v\n", err)
}
}
time.Sleep(BackupCleanupTick)
}
}

func panicTelemetryHandler(panicName string) {
activity := wshrpc.ActivityUpdate{NumPanics: 1}
err := telemetry.UpdateActivity(context.Background(), activity)
Expand Down Expand Up @@ -413,6 +433,7 @@ func main() {
go stdinReadWatch()
go telemetryLoop()
go updateTelemetryCountsLoop()
go backupCleanupLoop()
go startupActivityUpdate(firstLaunch) // must be after startConfigWatcher()
blocklogger.InitBlockLogger()
go wavebase.GetSystemSummary() // get this cached (used in AI)
Expand Down
1 change: 1 addition & 0 deletions docs/docs/config.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ wsh editconfig
| editor:stickyscrollenabled | bool | enables monaco editor's stickyScroll feature (pinning headers of current context, e.g. class names, method names, etc.), defaults to false |
| editor:wordwrap | bool | set to true to enable word wrapping in the editor (defaults to false) |
| editor:fontsize | float64 | set the font size for the editor (defaults to 12px) |
| 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) |
| preview:showhiddenfiles | bool | set to false to disable showing hidden files in the directory preview (defaults to true) |
| markdown:fontsize | float64 | font size for the normal text when rendering markdown in preview. headers are scaled up from this size, (default 14px) |
| markdown:fixedfontsize | float64 | font size for the code blocks when rendering markdown in preview (default is 12px) |
Expand Down
40 changes: 29 additions & 11 deletions frontend/app/aipanel/aimessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,15 @@ import { WaveUIMessage, WaveUIMessagePart } from "./aitypes";
import { WaveAIModel } from "./waveai-model";

const AIThinking = memo(
({ message = "AI is thinking...", reasoningText }: { message?: string; reasoningText?: string }) => {
({
message = "AI is thinking...",
reasoningText,
isWaitingApproval = false,
}: {
message?: string;
reasoningText?: string;
isWaitingApproval?: boolean;
}) => {
const scrollRef = useRef<HTMLDivElement>(null);

useEffect(() => {
Expand All @@ -30,17 +38,21 @@ const AIThinking = memo(
return (
<div className="flex flex-col gap-1">
<div className="flex items-center gap-2">
<div className="animate-pulse flex items-center">
<i className="fa fa-circle text-[10px]"></i>
<i className="fa fa-circle text-[10px] mx-1"></i>
<i className="fa fa-circle text-[10px]"></i>
</div>
{isWaitingApproval ? (
<i className="fa fa-clock text-base text-yellow-500"></i>
) : (
<div className="animate-pulse flex items-center">
<i className="fa fa-circle text-[10px]"></i>
<i className="fa fa-circle text-[10px] mx-1"></i>
<i className="fa fa-circle text-[10px]"></i>
</div>
)}
{message && <span className="text-sm text-gray-400">{message}</span>}
</div>
{displayText && (
<div
ref={scrollRef}
className="text-sm text-gray-500 overflow-y-auto max-h-[2lh] max-w-[600px] pl-9"
className="text-sm text-gray-500 overflow-y-auto max-h-[3lh] max-w-[600px] pl-9"
>
{displayText}
</div>
Expand Down Expand Up @@ -172,7 +184,7 @@ const getThinkingMessage = (
parts: WaveUIMessagePart[],
isStreaming: boolean,
role: string
): { message: string; reasoningText?: string } | null => {
): { message: string; reasoningText?: string; isWaitingApproval?: boolean } | null => {
if (!isStreaming || role !== "assistant") {
return null;
}
Expand All @@ -182,7 +194,7 @@ const getThinkingMessage = (
);

if (hasPendingApprovals) {
return { message: "Waiting for Tool Approvals..." };
return { message: "Waiting for Tool Approvals...", isWaitingApproval: true };
}

const lastPart = parts[parts.length - 1];
Expand Down Expand Up @@ -214,7 +226,9 @@ export const AIMessage = memo(({ message, isStreaming }: AIMessageProps) => {
<div
className={cn(
"px-2 rounded-lg [&>*:first-child]:!mt-0",
message.role === "user" ? "py-2 bg-accent-800 text-white max-w-[calc(100%-20px)]" : null
message.role === "user"
? "py-2 bg-accent-800 text-white max-w-[calc(100%-20px)]"
: "min-w-[min(100%,500px)]"
)}
>
{displayParts.length === 0 && !isStreaming && !thinkingData ? (
Expand All @@ -232,7 +246,11 @@ export const AIMessage = memo(({ message, isStreaming }: AIMessageProps) => {
)}
{thinkingData != null && (
<div className="mt-2">
<AIThinking message={thinkingData.message} reasoningText={thinkingData.reasoningText} />
<AIThinking
message={thinkingData.message}
reasoningText={thinkingData.reasoningText}
isWaitingApproval={thinkingData.isWaitingApproval}
/>
</div>
)}
</>
Expand Down
Loading
Loading