Skip to content

Repository files navigation

ThreadDock

ThreadDock is a macOS-first desktop workbench for running multiple CLI agents through a shared task, event, storage, approval, and workspace model.

The current repository contains the desktop workbench plus real Provider transports:

  • Electron 43, React 19, TypeScript, Vite, and pnpm workspace setup.
  • Sandboxed Renderer with a narrow, schema-validated preload API.
  • Agent Core running in a separate Electron utility process.
  • Versioned normalized event protocol and task projection reducer.
  • Provider Registry with executable, version, login, model, and capability probes.
  • Real Codex App Server JSON-RPC adapter with native approvals, cancellation, and resume.
  • Claude Code stream-json adapter with cancellation and native session resume.
  • OpenCode ACP adapter with streamed updates, approval routing, cancellation, and resume.
  • Cursor and Gemini headless stream adapters with recorded fixtures and unavailable-state UI.
  • Deterministic Fake Adapter with streaming events and cancellation for development.
  • SQLite event store with ordered migrations, projections, and interrupted-run recovery.
  • Persisted execution timeline for messages, tools, diffs, artifacts, approvals, usage, and diagnostics.
  • Task search and workspace/Provider/status filters, rename, pin, archive, Trash restore, and confirmed permanent record deletion.
  • ChatGPT-style Projects that bind a user-approved local folder and keep new conversations grouped under that project without copying or deleting the source folder.
  • Reusable built-in and custom agent profiles with role instructions, Provider/model defaults, permission defaults, session policies, plugin skills, and immutable per-task snapshots.
  • Conversation-local approval bars, sidebar attention indicators, and redacted macOS notifications that navigate back to the task.
  • Multi-file diff and artifact workbench with contained open, reveal, and export actions.
  • Task/message forks using an explicit, labeled context replay with truncation reporting.
  • Provider-reported token/cost dashboards and reminder-only monthly budget thresholds.
  • Read-only Codex prompt assistance that predicts one inline next prompt and improves drafts without creating task records or modifying project files.
  • English and Simplified Chinese UI with system-language defaults, an immediate Settings switch, persisted preference, locale-aware dates/numbers, and localized desktop notifications.

Requirements

  • macOS
  • Node.js 22.19 or newer
  • pnpm 11.15.1

Development

pnpm install
pnpm test
pnpm typecheck
pnpm dev

Create a production build with:

pnpm build

THREADDOCK_DATA_DIR can override Electron's user-data directory for isolated local testing. The application creates threaddock.sqlite and its WAL files there. Database migrations run automatically in order through schema version 16.

Provider configuration

ThreadDock discovers CLI executables through PATH; each one may be overridden with an absolute executable path:

Provider Override
Codex THREADDOCK_CODEX_PATH
Claude Code THREADDOCK_CLAUDE_PATH
OpenCode THREADDOCK_OPENCODE_PATH
Cursor THREADDOCK_CURSOR_PATH
Gemini CLI THREADDOCK_GEMINI_PATH

The built-in ThreadDock Agent runs in the Agent Core process and does not require Pi or another Agent CLI to be installed. It is enabled by default; set THREADDOCK_AGENT_ENABLE=0 to hide and disable it. Add OpenAI-compatible or Anthropic model profiles under Settings → Models & credentials. On macOS, Electron safeStorage encrypts saved API keys before their ciphertext is persisted in SQLite; plaintext is never returned to the Renderer.

Environment-managed profiles remain available for development and deployment automation:

Model API Required variables Optional variables
OpenAI-compatible OPENAI_API_KEY, THREADDOCK_OPENAI_MODEL THREADDOCK_OPENAI_PROFILE_ID, THREADDOCK_OPENAI_PROFILE_REVISION, THREADDOCK_OPENAI_BASE_URL, THREADDOCK_OPENAI_CONTEXT_WINDOW, THREADDOCK_OPENAI_MAX_TOKENS
Anthropic ANTHROPIC_API_KEY, THREADDOCK_ANTHROPIC_MODEL THREADDOCK_ANTHROPIC_PROFILE_ID, THREADDOCK_ANTHROPIC_PROFILE_REVISION, THREADDOCK_ANTHROPIC_BASE_URL, THREADDOCK_ANTHROPIC_CONTEXT_WINDOW, THREADDOCK_ANTHROPIC_MAX_TOKENS

ThreadDock registers only its own bounded read_file, list_files, search_files, apply_patch, and run_process tools. It does not register Pi's default tools or arbitrary shell execution. Workspace realpaths and symlinks are checked, file and output sizes are bounded, processes use a fixed working directory and explicit executable/argv values, and one structured permission policy implements ask, auto_review, full_access, and the read-only hard limit. Runtime checkpoints are integrity checked and committed atomically with completion events. Old complete turns are deterministically compacted before the configured context budget is exhausted.

Saved endpoints must use HTTPS, except loopback HTTP for local development. An endpoint-origin or model-provider change requires a new profile; same-origin model/limit changes increment the profile revision. The first release does not provide custom proxy or self-signed CA settings. Model requests time out after 120 seconds and retry one bounded 429/5xx response with a maximum 30-second delay. The Runtime executes as trusted application code inside Agent Core; Tool Broker constrains model operations but is not an operating-system sandbox. See docs/security/agent-runtime.md for the full boundary and rollback behavior.

External CLI Provider credentials are not saved in SQLite. The Core passes only provider-specific, allowlisted environment values to each child process. Codex and OpenCode use structured approval callbacks when their runtime supports them; Claude, Cursor, and Gemini do not receive an approval-bypass or force flag.

