agl 22.0.1 → 22.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/readme.md CHANGED
@@ -1,547 +1,424 @@
1
- # AGL - Agent Gateway Layer
1
+ # AGL
2
2
 
3
- AGL, the Agent Gateway Layer, runs OpenCode and FlexHarness behind a passkey-authenticated controller with a detached CLI lifecycle and a foss.global browser UI.
3
+ 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.
4
4
 
5
- In local mode the controller binds its UI and `@api.global/typedsocket` RPC listener to `127.0.0.1` only — plain-HTTP admission rests on browser-supplied headers, so the loopback bind is the network boundary. Only proxy mode (`--behind-tls-proxy`) binds all interfaces, for a trusted TLS reverse proxy. The supervised OpenCode server always remains on `127.0.0.1`, uses a generated per-process Basic Auth password, and is accessed only through the official `@opencode-ai/sdk` v2 client. OpenCode's own browser application is not embedded, proxied, scraped, or duplicated.
5
+ 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.
6
6
 
7
7
  ## Issue Reporting and Security
8
8
 
9
9
  For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://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/](https://code.foss.global/) account to submit Pull Requests directly.
10
10
 
11
- ## Security posture
11
+ ## What AGL Provides
12
12
 
13
- The controller runs on `@api.global/typedserver` 11 and `@api.global/typedsocket` 8, whose exact package-major handshake completes before application RPC and whose client connection tags are default-deny and owner-controlled. The controller registers no `clientTagPolicy`, so every client-initiated tag mutation is rejected at the transport layer; connection identity and authentication state live exclusively in server-owned peer state, and event, terminal-output, and browser-frame pushes target connections by verifying that state server-side — never by client-managed tags. (Earlier revisions of this repository were deliberately withheld from publication until these upstream packages shipped, rather than concealing the boundary with a local shim.)
13
+ - One browser workspace for OpenCode and FlexHarness conversations.
14
+ - Explicit projects backed by existing directories on disk.
15
+ - Live text, reasoning, tool activity, todos, permissions, and questions.
16
+ - OpenCode and Flex model selection, including per-conversation choices.
17
+ - Conversation groups, ordering, search, rename, archive, and deletion.
18
+ - Synchronized drafts, file attachments, scratchpads, tasks, and goals.
19
+ - Native slash commands with transcript and Git-aware undo/redo.
20
+ - Optional detached Git worktrees owned and validated by the controller.
21
+ - Durable terminal resources that survive browser reloads.
22
+ - Durable browser resources for human use and permission-gated agent automation.
23
+ - Read-only Session Intelligence for questions about a conversation and related sessions.
24
+ - Passkey authentication, temporary CLI passwords, and bounded audit records.
25
+ - Detached start, verified stop, health status, and coordinated in-place upgrades.
14
26
 
15
- Setup-code attempts are globally rate limited (10 per minute, wrong codes included). During the pre-enrollment window a client that can reach the listener could therefore hold enrollment closed by burning the budget a deliberate lockout-over-guessing tradeoff: the window exists only before the first enrollment, and in local mode only loopback processes can reach it at all.
27
+ 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.
16
28
 
17
- ## Architecture
29
+ ## How It Works
18
30
 
19
31
  ```text
20
32
  Browser
21
- └─ exact configured HTTP(S) origin
22
- └─ controller UI + TypedSocket RPC
23
- (127.0.0.1:<controller-port> local mode; 0.0.0.0 behind the TLS proxy)
24
- ├─ WebAuthn passkey verification
25
- ├─ SmartData configuration, ceremonies, audit events, and public projections
26
- ├─ controller-owned Smartshell/node-pty terminal processes
27
- ├─ official OpenCode SDK v2
28
- │ └─ authenticated OpenCode server (127.0.0.1:<opencode-port>)
29
- └─ private framed IPC
30
- └─ isolated FlexHarness child
31
- ├─ filesystem and shell tools
32
- ├─ ephemeral read-only Session Intelligence workers
33
- ├─ provider adapters
34
- └─ kernel-keyed sealed provider credentials
33
+ -> AGL controller and authenticated TypedSocket API
34
+ -> official OpenCode SDK
35
+ -> private OpenCode server on 127.0.0.1
36
+ -> private framed IPC
37
+ -> isolated FlexHarness child
38
+ -> controller-owned terminals, browsers, uploads, and Git state
39
+ -> SmartData persistence and audit records
35
40
  ```
36
41
 
37
- The browser receives only controller DTOs and never receives the OpenCode Basic Auth password, provider credentials, raw provider responses, or the private database descriptor used to initialize the Flex child. An authenticated browser can register existing filesystem directories as projects and select models from either runtime's normalized catalog; the controller validates and scopes every resulting operation.
42
+ 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.
38
43
 
39
- OpenCode and Flex are independently supervised. An unexpected Flex child exit closes prior-generation operations and browser channels, then restarts with capped backoff and reconciles the exact registered projects before becoming ready. An unavailable or unsupported Flex runtime does not take down OpenCode, and durable bounded Flex projections remain readable while the Flex child is offline. Flex is currently supported on Linux x64 with Node.js 24 or 25; other supported controller platforms continue to provide OpenCode sessions and controller-owned terminals.
40
-
41
- FlexHarness private state is persisted in separate SmartData domains for scope metadata, public message projections, remembered permissions, canonical Agent events and archives, and background tool jobs. A fenced per-project writer lease excludes overlapping Flex children, while the parent reads a separately committed bounded public projection. Lease heartbeats and writes share one serialized transition boundary; an expired lease still held by the same controller recovers with a fresh owner token and incremented epoch before work continues. Hidden terminal staging and private canonical events never enter that browser-readable projection; a visible Flex save is acknowledged only after the corresponding bounded view is durable.
42
-
43
- Flex runtime implementation classes and child internals are private. Supported package surfaces include the `agl` CLI, protocol-v22 controller DTO and request contracts, the root-exported AGL home path helpers, and the root-exported controller-owned Git reversion types and engine.
44
+ 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.
44
45
 
45
46
  ## Requirements
46
47
 
47
- - Node.js 24 or newer
48
- - Git 2.43 or newer for workspace reversion and controller-owned worktrees
49
- - A WebAuthn-capable browser
50
- - Linux or macOS for detached `start` and CLI-managed `stop`
51
- - A POSIX user identity for data-using startup, migration, and embedded-database commands
52
- - Native `node-pty` support. When no prebuilt binary is available, installation requires Python 3, `make`, and a C/C++ build toolchain.
53
-
54
- No database server is required: the controller ships an embedded engine
55
- (`@push.rocks/smartdb`) by default. An external MongoDB-compatible server can
56
- be configured instead.
57
-
58
- The package pins `@opencode-ai/sdk` and the official OS/architecture-specific OpenCode binary packages to the same tested version. It resolves the lockfile-installed platform executable directly, without a wrapper postinstall or unrelated global binary. This release is tested against OpenCode `1.18.15`.
59
-
60
- The OpenCode child, Flex child, and controller-owned terminal shells receive only ordinary runtime path, user, locale, temporary-directory, XDG, proxy, and CA environment variables by default. `AGL_HOME` is the deliberate controller-owned path exception; controller/database variables and unrelated parent secrets are not inherited. Explicitly delegate additional environment variables when needed:
61
-
62
- ```sh
63
- export HARNESS_CONTROLLER_FORWARD_ENV='ANTHROPIC_API_KEY,OPENAI_API_KEY'
64
- ```
48
+ - Node.js 24 or newer.
49
+ - pnpm 11 or newer.
50
+ - Git 2.43 or newer for workspace history and managed worktrees.
51
+ - A WebAuthn-capable browser.
52
+ - Linux or macOS for detached `start`, `stop`, and managed upgrades.
53
+ - Native `node-pty` support.
65
54
 
66
- The comma-separated names must already exist in the controller environment. Controller variables, the delegation directive itself, and generated OpenCode server credential names can never be forwarded. Forwarded variables are present in the Flex child's sanitized runtime as well as OpenCode's.
55
+ When a prebuilt `node-pty` binary is unavailable, installation also needs Python 3, `make`, and a C/C++ toolchain.
67
56
 
68
- Flex provider authentication does not consume delegated environment credentials: its initial provider integration uses the OpenAI device OAuth flow in the authenticated settings UI. The controller's source-of-truth credential persistence is a Flex-child-owned `SmartSecretSealedFileStore`, with authenticated ciphertext under the controller data directory and its master key in the Linux kernel keyring; provider credentials never enter SmartData or a controller plaintext filesystem fallback. Existing kernel-only credentials migrate idempotently before Flex becomes ready, preserving their exact validated bytes and deleting the source only after the sealed destination is confirmed. A fresh empty store creates its master key normally; retained ciphertext with unavailable key material, conflicting or malformed credentials, and unreconciled mutation outcomes fail closed instead of replacing credential material. An individually absent credential marks that connection as requiring reauthentication. Settings shows normalized account quota windows without exposing the credential or raw provider response and directs the user to log out and connect OpenAI again when possible.
57
+ FlexHarness currently runs on Linux x64 with Node.js 24 or 25. Other supported controller platforms still provide OpenCode and terminal functionality.
69
58
 
70
- `OPENCODE_AUTH_CONTENT` is rejected case-insensitively before any controller-owned OpenCode, Flex, or terminal runtime is spawned. The controller never reads or edits OpenCode's `auth.json`; a selected Flex OpenAI credential reaches the controller only through private framed IPC, then reaches the authenticated loopback OpenCode process through the official SDK's auth endpoint.
59
+ 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.
71
60
 
72
- Credential-free `HTTP_PROXY` and `HTTPS_PROXY` URLs are part of the minimal runtime environment. Proxy URLs containing user information, and malformed proxy values, require explicit delegation through `HARNESS_CONTROLLER_FORWARD_ENV`.
61
+ ## Install
73
62
 
74
- ## Install and build
63
+ Install the public package and allow its native terminal dependencies to build:
75
64
 
76
65
  ```sh
77
- pnpm install
78
- pnpm build
79
- node cli.js help
66
+ pnpm add --global agl \
67
+ --allow-build=node-pty \
68
+ --allow-build=fs-ext-extra-prebuilt
80
69
  ```
81
70
 
82
- The package is published to npm as `agl` with the binary name `agl`:
71
+ To install from a specific registry, use pnpm's normal registry option:
83
72
 
