Skip to content

About

Extensible desktop code-agent platform built on the Pi runtime, with full-stack plugins and an AI-oriented development toolkit.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

 
 

Latest commit

 

History

4,634 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Mortise

License Contributor Covenant

Mortise is an independently maintained, user-extensible desktop Agent platform. It combines a multi-session Electron client, a headless/WebUI server, the Pi agent runtime, skills, automations, and extension-contributed UI.

This repository owns its release line and package identities. Mortise does not import updates from the repositories it was forked from, and it does not assume any hosted Mortise cloud service exists. Optional update, viewer, OAuth relay, and documentation MCP endpoints are deployment configuration.

Installation

Build from Source

git clone https://github.com/hrhgit/mortise.git
cd mortise
bun install
bun run electron:start

On Windows, double-click Mortise-Desktop-Tool.cmd to start, restart, or package the source desktop application, launch or stop the WebUI, or build the Developer Kit. Platform-specific package commands remain available through the root package.json. The existing icon is a temporary placeholder until Mortise artwork is ready.

Features

  • Multi-Session History: Desktop app with session management, unread tracking, search, and deletion
  • Claude Code Experience: Streaming responses, tool visualization, real-time updates
  • Multiple LLM Connections: Add multiple AI providers with app defaults and per-session overrides
  • Multi-Provider Support: Run sessions with Google AI Studio, ChatGPT Plus, GitHub Copilot, or OpenAI API keys alongside Anthropic
  • MCP Integration: Connect local and remote Model Context Protocol servers
  • Bundled Extensions: Precompiled extensions can own session controls, approvals, and rich conversation UI
  • Background Tasks: Run long-running operations with progress tracking
  • Dynamic Status System: Customizable session workflow states (Todo, In Progress, Done, etc.)
  • Theme System: Cascading themes at app and workspace levels
  • Multi-File Diff: VS Code-style window for viewing all file changes in a turn
  • Skills: Specialized agent instructions stored per-workspace
  • File Attachments: Drag-drop images, PDFs, Office documents with auto-conversion
  • Automations: Event-driven automation — create agent sessions on label changes, schedules, tool use, and more

Quick Start

  1. Launch the app after installation
  2. Choose API Connection: Use Anthropic (API key or Claude Max), Google AI Studio, ChatGPT Plus (Codex OAuth), or GitHub Copilot OAuth
  3. Create a workspace: Set up a workspace to organize your sessions
  4. Start chatting: Create sessions and interact with Claude

Desktop App Features

Session Management

  • Inbox/Archive: Sessions organized by workflow status
  • Flagging: Mark important sessions for quick access
  • Status Workflow: Todo → In Progress → Needs Review → Done
  • Session Naming: AI-generated titles or manual naming
  • Session Persistence: Full conversation history saved to disk

Keyboard Shortcuts

Shortcut Action
Cmd+N New chat
Cmd+1/2/3 Focus sidebar/list/chat
Cmd+/ Keyboard shortcuts dialog
Enter Send message
Shift+Enter New line

Remote Server (Headless)

Mortise can run as a headless server on a remote machine (e.g., a Linux VPS), with the desktop app connecting as a thin client. This lets you keep long-running sessions alive, access them from multiple machines, and run compute-heavy tasks on a powerful server.

Quick Start

From the monorepo root:

# Generate a token and start the server
MORTISE_SERVER_TOKEN=$(openssl rand -hex 32) bun run packages/server/src/index.ts

The server prints the connection details on startup:

MORTISE_SERVER_URL=ws://203.0.113.5:9100
MORTISE_SERVER_TOKEN=<generated-token>

Copy these values and use them to connect the desktop app.

Connecting the Desktop App

Launch the Electron app in thin-client mode by passing the server URL and token:

MORTISE_SERVER_URL=wss://203.0.113.5:9100 MORTISE_SERVER_TOKEN=<token> bun run electron:start

In thin-client mode, the desktop app renders the UI but all session logic, tool execution, and LLM calls run on the remote server.

Environment Variables

