Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
3e0057d
Create VellumAssistantShared library target for code reuse
ashleeradka Feb 14, 2026
2c302c3
Restructure directory layout for multi-platform support
ashleeradka Feb 14, 2026
5ab4b1c
Address PR feedback: fix Resources directory and port overflow
ashleeradka Feb 14, 2026
f2aad9e
Resolve merge conflicts with main
ashleeradka Feb 14, 2026
0d392fc
Add missing public initializers and properties
ashleeradka Feb 14, 2026
b672007
Merge main into ios/shared-library-foundation
ashleeradka Feb 14, 2026
49b9aca
Fix access control and iOS compatibility issues in shared IPC layer
ashleeradka Feb 14, 2026
91d776d
Complete public API consistency and add platform safety guards
ashleeradka Feb 14, 2026
bd4985c
Fix SendError visibility and clarify VellumAssistantLib platform rest…
ashleeradka Feb 14, 2026
bc4ae95
Fix platform coverage consistency and remove fragile Resources workar…
ashleeradka Feb 14, 2026
9231d63
Fix @MainActor deinit data race in DaemonClient
ashleeradka Feb 14, 2026
610fe14
Fix build script clean paths and revise deinit approach
ashleeradka Feb 14, 2026
082e1e2
Add explicit Network framework linking to VellumAssistantShared
ashleeradka Feb 14, 2026
d66bc04
Merge main into ios/shared-library-foundation
ashleeradka Feb 14, 2026
46d67f3
Add clients/ directory README for code organization documentation
ashleeradka Feb 14, 2026
8fc0fce
Add imageData field to ToolResultMessage for protocol completeness
ashleeradka Feb 14, 2026
e3092be
Add missing public initializers to IPC message types
ashleeradka Feb 14, 2026
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
12 changes: 12 additions & 0 deletions clients/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.build/
build/
dist/
.swiftpm/
*.xcuserdata
xcuserdata/
DerivedData/
.dev/
.DS_Store
context.md
Local.xcconfig
daemon-bin/
23 changes: 23 additions & 0 deletions clients/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 23 additions & 5 deletions clients/macos/Package.swift → clients/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@ import PackageDescription
let package = Package(
name: "vellum-assistant",
platforms: [
.macOS(.v14)
.macOS(.v14),
.iOS(.v17)
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
],
products: [
.library(
name: "VellumAssistantLib",
targets: ["VellumAssistantLib"]
),
.library(
name: "VellumAssistantShared",
targets: ["VellumAssistantShared"]
),
.executable(
name: "vellum-assistant",
targets: ["vellum-assistant"]
Expand All @@ -21,10 +26,23 @@ let package = Package(
.package(url: "https://github.com/sparkle-project/Sparkle", from: "2.0.0"),
],
targets: [
.target(
name: "VellumAssistantShared",
dependencies: [],
path: "shared",
swiftSettings: [
.enableUpcomingFeature("BareSlashRegexLiterals")
],
linkerSettings: [
.linkedFramework("Network") // Required for DaemonClient (NWConnection)
]
),
// VellumAssistantLib: macOS-only target (links AppKit, ScreenCaptureKit, etc.)
// iOS apps should depend only on VellumAssistantShared, not this target.
.target(
name: "VellumAssistantLib",
dependencies: ["HotKey", "Sparkle"],
path: "vellum-assistant",
dependencies: ["VellumAssistantShared", "HotKey", "Sparkle"],
path: "macos/vellum-assistant",
exclude: ["Resources/Info.plist"],
resources: [
.process("Resources/Assets.xcassets"),
Expand All @@ -49,12 +67,12 @@ let package = Package(
.executableTarget(
name: "vellum-assistant",
dependencies: ["VellumAssistantLib"],
path: "vellum-assistant-app"
path: "macos/vellum-assistant-app"
),
.testTarget(
name: "vellum-assistantTests",
dependencies: ["VellumAssistantLib"],
path: "vellum-assistantTests"
path: "macos/vellum-assistantTests"
)
]
)
153 changes: 153 additions & 0 deletions clients/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Clients Directory

This directory contains native client applications for the Vellum Assistant, organized for code reuse between platforms.

## Structure

```
clients/
├── Package.swift # Multi-platform Swift Package Manager manifest
├── shared/ # VellumAssistantShared - cross-platform code
│ ├── IPC/ # Daemon communication (both macOS and iOS)
│ └── App/ # Shared app utilities
├── macos/ # macOS-specific code
│ ├── vellum-assistant/ # VellumAssistantLib - macOS app logic
│ ├── vellum-assistant-app/ # Executable entry point
│ ├── build.sh # Build script (wraps SPM → .app → codesign)
│ └── CLAUDE.md # Development guide for Claude Code
└── ios/ # (Future) iOS-specific code
└── vellum-assistant-ios/ # iOS app (planned in PR 4-6)
```

## Targets

### VellumAssistantShared (Library)
**Platforms**: macOS 14+, iOS 17+
**Purpose**: Platform-agnostic code shared between macOS and iOS apps

**Contains**:
- **IPC layer** (`DaemonClient`, `IPCMessages`) - Network communication with daemon
- macOS: Unix domain socket (`~/.vellum/vellum.sock`)
- iOS: TCP connection (configurable hostname:port)
- **Shared utilities** (signing, configuration)

**Dependencies**: None (only system frameworks: Network)

### VellumAssistantLib (Library)
**Platforms**: macOS 14+ only
**Purpose**: macOS application logic

**Contains**:
- UI (AppKit views, panels, overlays)
- Computer-use features (accessibility, screen capture, input injection)
- macOS-specific integrations (menu bar, hotkeys, voice input)

**Dependencies**: VellumAssistantShared, HotKey, Sparkle
**Frameworks**: AppKit, ScreenCaptureKit, ApplicationServices, Vision, Speech

**⚠️ iOS apps should NOT depend on this target** - it links macOS-only frameworks.

### vellum-assistant (Executable)
**Platforms**: macOS 14+
**Purpose**: Thin entry point for macOS app

**Contains**: Just `@main` app delegate setup
**Dependencies**: VellumAssistantLib

## Building

### macOS App
```bash
cd clients/macos
./build.sh # Build debug .app
./build.sh run # Build + launch
./build.sh release # Build release
./build.sh test # Run tests
./build.sh clean # Remove artifacts
```

The build script:
1. Runs `swift build` from `clients/macos/` (SPM finds `../Package.swift` automatically)
2. Packages binary into `dist/Vellum.app` bundle
3. Codesigns with ad-hoc signature (or release identity)

### iOS App (Future)
Planned for PR 4-6 of the iOS rollout. Will depend only on VellumAssistantShared.

## Code Reuse Strategy

**~45-50% code reuse** between macOS and iOS achieved through:

1. **Shared IPC layer** - Both platforms communicate with daemon (different transport)
2. **Shared design system** (PR 2) - Tokens and components with conditional compilation
3. **Shared ViewModels** (PR 3) - ChatViewModel, message models work on both platforms

**Platform-specific**:
- **UI frameworks**: AppKit (macOS) vs UIKit (iOS)
- **Computer-use**: AXUIElement + CGEvent (macOS only, sandboxing prevents on iOS)
- **Screen recording**: ScreenCaptureKit (macOS) vs ReplayKit (iOS)
- **App lifecycle**: NSStatusItem (macOS) vs UIScene (iOS)

## Migration from Single-Platform

This structure was introduced in PR #1821 (iOS shared library foundation). Before this:
- `clients/macos/Package.swift` - Single-platform package
- `clients/macos/vellum-assistant/IPC/` - macOS-only IPC code

After migration:
- `clients/Package.swift` - Multi-platform package
- `clients/shared/IPC/` - Cross-platform IPC code
- All 25+ IPC message types have `public` access and explicit `public init()`

## Development

### Adding Shared Code
1. Place platform-agnostic code in `clients/shared/`
2. Mark all types as `public` (cross-module access)
3. Add explicit `public init()` to all structs (memberwise inits are internal)
4. Use `#if os(macOS)` / `#elseif os(iOS)` for platform-specific code

### Adding macOS-Only Code
1. Place in `clients/macos/vellum-assistant/`
2. Import `VellumAssistantShared` for access to IPC types
3. Can use AppKit, ScreenCaptureKit, etc. freely

### Adding iOS Code (Future)
1. Place in `clients/ios/vellum-assistant-ios/`
2. Import `VellumAssistantShared` for IPC, design tokens, ViewModels
3. DO NOT import `VellumAssistantLib` (macOS-only)

## Known Limitations

### iOS TCP Connection
- Currently plaintext (no TLS)
- Safe for localhost development only
- TLS layer tracked for PR 11 (Daemon authentication)

### iOS Signing Operations
- iOS clients log errors when daemon sends signing requests
- Cannot send error responses (protocol limitation)
- Daemon should detect iOS clients and avoid sending these messages

### iOS Localhost Default
- iOS defaults to `localhost:8765` in UserDefaults
- Real device usage requires configuring daemon hostname
- PR 6 (iOS settings/onboarding) will provide UI for configuration

## Documentation

- **macOS development**: See `clients/macos/CLAUDE.md`
- **PR #1821**: [iOS shared library foundation](https://github.com/vellum-ai/vellum-assistant/pull/1821)
- **iOS rollout plan**: See `.private/plans/sharded-mapping-shannon.md` (13 PRs)

## Testing

```bash
cd clients/macos
./build.sh test # All SPM tests (both shared and macOS-specific)
```

Tests use mock implementations of protocols for dependency injection:
- `DaemonClientProtocol` → `MockDaemonClient`
- `AccessibilityTreeProviding` → `MockAccessibilityTree`
- `ScreenCaptureProviding` → `MockScreenCapture`
4 changes: 2 additions & 2 deletions clients/macos/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ case "$CMD" in
;;
clean)
echo "Cleaning..."
rm -rf "$SCRIPT_DIR/dist" "$SCRIPT_DIR/.build"
rm -rf "$SCRIPT_DIR/dist" "$SCRIPT_DIR/../.build"
echo "Done."
exit 0
;;
Expand All @@ -80,7 +80,7 @@ if [ "$CMD" = "release" ]; then
SWIFT_FLAGS="-c release"
# Force clean for release builds to prevent stale artifacts in production
echo "Release build: forcing clean to ensure no stale artifacts..."
rm -rf "$SCRIPT_DIR/dist" "$SCRIPT_DIR/.build"
rm -rf "$SCRIPT_DIR/dist" "$SCRIPT_DIR/../.build"
fi

# 1. Build with SPM
Expand Down
1 change: 1 addition & 0 deletions clients/macos/vellum-assistant/Ambient/AmbientAgent.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Foundation
import VellumAssistantShared
import AppKit
import Combine
import UserNotifications
Expand Down
1 change: 1 addition & 0 deletions clients/macos/vellum-assistant/App/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import AppKit
import VellumAssistantShared
import Combine
import CoreText
import HotKey
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Foundation
import VellumAssistantShared
import os

private let log = Logger(
Expand Down
1 change: 1 addition & 0 deletions clients/macos/vellum-assistant/ComputerUse/Session.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Foundation
import VellumAssistantShared
import CoreGraphics
import AppKit
import os
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Foundation
import VellumAssistantShared
import os

private let log = Logger(subsystem: Bundle.main.bundleIdentifier ?? "com.vellum.vellum-assistant", category: "TextSession")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import AppKit
import Foundation
import VellumAssistantShared

enum ChatRole: String {
case user
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SwiftUI
import VellumAssistantShared
import UniformTypeIdentifiers

struct ChatView: View {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Foundation
import VellumAssistantShared
import os
import UniformTypeIdentifiers
import AppKit
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SwiftUI
import VellumAssistantShared

/// Inline card widget for displaying structured information in chat.
/// Supports template-based rendering for specialized layouts (e.g. weather forecasts).
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SwiftUI
import VellumAssistantShared

/// Fallback view for unsupported inline surface types.
struct InlineFallbackChip: View {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SwiftUI
import VellumAssistantShared

/// Inline list widget for selectable items in chat.
struct InlineListWidget: View {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SwiftUI
import VellumAssistantShared

/// Routes an `InlineSurfaceData` to the correct inline widget view.
struct InlineSurfaceRouter: View {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SwiftUI
import VellumAssistantShared

/// Inline table widget with selectable rows and action support.
struct InlineTableWidget: View {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SwiftUI
import VellumAssistantShared

// MARK: - Data Model

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SwiftUI
import VellumAssistantShared

struct ToolCallChip: View {
let toolCall: ToolCallData
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SwiftUI
import VellumAssistantShared

struct ToolConfirmationBubble: View {
let confirmation: ToolConfirmationData
Expand Down Expand Up @@ -274,12 +275,12 @@ struct ToolConfirmationBubble: View {
riskLevel: "medium",
diff: nil,
allowlistOptions: [
.init(label: "git push", description: "This exact command", pattern: "git push"),
.init(label: "git *", description: "Any git command", pattern: "git *"),
ConfirmationRequestMessage.ConfirmationAllowlistOption(label: "git push", description: "This exact command", pattern: "git push"),
ConfirmationRequestMessage.ConfirmationAllowlistOption(label: "git *", description: "Any git command", pattern: "git *"),
],
scopeOptions: [
.init(label: "This project", scope: "/Users/test/project"),
.init(label: "Everywhere", scope: "everywhere"),
ConfirmationRequestMessage.ConfirmationScopeOption(label: "This project", scope: "/Users/test/project"),
ConfirmationRequestMessage.ConfirmationScopeOption(label: "Everywhere", scope: "everywhere"),
]
),
onAllow: {},
Expand Down Expand Up @@ -311,10 +312,10 @@ struct ToolConfirmationBubble: View {
riskLevel: "medium",
diff: nil,
allowlistOptions: [
.init(label: "npm install", description: "This exact command", pattern: "npm install"),
ConfirmationRequestMessage.ConfirmationAllowlistOption(label: "npm install", description: "This exact command", pattern: "npm install"),
],
scopeOptions: [
.init(label: "Everywhere", scope: "everywhere"),
ConfirmationRequestMessage.ConfirmationScopeOption(label: "Everywhere", scope: "everywhere"),
],
state: .approved
),
Expand Down
Loading