Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
> [!WARNING]
> Local domain monitoring: [Cloudflare status setup and coverage](docs/cloudflare-monitor.md). The Office computer performs the checks with a locally referenced credential file; no monitor is deployed to the websites.
>
> **Work in progress.** Agent Office is built for one person's workflow — mine — and it changes fast as I iterate on it.
> Expect breaking changes between releases: keys that move, screens that get redrawn, features that come and go
> without notice. If it's close to what you want, fork or clone it and bend it into what you need it to be.
Expand Down Expand Up @@ -92,6 +94,8 @@ agent-office setup # the first-start walkthrough again

Every option is in [docs/configuration.md](docs/configuration.md). Choosing models and providers per worker is in [docs/agents.md](docs/agents.md).

This local integration build also offers **Goose**, **Cline**, and **OpenHands** in the worker picker. The three agents use their existing NVIDIA credentials and settings; Agent Office does not store an NVIDIA key. Goose and Cline run in their interactive terminals. OpenHands connects to the running Agent Canvas service on `127.0.0.1:8000` through a local terminal bridge. See [the local agent setup and recovery notes](docs/agents.md#goose-cline-and-openhands-in-this-local-build).

To run it from a clone instead:

```bash
Expand Down
20 changes: 18 additions & 2 deletions docs/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

Back to the [README](../README.md).

Every new worker starts on the office's **Default worker**: a provider, model and effort an admin picks in ⚙️ Settings → **🤖 Workers** (the `--agent` with its own default model until someone does). The hire, ask, queue, issue and meeting windows show it on one line; click **✏️ Edit** there to pick **Claude Code**, **OpenCode**, **Codex**, **Grok**, **Muse**, **DeepSeek Harness**, **Pi** or **Cursor**, a model and an effort for just that worker or task, and **↺ Use the default** to go back. Board agents and tasks the Queue agent adds, which nobody picks for, start on the default too. Existing workers keep their provider when prompted or resumed, and queued tasks keep their choice when retried or restored after a restart.
Every new worker starts on the office's **Default worker**: a provider, model and effort an admin picks in ⚙️ Settings → **🤖 Workers** (the `--agent` with its own default model until someone does). The hire, ask, queue, issue and meeting windows show it on one line; click **✏️ Edit** there to pick **Claude Code**, **OpenCode**, **Codex**, **Grok**, **Muse**, **DeepSeek Harness**, **Pi**, **Cursor**, **Goose**, **Cline**, or **OpenHands**, with any model and effort fields supported by that provider for just that worker or task, and **↺ Use the default** to go back. Board agents and tasks the Queue agent adds, which nobody picks for, start on the default too. Existing workers keep their provider when prompted or resumed, and queued tasks keep their choice when retried or restored after a restart.

Every harness takes both a **Model** and an **Effort**, each optional: leave one on **Default** and the harness uses its own settings.
The providers in the table take a **Model** and sometimes an **Effort**: leave one on **Default** and the harness uses its own settings. Goose, Cline, and OpenHands use the NVIDIA configurations already verified on this computer and do not offer those fields in this local build.

| Harness | Model | Effort |
| --- | --- | --- |
Expand All @@ -18,6 +18,22 @@ Every harness takes both a **Model** and an **Effort**, each optional: leave one

The efforts are low, medium, high, extra high and max. Where a harness's own catalog says which of them a model supports (Codex and OpenCode do), the ones it doesn't are greyed out once that model is picked. A harness whose models can't be listed (its CLI isn't installed, or isn't signed in) still takes a model id typed in. A **Custom** `--agent` is run as it is, so it has neither field: set its model and effort in `--agent-args`.

## Goose, Cline, and OpenHands in this local build

This local build adds three provider choices. Their API credentials stay in each application's existing store; Agent Office neither requests nor stores an NVIDIA API key. The Office listener remains on `127.0.0.1` unless an operator explicitly changes `--host`.

| Worker | What must already be running or installed | How it operates in the office | Restart behavior |
| --- | --- | --- | --- |
| Goose | `goose` CLI with its existing NVIDIA sign-in | Interactive PTY, one named session per desk, using NVIDIA Nemotron 3.5 Super VL and low thinking effort. Native hooks report work and completion to meetings and the queue. Standalone Goose preferences are unchanged. | Resumes the same named session and Office model. If that session was removed externally, the terminal reports the missing session. |
| Cline | `cline` CLI with the existing Cline NVIDIA sign-in | Interactive PTY with the verified NVIDIA provider and model; Office requests disabled automatic approval at launch. Cline's native setting can still change: keep **Auto-approve all disabled** for manual review and approve requested actions in the terminal. | A living PTY survives an ordinary Office restart through the host process. If Cline itself exited, waking the desk starts a fresh conversation because the CLI's `--id` path ignores a new initial prompt. |
| OpenHands | Agent Canvas running on `127.0.0.1:8000` with an active NVIDIA agent profile | A local terminal bridge follows the authenticated API. Long runs have no overall turn deadline; individual requests time out, status is reported each minute, and Ctrl+C interrupts the run. | Reconnects to an ongoing saved conversation after checking its workspace and NVIDIA model, without submitting a second prompt. If the conversation is gone, the next prompt starts a new one. |

Pick one of these names when hiring a desk worker or set Goose as the default with `--agent goose`. The current local build uses each worker's verified NVIDIA model; its hire dialog has no separate model or effort field for these three. A failure to find the CLI or the Agent Canvas service appears on that worker's terminal. OpenHands actions requiring confirmation are approved in Agent Canvas.

Cline uses project lifecycle hooks and its native local state mode so a shared background hub cannot lose the Office worker identity. Its existing provider store is reused. This mode is not an OS sandbox. A tool check is shown as needing input until the native tool proceeds; inspect the terminal for approval requests.

A stopped meeting can be continued with **Resume meeting** while its original workers and notes remain. Completed notes are reused; a missing or exited worker must be recovered first. Windows Services can discover web servers under a worker's process tree. A detached server whose parent has already exited cannot currently be assigned reliably, so keep the preview running under its worker terminal.

What's picked shows next to the worker everywhere it's named (the Workers panel, its task card, its terminal's title, the queue), by the model's name rather than its family: **Opus 5.5**, not "Opus". For Claude Code that name comes from the session itself once the worker has spoken, so a worker left on Default says which model the default is, and one switched with `/model` in its terminal says the new one.

For OpenCode, the **Model** field selects the initial model for a new worker or queue task. Suggestions come from `opencode models`; you can also enter a `provider/model` ID. Leave it empty to use your OpenCode settings (including configured CLI arguments). An explicit choice overrides configured model arguments for that launch and stays with a queued task when retried. **Effort** is what OpenCode calls the model's *variant*. Its TUI has no flag for one, and sends each message at the variant it remembers for the model from whichever session used it last, so the office's plugin puts the worker's first message on the effort picked for it; the TUI carries on from that message, and `Ctrl+T` in the terminal still changes it. An effort the model has no variant for changes nothing.
Expand Down
25 changes: 25 additions & 0 deletions docs/cloudflare-monitor-template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Agent Office Cloudflare monitoring template v1.0

Owner correction, 2026-10-23: Services means the owner's Cloudflare-hosted domains and their errors. Monitoring must originate in Agent Office on this computer. Never import the inaccurate historical domain inventory. Preserve local preview support and all active agent tasks. Hermes remains desktop only and is a separate pending task.

## M1 Requirements and architecture

An Office-owned timer discovers current zones, web DNS names, Worker domain bindings, route mappings, Worker scripts and Pages using the designated removable-drive Cloudflare credential file. The path is local configuration; the token is read only in server memory, never copied into configuration, logs, CLI arguments, browser state or repository. Only GET metadata and a fixed read-only GraphQL analytics query are permitted. No production resource/configuration mutations, deployment, server monitor, cron, or Tail Worker installation.

Every 60 seconds Office independently probes discovered public HTTPS website roots, with bounded concurrency, DNS/IP checks, certificate validation, response limits and an overall deadline. Local/private IPs, credentials in URLs, alternate protocols and arbitrary caller URLs are rejected. Redirects are reported without following unverified targets. Do not mistake Access/challenge/auth, redirects, zero traffic, stale observations or missing API permissions for complete application health.

Refresh discovery and the last-15-minute Worker aggregate requests/errors/outcomes every five minutes. Preserve observation timestamps, capability failures and incomplete pagination. Pages/API permission errors remain visible. Only latest observations generated by this monitor may be persisted; they are explicitly stale on reload until refreshed. Never seed from earlier inventory files. Bound all arrays and persisted history. Do not collect raw HTTP bodies or console logs into persistent state. Initially show error classes/outcomes/counts; unavailable detailed application logs must be explicit.

## M2 Interfaces and UX

Local `.agent-office/cloudflare-monitor.json` contains only `credentialFile` and `enabled`. No unauthenticated monitoring results or configuration API. A signed-in GET `/api/domain-monitor` returns sanitized current state. Start/stop through Office timer lifecycle, not panel visibility or an agent's task. The Services window exposes Cloudflare monitoring, and the menu offers a direct Cloudflare status action. Show monitor location, time of last/next check, per-domain HTTPS status/latency/error/ray ID, Worker mapping and aggregate error outcomes, capability errors, and stale/closed-Office limitations. Close button and Esc retain Office focus behavior. 2D Services should expose the same monitoring panel.

## M3 Verification and security

Unit/regression tests: missing drive/profile; ambiguous credentials; pagination/failure; new/deleted live domains without saved inventory; SSRF/private IPv4/IPv6/rebinding/redirect protections; HTTP 2xx/3xx/401/403/5xx/network timeout; no API/token text echoed; read-only query shape; no-data/permission errors; sanitized serialization; repeated timers/concurrency/shutdown; size guard, typecheck, build, relevant Office tests and bounded full-suite attempt. Apply provisional47 (authoritative checklist missing), current primary Cloudflare API documentation, CWE-918/200/400/532, OWASP SSRF/secret handling.

Live acceptance: use G-drive key against current Cloudflare API, compare discovered sites with live API outputs, probe actual domain URLs, observe aggregate analytics permission/data, display actual results in installed Office and save screenshot. Prove updates occur with the panel closed and no agent assigned. Mock security tests are not live-domain evidence. Check unsupported logs/capabilities explicitly.

## M4 Release/recovery

Branch work from freshly fetched origin/main with existing verified Office integration commits retained. Build isolated candidate, preserve installed dist/config/agent state, use graceful restart only after source tests pass. Retain original profile path, never copy keys. Validate live workers/queue/session IDs after rollout. Hash installed files and verify backup restoration. Rollback removes monitor registration/config and restores the prior build; no Cloudflare change requires rollback. Record review/audit/tests/limitations, update durable task tracker and the single checkpoint. Ship a PR as required by repository AGENTS.md; inability to push is reported accurately.
17 changes: 17 additions & 0 deletions docs/cloudflare-monitor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Cloudflare monitoring from Agent Office

Open **☰ → Cloudflare status**, **Services → Cloudflare domains, status and errors**, or the **Cloudflare status** button in 2D view. Agent Office performs the checks on the computer running Office, independently of agents and whether the panel is open. Keep Office running; shutdown, sleep or network loss interrupts monitoring. No monitoring Worker or website-side agent is installed.

Create `cloudflare-monitor.json` in the Office building's `.agent-office` directory:

```json
{ "enabled": true, "credentialFile": "G:/your-folder/Cloudflare account API key.txt" }
```

The credential file must contain exactly one `Authorization: Bearer TOKEN` and one Cloudflare `/accounts/ACCOUNT_ID/` URL. Keep it private. Configuration stores its path only; Office reads credentials in memory. Drive removal/rotation is detected. Do not put a token in source, launch arguments, prompts or browser settings.

Every five minutes Office reads the live Cloudflare API for zones, web DNS names, Worker custom domains/routes/scripts and Pages. It never imports saved domain inventories. API coverage/permission failures are shown. Every minute it independently requests HTTPS `/` on discovered domains, with public DNS addresses pinned to the connection, TLS verification, deadlines, response bounds and four-request concurrency. Redirect destinations are not followed. Wildcard-only names cannot be probed without a concrete domain.

Statuses distinguish reachable, redirect, restricted, error and unknown. A reachable root does not validate login, APIs, checkout or every path. Cloudflare error codes/ray IDs are shown when observed. Last-15-minute aggregate Worker analytics shows request/error counts and outcome categories. No returned traffic does not establish zero errors. These are sampled analytics, not complete console/exception logs. Missing permissions remain visible and telemetry settings are never changed automatically.

Only authenticated Office sessions can read `/api/domain-monitor`. Current observations are saved atomically in `cloudflare-monitor-latest.json`; startup always performs fresh discovery rather than loading it as inventory. No raw page bodies, cookies, credentials or console logs are saved. Close with ✕ or Esc. To disable, set `enabled` false locally; it takes effect at the next check. Restore the prior installed build/configuration backup to roll back.
2 changes: 2 additions & 0 deletions src/client/features/hud/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { toggleFloorMenu } from '../../ui/floormenu';
import { openHelp } from '../../ui/hud';
import { mountHud } from '../../ui/menu';
import { openServices } from '../../ui/services';
import { openDomainMonitor } from '../../ui/domain-monitor';
import { openSettings, type SettingsPane } from '../../ui/settings';
import { needsSigningIn, openSignIns } from '../../ui/signins';
import { openTeam } from '../../ui/team';
Expand Down Expand Up @@ -53,6 +54,7 @@ export function installHud(ctx: Ctx, core: CoreState, parts: HudParts) {
{ id: 'pulls', icon: '🔀', label: 'Pull requests', section: 'Open', count: () => store.pulls.items.filter((p) => p.state === 'OPEN').length, run: () => openBoard('pulls', net, actions.boardActions()) },
{ id: 'queue', icon: '📋', label: 'Task queue', section: 'Open', count: () => store.queue.tasks.filter((t) => t.status !== 'done').length, title: () => 'Issues and tasks waiting for a worker', run: waiting.showQueue },
{ id: 'services', icon: '🌐', label: 'Services', section: 'Open', count: () => store.services.items.length, title: () => 'Web servers the workers are running', run: () => openServices() },
{ id: 'cloudflare-status', icon: '☁️', label: 'Cloudflare status', section: 'Open', title: () => 'Domain availability and Worker errors monitored by this Office', run: () => openDomainMonitor() },
{ id: 'whiteboard', icon: '📝', label: 'Whiteboard', section: 'Open', title: () => 'Draw together, live', run: () => openWhiteboard(net) },
// Up on the top bar while a meeting is on: what's being worked through in the meeting room.
{
Expand Down
2 changes: 2 additions & 0 deletions src/client/lite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { openBoard } from './ui/boards';
import type { BoardActions } from './ui/github/prompts';
import { openPull, routePullMessage } from './ui/pull';
import { openQueue } from './ui/queue';
import { mountDomainMonitorLink } from './ui/domain-monitor';
import { openAsk } from './ui/ask';
import { openMeeting, type MeetingPreset } from './ui/meeting';
import { openSignIns } from './ui/signins';
Expand Down Expand Up @@ -320,6 +321,7 @@ function showMeeting(preset?: MeetingPreset) {
$('btn-issues').addEventListener('click', () => openBoard('issues', net, boardActions()));
$('btn-pulls').addEventListener('click', () => openBoard('pulls', net, boardActions()));
$('btn-queue').addEventListener('click', () => openQueue(net, { openTerminal: openWorker }));
mountDomainMonitorLink($('btn-queue'));
$('btn-new').addEventListener('click', () => sendToWorker('✨ New task'));

function renderNav() {
Expand Down
8 changes: 8 additions & 0 deletions src/client/ui/domain-monitor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.domain-monitor { overflow:auto; max-height:76vh; }
.monitor-table { width:100%; border-collapse:collapse; font-size:13px; }
.monitor-table th,.monitor-table td { padding:9px 7px; text-align:left; border-bottom:1px solid #8885; vertical-align:top; overflow-wrap:anywhere; }
.monitor-table th { position:sticky; top:0; background:var(--panel,#fff4df); }
.monitor-table small { display:block; }
.monitor-warning,.monitor-error { color:#9d2020; font-weight:600; }
.monitor-reachable { color:#226b3b; }
.monitor-restricted,.monitor-redirect,.monitor-unknown { color:#795413; }
Loading