AGL
AGL is a self-hosted workspace for working with OpenCode and FlexHarness from one secure browser interface. It runs both agent runtimes behind a local controller, keeps conversations organized by project, and adds durable resources, model management, permission handling, workspace history, and safe lifecycle management.
The default setup is local-first: AGL listens only on 127.0.0.1, opens no agent runtime directly to the network, and protects the browser with a passkey. A trusted TLS reverse proxy can provide remote access when needed.
Issue Reporting and Security
For reporting bugs, issues, or security vulnerabilities, please visit community.foss.global/. This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a code.foss.global/ account to submit Pull Requests directly.
What AGL Provides
- One browser workspace for OpenCode and FlexHarness conversations.
- Explicit projects backed by existing directories on disk.
- Live text, reasoning, tool activity, todos, permissions, and questions.
- OpenCode and Flex model selection, including per-conversation choices.
- Conversation groups, ordering, search, rename, archive, and deletion.
- Synchronized drafts, file attachments, scratchpads, tasks, and goals.
- Native slash commands with transcript and Git-aware undo/redo.
- Optional detached Git worktrees owned and validated by the controller.
- Durable terminal resources that survive browser reloads.
- Durable browser resources for human use and permission-gated agent automation.
- Read-only Session Intelligence for questions about a conversation and related sessions.
- Passkey authentication, temporary CLI passwords, and bounded audit records.
- Detached start, verified stop, health status, and coordinated in-place upgrades.
AGL does not replace either harness. OpenCode remains the owner of OpenCode sessions, FlexHarness remains the owner of Flex sessions, and every mutation is sent to the runtime that owns the conversation.
How It Works
Browser
-> AGL controller and authenticated TypedSocket API
-> official OpenCode SDK
-> private OpenCode server on 127.0.0.1
-> private framed IPC
-> isolated FlexHarness child
-> controller-owned terminals, browsers, uploads, and Git state
-> SmartData persistence and audit records
OpenCode and FlexHarness are supervised independently. Flex can restart without taking down OpenCode, and an unsupported Flex runtime does not prevent OpenCode sessions or controller-owned terminals from working.
OpenCode is automatically recovered only after AGL observes the exact owned child exit. AGL immediately fences the exited runtime generation, aborts its in-flight requests, completes generation cleanup, and retries a fresh health-checked runtime before reopening admission. Generic or otherwise uncertain OpenCode failure remains fail-closed and is not automatically restarted. Fatal Flex child failures close admission and use bounded status and resource cleanup; if graceful disposal stalls, a watchdog forces the isolated child to exit so exact-generation recovery can proceed.
The browser receives normalized AGL data. It never receives the OpenCode server password, provider credentials, private database descriptors, raw provider responses, or Flex private snapshots.
Live Transcript Delivery
Controller protocol v24 sends bounded live Assistant text and reasoning deltas when the browser already owns the exact preceding transcript revision. Every delta carries its stream epoch, revision, and UTF-8 coordinates; Flex deltas also carry the harness's authoritative transcript order. The browser applies only contiguous, correctly sized updates to the canonical transcript; a gap, stale owner, invalid coordinate, or queue overflow blocks further deltas from that epoch and requests authoritative session-detail hydration instead. Only adjacent compatible pending deltas may be coalesced, so interleaved updates retain their original order.
Streaming text is rendered without Markdown parsing. AGL refreshes the affected messages and enables normal Markdown rendering after the harness reports terminal state or authoritative hydration settles the transcript. This keeps long responses responsive without creating a second browser-side message graph.
Requirements
- Node.js 24 or newer.
- pnpm 11 or newer.
- Git 2.43 or newer for workspace history and managed worktrees.
- A WebAuthn-capable browser.
- Linux or macOS for detached
start,stop, and managed upgrades. - Native
node-ptysupport.
When a prebuilt node-pty binary is unavailable, installation also needs Python 3, make, and a C/C++ toolchain.
FlexHarness currently runs on Linux x64 with Node.js 24 or 25. Other supported controller platforms still provide OpenCode and terminal functionality.
AGL includes an embedded SmartDB engine, so a separate database server is optional. It also pins the official OpenCode SDK and platform binary packages to one tested version.
Install
Install the public package and allow its native terminal dependencies to build:
pnpm add --global agl \
--allow-build=node-pty \
--allow-build=fs-ext-extra-prebuilt
To install from a specific registry, use pnpm's normal registry option:
pnpm add --global agl \
--registry=https://registry.npmjs.org \
--allow-build=node-pty \
--allow-build=fs-ext-extra-prebuilt
Confirm the installation:
agl --version
agl help
Quick Start
Start AGL without creating an implicit project:
agl start
The first start prints a local URL and a one-time setup code. Open the URL, enter the code, and create the controller's passkey.
To register one existing directory during startup:
agl start --directory /absolute/path/to/project
AGL never derives a project from the shell's current directory and never creates project directories. Additional projects can be registered from the browser.
Check controller and harness health:
agl status
agl status --json
The default ports are:
- Browser URL and default WebAuthn origin:
http://localhost:4097. - Controller bind endpoint:
127.0.0.1:4097. - Private OpenCode server:
127.0.0.1:4098.
Everyday Workflows
Projects
A project is an explicitly registered existing directory. Every conversation, durable resource, and workspace operation is scoped to one project. Conversation model overrides are project/session scoped, while harness default models are controller-wide settings.
The browser accepts absolute paths and paths relative to the configured projects root. It suggests matching directories while typing, rejects dot navigation, and reports unavailable paths instead of creating them.
AGL records the complete filesystem ancestry for each project and revalidates it before path-sensitive work. Durable terminal and browser resources must be retired before project removal. Removal then cleans session state, Flex state, Git captures and managed worktrees, and project metadata through a resumable process; it never deletes files in the registered working tree.
Conversations
Use New conversation for OpenCode or choose Flex from the New menu. A new conversation remains a browser draft until its first message is submitted, so abandoning it does not leave an empty harness session.
Conversation features include:
- Tip-first transcript loading with older-history pagination.
- Live Assistant text, reasoning, tool state, todos, and subagent cards.
- Inline permission requests and questions in transcript order.
- Synchronized drafts and private temporary attachments.
- Durable model choices and scratchpads.
- Rename, archive, delete, abort, and session-specific Ask/Yolo controls.
- Token and context metrics when the harness can prove them from complete history.
Archived conversations are hidden from the normal list and available in the archived-conversations dialog. Archive is admitted only after active prompts, permissions, commands, and Session Intelligence work have settled.
AGL never automatically redispatches a Flex archive whose outcome is unknown. It performs bounded exact-session readback and finalizes a confirmed archive. Otherwise it stops the exact Flex generation before releasing archive admission; if that stop cannot yet be confirmed, AGL retains the fences and retries cleanup until the generation exits. An authoritative refresh that confirms the conversation is archived clears a stale browser error.
Groups And Ordering
Conversations can be grouped and reordered per project. Drag a conversation into a group, reorder it within or between groups, or use a group's context menu to rename or remove the group.
Removing a group does not remove its conversations. The conversations return to the ungrouped section in their existing order. Confirmed layouts are revisioned, synchronized across browsers, and preserved if a later layout request fails.
Models And Provider Accounts
AGL stores an independent default model for OpenCode and FlexHarness. A conversation can override its harness default without changing other conversations.
Flex provider accounts are connected from Settings through the provider's supported login flow. Credentials remain in a Flex-owned sealed store and never enter SmartData or the browser. Account model catalogs, quota windows, and runtime availability are exposed as bounded normalized metadata.
An active Flex OpenAI account can also be selected for the controller-owned OpenCode runtime. AGL pauses the exact active OpenCode cohort, replaces the private OpenCode runtime, verifies health, and reopens admission only after the transition is settled.
Commands And Workspace History
The composer exposes one harness-aware slash-command catalog. OpenCode templates and built-ins remain OpenCode-owned; Flex commands are listed and executed through FlexHarness.
Flex /undo and /redo move transcript and workspace history together. For Git-backed projects, AGL captures tracked state and non-ignored untracked paths in controller-private storage without adding controller refs or generated objects to the source repository. Unsupported or ambiguous repository states fail closed or become explicit non-revertible barriers.
Flex also supports explicit managed worktrees:
/worktree create
/worktree list
/worktree remove <worktreeId>
Worktrees are never created automatically. Removal refuses a worktree with tracked, untracked, or ignored changes.
Tasks, Goals, Scratchpads, And Delegation
Flex conversations include session-local tools for structured tasks, the current goal, and durable scratchpad context. These records are generation-fenced so deleted and recreated sessions cannot inherit stale state.
Flex can delegate one level to a general subagent. Child permissions remain visible in the parent conversation, while active and retained child transcripts are available through bounded task cards and a read-only drill-in.
Session Intelligence
Session Intelligence can answer a question about one conversation and, after reading it successfully, inspect related sessions in the same project. It runs in an ephemeral, read-only FlexHarness session with no filesystem, shell, browser, mutation, or delegation tools.
Answers and scratchpad suggestions are bounded and revision-aware. At most four analyses run controller-wide and one per source conversation.
Resources
Resources are durable project-scoped capabilities separate from conversations.
Terminals
A terminal resource runs a real shell in the project directory through the controller's shell-free node-pty integration.
- Closing or reloading the browser does not stop the terminal.
- Reattaching replays bounded in-memory scrollback.
- Terminal title, attachment, stopped state, and last exit code are durable.
- Terminal input, output, and scrollback are never persisted.
- A terminal can be renamed, stopped, restarted, detached, or retired.
AGL allows at most eight running terminals per project and 32 controller-wide.
Browsers
A browser resource provides tabs, navigation, viewport control, mouse, keyboard, and text input through a human canvas. Protocol v24 opens one authenticated persistent event stream and one paired operation stream for each human view. Every pair is bound to the exact peer, credential, resource authority, attachment revision, view, and stream generation.
State and operation results use bounded JSON packets. A frame is sent as one validated metadata packet immediately followed by its raw image bytes, rather than through a public URL or base64 JSON payload. Operation IDs, pending bytes, acknowledgements, event delivery, and recovery attempts are all bounded. Only adjacent compatible pointer-move or viewport operations can be coalesced, while cancellation retains a separately fenced path to live work.
The browser installs its renderer before activating a view. A recoverable stream failure suspends input, retires the old pair, opens and starts the next stream generation, and resumes the renderer's listeners before activating the replacement. Activation refreshes the runtime frame boundary, and input resumes only after that boundary arrives. Stale activation, recovery, cancellation, and close messages cannot act on another generation.
When attached to a Flex conversation, the resource can provide permission-gated navigate, snapshot, screenshot, click, fill, and press actions to that exact run. Every channel is bound to the project, resource revision, session, run, actor, and current Flex generation.
Authentication
AGL creates exactly one passkey credential per controller database. Enrollment is first-wins and there is no weaker online passkey-reset path.
The one-time setup code expires after 30 minutes. Omitting --setup-code uses a random 32-byte code. A custom setup code is visible in shell history and weak codes are recoverable by anyone who can read the database, so prefer the generated value.
For temporary or scripted browser access, mint a CLI password:
agl temp-password --ttl-hours 2
Temporary passwords expire after at most 24 hours. Only their SHA-256 hashes are stored, and at most eight can be active.
Authentication belongs to one physical TypedSocket connection. Resume tokens are single-use, rotate on resume, remain only in browser session storage, and are invalidated by controller restart.
CLI
agl start [options]
agl status [--port 4097] [--json]
agl stop [--port 4097]
agl upgrade [--port 4097] [--registry <url>] [--grace-period-seconds 300] [--continue-sessions] [--json]
agl foreground [options]
agl temp-password [--port 4097] [--ttl-hours 24]
agl help
agl --version
Important startup options:
--port <port> Controller port; default 4097
--opencode-port <port> Private OpenCode port; default 4098
--directory <path> Register one existing project on this start
--projects-root <path> Base for relative project paths and suggestions
--public-origin <origin> Exact browser WebAuthn origin
--rp-id <hostname> WebAuthn relying-party ID
--behind-tls-proxy Declare trusted TLS termination upstream
--setup-code <code> Supply an operator-chosen initial setup code
Runtime configuration is durable and immutable per controller port. Later management commands must use the same --port. The --directory option is transient and can register another project during a later stopped start.
stop verifies the exact package CLI, process, process group, command, port, and process-start fingerprint before signaling anything. AGL never kills processes by name.
Upgrades
Use the active global installation:
agl upgrade --port 4097
By default, upgrade package commands use pnpm's effective registry configuration. Override it for one durable upgrade transaction when needed:
agl upgrade --port 4097 --registry=https://registry.npmjs.org
Registry URLs must use HTTPS, except that HTTP is accepted for localhost, 127.0.0.1, and [::1]. They cannot contain credentials, query parameters, or fragments. Keep authentication in pnpm configuration rather than command arguments.
The upgrade worker:
- Resolves the registry's
latestversion and never downgrades. - Pauses the exact active-session cohort before stopping a running controller.
- Waits for admitted operations and data writers to drain.
- Installs with the required native build allowances.
- If the controller was running, restarts the exact target and reopens prompt admission.
- Preserves its transaction across worker or controller failure.
Use --continue-sessions to submit the continuation message to sessions paused for the upgrade. Without it, AGL reopens admission without sending another message.
If a forward-only upgrade worker is lost after target startup or package commit begins, a later fixed AGL installation can adopt exactly one stalled transaction during an explicit agl upgrade. Adoption requires a free port, no live worker, controller, Flex child, temporary-password writer, or token-bound metadata, and a compatible installed version. It preserves the original preparation timeline and paused cohort. Do not delete upgrade files, copy transactions between roots, downgrade the package, or bypass recovery with agl start.
Upgrade coordination lives under $AGL_HOME/upgrade. Private logs live under $AGL_HOME/logs.
Migrating From hcon
Starting from @modelprofile.com/harness-controller@20.0.2, run the active command twice:
hcon upgrade --port 4097
hcon upgrade --port 4097
The first invocation installs the same-package bridge. The second performs the exact package transition to AGL. Wait for each command to finish and use the controller's configured port. Afterward, use agl for all management commands.
The historical hcon transition uses pnpm's configured registry and does not support --registry. Configure pnpm before starting that migration when a specific registry is required.
AGL Home And Persistence
AGL owns one private root:
- Global installations:
$XDG_CONFIG_HOME/agl, normally~/.config/agl. - Repository checkouts:
<checkout>/.nogit/agl. - Explicit override: absolute normalized
AGL_HOMEwhose parent already exists.
Important directories include:
databasefor embedded SmartDB data.credentialsfor sealed Flex provider credentials.git-reversionfor private captures and managed worktrees.logsfor controller and upgrade diagnostics.upgradefor durable upgrade coordination.runtimefor private browser, upload, socket, and OpenCode state.migrationfor verified migration journals and retained source records.
Do not move these directories or edit migration and upgrade journals by hand.
SmartData stores runtime configuration, passkey metadata, projects, layouts, model choices, scratchpads, tasks, goals, resources, Flex public projections, provider metadata, and bounded audit events. Prompt text is not written to audit records.
Terminal I/O, active browser views and their paired transport streams, composer drafts and their pending attachment state, active capability leases, and running processes remain memory-only. Once a prompt is submitted, attachment files exist only in private operation directories for the lifetime of that exact harness operation.
Library consumers can resolve the same home contract:
import { bindAGLHomeEnvironment, resolveAGLHomePaths } from 'agl';
const paths = resolveAGLHomePaths();
const childEnvironment = bindAGLHomeEnvironment(process.env, paths);
Database Configuration
The embedded database requires no separate service. Override its directory with an absolute path:
export HARNESS_CONTROLLER_DB_DIR='/private/absolute/path'
To use an external MongoDB-compatible server:
export HARNESS_CONTROLLER_MONGO_URL='mongodb://user:password@127.0.0.1:27017'
export HARNESS_CONTROLLER_MONGO_DB='opencode_controller'
HARNESS_CONTROLLER_MONGO_DB defaults to opencode_controller.
Runtime Environment
OpenCode, FlexHarness, and terminal shells receive a minimal allowlisted environment. Controller and database variables, unrelated parent secrets, and generated OpenCode credentials are not inherited. AGL_HOME is the intentional exception: AGL forwards its canonical value so controller-owned child processes resolve the same private root.
Delegate additional non-controller variables explicitly when required:
export HARNESS_CONTROLLER_FORWARD_ENV='MY_TOOL_CONFIG,MY_CA_FILE'
Flex provider credentials do not use this mechanism. They stay in the sealed provider store. OPENCODE_AUTH_CONTENT is always rejected; AGL never reads or edits OpenCode's user-wide auth.json.
Credential-free HTTP_PROXY and HTTPS_PROXY values are forwarded. Proxy URLs containing credentials are not forwarded automatically.
Remote Browser Access
Remote WebAuthn requires HTTPS. Start AGL behind a trusted TLS reverse proxy:
export HARNESS_CONTROLLER_TRUSTED_PROXY_SECRET="$(node -e "console.log(require('node:crypto').randomBytes(32).toString('base64url'))")"
agl start \
--public-origin https://controller.example.com \
--rp-id controller.example.com \
--behind-tls-proxy
The proxy must:
- Remove client-supplied copies of AGL transport headers.
- Inject the configured secret as
X-Harness-Controller-Proxy-Secret. - Set
X-Forwarded-Proto: httpson WebSocket upgrades. - Preserve the exact public Host and Origin.
- Forward WebSocket upgrades.
- Restrict direct access to the backend listener.
Backend isolation is mandatory. Proxy mode binds the controller to all interfaces, while origin-less loopback CLI management remains intentionally available. Never expose the plain backend port directly.
Security Boundaries
- Local mode binds the controller to loopback only.
- The private OpenCode server always remains on loopback and uses a generated per-process password.
- Application RPC starts only after the exact TypedSocket package-major handshake.
- Client-managed connection tags are disabled; authentication and routing use server-owned peer state.
- Setup attempts are globally rate limited before enrollment.
- Permission auto-accept is an explicit server policy, never an assumption based on an open browser.
- Questions are never auto-answered.
- Unknown, malformed, concurrent, or unverifiable lifecycle state fails closed.
The global auto-accept permissions (yolo) setting replies once to pending permissions across registered projects and writes those replies to the audit log. Each conversation also has a run-local Ask/Yolo switch. Neither mode creates persistent broad provider grants.
Development
Install dependencies and run the project checks:
pnpm install
pnpm run build
pnpm run check:test
pnpm test
Run the CLI from source:
node cli.js help
node cli.js foreground --port 4097
The default test suite uses disposable embedded databases. Set HARNESS_CONTROLLER_TEST_MONGO_URL to include the external MongoDB integration test.
License and Legal Information
This repository's own source code is licensed under the MIT License. A copy of the license can be found in license.md.
The distributed browser bundle also includes SmartBrowser 4.0.2 under the MIT License and PDF.js 4.10.38 under the Apache License, Version 2.0. See third-party-notices.md for attribution, modification details, and license terms.
Please note: The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
Trademarks
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
Company Information
Task Venture Capital GmbH
Registered at District Court Bremen HRB 35230 HB, Germany
For any legal inquiries or further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.