# Company OS External Adapter Lane

This is the first Company OS-native external-adapter contract. It is inspired by
Paperclip's external adapter, but it is **Company OS-native first** and makes
**No Paperclip drop-in claim**.

## Product Contract

`adapter_type = "external"` means:

- Company OS does not run a server-side model for this wake.
- Company OS does not emit usage or `costUsd` for this wake.
- The external runner does the compute and writes results back through Company
  OS APIs, run events, task notes, connector sessions, and skills surfaces.
- The lane reuses existing `company_agents.adapter_config` rows and the
  verified-run timeline rather than creating a second ledger.

Capability declaration:

| Capability | Value |
| --- | --- |
| `supportsInstructionsBundle` | true |
| `supportsLocalAgentJwt` | true |
| `supportsSkills` | true |
| `requiresMaterializedRuntimeSkills` | false |
| `supportsModelProfiles` | false |

The runner, not Company OS, materializes desired skills. This keeps compute and
runtime-specific files outside the server while preserving a durable desired
state in the Company OS ledger.

## Wake Modes

`noop` is the default. It records a cost-safe wake plan and returns immediately.
Use this when an embodied local runner is already polling Company OS.

`webhook` sends a wake notification to `url` and then returns. The body shape is
intended to be compatible with existing runner workers: payload template plus
agent/run/context fields.

`bridged` is the live-mirroring mode. It is bounded by:

- `claimGraceMs` default `12000`: if no runner claims the wake, degrade to
  webhook/noop-style completion.
- `bridgedTimeoutMs` default `600000`: once claimed, finalize after runner
  silence.
- `bridgedMaxMs` default `3600000`: absolute lifetime cap.
- `pollIntervalMs` default `1500`: relay polling cadence.

These defaults are part of the safety contract: bridged wake behavior must not
hang runs indefinitely.

## Config Schema

The native schema is exposed at:

```text
GET /api/company/external-adapter/contract
```

The schema has two UI groups:

- **Wake:** `mode`, `url`, `method`, `headers`, `payloadTemplate`, `timeoutMs`,
  `claimGraceMs`, `bridgedTimeoutMs`, `bridgedMaxMs`, `pollIntervalMs`.
- **Runner:** `model`, `effort`, `maxTurnsPerRun`, `workspaceStrategy`,
  `instructionsFilePath`.

Runner fields are hints for the external runner. Company OS stores them and
reports them, but does not run a model because of them.

## Config Diagnostics

The config diagnostics surface is intentionally adapter-specific so operators
can test external wake configuration before assigning an agent to the lane.

Diagnostics are exposed at:

```text
POST /api/company/external-adapter/diagnose
```

with either the config directly or `{ "adapterConfig": { ... } }`.

Diagnostics:

- `noop` passes with an informational cost-safe check.
- `webhook` and `bridged` require an absolute `http(s)` URL.
- invalid URLs and unsupported protocols are errors.
- webhook/bridged configs without auth-like headers warn with
  `external_webhook_unauthenticated`.

## Integration Points

This first slice deliberately stops at a safe contract and diagnostic surface.
It reuses:

- `company_agents.adapter_type` and `adapter_config`
- markdown agent definitions and inline runtime declarations
- repo and Company OS skills
- connector accounts, sessions, and trigger subscriptions
- run execution events and verified-run output/cost badges
- `model_gateway.rs` for paid provider budget enforcement

Next implementation slice: make the worker dispatcher honor
`adapter_type = "external"` by producing a no-model wake run, then add bridged
relay endpoints for `/progress/:runId` and `/complete/:runId`.