84
73
  ```sh
85
74
  pnpm add --global agl \
75
+ --registry=https://registry.npmjs.org \
86
76
  --allow-build=node-pty \
87
77
  --allow-build=fs-ext-extra-prebuilt
88
- agl help
89
78
  ```
90
79
 
91
- Alternatively, install from the repository:
80
+ Confirm the installation:
92
81
 
93
82
  ```sh
94
- git clone https://code.foss.global/modelprofile.com/agl.git
95
- cd agl && pnpm install && pnpm build
96
- node cli.js help
83
+ agl --version
84
+ agl help
97
85
  ```
98
86
 
99
- ### Bridge migration to AGL
87
+ ## Quick Start
100
88
 
101
- The package transition is deliberately split across two invocations.
102
- Starting from `@modelprofile.com/harness-controller@20.0.2`, run
103
- the active `hcon` command twice and wait for each command to finish:
89
+ Start AGL without creating an implicit project:
104
90
 
105
91
  ```sh
106
- hcon upgrade --port 4097
107
- hcon upgrade --port 4097
92
+ agl start
108
93
  ```
109
94
 
110
- The first invocation installs the exact same-package bridge
111
- `@modelprofile.com/harness-controller@20.0.3` through coordination transaction
112
- format 2; its command remains `hcon`. The second invocation, once the exact
113
- target is available, resolves and validates `agl@21.0.0` before controller
114
- preparation or package mutation, then performs the format-3 cross-package
115
- transition. After that invocation succeeds, `hcon` is replaced and all further
116
- management commands use `agl`:
95
+ 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.
96
+
97
+ To register one existing directory during startup:
117
98
 
118
99
  ```sh
119
- agl help
100
+ agl start --directory /absolute/path/to/project
120
101
  ```
121
102
 
122
- Use the controller's configured port instead of `4097` for both invocations.
123
- Do not skip the bridge or manually replace the package. This package transition
124
- itself leaves controller data under `$XDG_CONFIG_HOME/hcon`. The first data-using
125
- command (`start`, `foreground`, `upgrade`, or `temp-password`) from an AGL
126
- version with `AGL_HOME` support subsequently moves that retained state into the
127
- canonical AGL home described below without changing persisted
128
- controller, authentication, project, session, Flex, or Git identities.
129
-
130
- ### Upgrade behavior
131
-
132
- AGL uses upgrade management version 2.
133
-
134
- Each invocation launches a detached worker with a private log and follows it to
135
- a terminal result. The same-package invocation compares the installed semantic
136
- version with the old package's npm `latest` tag and never downgrades; the exact
137
- 20.0.3 bridge invocation instead resolves the fixed AGL target described above.
138
- If the controller is running, it authorizes the worker through a one-time local
139
- grant, asks the controller to accept preparation through a bounded management
140
- request, and polls the private durable transaction while the controller
141
- gracefully pauses the exact active-session cohort. Only preparation completed
142
- within the absolute grace deadline and durably committed to the transaction lets
143
- the worker stop the exact verified process, change the package installation, and
144
- restart the controller with its existing environment and stored configuration.
145
- If the controller is stopped, it remains stopped. The public management
146
- transport exposes versioned launch, prepare-begin, legacy prepare-completion,
147
- and finalize methods, but every fresh state-changing handler accepts only a
148
- locally created, single-use 256-bit grant bound to the exact controller process
149
- and package installation.
150
-
151
- In normal progress mode, the command prints the worker PID and log path before
152
- allowing the worker to stop anything; `--json` records those values durably
153
- before admission and emits them only in its terminal document. Concurrent
154
- upgrades and pnpm-global controller starts are serialized. A same-package
155
- failure before target startup restores the previous version when safe. During
156
- the format-3 package transition, failures before the durable target-commit
157
- boundary restore exact bridge-only ownership; from that boundary onward,
158
- recovery converges only to exact `agl@21.0.0` and never restores the bridge.
159
- Session compensation is attempted where rollback remains safe, but any session
160
- whose pause cleanup cannot be proven settled remains fail-closed instead of
161
- receiving an overlapping continuation. Once the new controller has been
162
- invoked, automatic rollback is intentionally disabled because startup may have
163
- applied forward migrations. A retained target process keeps its transaction
164
- token and session barrier and remains fail-closed until exact recovery can
165
- complete; inspect its retained log and correct the reported problem rather than
166
- spawning another controller or reinstalling the previous package. A
167
- stopped-controller upgrade has no inherited cohort and leaves the target
168
- stopped. The worker also refuses an upgrade when another pnpm-global controller
169
- instance exists outside the exact authorized process.
170
-
171
- New coordination transactions live under `$AGL_HOME/upgrade`. A target invoked
172
- by an already-released 21.0.2 worker recognizes that token-bound worker's legacy
173
- `/tmp/harness-controller-upgrade-*` directory only until the inherited
174
- transaction reaches its terminal cleanup; unrelated or unbound legacy paths are
175
- never used as fallback coordination.
176
-
177
- `hcon upgrade` must be run from the active pnpm-global installation and is currently supported on Linux and macOS. Before starting the AGL transition, if a prior manual same-package upgrade replaced the management CLI while an older controller remained running, restore that exact older package first so its process identity can be stopped safely:
103
+ AGL never derives a project from the shell's current directory and never creates project directories. Additional projects can be registered from the browser.
104
+
105
+ Check controller and harness health:
178
106
 
179
107
  ```sh
180
- pnpm add --global @modelprofile.com/harness-controller@<previous-version>
181
- hcon stop --port 4097
182
- pnpm add --global @modelprofile.com/harness-controller@latest
183
- hcon start --port 4097
108
+ agl status
109
+ agl status --json
184
110
  ```
185
111
 
186
- This repair sequence does not replace the two bridge-migration invocations.
112
+ The default ports are:
187
113
 
188
- ## AGL home
114
+ - Browser URL and default WebAuthn origin: `http://localhost:4097`.
115
+ - Controller bind endpoint: `127.0.0.1:4097`.
116
+ - Private OpenCode server: `127.0.0.1:4098`.
189
117
 
190
- AGL-owned persistent state has one private root selected by `AGL_HOME`. The
191
- in-progress home migration also uses a transient sibling lock beside that root;
192
- it is not normal persisted state. Installed packages default to
193
- `$XDG_CONFIG_HOME/agl` (normally `~/.config/agl`); repository checkouts default to
194
- `<checkout>/.nogit/agl`. `AGL_HOME`, when supplied, must be an absolute normalized
195
- non-root path whose direct parent already exists; this parent requirement also
196
- applies to the selected default root. AGL owns
197
- these namespaces:
118
+ ## Everyday Workflows
198
119
 
199
- - `database` for the embedded SmartDB storage root;
200
- - `credentials` for sealed Flex provider credentials;
201
- - `git-reversion` for captures and controller-owned worktrees;
202
- - `logs` for controller and upgrade logs;
203
- - `upgrade` for durable upgrade coordination;
204
- - `cache` for AGL-owned caches;
205
- - `runtime/browser`, `runtime/uploads`, `runtime/sockets`, and
206
- `runtime/opencode` for private runtime state;
207
- - `migration` for the current journal and verified retained migration records.
120
+ ### Projects
208
121
 
209
- Library consumers can resolve the same root-exported path contract and bind it
210
- into a child environment without deriving paths from the child's CWD:
122
+ 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.
211
123
 
212
- ```ts
213
- import { bindAGLHomeEnvironment, resolveAGLHomePaths } from 'agl';
124
+ 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.
214
125
 
215
- const paths = resolveAGLHomePaths();
216
- const childEnvironment = bindAGLHomeEnvironment(process.env, paths);
217
- ```
126
+ 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.
218
127
 
219
- `resolveAGLHomePaths()` returns `development`, `root`, `database`,
220
- `credentials`, `gitReversion`, `logs`, `legacyLogs`, `upgrade`, `cache`,
221
- `runtime`, `browserRuntime`, `uploads`, `sockets`, `openCodeRuntime`, and
222
- `migration`. `bindAGLHomeEnvironment()` returns a copy of the supplied
223
- environment with `AGL_HOME` bound to `paths.root`.
224
-
225
- The package installation itself, user-selected project directories, an explicit
226
- `HARNESS_CONTROLLER_DB_DIR` (including one that names a recognized legacy
227
- SmartDB path), an external MongoDB server, and OpenCode's own user-wide
228
- configuration are not AGL-owned state and remain outside this root.
229
- `AGL_HOME` is bound into controller, upgrade, Flex, and OpenCode process
230
- boundaries so a detached process never derives ownership from its launch CWD.
231
-
232
- The one-time v23 home migration runs before database model access from `start`,
233
- `foreground`, `upgrade`, or `temp-password`. It first
234
- normalizes the prior `harness-controller` to `hcon` migration when necessary,
235
- then uses SmartDB and SmartSecret relocation APIs for the active database and
236
- sealed credentials. Git state, browser state, logs, and verified legacy records
237
- move under the same journal. The migration is same-filesystem only, checks
238
- private ownership, exact source inventories, live writers, sockets, symlinks,
239
- target collisions, and source identities before mutation, and safely reconciles
240
- an interruption. Active controller-owned Git worktree state blocks migration
241
- because Git's linked-worktree metadata contains absolute paths. Unknown or
242
- ambiguous state fails closed. Verified historical database material is retained
243
- under `$AGL_HOME/migration`; committed legacy roots are removed only when their
244
- known entries are empty. Never move these directories or edit their journals by
245
- hand.
246
-
247
- An explicit non-default `AGL_HOME` in a development checkout starts as an
248
- isolated fresh home and does not consume that checkout's shared legacy
249
- `.nogit` state.
250
-
251
- ## Database configuration
252
-
253
- Structured application state is stored exclusively through SmartData. There is no PID, configuration, authentication, or JSON persistence fallback on the filesystem.
254
-
255
- By default the controller runs an embedded `@push.rocks/smartdb` engine — no
256
- external database server. Storage lives under
257
- `$AGL_HOME/database`. Override the directory with
258
- `HARNESS_CONTROLLER_DB_DIR` (absolute path). Explicit database paths and
259
- external MongoDB configurations are never moved. The engine listens on a
260
- deterministic Unix socket under `$AGL_HOME/runtime/sockets`, and a second controller
261
- process on the same machine (for example `agl temp-password`) attaches to the
262
- running daemon's engine instead of opening the storage files again. The complete
263
- embedded Unix socket path must be no longer than 100 UTF-8 bytes; a second
264
- controller must use the same `AGL_HOME` and database directory to attach.
265
- During the one-time home migration only, AGL also probes the published legacy
266
- `/tmp/harness-controller-<16-hex>.sock` name to prove that the old embedded
267
- engine is stopped. New runtime sockets are never created at that legacy path.
268
-
269
- When upgrading from a controller release backed by FlexHarness 2, the new Flex child migrates each active project's legacy snapshot during initialization, before admitting runtime work. Migration preflights every destination, writes per-session domains before publishing scope discovery, repairs crash-interrupted streaming messages as cancelled, and safely reruns after partial completion. A durable completion marker prevents the retained source from being reapplied after live FlexHarness 3 state evolves. The legacy private snapshot remains retained as a non-authoritative recovery source until that project is removed; startup also repairs any bounded public projection whose prior save was not acknowledged.
270
-
271
- To use an external MongoDB-compatible server instead, set:
128
+ ### Conversations
272
129
 
273
- ```sh
274
- export HARNESS_CONTROLLER_MONGO_URL='mongodb://user:password@127.0.0.1:27017'
275
- export HARNESS_CONTROLLER_MONGO_DB='opencode_controller'
276
- ```
130
+ 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.
277
131
 
278
- `HARNESS_CONTROLLER_MONGO_DB` defaults to `opencode_controller` in both modes.
132
+ Conversation features include:
279
133
 
280
- For local package development, MongoDB is declared as the GitZone service:
134
+ - Tip-first transcript loading with older-history pagination.
135
+ - Live Assistant text, reasoning, tool state, todos, and subagent cards.
136
+ - Inline permission requests and questions in transcript order.
137
+ - Synchronized drafts and private temporary attachments.
138
+ - Durable model choices and scratchpads.
139
+ - Rename, archive, delete, abort, and session-specific Ask/Yolo controls.
140
+ - Token and context metrics when the harness can prove them from complete history.
281
141
 
282
- ```sh
283
- gitzone services start mongo
284
- gitzone services status
285
- ```
142
+ 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.
286
143
 
287
- Use the `MONGODB_URL` from `.nogit/env.json` as `HARNESS_CONTROLLER_MONGO_URL`. GitZone service data remains under `.nogit/` and is not application persistence or package content.
144
+ ### Groups And Ordering
288
145
 
289
- ## First start
146
+ 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.
290
147
 
291
- Start AGL without creating an implicit project:
148
+ 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.
292
149
 
293
- ```sh
294
- agl start
295
- ```
150
+ ### Models And Provider Accounts
296
151
 
297
- To register one existing directory during startup, pass it explicitly:
152
+ AGL stores an independent default model for OpenCode and FlexHarness. A conversation can override its harness default without changing other conversations.
298
153
 
299
- ```sh
300
- agl start --directory /absolute/path/to/project
301
- ```
302
-
303
- The detached process first reserves the controller listener (`127.0.0.1:4097` in local mode; `0.0.0.0:4097` only with `--behind-tls-proxy`), spawns the isolated Flex child, starts the official OpenCode server in headless server mode on `127.0.0.1:4098`, and validates local PTY support. On an unsupported runtime the spawned Flex child reports `unsupported`; Flex startup is isolated and does not block OpenCode readiness. Before the first enrollment only, the terminal prints a one-time setup code — a random 32-byte value by default, or the operator's own value when `--setup-code` is passed.
154
+ 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.
304
155
 
305
- Open the printed URL, enter the setup code, and create a passkey. The code expires after 30 minutes and is atomically consumed by the first successful credential enrollment. If an unenrolled code expires, stop and start the controller to rotate it.
156
+ 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.
306
157
 
307
- `--setup-code <code>` accepts 4–256 printable non-whitespace ASCII characters and trades entropy for convenience — understand what it weakens before using it. The enrollment rate limiter (10 attempts per minute) is in-memory and resets on every restart, so a fixed code accumulates guessing budget across restarts instead of being invalidated by rotation the way random codes are. The stored verifier is an unsalted SHA-256 hash, so a low-entropy code is trivially recoverable by anything that can read the controller database. And while the code never appears in the detached child's process list (it travels over parent-child IPC), it is visible in the parent CLI's own argv and your shell history. On a shared machine, prefer a long random value or omit the option entirely.
158
+ ### Commands And Workspace History
308
159
 
309
- Exactly one passkey credential exists per controller database, permanently: enrollment is first-wins and no re-enrollment path is exposed. **A lost passkey is not recoverable through the controller** — recovery means deleting the controller's database (or its auth collections) and enrolling again from a fresh setup code. This is deliberate; an online credential-reset path would be a second, weaker way in. Up to eight separately expiring CLI temporary-password credentials may be active at once.
160
+ 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.
310
161
 
311
- Authentication is bound to the exact TypedSocket peer and disappears when that socket disconnects. A fresh browser connection can authenticate with a passkey or CLI temporary password, or re-authenticate by consuming the prior peer's bearer resume token. The raw resume token is kept only in browser session storage while its SHA-256 hash stays in controller memory; it is single-use, rotates on every resume, is invalidated by controller restart, and has a fixed maximum chain lifetime of 12 hours that is additionally capped by temporary-password expiry. The controller does not issue cookies, trusted client tags, or localhost authentication bypasses.
312
-
313
- ## CLI
162
+ 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.
314
163
 
315
- ```sh
316
- agl start [options]
317
- agl status [--port 4097] [--json]
318
- agl stop [--port 4097]
319
- agl upgrade [--port 4097] [--grace-period-seconds 300] [--continue-sessions] [--json]
320
- agl foreground [options]
321
- agl temp-password [--port 4097] [--ttl-hours 24]
322
- agl help
323
- agl --version
324
- ```
325
-
326
- Initial runtime options:
164
+ Flex also supports explicit managed worktrees:
327
165
 
328
166
  ```text
329
- --port <port> controller port, default 4097
330
- --opencode-port <port> loopback OpenCode port, default 4098
331
- --directory <path> register this existing directory as an initial project;
332
- omitted means no implicit project
333
- --projects-root <path> default base directory: relative project paths and
334
- initial suggestions resolve here; explicitly typed
335
- absolute projects may be outside it; fresh installs
336
- default to the user home and it is immutable afterward
337
- --public-origin <origin> exact browser WebAuthn origin
338
- --rp-id <hostname> WebAuthn RP ID
339
- --behind-tls-proxy declare trusted TLS termination upstream
340
- --setup-code <code> operator-chosen setup code (see the warning above)
167
+ /worktree create
168
+ /worktree list
169
+ /worktree remove <worktreeId>
341
170
  ```
342
171
 
343
- Runtime configuration is durable and immutable per controller port. Later management commands must use the same `--port`; omitted initial runtime options are loaded from SmartData. `--directory` is different: it is a transient request to register one existing project and may be supplied on a later start without changing the durable runtime configuration.
172
+ Worktrees are never created automatically. Removal refuses a worktree with tracked, untracked, or ignored changes.
344
173
 
345
- `upgrade` keeps the CLI attached to its detached, crash-survivable worker and streams progress until the durable transaction succeeds or fails. Before stopping a running controller it closes new harness-prompt admission, durably records the exact cohort and absolute preparation deadline, then acknowledges the bounded request while controller-owned work waits for active operations and asks those sessions to pause at the earliest convenience. The worker polls that private transaction with bounded source-process checks; it cannot stop the controller or enter package installation until preparation completion is durably committed on time. The global upgrade lock also drains controller-start and `temp-password` database-writer leases, then package replacement requires the source port to be closed and no exact source controller, `temp-password`, or Flex child writer to remain. A failed or expired preparation compensates settled pauses and leaves uncertain cleanup fail-closed. The retained cohort is limited to 64 exact sessions. `--grace-period-seconds` accepts 1–3600 seconds and defaults to 300. Without `--continue-sessions`, the upgraded controller reopens prompt admission without sending another message. With it, the matching continuation message is submitted at most once to the durably retained paused cohort after the upgraded controller returns. Rollback recovery attempts to compensate paused sessions within a separate maximum 60-second finalization window and does not overlap an unresolved pause with a continuation. `--json` replaces progress lines with one terminal JSON document after worker admission; preflight and launch failures still use normal CLI errors. That document includes worker identity and log path, whether the controller was running, source version, optional target version, terminal phase/success/error, and pause/continuation state counts; failures exit nonzero after writing it.
174
+ ### Tasks, Goals, Scratchpads, And Delegation
346
175
 
347
- The detached CLI does not write a PID file. It appends the controller's stderr diagnostics to `$AGL_HOME/logs/controller-<port>.log` (rotated to `.old` past 1 MiB on start; stdout stays discarded because the transport logs every connection there). The log file must be a regular file owned by the current user without group or world access — the start fails loudly rather than logging somewhere surprising. The initial setup code travels once over parent-child IPC. `status` connects only to `127.0.0.1` and validates package and protocol identity. `stop` additionally validates the exact CLI path (relative launches are resolved against the process's working directory), command, effective port (an argv without `--port` verifies as the default port), process group, and process-start fingerprint. It sends cooperative `SIGTERM` only to that exact PID so the controller can stop harnesses and database resources in order. If the process remains after 30 seconds, a foreground controller receives exact-PID `SIGKILL`; a detached controller must still be the reverified process-group leader before its complete group receives `SIGKILL`. It never kills processes by name.
176
+ 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.
348
177
 
349
- `foreground` is available for attached operation and diagnostics. Detached management is intentionally limited to Linux and macOS in this release.
178
+ 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.
350
179
 
351
- ### CLI temporary password
180
+ ### Session Intelligence
352
181
 
353
- `temp-password` mints a random 24-byte password that signs the browser UI in without a passkey the "Use a CLI temporary password" link on both the initial setup screen and the normal login screen opens the matching input. It exists for temporary and scripted access (for example, using shell access to authorize a browser that does not hold the enrolled passkey, or letting an agent drive the real UI for debugging) without enrolling another passkey.
182
+ 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.
354
183
 