The Composer shows a Thinking selector only when the selected Provider and model expose a safe per-task native control. Codex values are discovered from its running App Server model catalog; Claude Code uses its native CLI levels. Providers without a safe per-task control remain on their model default rather than receiving a guessed mapping.

Tasks are persistent conversations. Select an existing task to send a follow-up through its native Provider session; use New task only to start a separate conversation. Codex, Claude Code, OpenCode, and the development Fake Adapter can resume their sessions. Providers without native resume remain readable but do not receive a guessed transcript replay.

Agent profiles are reusable conversation templates rather than hard-coded job roles. Built-in profiles inherit the currently selected Provider, and custom profiles may optionally suggest a Provider, model profile, model, thinking level, permission mode, session policy, and up to four enabled plugin skills. Starting a task snapshots the selected profile and skills locally so later profile edits do not rewrite task history. The built-in Runtime persists role instructions in its system prompt; external Provider sessions receive them only when the session is created while the recorded user message remains the original prompt. ThreadDock retains the authoritative task, snapshot, events, and Provider session identifier; Provider-side data retention remains governed by the selected Provider.

Projects are persistent conversation containers backed by a local folder selected through the native macOS directory picker. Starting a conversation from a project reuses that authorized folder automatically. Removing a project only detaches its conversations; it never deletes the folder, conversation history, timeline events, or recorded file operations.

Forks are intentionally different from ordinary continuation: ThreadDock starts a new Provider session and replays the recorded conversation up to the selected message. The UI labels this mode and reports when older messages were omitted to fit the replay limit.

Prompt assistance uses a temporary Codex session with a read-only sandbox, includes the authoritative project or conversation context, and does not persist a task, run, event, or usage record in ThreadDock.

Moving a task to Trash is reversible. Permanent deletion requires two confirmations and removes the task plus its SQLite-backed runs, events, approvals, usage projections, relations, and artifact records. It never deletes files from a user-selected workspace.

Language, notification, and usage-budget preferences are stored locally in SQLite under the language, notifications, and usageBudget setting keys. Supported language values are en and zh-CN; without a saved value, ThreadDock follows the operating-system language and falls back to English for other locales. Notifications are enabled by default but omit task titles, prompts, commands, paths, and response content. Cost totals and cost-budget warnings appear only when a Provider reported cost; ThreadDock does not estimate cost. Usage totals cover the complete filtered ledger rather than only the visible page, and soft-deleted tasks remain in that ledger until permanent deletion. Task and usage history use stable cursor pagination with Load more controls, and Dashboard and notification thresholds use the local calendar month.

The development Fake Adapter creates a uniquely named fake-response-<turn>.txt artifact in the selected workspace so the artifact workbench has a deterministic local happy path.

For an opt-in real account smoke test, run one Provider at a time:

THREADDOCK_LIVE_PROVIDER=codex pnpm exec vitest run tests/live-provider-smoke.test.ts

Use claude or opencode in place of codex. This sends the fixed harmless prompt in the test to the selected Provider account, so run it only when that egress is intended.

The built-in Runtime has a separate, explicitly cost-gated live smoke and first-token benchmark. Load the matching API key and model variables through your shell or secret manager, then run one model Provider at a time:

THREADDOCK_LIVE_MODEL_PROVIDER=openai THREADDOCK_LIVE_MODEL_COST_ACK=1 \
  pnpm exec vitest run tests/live-threaddock-agent-smoke.test.ts

THREADDOCK_LIVE_MODEL_PROVIDER=anthropic THREADDOCK_LIVE_MODEL_COST_ACK=1 \
  pnpm exec vitest run tests/live-threaddock-agent-smoke.test.ts

The live test defaults to 20 samples, caps each response at 32 output tokens, uses an isolated empty workspace, denies any requested tool action, and prints only model/profile identifiers plus latency and checkpoint-size summaries. Override the bounded sample count with THREADDOCK_LIVE_SAMPLES=1..50 or output limit with THREADDOCK_LIVE_MAX_TOKENS=8..128. Never commit API keys to the repository.

Run the local SQLite checkpoint benchmark without network access or credentials:

THREADDOCK_BENCHMARK=1 \
  pnpm exec vitest run tests/agent-runtime-performance.test.ts

Workspace Layout

  • apps/desktop: Electron Main, Agent Core utility process, preload, and React UI.
  • packages/protocol: normalized events and IPC schemas.
  • packages/core: adapter interface, async event queue, and task reducer.
  • packages/storage: SQLite migrations, persistence, and recovery.
  • packages/agent-runtime: Pi-derived built-in model loop and persistent Runtime facade.
  • packages/adapters/*: ThreadDock Agent, Fake, Codex, Claude, OpenCode, Cursor, Gemini, and shared transports.
  • migrations: ordered application-database migrations.
  • docs/development-records: implementation and validation records.

Safety Boundary

The Renderer has no Node.js integration and cannot access SQLite, the filesystem, or process execution directly. Directory selection is handled by Electron Main; task execution and persistence are handled by Agent Core. Provider processes are launched as an executable plus argument array, never through a shell. ThreadDock does not enable --force, --auto, --yolo, or permission-bypass modes by default.

Artifact actions accept only an artifact ID. Electron Main loads the authoritative workspace and relative path from Agent Core, resolves real paths, rejects traversal and symlink escapes, then pins the authorized regular file to an open file handle. Open and Reveal operate on a session-local verified preview copy, while Export streams from the pinned handle; later path swaps cannot redirect those operations outside the workspace. Pending approvals are expired on application restart because their originating Provider process no longer exists.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages