Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 7 additions & 0 deletions .roo/rules/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ It has a TypeScript/React frontend and a Go backend. They talk together over `ws
- Import the "cn" function from "@/util/util" to do classname / clsx class merge (it uses twMerge underneath)
- For element variants use class-variance-authority
- Do NOT create private fields in classes (they are impossible to inspect)
- Use PascalCase for global consts at the top of files
- **Component Practices**:
- Make sure to add cursor-pointer to buttons/links and clickable items
- NEVER use cursor-help (it looks terrible)
Expand All @@ -48,6 +49,12 @@ It has a TypeScript/React frontend and a Go backend. They talk together over `ws
- _never_ use cursor-help, or cursor-not-allowed (it looks terrible)
- 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.

### RPC System

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.

For normal "server" RPCs (where a frontend client is calling the main server) you should implement the RPC call in `pkg/wshrpc/wshserver.go`.

### Code Generation

- **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`.
Expand Down
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