Files
PaperClipAI/docs/specs/agent-config-ui.md
T
e0d90339bf fix(runner): make task-capable plugin environments Runner-only (#17380)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - An environment plugin can run Paperclip Runner tasks through a
`driver: "plugin"` environment.
> - Core lets only the `paperclip_runner` adapter use a plugin
environment.
> - A Claude agent cannot get onto the Runner there. Runner selection
needs a reported platform for Claude, and a plugin target reports none.
> - The adapter Test route also checks the driver against the requested
harness before it resolves the runner.
> - This pull request makes task-capable plugin environments Runner-only
and checks the Test driver after runner selection.
> - The benefit is that Claude and Codex agents can use a Runner task
environment, and the UI cannot produce a legacy agent there.

## Linked Issues or Issue Description

Refs #17029. Refs #16978.

**What happened?**

A Claude Code agent with a plugin environment that runs Runner tasks
fails the connection test. The error is: `Environment driver "plugin" is
not allowed here. Allowed drivers: local, ssh, sandbox`. The Runner
selector in Advanced shows Paperclip Runner as unavailable, so the user
cannot fix the agent in the form.

**Expected behavior**

A Claude or Codex agent on that environment uses Paperclip Runner. The
connection test passes the driver check. Legacy is not offered, because
a legacy adapter cannot run on a plugin environment.

**Steps to reproduce**

1. Install an environment plugin whose driver declares `supportsTasks:
true`.
2. Create a plugin environment for that driver.
3. Open an agent with the Claude Code harness and select that
environment.
4. Click Test. The test fails with the driver error above.

**Agent adapter(s) involved**

Claude Code, Codex (through Paperclip Runner).

**Additional context**

Two gates caused the failure:

- `agentRunnerAvailability` requires a platform for `claude_local`. A
plugin target is `{ driver: "plugin" }`, so Claude resolves to Legacy.
- The Test route ran `assertAdapterTestEnvironmentForCompany` with the
URL adapter type before `resolveNewAgentRunnerForCompany`. The plugin
driver is allowed only for `paperclip_runner`, so the check failed
before selection could change the adapter.

## What Changed

- Runner target resolution marks a plugin environment as `runnerTasks`
when its plugin manifest declares `supportsTasks` for the driver key and
has the `environment.drivers.register` capability. The check reads the
manifest only. Run admission still verifies a ready plugin and a live
worker.
- `agentRunnerAvailability` returns Paperclip Runner as the only runner
for a `runnerTasks` target. It skips the platform check, because the
plugin provisions the host and launches its own pinned Runner. Harnesses
that the Runner does not support keep the Legacy result.
- The adapter Test route runs the company-binding check first, as
before. It runs the driver check after runner selection, with the
resolved adapter type. The driver check still runs before secret
resolution, leases, and the adapter probe.
- A saved adapter that cannot run on the requested environment no longer
keeps its saved execution in the Test route. It re-resolves its runner.
- `assertAdapterTestEnvironmentBinding` is a new helper that holds the
binding half of the guard. `assertAdapterTestEnvironmentForCompany`
keeps its behavior and calls it.
- The agent form switches a Legacy runner choice to Paperclip Runner
when the selected environment offers no Legacy runner. The test and the
save then send the Runner choice.
- `docs/specs/agent-config-ui.md` records the Runner-only rule and the
Run test order.

## Verification

- `pnpm --filter @paperclipai/server exec tsc --noEmit` passes.
- `pnpm --filter @paperclipai/ui exec tsc -b` passes.
- Server suites pass: `agent-runner-selection.test.ts`,
`agent-test-environment-routes.test.ts`,
`agent-permissions-routes.test.ts`,
`environment-selection-route-guards.test.ts`,
`agent-harness-verification.test.ts`,
`plugin-runner-task.integration.test.ts`, `adapter-routes.test.ts`,
`adapter-auth-signal-routes.test.ts`,
`agent-adapter-validation-routes.test.ts`,
`agent-hire-ai-connections.test.ts`.
- UI suites pass: `AgentConfigForm.render.test.tsx` and
`NewAgent.test.tsx`.
- New tests cover these cases:
- A task-capable plugin target offers only Paperclip Runner for Codex
and Claude.
  - Claude saves as `paperclip_runner` with ACPX on that target.
- A driver without `supportsTasks`, a different driver key, a missing
capability, an unknown plugin, or a malformed config keeps the existing
plugin target.
  - The Test route checks the driver against the resolved adapter.
- The Test route still rejects a plugin environment for a legacy
adapter, before any secret resolution.
- A saved adapter re-resolves only when it cannot run on the requested
environment.
- The form switches a saved or new Legacy choice to Paperclip Runner on
a Runner-only environment.
- I ran the new route and form tests against the old code. They fail
without the change.
- The full server `typecheck` script did not complete locally. Its
Runner vendor step needs `cargo`, which this machine does not have. CI
runs the full script.
- I did not run a live plugin environment end to end.

## Risks

- Behavior change: Codex on a task-capable plugin environment now offers
only Paperclip Runner. Before, the selector also showed Legacy, but Core
rejected a legacy save on that environment. No working setup loses an
option.
- The `runnerTasks` decision reads the plugin manifest. If the plugin is
down, the agent still selects the Runner, and the run fails at admission
with the existing plugin error.
- The adapter auth-signal route still checks the driver with the
requested harness. This PR does not change that route.
- An explicit Legacy save through the API on a Runner-only environment
still fails with the existing driver error. The UI no longer offers that
choice.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`) wrote the change and the tests in
Claude Code, with shell, file editing, and local test runs.
- Claude Fable 5.1 (`claude-fable-5-1`) did the earlier diagnosis in the
same session.
- The session does not expose the context window size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 15:21:09 -07:00

15 KiB
Raw Blame History

Agent Configuration & Activity UI

Current implementation (2026-09-07)

The shipped new-agent flow starts from Agents → New Agent. A small dialog collects a name and an enabled adapter, then opens a setup page with numbered navigation. Claude, Codex, and Grok have a subscription/API-key connection step. Configuration provides a searchable, free-text model selector and an environment selector; additional settings remain on the full agent page. Finish setup submits the existing governed hire request. Confirmation links to configuration and opens a new task with the created agent assigned; agents awaiting approval cannot be assigned work yet.

On Cloud, the new-agent picker and direct setup links allow Claude, Codex, OpenCode, and Grok. Four available adapters use a two-column grid on desktop and mobile. Grok uses the existing grok_local adapter with an xAI subscription or API-key connection and the managed sandbox environment. The picker and connection step share provider marks that adapt to light and dark mode. An adapter must also be loaded and enabled to be available.

Paperclip Runner offers native Codex (app server), Claude via ACPX, and OpenCode. The selected provider is passed through the existing runner configuration builder. Codex's default is the adapter catalog default. OpenCode and Pi require a provider/model ID; OpenRouter uses openrouter/<provider>/<model> and OPENROUTER_API_KEY. Entered keys are tested through the probe-only testCredentials field without storage. Finishing setup saves each key as an isolated user secret so a failed test cannot overwrite another agent’s credential, and an existing organization secret can also be bound. Agent configuration and revisions contain references, never the entered key.

A plugin environment whose driver declares supportsTasks runs agents only through Paperclip Runner. On that environment the Runner selector offers no Legacy runner, Codex and Claude need no reported platform, and a Legacy choice switches to Paperclip Runner.

Run test uses the chosen environment and current configuration. It resolves the runner before it checks that the environment driver allows the resulting adapter. For Claude/Codex readiness checks that do not make a model request, setup also invokes the adapter's existing CLI hello probe with the same environment and credentials. Runtime failures, provider failures, warnings, and in-progress tests use the same compact result card in setup and full configuration. Warnings remain distinguishable from blocking failures.

The new-agent dialog also retains an external-agent invitation link, with an optional message, one-time onboarding prompt, and clipboard fallback. External agents still require organization-admin approval.

The full configuration page retains the contextual navigation and existing instruction-file editor, skills, tools, permissions, API keys, and revision behavior. Secrets & variables groups environment bindings with API secret-access grants. Its page-level Save/Discard actions include editor-local environment drafts. Activity, runs, costs, and budgets link to the company audit views scoped to the agent.

The sections below are the original design reference; the implementation summary above supersedes their creation-dialog layout.

Context

Agents are the employees of a Paperclip company. Each agent has an adapter type (claude_local, codex_local, process, http) that determines how it runs, a position in the org chart (who it reports to), a heartbeat policy (how/when it wakes up), and a budget. The UI at /agents needs to support creating and configuring agents, viewing their org hierarchy, and inspecting what they've been doing -- their run history, live logs, and accumulated costs.

This spec covers three surfaces:

  1. Agent Creation Dialog -- the "New Agent" flow
  2. Agent Detail Page -- configuration, activity, and logs
  3. Agents List Page -- improvements to the existing list

1. Agent Creation Dialog

Follows the existing NewIssueDialog / NewProjectDialog pattern: a Dialog component with expand/minimize toggle, company badge breadcrumb, and Cmd+Enter submit.

Fields

Identity (always visible):

Field Control Required Default Notes
Name Text input (large, auto-focused) Yes -- e.g. "Alice", "Build Bot"
Title Text input (subtitle style) No -- e.g. "VP of Engineering"
Role Chip popover (select) No general Values from AGENT_ROLES: ceo, cto, cmo, cfo, engineer, designer, pm, qa, devops, researcher, general
Reports To Chip popover (agent select) No -- Dropdown of existing agents in the company. If this is the first agent, auto-set role to ceo and gray out Reports To. Otherwise required unless role is ceo.
Capabilities Text input No -- Free-text description of what this agent can do

Adapter (collapsible section, default open):

Field Control Default Notes
Adapter Type Chip popover (select) claude_local claude_local, codex_local, process, http
Test environment Button -- Runs adapter-specific diagnostics and returns pass/warn/fail checks for current unsaved config
CWD Text input -- Working directory for local adapters
Prompt Template Textarea -- Supports {{ agent.id }}, {{ agent.name }} etc.
Model Text input -- Optional model override

Adapter-specific fields (shown/hidden based on adapter type):

claude_local:

Field Control Default
Max Turns Per Run Number input 80
Skip Permissions Toggle true

codex_local:

Field Control Default
Search Toggle false
Bypass Sandbox Toggle true

process:

Field Control Default
Command Text input --
Args Text input (comma-separated) --

http:

Field Control Default
URL Text input --
Method Select POST
Headers Key-value pairs --

Runtime (collapsible section, default collapsed):

Field Control Default
Context Mode Chip popover thin
Monthly Budget (cents) Number input 0
Timeout (sec) Number input 900
Grace Period (sec) Number input 15
Extra Args Text input --
Env Vars Key-value pair editor --

Heartbeat Policy (collapsible section, default collapsed):

Field Control Default
Enabled Toggle true
Interval (sec) Number input 300
Wake on Assignment Toggle true
Wake on On-Demand Toggle true
Wake on Automation Toggle true
Cooldown (sec) Number input 10

Behavior

  • On submit, calls agentsApi.create(companyId, data) where data packs identity fields at the top level and adapter-specific fields into adapterConfig and heartbeat/runtime into runtimeConfig.
  • After creation, navigate to the new agent's detail page.
  • If the company has zero agents, pre-fill role as ceo and disable Reports To.
  • The adapter config section updates its visible fields when adapter type changes, preserving any shared field values (cwd, promptTemplate, etc.).

2. Agent Detail Page

Restructure the existing tabbed layout. Keep the header (name, role, title, status badge, action buttons) and add richer tabs.

Header

[StatusBadge]  Agent Name                    [Invoke] [Pause/Resume] [...]
               Role / Title

The [...] overflow menu contains: Terminate, Reset Session, Create API Key.

Tabs

Overview Tab

Two-column layout: left column is a summary card, right column is the org position.

Summary card:

  • Adapter type + model (if set)
  • Heartbeat interval (e.g. "every 5 min") or "Disabled"
  • Last heartbeat time (relative, e.g. "3 min ago")
  • Session status: "Active (session abc123...)" or "No session"
  • Current month spend / budget with progress bar

Org position card:

  • Reports to: clickable agent name (links to their detail page)
  • Direct reports: list of agents who report to this agent (clickable)

Configuration Tab

Editable form with the same sections as the creation dialog (Adapter, Runtime, Heartbeat Policy) but pre-populated with current values. Uses inline editing -- click a value to edit, press Enter or blur to save via agentsApi.update().

Sections:

  • Identity: name, title, role, reports to, capabilities
  • Adapter Config: all adapter-specific fields for the current adapter type
  • Heartbeat Policy: enable/disable, interval, wake-on triggers, cooldown
  • Runtime: context mode, budget, timeout, grace, env vars, extra args

Each section is a collapsible card. Save happens per-field (PATCH on blur/enter), not a single form submit. Validation errors show inline.

Runs Tab

This is the primary activity/history view. Shows a paginated list of heartbeat runs, most recent first.

Run list item:

[StatusIcon] #run-id-short   source: timer     2 min ago     1.2k tokens   $0.03
             "Reviewed 3 PRs and filed 2 issues"

Fields per row:

  • Status icon (green check = succeeded, red X = failed, yellow spinner = running, gray clock = queued, orange timeout = timed_out, slash = cancelled)
  • Run ID (short, first 8 chars)
  • Invocation source chip (timer, assignment, on_demand, automation)
  • Relative timestamp
  • Token usage summary (total input + output)
  • Cost
  • Result summary (first line of result or error)

Clicking a run opens a run detail inline (accordion expand) or a slide-over panel showing:

  • Full status timeline (queued -> running -> outcome) with timestamps
  • Session before/after
  • Token breakdown: input, output, cached input
  • Cost breakdown
  • Error message and error code (if failed)
  • Exit code and signal (if applicable)

Log viewer within the run detail:

  • Streams heartbeat_run_events for the run, ordered by seq
  • Each event rendered as a log line with timestamp, level (color-coded), and message
  • Events of type stdout/stderr shown in monospace
  • System events shown with distinct styling
  • For running runs, auto-scrolls and appends live via WebSocket events (heartbeat.run.event, heartbeat.run.log)
  • "View full log" link fetches from heartbeatsApi.log(runId) and shows in a scrollable monospace container
  • Truncation: show last 200 events by default, "Load more" button to fetch earlier events

Issues Tab

Keep as-is: list of issues assigned to this agent with status, clickable to navigate to issue detail.

Costs Tab

Expand the existing costs tab:

  • Cumulative totals from agent_runtime_state: total input tokens, total output tokens, total cached tokens, total cost
  • Monthly budget progress bar (current month spend vs budget)
  • Per-run cost table: date, run ID, tokens in/out/cached, cost -- sortable by date or cost
  • Chart (stretch): simple bar chart of daily spend over last 30 days

Properties Panel (Right Sidebar)

The existing AgentProperties panel continues to show the quick-glance info. Add:

  • Session ID (truncated, with copy button)
  • Last error (if any, in red)
  • Link to "View Configuration" (scrolls to / switches to Configuration tab)

3. Agents List Page

Current state

Shows a flat list of agents with status badge, name, role, title, and budget bar.

Improvements

Add "New Agent" button in the header (Plus icon + "New Agent"), opens the creation dialog.

Add view toggle: List view (current) and Org Chart view.

Org Chart view:

  • Tree layout showing reporting hierarchy
  • Each node shows: agent name, role, status badge
  • CEO at the top, direct reports below, etc.
  • Uses the agentsApi.org(companyId) endpoint which already returns OrgNode[]
  • Clicking a node navigates to agent detail

List view improvements:

  • Add adapter type as a small chip/tag on each row
  • Add "last active" relative timestamp
  • Add running indicator (animated dot) if agent currently has a running heartbeat

Filtering:

  • Tab filters: All, Active, Paused, Error (similar to Issues page pattern)

4. Component Inventory

New components needed:

Component Purpose
NewAgentDialog Agent creation form dialog
AgentConfigForm Shared form sections for create + edit (adapter, heartbeat, runtime)
AdapterConfigFields Conditional fields based on adapter type
HeartbeatPolicyFields Heartbeat configuration fields
EnvVarEditor Key-value pair editor for environment variables
RunListItem Single run row in the runs list
RunDetail Expanded run detail with log viewer
LogViewer Streaming log viewer with auto-scroll
OrgChart Tree visualization of agent hierarchy
AgentSelect Reusable agent picker (for Reports To, etc.)

Reused existing components:

  • StatusBadge, EntityRow, EmptyState, PropertyRow
  • shadcn: Dialog, Tabs, Button, Popover, Command, Separator, Toggle

5. API Surface

All endpoints already exist. No new server work needed for V1.

Action Endpoint Used by
List agents GET /companies/:id/agents List page
Get org tree GET /companies/:id/org Org chart view
Create agent POST /companies/:id/agents Creation dialog
Update agent PATCH /agents/:id Configuration tab
Pause/Resume/Terminate POST /agents/:id/{action} Header actions
Reset session POST /agents/:id/runtime-state/reset-session Overflow menu
Create API key POST /agents/:id/keys Overflow menu
Get runtime state GET /agents/:id/runtime-state Overview tab, properties panel
Invoke/Wakeup POST /agents/:id/heartbeat/invoke Header invoke button
List runs GET /companies/:id/heartbeat-runs?agentId=X Runs tab
Cancel run POST /heartbeat-runs/:id/cancel Run detail
Run events GET /heartbeat-runs/:id/events Log viewer
Run log GET /heartbeat-runs/:id/log Full log view

6. Implementation Order

  1. New Agent Dialog -- unblocks agent creation from the UI
  2. Agents List improvements -- add New Agent button, tab filters, adapter chip, running indicator
  3. Agent Detail: Configuration tab -- editable adapter/heartbeat/runtime config
  4. Agent Detail: Runs tab -- run history list with status, tokens, cost
  5. Agent Detail: Run Detail + Log Viewer -- expandable run detail with streaming logs
  6. Agent Detail: Overview tab -- summary card, org position
  7. Agent Detail: Costs tab -- expanded cost breakdown
  8. Org Chart view -- tree visualization on list page
  9. Properties panel updates -- session ID, last error

Steps 1-5 are the core. Steps 6-9 are polish.

Native ACPX connection tests reject unsupported local platforms before a host CLI login can incorrectly mark the runner connected. The existing verified Claude ACPX runtime requires Linux x64; remote environments are evaluated independently of the control-plane host platform.