Agent work, woven across harnesses.
A native workspace for conversations, code changes, durable work, and review — Pi first, harness-neutral by design.
Heddlework is an agent development environment built around a simple idea: the workspace should outlive any one agent harness. Conversations, diffs, tasks, artifacts, review state, and workspace safety belong to the product; Pi, Codex, Claude, and future runtimes connect through adapters.
Today, Heddlework is a fast native desktop preview for Pi, rendered with GPUIX. The longer-term direction adds durable dependency-aware work, safe checkout lanes, scheduling, triage, and Fabric-powered orchestration without introducing another hidden agent loop.
Warning
Heddlework is under active development. The interaction model, persistence format, adapter API, and packaging story may change. It is useful today as a source-built Pi desktop client, but it is not yet a signed or supported desktop release.
| Area | Status |
|---|---|
| Native desktop shell | Available — macOS, Linux, and Windows through GPUIX |
| Pi adapter | Available — Pi RPC, persisted sessions, editable queued work, models, thinking, compaction, and extension UI |
| Diff and review surfaces | Available — native virtualized diffs, wrapping, file filtering, and changed-file entry points |
| Fabric presentation | Available — rich nested fabric_exec activity and audit disclosures |
| Flows projection | Preview — queue/session rails, per-task pages, Triage, sequential and Fabric-parallel intake |
| Scheduled runtime | Preview — durable one-time, interval, and daily jobs that enqueue fresh Pi sessions |
| Durable dependency graph | In design — checkout lanes, retries, mutation receipts, and artifacts |
| Codex adapter | Planned |
| Claude adapter | Planned |
| Signed desktop distribution | Planned |
| Web and mobile companions | Preview — authenticated browser/PWA client with a responsive mobile layout |
A heddle guides one thread inside a loom harness. Heddlework applies that model to agentic development: harness adapters carry execution, while one workspace keeps the human-visible pattern coherent.
| Capability | What it unlocks | |
|---|---|---|
| 🧵 | Harness-neutral core | Pi is the first adapter, not the product boundary. Codex and Claude can join without replacing session, workspace, or review concepts. |
| 💬 | Durable conversations | Searchable project-scoped sessions, streaming responses, reasoning, tool activity, in-session branches, explicit forks, queued steering, and compaction. |
| 🧭 | Work lifecycle | Active, snoozed, and settled threads with persistent navigation designed to grow into scheduled and dependency-aware work. |
| 🧩 | Native surfaces | Conversation, settings, notifications, changed files, and extensible right-side work surfaces in one GPU-rendered shell. |
| 🔎 | Review without context switching | Native virtualized diffs, wrapped lines, file filtering, mutation summaries, and artifact-oriented direction. |
| 🛡️ | Explicit authority | Harnesses remain authoritative for their execution and transcripts; Heddlework projects them instead of inventing a second agent loop. |
Heddlework currently launches Pi as an RPC sidecar. The transport boundary in src/pi/transport.ts is intentionally small; future harness adapters can implement the same application-facing contract while preserving their own execution semantics.
Requirements:
- Bun 1.3+
- a working
piexecutable onPATH - a platform supported by GPUIX: macOS, Linux, or Windows
git clone https://github.com/monotykamary/heddlework.git
cd heddlework
bun install --frozen-lockfile
bun run setup:native
bun run start -- /path/to/repositoryFor UI development without credentials or a Pi process:
bun run demo -- /path/to/repositoryFor native UI development with reload-on-save (the process restarts so GPUIX rebuilds its input state):
bun run dev -- /path/to/repositoryBuild the current unsigned executable:
bun run build
./dist/heddlework /path/to/repositoryinstall.sh automates the paths above. It first asks which harness the machine should run, then installs Pi and configures API providers:
./install.sh # interactive menu
./install.sh heddle # Heddlework + Pi (native desktop; builds GPUIX, needs Rust)
./install.sh pi # Pi + Fabric (plain TUI; needs only Node.js)The first menu option compiles the pinned GPUIX runtime and Heddlework itself; the second installs pi and pi-fabric and stops there. Either path offers each known provider in turn and stores the key in ~/.pi/agent/auth.json with hidden input, owner-only (0600) permissions, and terminal echo restored if the entry is interrupted. Non-interactive runs (HEDDLEWORK_NONINTERACTIVE=1) never prompt: they copy provider keys out of the environment instead. Run ./install.sh --help for every flag and variable. Interactive prompts are covered by a real-PTY harness; see Installer PTY harness.
Pointing Pi somewhere other than api.openai.com — Ollama, LM Studio, vLLM, LiteLLM, or a gateway — goes through models.json, where Pi keeps compatible endpoints. The installer writes it from the custom-endpoint prompt, or from HEDDLEWORK_OPENAI_BASE_URL and HEDDLEWORK_OPENAI_MODEL (with HEDDLEWORK_OPENAI_API, HEDDLEWORK_OPENAI_NAME, and HEDDLEWORK_OPENAI_KEY) for an unattended run. The endpoint becomes its own provider id, custom-openai by default, so the built-in OpenAI catalog stays intact and both remain selectable in /model; providers already in the file are preserved. A supplied key is stored in auth.json instead of being inlined, and models.json references the environment variable so the same endpoint also works where that file is unavailable, as in CI or a container. ./install.sh --write-model-config writes only that file and exits, which is the mode the container entrypoint calls.
The browser client renders the shared workbench UI and talks to the same controller used by the native window. Remote access is off by default. To build, start a loopback headless host, and print a fragment-based pairing link:
bun run build:web
HEDDLEWORK_HOST_PRINT_TOKEN=1 bun run host -- /path/to/repositoryFor rebuild-on-save development, use bun run dev:web -- /path/to/repository (optionally with HEDDLEWORK_DEMO=1). Open the printed http://127.0.0.1:4817/#token=… URL. Non-loopback access additionally requires HEDDLEWORK_HOST_ALLOW_NETWORK=1 and exact HEDDLEWORK_HOST_ORIGINS; use HTTPS before sending pairing credentials over an untrusted network. See Community web port for runnable LAN/TLS setups, authentication, terminal architecture, validation scope, source attribution, and current mobile limitations.
Dockerfile builds that same host — Bun, Node, Pi, and pi-fabric — without the Rust/GPUIX toolchain, so any machine with Docker can serve the browser client:
cp .env.example .env # set HEDDLEWORK_HOST_ORIGINS and any provider keys
docker compose updocker build -t heddlework:latest .
docker run --rm -p 4817:4817 \
-e HEDDLEWORK_HOST_PRINT_TOKEN=1 \
-e HEDDLEWORK_HOST_ORIGINS=http://localhost:4817 \
-e ANTHROPIC_API_KEY=sk-ant-... \
-v "$PWD:/workspace" -v heddlework-state:/state \
heddlework:latestPi and the workspace host never run as root. The container starts as root only to drop privileges: with HOST_UID/HOST_GID it clones the shipped user's identity to match the host owner, so a bind-mounted workspace stays writable when agents edit files or run git. Pi's agent directory is seeded from the image on first boot and then lives in the /state volume, so sessions, auth.json, and installed extensions survive container replacement, while pre-seeded mounted state always wins. Setting HEDDLEWORK_OPENAI_BASE_URL and HEDDLEWORK_OPENAI_MODEL serves a local or gateway endpoint instead of api.openai.com, through the same writer the installer uses; on Docker Desktop the host is reachable as host.docker.internal. Dockerfile.native instead compiles the desktop executable for Linux and copies it into a mounted /out directory for the host to run. Both images are built and smoke-tested in .github/workflows/docker.yml.
Linux users can install the current unsigned build into their user application menu:
bun run build
HEDDLEWORK_PI="$(command -v pi)" ./packaging/linux/install-user.shThe installer uses the standalone executable, a square scalable icon, and an absolute-path launcher so app-grid launches do not depend on the GUI session inheriting Bun or Pi from shell startup files. See Linux desktop integration for installed paths, GNOME cache guidance, removal, and diagnostic-log precautions.
The bottom dock and right workbench surface share in-process PTY sessions, an incremental VT grid, and one fixed-cell native GPUI host. A versioned 16-byte cell buffer replaces thousands of React nodes and large style graphs; GPUI paints backgrounds and framebuffer block masks through one nearest-sampled 2×2-per-cell texture, caches shaping without ANSI colors, and paints remaining glyph-atlas masks directly. Fractional cell advances keep the first and right TUI columns attached without xterm.js. Bun's small PTY fragments are coalesced at synchronized-frame boundaries, complete CSI controls take an allocation-light parser path, ordinary presentation is paced at 125 Hz, and completed DEC frames plus causal input responses bypass that deadline without exposing partial frames. Native Tab capture forwards Tab and Shift+Tab to the PTY while keeping focus in the terminal. Runtime settings cover installed fonts, ligatures, Nerd Font fallback, muted emoji, and contrast-safe light/dark palettes. bun run benchmark:terminal measures the complete 160×50 VT → React → NAPI → GPUI frame. See Terminal architecture and rendering.
The right workbench panel now hosts an in-process CEF/Chromium child view rather than WKWebView or the user's system-browser profile. Tabs, lossless acknowledged navigation commands, fullscreen geometry, app-owned profiles, private sessions, and agent-access policy remain in Heddlework; GPUix owns the native child view, sandboxed Chromium helpers, input focus, and browser lifecycle. Native CEF fails closed outside a valid macOS app bundle. Build the linked GPUix runtime with bun run build:browser first; Heddlework then requires and hash-verifies its CEF manifest while building dist/Heddlework.app. Run bun run smoke:browser after packaging for a hidden local-page smoke covering native loads, profiles, command acknowledgements, sandboxed helpers, and clean shutdown. Set HEDDLEWORK_WITHOUT_CEF=1 only for an explicit browser-free macOS build. The same browser service can later target a remote/mobile host without loading CEF into the WASM client. See Browser architecture, security, and packaging.
Submitting while an agent run is active stages the input in Heddlework's editable queue instead of immediately surrendering it to Pi's immutable RPC queue. Enter creates a steering row for the next healthy turn boundary; the visible Queue action and Option/Alt+Enter create a follow-up for agent_settled. While idle, ordinary Enter still starts immediately, Queue parks work in a paused plan, and Enter on an empty composer—or the primary action—resumes its oldest row. The collapsed strip is inset like an upside-down checkout bar and tucks behind the composer; expanding it springs upward into a single bounded scroll surface where rows can be edited, held, removed, moved between lanes, explicitly steered, or reordered from their left drag handles.
Steering drains one row at a healthy turn_end; follow-ups drain one row after agent_settled, and lane-local FIFO order is preserved. A row hold blocks only its own lane. Delivery holds during compaction. Abort pauses the remaining tail until Resume is selected, while the controller's graceful pause waits for in-flight tools before stopping Pi. A terminal error also holds the tail, but a healthy retry or overflow-compaction recovery releases it automatically; otherwise it remains available for manual resume. Skills, prompt templates, and extension commands remain raw until Pi accepts them. Every built-in slash command is a queue barrier and is intercepted by Heddlework whether submitted idle or queued, so /name, /compact, /new, and similar controls are never collapsed into model prompts. A queued /new waits for its parent run to settle and starts a child-linked Pi session before the lane continues. Image-bearing slash text remains a normal message so attachments are never discarded. The Fabric gate in the queue strip selects live peers and inserts a cancellable /fabric await barrier through an input-only bridge; it does not inject model context. The owned queue is persisted per workspace. Stable rows, Flow correlation, lane identity, row holds, pause state, and images survive restart; if the process stopped during an uncertain dispatch, restoration pauses with a recovery hold instead of risking a duplicate control or prompt. Pi-native steering and follow-up entries are still mirrored as locked rows because RPC does not expose mutation operations for them.
Flows is a full-page read-only projection opened from the button above Search. Work merges every Heddlework-owned queue row with ordinary, scheduled, and historical Flow-named Pi sessions. Steer and follow-up lanes become sequential rail groups immediately: messages are tasks, /new is a pending session handoff, and /fabric await branches onto a connected peer-gate lane before rejoining the queue. After a handoff runs, Pi's own parentSession metadata keeps the resulting sessions on the same causal rail. Queue in chat returns to the fresh/current thread composer instead of opening a separate task form, so manual work has no Flow definition or lifecycle record. A task page shows the projected prompt or control, remaining queue primitives, current Fabric workers, and Pi result. Sessions older than seven days collapse into Settled until they run again or the user restores them. Triage derives successful and failed entries from terminal assistant stop reasons and tracks local read receipts. Pi remains authoritative for status and output; Heddlework stores only presentation annotations—settle/unsettle receipts, optional priority overrides, and user labels. Automatic priority rises with session duration.
Parallel work is also observational: any Pi session fans into agent rails only when fabric_exec reports participant previews or completed agent audits. It does not require a Flow name, managed child sessions, extra tools, or context markers. A user can queue an ordinary orchestration prompt from chat; scheduled parallel jobs still compile one prompt that asks Pi Fabric to spawn and join concurrent workers, while Fabric remains the execution authority.
Scheduled stores one-time, interval, and daily job definitions in the Heddlework runtime. Each due occurrence becomes the same queue primitives and a fresh Pi session, so schedules own intent while Pi sessions remain run history. The desktop runtime must be running; an occurrence for a different active workspace remains durably pending until that workspace runtime is available.
Pi RPC's get_commands response intentionally excludes built-in TUI commands, so Heddlework adds Pi's complete built-in command catalog to native completion and gives built-ins precedence over conflicting extension names. RPC-backed commands cover model and thinking changes, HTML export, session naming and stats, forking and cloning, new sessions, compaction with custom instructions, and resource reload. Host-backed commands open Heddlework's settings, model, thinking, resume, and session-tree surfaces, copy the last response, or quit cleanly. Prompt templates, skills, /llama, and other extension commands continue through Pi's prompt RPC so Pi performs their normal expansion or command handler.
Pi RPC exposes the full session tree through get_tree, but not the mutating half of /tree. Heddlework reads that tree natively and delegates selections through a control-only extension to Pi core's ExtensionCommandContext.navigateTree(), the same in-place navigation path used by the TUI. Active transcript paging starts from Pi's leafId, so switching branches keeps the session file and ID while restoring the selected branch. See Pi session tree integration.
Pi does not currently expose RPC operations for /scoped-models, /import, /share, /trust, /login, /logout, /hotkeys, or /changelog. Heddlework recognizes these commands and reports the protocol limitation instead of sending their text to the model. Authentication and trust changes can still be made from interactive Pi before reconnecting Heddlework.
Extension interactions are hosted in the main conversation area rather than embedded in the composer. Heddlework queues concurrent requests, shows searchable described choices and timeout countdowns, renders extension statuses, discovers slash commands, and retains visible custom-message media. Known contracts add a full tabbed ask_user_question questionnaire and recursive /fabric settings navigation without moving execution or persistence authority out of Pi. See Pi extension UI.
The web and responsive mobile source preview is available today, but supported distribution channels are not.
| Channel | Intended path |
|---|---|
| Desktop | Signed and notarized macOS, Windows, and Linux downloads from GitHub Releases, followed by native package-manager channels |
| Web | Supported deployment of the current browser/PWA client |
| Mobile | Native companion clients beyond the current responsive browser preview |
The desktop application remains the primary environment for local repositories, terminals, worktrees, and native agent processes. Web and mobile remain additional surfaces over the same workspace model, not separate products.
- Open a repository — pass a path or use the native directory picker.
- Choose or resume work — Heddlework discovers persisted Pi sessions across projects without eagerly loading full transcripts.
- Collaborate — prompt, steer, abort, switch models, adjust thinking, paste images, and answer extension dialogs.
- Inspect execution — expand reasoning, ordinary tools, and nested Fabric activity without leaving the timeline.
- Review changes — open the working-tree diff, filter files, wrap long lines, and keep large patches virtualized.
- Organize the result — snooze or settle threads and review persistent notification history.
If Pi is not discoverable from the desktop application environment:
HEDDLEWORK_PI=/absolute/path/to/pi bun run start -- /path/to/repository| Variable | Purpose |
|---|---|
HEDDLEWORK_CWD |
Workspace path instead of the positional argument |
HEDDLEWORK_PI |
Absolute Pi executable path (recommended for desktop launchers) |
HEDDLEWORK_PROVIDER |
Initial provider passed to Pi |
HEDDLEWORK_MODEL |
Initial model passed to Pi |
HEDDLEWORK_SESSION |
Pi session file to resume |
HEDDLEWORK_NO_SESSION=1 |
Disable Pi session persistence |
HEDDLEWORK_DEMO=1 |
Use the deterministic no-credentials demo transport |
HEDDLEWORK_DEBUG_OVERLAY=full |
Show GPUIX frame timings (minimal is also supported) |
HEDDLEWORK_HOST=1 |
Opt the native desktop process into serving the workspace host |
HEDDLEWORK_HOST_PORT |
Host port (default 4817) |
HEDDLEWORK_HOST_BIND |
Bind address (default 127.0.0.1) |
HEDDLEWORK_HOST_ALLOW_NETWORK=1 |
Required before binding a non-loopback address |
HEDDLEWORK_HOST_ORIGINS |
Comma-separated exact browser origins; required for non-loopback binding |
HEDDLEWORK_HOST_PRINT_TOKEN=1 |
Print the fragment-based pairing URL; keep process output private |
HEDDLEWORK_WEB_ROOT |
Built web-client directory; defaults to dist/web when present |
Heddlework follows a small supervised component model inspired by Cordis:
src/core/kernel.tsowns deferred service injection, typed events, keyed contributions, dependent lifetimes, and reverse-order cleanup.src/workbench/plugins.tscomposes transport, session discovery, workspace diff, and controller capabilities without giving the controller concrete providers to construct.src/pi/transport.tsdefines the application-facing harness transport seam; Pi RPC is its first provider.src/workbench/controller.tsprojects harness events into UI state, owns the durable delivery queue, and rehydrates from authoritative transcripts.src/flows/runtime.tspersists schedule intent and compiles due occurrences into the same queue primitives authored directly in chat.src/flows/projection.tsderives Work and Triage from queue rows and Pi session summaries without a task lifecycle database;src/flows/fabric-projection.tsderives parallel branches from Fabric participant audits;src/workbench/thread-metadata-store.tspersists only local disposition, read, priority, and label annotations.src/plugin-api.tsis the narrow source-level facade for feature authors.src/ui/extensions.tshosts observable, reversible feature manifests; one plugin can contribute several native workbench surfaces.src/ui/core-extension.tsxregisters the shipped surfaces through the same contract available to future user plugins.src/ui/tool-presenters.tsremains a keyed presentation contribution slot.src/ui/transcript-projection.tsturns loaded transcript semantics into stable, progressively disclosed native rows; see Transcript projection.src/ui/contains the shared GPUIX shell and never owns harness execution truth.
The UI boundary is intentionally not a microfrontend platform: extensions share one React/GPUIX runtime and register coarse feature surfaces rather than independent applications or a plugin per visual element. See UI extensions.
Heddlework applies the two dimensions from A Programming Paradigm for Spatiotemporal Composability at capability boundaries:
- Temporal composability: every registration, listener, timer, process, and other owned effect has an inverse attached to the same plugin, controller, or React lifecycle. Unloading the owner withdraws those effects in reverse order; operations outside the process boundary use their protocol's cancellation or compensation semantics.
- Spatial composability: replaceable capabilities are typed services, consumers declare them through
requires, and providers are constructed only by provider plugins or the composition root. Internal collaborators receive narrow interfaces or props rather than locating providers globally. - Component granularity: a Cordis component is an independently loadable capability or integration, not every source file or React component. Cohesive UI implementation may be split into static modules while remaining under one feature lifecycle; new plugins and contribution seats require an independently replaceable behavior and a demonstrated consumer.
Pi remains authoritative for Pi messages and sessions. Streaming state is temporary and replaced by get_messages after completion. Future adapters must preserve the same authority rule for their harnesses.
- Native GPU-rendered desktop shell
- Pi RPC adapter and persisted-session browser
- Streaming conversation, tools, images, extension UI, and notifications
- Virtualized transcript and large-diff paths
- Diff, settings, notification, and surface shells
- Cordis-style service/event lifetimes and in-process UI surface manifests
- External plugin discovery and compatibility metadata
- Stable harness adapter protocol
- Codex and Claude adapters
- Projection-first Flows with chat-authored sequential queues, participant-truth Fabric graphs, task pages, and Triage
- Durable one-time, interval, and daily scheduled Flow runtime
- Durable dependency graphs and safe checkout lanes
- Mutation receipts and artifact review
- Signed desktop installers and automatic updates
- Authenticated web workspace client and responsive mobile browser preview
- Native mobile companion clients
bun install --frozen-lockfile
bun run setup:native
bun run typecheck
bun run typecheck:web
bun run test
bun run test:web
bun run build
bun run build:websetup:native builds matching GPUix native/React packages from the full revisions in
gpuix-runtime.json, then links them into this checkout. Run it again after bun install
restores the stock dependency. It reuses verified builds and removes its private Cargo
target after installation. Rust and the platform GPUI system dependencies are required;
macOS additionally needs Xcode's Metal toolchain. The macOS default includes CEF;
HEDDLEWORK_WITHOUT_CEF=1 bun run setup:native explicitly builds without embedded Chromium.
Set HEDDLEWORK_KEEP_BUILD_CACHE=1 only when iterating on native code.
Linux window controls follow the compositor's effective decoration mode: client-side
windows receive minimize, maximize/restore, close, and native drag/resize regions;
server-decorated windows keep compositor controls. HEDDLEWORK_REDUCED_MOTION=1 disables
the control press animation. See Linux integration and
platform alignment for validation scope.
The native test suite exercises the real GPUIX reconciler and covers shell interactions, session paging, transcript following, clipboard media, extension dialogs, notifications, native diff virtualization, deep-scroll performance, and spring panel geometry.
- The desktop layout and interaction language adapt the MIT-licensed T3 Code interface.
- Icons include paths adapted from Lucide.
- Pi integration targets Pi.
- Native rendering is provided by GPUIX.
- The web/host/DOM source preview cherry-picks and adapts the MIT-licensed 0xCUB3 fork at
d55e9ad; see port scope and attribution.
See THIRD_PARTY_NOTICES.md for complete attribution.
MIT