355
- The command talks to the controller database directly, so it requires the same database environment as the running controller (in embedded mode it attaches to the daemon's engine socket) — minting authority is database access, and nothing network-reachable can issue one. Only the SHA-256 hash is stored. The lifetime is between one minute and 24 hours (`--ttl-hours`, default 24), expiry is enforced on every password lookup and subsequent resume, at most 8 can be active at once, and the password is reusable until expiry. An already-authenticated TypedSocket peer remains authenticated until disconnect even if its password expires. Logins use the same exact origin checks, generic unauthenticated admission limits, per-connection authentication, and audit trail (`temppassword.create` / `temppassword.login`); WebAuthn-specific ceremony limits do not apply. A temporary password can also authenticate one browser while initial passkey setup remains pending; it neither consumes the setup code nor enrolls a passkey, and unauthenticated browsers continue to see the setup screen.
184
+ Answers and scratchpad suggestions are bounded and revision-aware. At most four analyses run controller-wide and one per source conversation.
356
185
 
357
- ## Projects and default models
186
+ ## Resources
358
187
 
359
- Startup preserves the exact project registry: an empty controller remains
360
- empty, and only an explicit `--directory` registers an initial project. The
361
- supervised OpenCode server runs from
362
- `$AGL_HOME/runtime/opencode/<controller-hash>`, never from a project or the
363
- invoking shell's CWD for its implicit runtime state. Relative `--directory` and
364
- `--projects-root` values are resolved against the invoking shell before the
365
- detached process starts. A stored `projectsRoot` is validated lexically during
366
- startup and resolved only when a relative project operation needs it, so a
367
- temporarily unavailable root does not stop the controller. This v23 persisted
368
- document/home migration revision supersedes the historical v2 automatic-workspace
369
- registration; the wire protocol remains v22.
188
+ Resources are durable project-scoped capabilities separate from conversations.
370
189
 
371
- Chats are organized into projects. A project is any registered **existing** directory on disk: the browser's add-project field accepts full absolute paths (`/any/path/on/disk`) as well as paths relative to the configured `--projects-root` default base (`org/repo`), and suggests matching folders live while typing either form — absolute queries browse the actual filesystem. Hidden directories are never suggested (though an explicitly typed absolute path may contain dot segments), dot navigation is rejected, and registration fails visibly when the path does not exist — the controller never creates project directories. Projects inside the default base get short relative display names; projects elsewhere are named by their absolute path. Every session method is scoped to one project. One supervised OpenCode server serves all projects through per-call directory scoping, while the Flex child resolves each session to its registered project root before providing filesystem or shell tools. Startup never derives or registers a project from the process CWD. Removing a project first blocks new resource operations and requires every durable terminal and browser resource to be retired. The controller then persists removal intent before destructive work, hides the project from normal admission, drains Session Intelligence and harness operations, clears controller-owned state, asks Flex to remove that exact project namespace, and removes controller-owned Git worktrees, worktree records, and reversion capture roots. Interrupted removals resume on startup and remain pending when required runtime cleanup cannot be confirmed. Flex storage is never deleted merely because a project is absent from an active-project snapshot. Files in the registered project working tree are never deleted; path cleanup is limited to controller-owned worktrees, while controller-private capture stores and worktree records are removed only from controller-owned storage. Note the trust consequence: an authenticated browser session can register and operate on any directory the controller's user can read.
190
+ ### Terminals
372
191
 
373
- Registration persists the complete ordered device/inode ancestry from the filesystem root through every project path component, with the target recorded as a directory. Controller requests, Flex tools, Git reversion, background cleanup, and shutdown revalidate the entire chain before using the path, so preserving the project leaf inode beneath a substituted ancestor does not authorize the replacement. The first upgrade to this format binds each currently available truly legacy project exactly once under its owning controller. A legacy project that is unavailable during that migration remains terminally unbound, and an incomplete pre-release `bound` record becomes terminally unbound instead of acquiring missing ancestors. Such projects stay visible for operator diagnosis but are excluded from runtime admission and path-sensitive cleanup. Their removal retires controller-owned metadata while intentionally retaining retired resource tombstones and path-scoped OpenCode, Flex, and Git artifacts. A pending bound removal remains durable until its original complete binding is available, rather than completing against an untrusted replacement.
192
+ A terminal resource runs a real shell in the project directory through the controller's shell-free node-pty integration.
374
193
 
375
- The settings dialog stores one independent default model per session harness. OpenCode models come from its configured provider catalog. Flex models are associated with an opaque provider connection. After OpenAI device login succeeds, the browser automatically starts that connection's catalog refresh and renders its progress live; active connections can refresh models and account usage manually. While Flex recovers, `controller.provider.connection.list` returns allowlisted persisted account metadata with `runtimeAvailable: false`; Settings preserves those accounts, labels them read-only, and blocks login, logout, activation, catalog refresh, and usage refresh until both the RPC and controller health report readiness. A stored default applies to that harness across projects and browsers. The chat composer exposes an account picker for Flex and stores its exact model plus reasoning-effort choice durably for that controller/project/session, without changing either global default. Every Flex choice is validated by the child against the active connection and its refreshed catalog before persistence or execution. Automatic selection occurs only when exactly one active account has exactly one provider-advertised default model; multiple accounts or ambiguous defaults require an explicit choice. Logging out first persists a non-active connection fence, then clears every matching session choice and the matching global default before removing the credential. Provider-scoped serialization prevents a concurrent update from restoring those choices, while live and startup recovery finish any fenced logout interrupted by a timeout or process exit. Project, provider, model, and settings changes are pushed live over the authenticated TypedSocket connection.
194
+ - Closing or reloading the browser does not stop the terminal.
195
+ - Reattaching replays bounded in-memory scrollback.
196
+ - Terminal title, attachment, stopped state, and last exit code are durable.
197
+ - Terminal input, output, and scrollback are never persisted.
198
+ - A terminal can be renamed, stopped, restarted, detached, or retired.
376
199
 
377
- Each active OpenAI connection also offers **Use for OpenCode**. Switching seals only OpenCode admission, captures the exact active OpenCode cohort, waits its accepted work to settle, and asks those conversations to pause before replacing the controller-owned OpenCode supervisor, SDK client, and event stream. Once accepted, the switch is owned by the controller: it survives disconnection of the initiating browser peer, but controller shutdown still aborts it. The selected credential is installed through the official SDK; only the opaque selected connection ID is persisted, after the staged runtime is healthy. Every captured conversation must accept its continuation prompt before admission reopens; an incomplete continuation stops the selected runtime and leaves OpenCode sealed. Flex conversations remain available throughout. A pre-commit failure keeps the old runtime only after successful compensation and a fresh health check, or restores only a confirmed prior controller-managed account after replacement; if neither is safe, OpenCode stops and admission remains sealed. The persisted selection is restaged before OpenCode-dependent startup work after every controller restart, and restoration failure likewise leaves OpenCode fail-closed. The selected account cannot be logged out until another active account has replaced it.
200
+ AGL allows at most eight running terminals per project and 32 controller-wide.
378
201
 
379
- Controller protocol v22 exposes one harness-aware slash surface through `controller.slash.list` and `controller.slash.execute`. The list returns native built-ins, templates, or handlers with hints, availability, workspace-reversion capability, and exact undo/redo state for the selected session. The browser shows local harness built-ins immediately, enriches them from the session-scoped server catalog, and delegates keyboard, touch, and composition behavior to the controlled `dees-harness-chat` composer. Flex session details also show the deduplicated affected Git repository union and any truncated or non-revertible history barrier reported by the catalog. Execution consumes one exact attachment-free draft revision. OpenCode templates expand server-side and honor command-pinned agents and models; OpenCode `compact`, `init`, `undo`, and `redo` remain native operations. Flex delegates parsing, listing, and execution to FlexHarness. Only an authoritative unknown command falls back to an ordinary prompt, preserving the draft bytes exactly under the same submission reservation; malformed commands, known-command failures, cancellation, timeout, disconnect, and unknown transport outcomes never fall back.
202
+ ### Browsers
380
203
 
381
- Flex `/undo` and `/redo` move transcript and workspace history together. Starting new Flex work from an undone position commits a branch and permanently removes that redo history. Retention pruning and successful Agent-event archival bound older traversal, and legacy transcript-only history is a non-revertible barrier. When a project lies inside a Git working tree, that containing repository is one atomic reversion unit: capture and apply cover tracked state plus non-ignored untracked paths across that working tree rather than only the registered project subdirectory; ignored paths remain outside reversion and unchanged. Descendant repositories are separate atomic units. Current `HEAD` and its attached branch may move between captured sides and remain reversible. Shallow repositories, partial clones, promisor remotes, object-directory alternates, unsupported object formats, and invalid repository layouts are rejected before capture. In-progress Git operations are recorded as non-revertible barriers; later out-of-scope ref, symbolic-ref graph, worktree or nested-repository topology, repository identity, and ignore-policy changes fence traversal. An index replaced by an unsupported file fails apply rather than becoming a barrier.
204
+ A browser resource provides tabs, navigation, viewport control, mouse, keyboard, and text input through a human canvas. Frames use finite authenticated binary streams rather than public URLs or base64 JSON payloads.
382
205
 
383
- Each capture owns one controller-private `captures/<captureKey>` root. Every participating repository gets a private bare Git metadata store and disposable object generations; the bound source object database is available only as a read-only alternate. Capture, finalize, inspection, and release therefore do not add controller refs, metadata blobs, trees, or generated objects to the source repository. Apply is the sole capture-traversal operation that mutates captured worktree, index, or ref state: before mutation it creates an operation-unique, self-contained closure pack in private storage, imports that exact bounded pack without a reverse index or controller ref, and retains it with an operation-owned `.keep` file through roll-forward or compensation. The explicit worktree create/remove methods separately update the source repository's linked-worktree metadata. Private publication and capture-root lifecycle are journaled so startup can recover interrupted publication, apply, creation, and deletion without trusting files inside the tree being removed.
206
+ 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.
384
207
 
385
- Library consumers can construct the root-exported `ControllerGitReversion` with `IGitReversionOptions`, register each project with its canonical `IControllerProjectFilesystemIdentity`, and call `init()` or `recover()` before source-sensitive work. Its default private root is under `$AGL_HOME/git-reversion`; `temporaryRoot` can still supply an explicit absolute owner root. Capture flow uses `IGitReversionCaptureRequest` with `prepare()`, `inspectCapture()`, and `finalize()`; undo and redo use `IGitReversionApplyRequest` with `inspectApply()` and `apply()`. `IGitReversionMetadataRequest` drives `listAffected()` and `getReversionMetadata()`, while `IGitReversionWorktreeRequest` and `IGitReversionWorktreeRemoveRequest` drive the detached-session worktree methods. Only when no further undo, redo, inspection, or recovery is needed should terminal cleanup call `release()` with `IGitReversionReleaseRequest`, which permanently deletes the capture root. Call `close()` to seal admission and drain or fail closed on owned work.
208
+ ## Authentication
386
209
 
387
- Flex also exposes `/worktree create`, `/worktree list`, and `/worktree remove <worktreeId>`. These commands explicitly create and manage detached, locked, controller-owned worktrees for the current conversation; worktrees are never created automatically. Creation durably binds the private parent before `git worktree add`, then binds the child before activation. Recovery, cleanup, and removal revalidate the required parent and child bindings; a replacement is fenced and retained. Listing validates the registered project before reading bounded private records, but does not dereference or revalidate every listed worktree path. The run-scoped `git_worktree` tool provides the same create/list/remove operations to the agent, with permission required for create and remove. Removal refuses any worktree containing tracked, untracked, or ignored changes.
210
+ AGL creates exactly one passkey credential per controller database. Enrollment is first-wins and there is no weaker online passkey-reset path.
388
211
 
389
- Git recovery is an engine-wide admission barrier and all queues, commands, capture stores, metadata, generated objects, worktrees, scans, and path sets are bounded. At most four Git commands run concurrently and 256 commands or ownership waits may queue. The default persistent bounds include 1,024 captures, 128 controller-owned worktrees, 64 repositories per project, 1 GiB and 1,000,000 objects per generation, 4 GiB and 4,000,000 objects per capture, and 16 GiB and 16,000,000 objects controller-wide. Limit breaches fail closed or create a durable non-revertible barrier where capture can safely report one. Repository-free captures use the same private lifecycle and remain until explicit release or project removal; they are never TTL/LRU evicted.
212
+ 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.
390
213
 
391
- Filesystem checks intentionally document, rather than overstate, their authority boundary: validation and use are separate pathname operations, so a same-UID actor able to replace components in the narrow interval between the final identity check and the operation can still race them. The implementation does not claim descriptor-relative traversal or Linux `openat2` elimination of that race.
214
+ For temporary or scripted browser access, mint a CLI password:
392
215
 
393
- The UI shows the running controller version in the header and compares it with the version compiled into the browser bundle. Upgrade status carries the source and target versions before shutdown, survives reconnect through a sanitized projection of the durable private transaction plus browser session storage, and triggers one cache-busted reload when the upgraded server returns. A repeated mismatch stops in an explicit stale-bundle state instead of looping. After authentication, the bottom status bar samples whole-system CPU usage, active memory usage, and aggregate non-virtual-interface network receive/transmit rates approximately every two seconds through the protected `controller.system.metrics` method. CPU flashes a warning after whole-system usage remains above 50% for 10 seconds. Host metrics are neither requested nor rendered before authentication.
216
+ ```sh
217
+ agl temp-password --ttl-hours 2
218
+ ```
394
219
 
395
- ## Remote browser access
220
+ Temporary passwords expire after at most 24 hours. Only their SHA-256 hashes are stored, and at most eight can be active.
396
221
 
397
- WebAuthn requires a secure context away from `localhost`. Plain HTTP is therefore accepted only for the exact development origin `http://localhost:<controller-port>`.
222
+ 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.
398
223
 
399
- ### TLS reverse proxy
224
+ ## CLI
400
225
 
401
226
  ```sh
402
- export HARNESS_CONTROLLER_TRUSTED_PROXY_SECRET="$(node -e "console.log(require('node:crypto').randomBytes(32).toString('base64url'))")"
403
- agl start \
404
- --public-origin https://controller.example.com \
405
- --rp-id controller.example.com \
406
- --behind-tls-proxy
227
+ agl start [options]
228
+ agl status [--port 4097] [--json]
229
+ agl stop [--port 4097]
230
+ agl upgrade [--port 4097] [--registry <url>] [--grace-period-seconds 300] [--continue-sessions] [--json]
231
+ agl foreground [options]
232
+ agl temp-password [--port 4097] [--ttl-hours 24]
233
+ agl help
234
+ agl --version
407
235
  ```
408
236
 
409
- Remote mode deliberately requires a trusted TLS reverse proxy so loopback CLI management remains available over the controller's HTTP listener. The proxy must remove client-supplied copies of the transport headers, inject the configured secret as `X-Harness-Controller-Proxy-Secret`, and set `X-Forwarded-Proto: https` on WebSocket upgrades; browsers never receive the secret. The controller rejects browser WebSockets without both values. Preserve the exact public `Host` and browser `Origin`, forward WebSocket upgrades, apply source-address limits to upgrades and unauthenticated ceremonies, and restrict direct access to the controller backend to the trusted proxy network. Do not expose an unprotected plain-HTTP backend route.
237
+ Important startup options:
410
238
 
411
- **Backend isolation is a hard requirement in proxy mode, not defence in depth.** The backend listener binds all interfaces there, and origin-less WebSocket connections (the loopback CLI management path) are admitted without the proxy secret by design — the controller cannot see the peer's network address to restrict this itself. Every pre-authentication surface on a directly reachable backend port is exactly as exposed as that port.
239
+ ```text
240
+ --port <port> Controller port; default 4097
241
+ --opencode-port <port> Private OpenCode port; default 4098
242
+ --directory <path> Register one existing project on this start
243
+ --projects-root <path> Base for relative project paths and suggestions
244
+ --public-origin <origin> Exact browser WebAuthn origin
245
+ --rp-id <hostname> WebAuthn relying-party ID
246
+ --behind-tls-proxy Declare trusted TLS termination upstream
247
+ --setup-code <code> Supply an operator-chosen initial setup code
248
+ ```
412
249
 
413
- The proxy secret authenticates only the TLS termination boundary. It is not a user credential and never replaces required per-connection passkey, temporary-password, or resume-token authentication.
250
+ 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.
414
251
 
415
- RP IDs must equal the public-origin hostname or a valid parent domain. Public suffixes, private multi-tenant suffixes, IP addresses, and unrelated domains are rejected.
252
+ `stop` verifies the exact package CLI, process, process group, command, port, and process-start fingerprint before signaling anything. AGL never kills processes by name.
416
253
 
417
- ## Session and permission control
254
+ ## Upgrades
418
255
 
419
- After per-connection user authentication, the UI can:
256
+ Use the active global installation:
420
257
 
421
- - list, create, select, and delete sessions in registered projects;
422
- - choose OpenCode or Flex when creating a session;
423
- - read normalized messages, reasoning, tools, usage, and todos through qualified, collision-safe runtime IDs;
424
- - follow bounded live OpenCode agent-shell output while a tool is running;
425
- - inspect session-level token metrics when the complete harness history makes them provable;
426
- - maintain a revisioned session scratchpad and ask Session Intelligence questions when `openai/gpt-5.6-luna` is connected;
427
- - send text prompts and abort a busy session;
428
- - display OpenCode and Flex permission requests as first-class UI state;
429
- - explicitly allow a permission once or reject it;
430
- - answer OpenCode questions with the advertised options (single- or multi-select) or a free-text answer where the question allows one.
258
+ ```sh
259
+ agl upgrade --port 4097
260
+ ```
431
261
 
432
- Permission requests and questions render **inline in the conversation**, interleaved with messages like any other turn; answered cards stay in the transcript showing what was decided. Oversized transcript pages preserve every selected source bundle while progressively omitting tool input, tool output, reasoning, and text payloads; individually oversized Flex messages already enter the public projection as metadata-preserving omission placeholders.
262
+ By default, upgrade package commands use pnpm's effective registry configuration. Override it for one durable upgrade transaction when needed:
433
263
 
434
- Opening a conversation is tip-first. Controller protocol v21 returns up to 20 newest source-message bundles immediately. When an intentional upward scroll reaches the transcript top, the browser requests one older page of up to 50 bundles; the same demand-driven behavior applies to the read-only subagent drill-in. Session metrics and Session Intelligence capability load through a separate auxiliary request and never delay the transcript. Paging keeps the newest 200 source bundles within a 512 KiB transcript budget, preserves the visible scroll anchor while prepending, and exposes partial history when retention, byte limits, cursor rejection, or a repeated cursor prevents safe continuation. A stream-epoch change invalidates that load and starts a fresh authoritative read. Provider cursors are opaque best-effort continuation tokens rather than immutable snapshots, so duplicate source IDs retain the newer core page and older pages only fill missing bundles. The exported `controllerInitialMessageBundleLimit`, `controllerMaximumMessageBundles`, and `controllerMaximumTranscriptBytes` constants define these bounds; `IControllerMessagePage.historyLimited` reports controller-side source-bundle or payload omission.
264
+ ```sh
265
+ agl upgrade --port 4097 --registry=https://registry.npmjs.org
266
+ ```
267
+
268
+ 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.
269
+
270
+ The upgrade worker:
435
271
 
436
- Live OpenCode tool output comes from ordinary `message.part.updated` snapshots on the official SDK's `GET /global/event` stream. Agent shell tools are named `bash`; their stdout and stderr are already combined by OpenCode. Running and errored tools expose the current output through `state.metadata.output`, while completed tools expose final output through `state.output`; the controller always treats these values as complete replacements, never append-only deltas. OpenCode retains only the final 30,000 UTF-16 code units in a running preview and normally limits final inline output to 2,000 lines or 50 KiB, with larger complete output available only in OpenCode's server-local temporary file. Controller tool events are additionally bounded to 48 KiB including their protocol envelope; the exported `controllerLiveToolPayloadNotice` marks an input or output deliberately omitted to keep that envelope bounded.
272
+ - Resolves the registry's `latest` version and never downgrades.
273
+ - Pauses the exact active-session cohort before stopping a running controller.
274
+ - Waits for admitted operations and data writers to drain.
275
+ - Installs with the required native build allowances.
276
+ - If the controller was running, restarts the exact target and reopens prompt admission.
277
+ - Preserves its transaction across worker or controller failure.
437
278
 
438
- Live Assistant text and reasoning use separate protocol-v21 message stream epochs and cursors, independent of tool delivery. The OpenCode adapter builds bounded replace-only snapshots from each authoritative `message.part.updated` base plus its exact `message.part.delta` sequence; missing bases, unsupported deltas, removals, reconnects, and size overflow fail closed to immediate authoritative hydration. Flex keeps its ordered child part snapshots on the same public event DTOs over Flex IPC v13. `controller.session.get` returns both `toolStreamCursor` and `messageStreamCursor`; browsers render current snapshots directly and retire a cursor-covered overlay only when the hydrated payload semantically represents it. Incomplete, elided, or failed hydration retains the overlay.
279
+ Use `--continue-sessions` to submit the continuation message to sessions paused for the upgrade. Without it, AGL reopens admission without sending another message.
439
280
 
440
- The global event stream has no replay cursor. The controller therefore assigns a monotonic revision inside a stream epoch, advances the epoch whenever a connection or delivery gap can have lost snapshots, and emits an ordered `harness.changed` barrier before later events from that epoch. `controller.session.get` merges controller-observed snapshots that raced the authoritative OpenCode read and returns the latest covered `toolStreamCursor`; browsers discard overlays from older epochs and reject delayed events at or below that cursor unless a matching terminal event supplies fields omitted by hydration. That omission-only supplement preserves every defined durable field and cannot override a newer terminal result. Interactive PTYs remain a separate controller-owned terminal feature and never share this agent-tool stream.
281
+ 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`.
441
282
 
442
- A new chat starts as a client-side draft: the chat pane shows the project directory the session will start in, and nothing is persisted until the first message is sent. The generic **New conversation** action always starts OpenCode; the New menu offers an explicit Flex chat. On first send, the selected OpenCode or Flex harness creates its session and receives the message in one step; an abandoned draft leaves no empty session behind. Right-clicking a conversation opens a context menu with **Rename** (updates the owning OpenCode or Flex harness's session title), **Archive** (archives it through that owning harness), and **Delete** (with confirmation). Archive requires the exact conversation to have no active prompt, pending permission, background command, or Session Intelligence job; active work must settle first. A successful delete removes the local conversation immediately and refreshes the authoritative list in the background, so New is available without waiting for a supplemental reload. Archived chats are hidden from the conversation list by default and live in a dedicated archived dialog reachable from the sidebar. Only top-level conversations appear in the list. Subagent child sessions stay internal to their parent chat: active children show bounded live transcripts, while recent completed or errored children remain as result cards. A dedicated read-only drill-in opens the larger retained child-session view.
283
+ Upgrade coordination lives under `$AGL_HOME/upgrade`. Private logs live under `$AGL_HOME/logs`.
443
284
 
444
- Flex titles can be changed while prompts are queued or running, while a permission is pending, and after archival. Each Flex conversation also receives FlexHarness's unpermissioned `rename_session` metadata tool. The Harness validates a non-empty title of at most 2,048 UTF-8 bytes, persists it as supplied, and updates only the exact project and session whose run invoked it.
285
+ ### Migrating From `hcon`
445
286
 
446
- Flex conversations expose FlexHarness's foreground `delegate` tool through one depth-1 `general` subagent. The child inherits the project's selected model and the same run-scoped project tools. Delegation, filesystem and shell mutations, and browser mutations remain permission-gated; Harness-owned `rename_session`, `task`, `goal`, and `scratchpad` operations do not request permission. Direct-child permissions render in the parent conversation and replies are routed to the exact child session. Flex IPC v13 carries cumulative child-session and model metadata so the existing task cards, live previews, retained results, and drill-in work before and after durable transcript hydration. Bounded public projection placeholders preserve as many task links as fit, and root conversations are retained before child sessions when the projection reaches its session limit.
287
+ Starting from `@modelprofile.com/harness-controller@20.0.2`, run the active command twice:
447
288
 
448
- Every Flex conversation also receives FlexHarness's session-local project-management tools: `task` lists and mutates structured work items, `goal` gets or changes the current goal, and `scratchpad` gets, replaces, appends, or clears durable working context. The controller stores this exact generation-fenced state with the conversation's controller-owned session state. Browser and Session Intelligence scratchpad writes synchronize into the same Flex snapshot, while task-only and goal-only writes advance only the independent project-management revision. Legacy Flex conversations are backfilled idempotently at startup after their session generations are repaired.
289
+ ```sh
290
+ hcon upgrade --port 4097
291
+ hcon upgrade --port 4097
292
+ ```
449
293
 
450
- Conversation cards are collapsed independently and use status accents for idle, working/retrying, finished, pending permission/question, and error states. A known chat auto-expands when it starts working; selecting a chat does not change expansion. Finished stays green in the current browser unless error or attention takes precedence, and clears on reconnect, its next local turn, or later work. Pending permissions and questions use purple, with unselected cards pulsing for attention, while OpenCode session errors stay red until later work starts. Durable terminal and browser resources remain in the separate Resources section.
294
+ 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.
451
295
 
452
- The application header can hide and restore the controller's conversation sidebar; focusing conversation search restores it automatically. Session metrics report only facts the controller can establish from exhaustive history. OpenCode can expose lifetime token use, compaction count, tokens before the latest compaction, current context use, and the active model's context limit. Current-context facts stay absent while a turn is busy, incomplete, errored, or not newer than the latest compaction. Flex currently exposes only provable lifetime token use from its bounded projection.
296
+ 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.
453
297
 
454
- Each OpenCode or Flex conversation has controller-owned state scoped to the exact controller, project, and qualified session. Its selected model is durable, and scratchpad saves use an expected revision so one browser cannot silently overwrite a newer edit. The model choice, scratchpad text, newest eight bounded Session Intelligence exchanges, and optional exact Flex project-management record persist in SmartData; deleting a session or project immediately replaces retained controller content with a tombstone before best-effort TTL cleanup. Flex project-management tombstones additionally carry the exact session generation so stale work cannot cross a deleted and recreated Flex session. Removing a project purges the retired Flex project-management namespace while preserving the controller tombstone.
298
+ ## AGL Home And Persistence
455
299
 
456
- Session Intelligence runs one ephemeral, in-memory FlexHarness session per analysis. That worker receives no filesystem, shell, browser, mutation, or subagent tools: it can only read a qualified session, list related sessions in the exact project after successfully reading the requested source, and submit structured `{ answer, scratchpad }` completion. OpenCode reads travel through an explicitly authenticated, exact-origin and exact-directory CrossHarness connection; Flex reads use the controller's integrity-checked public projection. A private generation-bound capability token authorizes those reads without entering prompts, tool schemas, logs, errors, or model-visible output. The selected OpenCode account is preferred when it can run the exact `openai/gpt-5.6-luna` model; otherwise exactly one capable active Flex connection is required, with child-side revalidation before execution and no model or variant fallback. At most four analyses run controller-wide and one per qualified source session, enforced in both controller and child. A newer user scratchpad revision wins over a concurrent intelligence update while preserving the answer and exposing the conflict. New jobs create no durable harness session or temporary-session cleanup obligation; startup and periodic cleanup remain only for temporary OpenCode sessions persisted by earlier releases.
300
+ AGL owns one private root:
457
301
 
458
- Conversations can be organized into **groups**: the "New group" button below the list creates one, dragging any conversation card onto a group files it there, and right-clicking a group header offers rename and delete. Desktop dragging opens a full-card insertion slot and reorders conversations within or between groups; deleting a group never touches its conversations, which return to the ungrouped section in their existing order. Group membership and explicit ungrouped order are stored per project in the controller database, while new or otherwise unlisted conversations follow the list's deterministic fallback order. The layout survives reloads and stays in sync across connected browsers. Archiving or deleting a chat removes it from the persisted layout. Durable resources have their own project section and are intentionally not part of the session layout. The divider beside the sidebar can be dragged between 260 and 520 pixels, supports arrow, Home, End, and Escape keys, and stores its width in the local browser; the divider is hidden in the stacked mobile layout.
302
+ - Global installations: `$XDG_CONFIG_HOME/agl`, normally `~/.config/agl`.
303
+ - Repository checkouts: `<checkout>/.nogit/agl`.
304
+ - Explicit override: absolute normalized `AGL_HOME` whose parent already exists.
459
305
 
460
- Controller protocol v4 adds `IControllerSession.attention`, authoritative `sessionStatus` and `sessionError` event markers, and the revisioned `IControllerSessionLayout` contract. `controller.sessiongroups.get` now returns the complete layout, while `controller.sessiongroups.update` accepts the ungrouped order and an `expectedRevision` for concurrent-write detection and returns the confirmed layout.
306
+ Important directories include:
461
307
 
462
- Controller protocol v5 adds `controller.session.discard-empty`. It discards only a controller-tracked session that has not been claimed by any session mutation; `discarded` reports deletion and `retryable` distinguishes a transient deletion failure from a session that is no longer eligible. This lets the browser clean up a materialized draft without risking deletion of a conversation another client has started using.
308
+ - `database` for embedded SmartDB data.
309
+ - `credentials` for sealed Flex provider credentials.
310
+ - `git-reversion` for private captures and managed worktrees.
311
+ - `logs` for controller and upgrade diagnostics.
312
+ - `upgrade` for durable upgrade coordination.
313
+ - `runtime` for private browser, upload, socket, and OpenCode state.
314
+ - `migration` for verified migration journals and retained source records.
463
315
 
464
- Controller protocol v6 qualifies harness and controller runtime identifiers such as sessions, messages, permission and question requests, jobs, logins, and terminals as `{ harnessId, nativeId }`; opaque provider connection IDs remain strings and are not qualified. It replaces the single OpenCode status with `IControllerStatus.harnesses[]`, adds the `harness.changed` event, and adds explicit `harnessId` ownership fields to session creation, model choices, and controller events. It also adds Flex sessions and provider management, changes settings to `defaultModels`, and adds the API-only `controller.session.messages.page` and `controller.session.message.get` methods. Persisted v5 OpenCode settings and layouts migrate automatically and idempotently to qualified IDs.
316
+ Do not move these directories or edit migration and upgrade journals by hand.
465
317
 
466
- Controller protocol v7 adds optional `IControllerSessionDetail.sessionMetrics`, the revisioned `scratchpad`, `sessionIntelligenceEnabled`, and bounded `intelligenceExchanges`. It adds the `controller.session.scratchpad.save` and `controller.session.intelligence.ask` methods. The browser consumes the matching `harness-scratchpad-save` and `harness-session-intelligence-ask` catalog events, while the application shell owns the Conversations toggle; stale responses remain scoped to the project and qualified session that initiated them.
318
+ 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.
467
319
 
468
- Controller protocol v8 adds `IControllerToolExecution`, `IControllerToolStreamCursor`, `IControllerToolCall.exitCode`, the exported `controllerMaxToolEventBytes` limit, the `session.tool.updated` event, and OpenCode stream-epoch barriers on `harness.changed`. Tool events are correlated by qualified session, message, part, and call identifiers, contain bounded replace-only snapshots, and are reconciled against the cursor returned by `controller.session.get`.
320
+ Terminal I/O, active browser views, frame 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.
469
321
 
470
- Controller protocol v9 adds `IControllerProviderConnection.status`, which distinguishes `active` connections from `reauthRequired` connections without exposing provider credentials or responses. The browser uses that state to prevent unusable model refreshes and direct reauthentication through logout followed by a new device login.
322
+ Library consumers can resolve the same home contract:
471
323
 
472
- Controller protocol v10 adds durable `IControllerSessionDetail.modelChoice`, optional initial choices on `controller.session.create`, `controller.session.model.update`, and metadata-only `controller.provider.connection.ratelimits.get`. Flex IPC v2 adds authoritative `model.choice.validate` and provider account rate-limit retrieval. Flex model options now carry the provider-advertised `isDefault` marker used for exact sole-account initialization.
324
+ ```ts
325
+ import { bindAGLHomeEnvironment, resolveAGLHomePaths } from 'agl';
473
326
 
474
- Controller protocol v12 adds `TControllerSessionId`, `TControllerTerminalId`, and the mixed `TControllerLayoutItemId` union to the revisioned project layout. The historical `sessionIds` and `ungroupedSessionIds` wire and persistence keys are retained, so existing conversation layouts require no data migration. Controller-owned entries are accepted only as exact qualified IDs and are reconciled against the project's live terminal manager inside the serialized layout update boundary.
327
+ const paths = resolveAGLHomePaths();
328
+ const childEnvironment = bindAGLHomeEnvironment(process.env, paths);
329
+ ```
475
330
 
476
- The same v12 protocol and Flex IPC v3 extend the existing `session.tool.updated` stream to Flex. FlexHarness's allowlisted tool-part snapshots are transferred through bounded active and terminal phases, receive an independent Flex stream epoch, and are merged into `controller.session.get` under the exact returned cursor until the durable terminal projection semantically covers them.
331
+ ## Database Configuration
477
332
 
478
- Controller protocol v13 and Flex IPC v4 add optional `{ messageIndex, partIndex }` transcript order to normalized messages. Flex projection chains are repaired from authoritative private snapshots before public reads, public message pages paginate by persisted source index, and sequential Flex parts retain their original reasoning, tool, and text chronology even when run messages share timestamps.
333
+ The embedded database requires no separate service. Override its directory with an absolute path:
479
334
 
480
- Controller protocol v14 adds controller-memory composer drafts with compare-and-swap revisions, `controller.session.draft.get`, `controller.session.draft.update`, and `session.draft.changed`. Authenticated browsers synchronize text and attachments for the same qualified session, while pre-session drafts remain local to one browser until the first session is materialized. Send, slash-command, and built-in-command requests consume one exact draft revision; successful admission clears only that revision, concurrent newer edits survive, and failed or outcome-unknown admissions retain the draft. Draft text is limited to 64 KiB of UTF-8; each draft accepts at most eight attachments, 10 MiB per attachment, and 10 MiB combined. Attachments are written into private controller-owned temporary directories and represented to OpenCode and Flex only by absolute paths. Flex IPC v5 correlates each optional upload directory with one exact run and exposes it only as an additional filesystem read root. Operation completion, session or project removal, child exit, and controller shutdown remove the corresponding temporary files.
335
+ ```sh
336
+ export HARNESS_CONTROLLER_DB_DIR='/private/absolute/path'
337
+ ```
481
338
 
482
- Controller protocol v15 adds sanitized upgrade status and `upgrade.changed` events. Upgrade management version 1 has one-time `controller.upgrade.prepare` and `controller.upgrade.finalize` grants so the source and target controller protocols may differ while the detached worker retains exact process authority. Durable private transaction state contains bounded versions, options, timestamps, progress text, token hashes, worker process identity, project/session identities, and transition outcomes. Browser status exposes only source version, target version, and phase, never tokens, grants, worker identity, or the paused cohort.
339
+ To use an external MongoDB-compatible server:
483
340
 
484
- Controller protocol v16 removes controller-owned prompt scheduling. Every OpenCode send is submitted immediately through `promptAsync` with a caller-selected native message ID; OpenCode alone owns coalescing and turn order, while the controller tracks only bounded draft/upload cleanup until the exact terminal parent is observed. Exact terminal reconciliation and explicit aborts settle that ownership; accepted operations have no wall-clock lifetime, while repeated recovery failure stops OpenCode and releases every remaining OpenCode operation. Flex IPC v7 continues to admit every Flex send immediately into FlexHarness's bounded per-session FIFO and correlates it by exact project, session, queue, run, and promoted message identities. Before either harness exposes the user turn through transcript hydration, the controller projects only accepted or outcome-unknown prompts as normal user bubbles and supplies the exact pending count to the composer. Flex text and reasoning parts stream from FlexHarness's canonical ordered part events through bounded replace-only controller events and reconcile under the existing Flex stream cursor; terminal text snapshots stop streaming, and durable projection hydration supersedes them. At that protocol revision, `/compact` was available for both harnesses while other built-ins remained OpenCode-only; protocol v22 supersedes that command boundary with the unified harness-native slash catalog.
341
+ ```sh
342
+ export HARNESS_CONTROLLER_MONGO_URL='mongodb://user:password@127.0.0.1:27017'
343
+ export HARNESS_CONTROLLER_MONGO_DB='opencode_controller'
344
+ ```
485
345
 
486
- Controller protocol v17 adds metadata-only `IControllerProviderConnection.selectedForOpenCode?: true`, present only on the selected connection, and `controller.provider.connection.activate-opencode`. Its public request is `{ providerConnectionId }`; its response is `{ activated: true, pausedSessions, continuedSessions }`. Flex IPC v8 adds the private credential-directory initialization field and `provider.connection.opencode-auth.get`; the latter returns OpenCode OAuth material only to the controller parent. Flex credentials move from individual kernel entries to the kernel-keyed sealed store through the fail-closed startup migration described above. OpenCode SDK traffic is generation-fenced until each bounded response body settles, while separate controller-only SDK traffic performs pause, auth installation, and continuation during the sealed restart transaction.
346
+ `HARNESS_CONTROLLER_MONGO_DB` defaults to `opencode_controller`.
487
347
 
488
- Controller protocol v18 adds durable project resources with explicit `terminal` and `browser` kinds. Resource attachment uses a stable authority plus a monotonic revision: the pending attachment is persisted before runtime side effects and committed only after reconciliation, retirement is persisted before cleanup, session deletion detaches first, and project removal is rejected while any non-retired resource remains. Startup resumes pending attachment and retirement work. Session layouts become session-only; migration removes legacy controller-terminal IDs without changing conversation order. The public API adds list, create, rename, attach, detach, start, stop, and retire methods plus exact-revision human browser-view methods and `resources.changed` events. The controller supports at most 64 active resources per project and 4,096 across the controller.
348
+ ## Runtime Environment
489
349
 
490
- Controller protocol v19 replaces `IControllerSessionDetail.messages` with the bundle-preserving `messagePage`, removes `sessionMetrics` and `sessionIntelligenceEnabled` from that core response, and adds `controller.session.auxiliary.get` returning `IControllerSessionAuxiliary`. `controller.session.get` reads only the newest bounded tail; top-level conversations and child-session drill-ins use `controller.session.messages.page` for best-effort older history. Message pages may report harness retention loss through `truncated` and controller transfer omission through `historyLimited`.
350
+ 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.
491
351
 
492
- Controller protocol v20 separates Assistant text/reasoning stream ordering from tool ordering on the v19 paging architecture. It generalizes the replace-only text and reasoning update DTOs to OpenCode and Flex, adds `IControllerMessageStreamCursor`, `IControllerSessionDetail.messageStreamCursor`, and `IControllerEvent.messageStreamEpoch`, and requires Flex source order while allowing OpenCode updates without one. The newest source-message bundles include snapshots covered by the returned cursor; older page requests stay source-authoritative and are fenced by both stream epochs in the browser. A stream-specific loss advances only its affected hydration barrier; saturation of the shared 512-event queue advances both. Retained Flex tool snapshots are bounded to 8 MiB and message snapshots to 16 MiB. Terminal durable transcript state remains authoritative over stale running snapshots. The provider-connection list includes `runtimeAvailable` so clients can distinguish live Flex data from the persisted metadata-only fallback. Flex IPC v10 additionally projects foreground Task lineage, family-scoped permissions, child-session identity, and child model metadata without changing the public controller protocol. Flex IPC v11 adds bounded host calls for exact project-management load, compare-and-swap save, tombstone, and namespace purge operations.
352
+ Delegate additional non-controller variables explicitly when required:
493
353
 
494
- Flex IPC v12 adds isolated Session Intelligence admission, status, cancellation, and capability-scoped CrossHarness read requests. Cancellation is serialized behind admission and does not acknowledge success until the ephemeral worker has disposed. On job failure, the controller cancels and drains the worker before revoking its read capability; capability closure aborts in-flight OpenCode, Flex projection, and legacy-session persistence reads and waits for them to drain. Flex child recovery fences new analysis admission, confirms prior-generation process exit, and drains retained read capabilities before starting the replacement generation.
354
+ ```sh
355
+ export HARNESS_CONTROLLER_FORWARD_ENV='MY_TOOL_CONFIG,MY_CA_FILE'
356
+ ```
495
357
 
496
- Controller protocol v21 replaces base64 browser-frame payloads with one finite integrity-checked server-created virtual stream per frame and adds `controller.browser.view.activate`. A human view opens inactive; the browser installs and starts its renderer before activation can begin frame delivery. Stream reads revalidate the exact authenticated physical peer, view, resource, attachment revision, and process-local BrowserRuntime lease authority. Request cancellation propagates through queued and active browser work, while disconnect, resource transition, queue overflow, and shutdown close the exact view and retain retry ownership until capability revocation succeeds. Flex IPC v13 adds the exact Flex run ID to every browser-channel binding and revalidates it before admitting the channel.
358
+ 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`.
497
359
 
498
- Controller protocol v22 replaces the separate command-list, session-command, and built-in-command requests with `controller.slash.list` and `controller.slash.execute`, and adds `session.history.changed` to invalidate stale transcript and reversion state after undo, redo, or branching. Flex IPC v14 carries FlexHarness's native slash catalog and execution result union, exact queue/run acknowledgement, request cancellation, session reversion metadata, complete project ancestry bindings, and parent-owned Git reversion and linked-worktree host requests. Git captures preserve index and worktree sides separately, keep generated metadata and objects private outside apply, and retain later non-conflicting edits across undo and redo. Project registration and removal are explicit serialized child requests; durable parent-side removal intent is required before Flex state, controller-owned linked worktrees, worktree records, or private capture roots are deleted.
360
+ Credential-free `HTTP_PROXY` and `HTTPS_PROXY` values are forwarded. Proxy URLs containing credentials are not forwarded automatically.
499
361
 
500
- The Resources section can create a **terminal**: a durable resource whose real shell is started directly by the controller through Smartshell's shell-free node-pty API in the project directory. On POSIX the controller validates the absolute `$SHELL` executable and falls back to `/bin/sh`; Windows foreground operation uses a trusted absolute `COMSPEC` or `SystemRoot` `cmd.exe`. The browser renders the terminal with xterm.js; keystrokes and output travel over the same authenticated TypedSocket connection as everything else — no extra port and no CDN scripts. Terminal resource identifiers are controller-generated opaque values and never expose process IDs. The resource, title, command, attachment, stopped state, and last exit code are durable; the running PTY and its scrollback remain memory-only. Every restart revalidates the project's complete filesystem identity and requires its canonical directory to equal the persisted terminal cwd before spawning. Output is delivered only to peers attached to that terminal; each peer has an independent replay cursor over at most 2 MiB of bounded controller scrollback, and trailing output is delivered before the terminal-ended event. Closing or reloading the browser never ends the PTY, and reattaching replays retained scrollback while it remains available. A terminal can be renamed, stopped, restarted, or retired from its resource context menu. Shutdown waits for independent PTY-root exit confirmation and fails closed instead of dropping ownership of a process whose exit cannot be confirmed. When several browsers view one terminal, the smallest client's grid determines the PTY size. The controller admits at most eight running terminals per project, 32 running terminals in total, and 32 attached peers per terminal. Terminal keystrokes are not written to the audit log; lifecycle actions are.
362
+ ## Remote Browser Access
501
363
 
502
- A **browser** resource is also durable. An authenticated human can open its exact committed attachment revision as an interactive canvas with tabs, navigation, viewport updates, mouse, wheel, keyboard, and text input. Browser frames use finite binary virtual streams rather than JSON/base64 payloads; each stream belongs to one exact server push and becomes unusable when its peer, view, attachment, or runtime authority is no longer current. A browser resource attached to a Flex session contributes a run-scoped BrowserRuntime tool provider through FlexHarness 4.1.1. The private parent-child channel is bound to the exact project, resource authority, attachment revision, Flex session, run, channel, actor, and live child generation; available agent actions are `navigate`, `snapshot`, `screenshot`, `click`, `fill`, and `press`. BrowserRuntime capability revocation and channel cleanup retain retry ownership after failures.
364
+ Remote WebAuthn requires HTTPS. Start AGL behind a trusted TLS reverse proxy:
503
365
 
504
- By default the controller never auto-accepts a permission. The settings dialog offers a global **auto-accept permissions (yolo)** toggle, persisted server-side: while enabled, the controller itself replies `once` to every pending permission request in all registered projects (including requests that were already waiting when the toggle turned on), and each auto-reply is written to the audit log like a manual one. Each chat additionally has an **Ask / Yolo** switch in its composer: Yolo auto-accepts that conversation's permission requests for the lifetime of the controller run without touching the global default (the durable setting stays the global toggle). Questions are never auto-answered. Persistent broad `always` grants remain unexposed by the protocol — the inline card's "always" answer grants once and OpenCode simply asks again — and no background or sleeping browser is treated as an approval agent: auto-accept is a deliberate server-side policy switch, not a client behavior.
366
+ ```sh
367
+ export HARNESS_CONTROLLER_TRUSTED_PROXY_SECRET="$(node -e "console.log(require('node:crypto').randomBytes(32).toString('base64url'))")"
505
368
 
506
- Project-scoped OpenCode events are accepted only from registered project directories. Flex events already carry their registered project scope. Both are normalized with their harness owner and pushed only to authenticated TypedSocket peers. The UI applies correlated OpenCode and Flex tool, Assistant text, and Assistant reasoning snapshots directly when their message already exists, retains bounded per-harness overlays when an event beats or is not semantically covered by hydration, and debounces ordinary session events into refreshes rather than polling harness state.
369
+ agl start \
370
+ --public-origin https://controller.example.com \
371
+ --rp-id controller.example.com \
372
+ --behind-tls-proxy
373
+ ```
507
374
 
508
- ## Persistence and audit boundaries
375
+ The proxy must:
509
376
 
510
- SmartData owns:
377
+ - Remove client-supplied copies of AGL transport headers.
378
+ - Inject the configured secret as `X-Harness-Controller-Proxy-Secret`.
379
+ - Set `X-Forwarded-Proto: https` on WebSocket upgrades.
380
+ - Preserve the exact public Host and Origin.
381
+ - Forward WebSocket upgrades.
382
+ - Restrict direct access to the backend listener.
511
383
 
512
- - the immutable runtime configuration;
513
- - setup authority hashes and generations;
514
- - passkey credentials and compare-and-swap counters;
515
- - single-use WebAuthn ceremony records with expiry;
516
- - project-scoped session-only layouts and controller/project/qualified-session model choices, scratchpads, bounded intelligence exchanges, exact Flex project-management records, revisions, and content-clearing tombstones;
517
- - durable project terminal and browser resources, attachment authorities and revisions, pending transitions, lifecycle state, and terminal stopped metadata;
518
- - Flex session snapshots, bounded public session/message projections, provider connection metadata, and selected-model metadata;
519
- - the opaque provider connection selected for the controller-owned OpenCode runtime;
520
- - bounded, correlated audit events for controller, setup completion, authentication, session, terminal, permission, question, and semantic browser actions. Periodic maintenance prunes toward seven-day and 2,000-record targets, with a 90-day database TTL fallback. Audited operations first persist an `attempted` record before any state transition or managed side effect, then append the terminal outcome. BrowserRuntime supplies the correlated operation identity for navigation, tab, and agent actions; frame acknowledgement, viewport updates, raw input, checkpoints, and transport acceptance do not create audit rows.
384
+ 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.
521
385
 
522
- Audit records may contain bounded peer, credential, session, or permission-request identifiers. They cannot contain setup codes, WebAuthn challenges or payloads, prompt text, OpenCode credentials, provider credentials, device-flow responses, or arbitrary fields.
386
+ ## Security Boundaries
523
387
 
524
- Flex private snapshots and public projections are published separately. The controller reads only integrity-checked public records. For each project, every public candidate is bounded to 2,048 sessions, 2,048 messages, 64 MiB of message data, and 480 KiB per message; up to four public candidates are retained per project. Pages contain at most 50 messages or 512 KiB. Unreferenced staged candidates are age-gated and reconciled through paged, batched maintenance. Provider credentials never enter these records.
388
+ - Local mode binds the controller to loopback only.
389
+ - The private OpenCode server always remains on loopback and uses a generated per-process password.
390
+ - Application RPC starts only after the exact TypedSocket package-major handshake.
391
+ - Client-managed connection tags are disabled; authentication and routing use server-owned peer state.
392
+ - Setup attempts are globally rate limited before enrollment.
393
+ - Permission auto-accept is an explicit server policy, never an assumption based on an open browser.
394
+ - Questions are never auto-answered.
395
+ - Unknown, malformed, concurrent, or unverifiable lifecycle state fails closed.
525
396
 
526
- Running terminal processes, output scrollback, peer cursors, grid state, active human browser views, browser-frame streams, BrowserRuntime capabilities and leases, Flex browser channels, and composer drafts are memory-only. Durable terminal resource identifiers may appear as bounded request identifiers in lifecycle audit records, but session layouts contain only OpenCode and Flex session IDs. Terminal input and output are never persisted. Draft text and attachment bytes are never written to SmartData or audit records. Attachment files exist only in private operation directories under `$AGL_HOME/runtime/uploads` for the lifetime of their exact harness operation and are removed on operation cleanup or process shutdown.
397
+ 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.
527
398
 
528
399
  ## Development
529
400
 
401
+ Install dependencies and run the project checks:
402
+
530
403
  ```sh
531
- pnpm build
404
+ pnpm install
405
+ pnpm run build
532
406
  pnpm run check:test
533
407
  pnpm test
534
408
  ```
535
409
 
536
- The default suite runs without a database service. To include the exact-persistence integration
537
- test against a disposable MongoDB test service, set `HARNESS_CONTROLLER_TEST_MONGO_URL` before
538
- running `pnpm test`; the test creates a unique database name for that run.
410
+ Run the CLI from source:
411
+
412
+ ```sh
413
+ node cli.js help
414
+ node cli.js foreground --port 4097
415
+ ```
539
416
 
540
- The production build uses `tsbuild tsfolders` so server code, shared TypedRequest contracts, and browser sources are checked and emitted, then `tsbundle` produces the static browser bundle.
417
+ The default test suite uses disposable embedded databases. Set `HARNESS_CONTROLLER_TEST_MONGO_URL` to include the external MongoDB integration test.
541
418
 
542
419
  ## License and Legal Information
543
420
 
544
- This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license.md](./license.md) file.
421
+ This repository contains open-source code licensed under the MIT License. A copy of the license can be found in [`license.md`](./license.md).
545
422
 
546
423
  **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.
547
424
 
@@ -553,7 +430,7 @@ Use of these trademarks must comply with Task Venture Capital GmbH's Trademark G
553
430
 
554
431
  ### Company Information
555
432
 
556
- Task Venture Capital GmbH
433
+ Task Venture Capital GmbH<br>
557
434
  Registered at District Court Bremen HRB 35230 HB, Germany
558
435
 
559
436
  For any legal inquiries or further information, please contact us via email at hello@task.vc.