Variable Required Default Description
MORTISE_SERVER_TOKEN Yes — Bearer token for client authentication
MORTISE_RPC_HOST No 127.0.0.1 Bind address (0.0.0.0 for remote access)
MORTISE_RPC_PORT No 9100 Bind port
MORTISE_RPC_TLS_CERT No — Path to PEM certificate file (enables wss://)
MORTISE_RPC_TLS_KEY No — Path to PEM private key file (required with cert)
MORTISE_RPC_TLS_CA No — Path to PEM CA chain file (optional, for client cert verification)
MORTISE_DEBUG No false Enable debug logging

TLS (Recommended for Remote Access)

When exposing the server over the network, TLS encrypts the WebSocket connection (wss:// instead of ws://).

Generate a self-signed certificate (development/testing):

./scripts/generate-dev-cert.sh
# Creates certs/cert.pem and certs/key.pem (valid 365 days)

Start the server with TLS:

MORTISE_SERVER_TOKEN=<token> \
MORTISE_RPC_HOST=0.0.0.0 \
MORTISE_RPC_TLS_CERT=certs/cert.pem \
MORTISE_RPC_TLS_KEY=certs/key.pem \
bun run packages/server/src/index.ts

The server will print MORTISE_SERVER_URL=wss://<your-public-ip>:9100.

For production, use certificates from a trusted CA (e.g., Let's Encrypt) or place the server behind a reverse proxy (nginx, Caddy) that terminates TLS.

Docker

docker run -d \
  -p 9100:9100 \
  -e MORTISE_SERVER_TOKEN=<token> \
  -e MORTISE_RPC_HOST=0.0.0.0 \
  -v mortise-data:/root/.mortise \
  mortise-server

To enable TLS in Docker, mount your certificates and set the env vars:

docker run -d \
  -p 9100:9100 \
  -e MORTISE_SERVER_TOKEN=<token> \
  -e MORTISE_RPC_HOST=0.0.0.0 \
  -e MORTISE_RPC_TLS_CERT=/certs/cert.pem \
  -e MORTISE_RPC_TLS_KEY=/certs/key.pem \
  -v ./certs:/certs:ro \
  -v mortise-data:/root/.mortise \
  mortise-server

CLI Client

A terminal client that connects to a running Mortise Agent server over WebSocket (ws:// or wss://). Use it for scripting, CI/CD pipelines, server validation, or when you prefer the command line.

Installation

# From the monorepo (requires Bun)
bun run apps/cli/src/index.ts --help

# Or add to your PATH
alias mortise-cli="bun run $(pwd)/apps/cli/src/index.ts"

Connection

The CLI reads connection details from flags or environment variables:

# Via environment (set once)
export MORTISE_SERVER_URL=ws://127.0.0.1:9100
export MORTISE_SERVER_TOKEN=<your-token>

# Or via flags
mortise-cli --url ws://127.0.0.1:9100 --token <token> ping

For TLS connections (wss://), use --tls-ca <path> for self-signed certificates.

Commands

Command Description
ping Verify connectivity (clientId + latency)
health Check credential store health
versions Show server runtime versions
workspaces List workspaces
sessions List sessions in workspace
connections List LLM connections
session messages <id> Print session message history
session delete <id> Delete a session
send <id> <message> Send message and stream AI response
cancel <id> Cancel in-progress processing
invoke <channel> [args] Raw RPC call with JSON args
listen <channel> Subscribe to push events (Ctrl+C to stop)
run <prompt> Self-contained: spawn server, run prompt, stream response, exit
--validate-server 21-step integration test (auto-spawns server if no --url)

Run Command Flags

Flag Default Description
--workspace-dir <path> — Register a workspace directory before running
--output-format <fmt> text Output format: text or stream-json
--no-cleanup false Skip session deletion on exit
--server-entry <path> — Custom server entry point
--provider <name> anthropic LLM provider (anthropic, openai, google, openrouter, groq, mistral, xai, etc.)
--model <id> (provider default) Model ID (e.g., claude-sonnet-4-5-20250929, gpt-4o, gemini-2.0-flash)
--api-key <key> — API key (or $LLM_API_KEY, or provider-specific env var)
--base-url <url> — Custom API endpoint for proxies or self-hosted models

The run command is fully self-contained — it spawns a headless server, creates a session, sends the prompt, streams the response, and exits. No separate server setup needed. An API key is resolved from --api-key, $LLM_API_KEY, or a provider-specific env var (e.g., $OPENAI_API_KEY, $GOOGLE_API_KEY).

Examples

# Quick connectivity check
mortise-cli ping

# List sessions (human-readable)
mortise-cli sessions

# Send a message and stream the AI response
mortise-cli send abc-123 "What files are in the current directory?"

# Pipe input
echo "Summarize this" | mortise-cli send abc-123

# JSON output for scripting
mortise-cli --json workspaces | jq '.[].name'

# Self-contained run (spawns its own server)
mortise-cli run "Summarize the README"
mortise-cli run --workspace-dir ./my-project "Summarize this repository"

# Multi-provider support
mortise-cli run --provider openai --model gpt-4o "Summarize this repo"
GOOGLE_API_KEY=... mortise-cli run --provider google --model gemini-2.0-flash "Hello"
mortise-cli run --provider anthropic --base-url https://openrouter.ai/api/v1 --api-key $OR_KEY "Hello"

# Validate the server (auto-spawns if no --url)
mortise-cli --validate-server
mortise-cli --validate-server --url ws://127.0.0.1:9100 --token <token>

Architecture

mortise/
├── apps/
│   ├── cli/                   # Terminal client (CLI)
│   └── electron/              # Desktop GUI (primary)
│       └── src/
│           ├── main/          # Electron main process
│           ├── preload/       # Context bridge
│           └── renderer/      # React UI (Vite + shadcn)
└── packages/
    ├── core/                  # Shared types
    └── shared/                # Business logic
        └── src/
            ├── agent/         # MortiseAgent, permissions
            ├── auth/          # OAuth, tokens
            ├── config/        # Storage, preferences, themes
            ├── credentials/   # pi auth.json thin wrapper (plaintext, 0600)
            ├── sessions/      # Session persistence
            └── statuses/      # Dynamic status system

Development

# Hot reload development (portmux-managed)
bun run electron:dev

# Build and run
bun run electron:start

# Type checking
bun run typecheck:all

# Debug logging (writes to ~/Library/Logs/@mortise/electron/)
# Logs are automatically enabled in development

On Windows, double-click Mortise-Desktop-Tool.cmd from the repo root for the source workflow. It provides the single user-facing entry point for starting, restarting, and packaging Mortise, launching or stopping the WebUI, and building the Developer Kit.

To launch the complete browser UI development environment on Windows, use “启动网页端” in Mortise-Desktop-Tool.cmd or run portmux start. It starts the authenticated headless server and Vite WebUI, automatically signs the local browser in, and opens the URL assigned by portmux. The RPC server uses the following port (WebUI + 1) and the shared ~/.mortise configuration. Launch it again to open another browser client connected to the same running WebUI; it does not start another backend or create an isolated configuration directory. Use “停止网页端” in the same tool to stop every portmux-managed WebUI project for this repository and clean up legacy untracked WebUI process trees.

Electron, standalone WebUI, RPC development server, and the component playground have separate portmux identities. Start them with bun run electron:dev, bun run webui:dev, bun run server:dev, and bun run playground:dev; each listens directly on its own assigned port. Electron, the WebUI headless server, and standalone server modes all use ~/.mortise by default. Set MORTISE_CONFIG_DIR when an isolated profile is needed.

Supported LLM Providers

Mortise supports multiple ways to connect to LLM providers:

Direct Connections

Provider Auth Notes
Anthropic API key or Claude Max/Pro OAuth Claude connection through the Pi provider runtime
Google AI Studio API key Gemini models with native Google Search grounding built in
ChatGPT Plus / Pro Codex OAuth Sign in with your ChatGPT subscription — uses OpenAI's Codex models
GitHub Copilot OAuth (device code) One-click authentication with your Copilot subscription

Third-Party & Self-Hosted Providers

Additional providers are supported through compatible provider connections by choosing a custom endpoint:

Provider Endpoint Notes
OpenRouter https://openrouter.ai/api Access Claude, GPT, Llama, Gemini, and hundreds of other models through a single API key. Use provider/model-name format (e.g. anthropic/claude-opus-4.7).
Vercel AI Gateway https://ai-gateway.vercel.sh Route requests through Vercel's AI Gateway with built-in observability and caching.
Ollama http://localhost:11434 Run open-source models locally. No API key required.
Custom Any URL Any OpenAI-compatible or Anthropic-compatible endpoint.

Architecture

Mortise uses a single agent backend:

  • Pi — powered by the Pi SDK, which handles Anthropic-compatible Claude connections, Google AI Studio, ChatGPT Plus (Codex OAuth), GitHub Copilot OAuth, OpenAI API key connections, and custom provider endpoints. Legacy anthropic provider identifiers are kept as compatibility aliases and route through this backend.

Configuration

Current Mortise state is stored under ~/.mortise/:

~/.mortise/
├── state.sqlite             # App configuration, Workspace registry, and drafts
└── agent/
    ├── auth.json            # Provider credentials
    ├── sessions/            # Session JSONL data
    └── skills/              # Mortise-global skills

Project skills are stored at <workspace>/.mortise/skills/; Mortise global skills are stored at ~/.mortise/agent/skills/. Retired config.json Workspace settings files are ignored and left untouched.

Legacy ~/.mortise/credentials.enc files are not silently imported into auth.json; upgrade migration backs up and clears the old path. Re-enter API keys or re-authenticate OAuth connections after upgrading from a pre-unification build.

Automations

Automations let you automate workflows by triggering actions when events happen — labels change, sessions start, tools run, or on a cron schedule.

Just ask the agent:

  • "Set up a daily standup briefing every weekday at 9am"
  • "Notify me when a session is labelled urgent"
  • "Notify me when a long-running automation finishes"
  • "Every Friday at 5pm, summarise this week's completed tasks"

You can also create and manage definitions from the Automations interface. Prompt actions use normal Session semantics: they create a new Session by default, or explicitly deliver to an existing or event Session with followUp or steer.

See the bundled Automations documentation for the full reference.

Advanced Features

Large Response Handling

Tool responses exceeding ~60KB are automatically summarized using Claude Haiku with intent-aware context. The _intent field is injected into MCP tool schemas to preserve summarization focus.

Deep Linking

External apps can navigate using mortise:// URLs:

mortise://allSessions                      # All sessions view
mortise://allSessions/session/session123   # Specific session
mortise://settings                         # Settings
mortise://action/new-chat                  # Create new session

Tech Stack

Layer Technology
Runtime Bun
AI Pi SDK agent server and provider runtime
Desktop Electron + React
UI shadcn/ui + Tailwind CSS v4
Build esbuild (main) + Vite (renderer)
Credentials Pi auth store (~/.mortise/agent/auth.json, plaintext JSON, 0600 permissions)

Troubleshooting

Debug Mode

To launch the packaged app with verbose logging enabled, use -- --debug (note the double dash separator):

macOS:

/Applications/Mortise\ Agents.app/Contents/MacOS/Mortise\ Agents -- --debug

Windows (PowerShell):

& "$env:LOCALAPPDATA\Programs\@mortiseelectron\Mortise.exe" -- --debug

Linux:

./mortise -- --debug

Logs are written to:

  • macOS: ~/Library/Logs/@mortise/electron/main.log
  • Windows: %APPDATA%\@mortise\electron\logs\main.log
  • Linux: ~/.config/@mortise/electron/logs/main.log

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

The former upstream trademark policy is retained only for legal provenance in UPSTREAM-TRADEMARK.md. It is not Mortise branding policy.

Contributing

We welcome contributions! Please see CONTRIBUTING.md for guidelines.

Security

To report security vulnerabilities, please see SECURITY.md.

About

Extensible desktop code-agent platform built on the Pi runtime, with full-stack plugins and an AI-oriented development toolkit.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages