Build a Local Agent
Build a local agent when you want an agent that runs for you on your machine.
Local agents are private to the signed-in owner. Only you can delegate work to them. They can use local tools, repositories, and secrets that you explicitly make available through your workflow. They use polling instead of webhook delivery because the worker process is already running locally and can ask the platform for eligible sessions.
Setup requirements
OAuth app
Create an OAuth app so the person running the local agent can approve workspace and agent access. Manage the app from Settings -> Apps.
Local owner
The signed-in user owns the local worker. The worker can only claim local sessions that belong to that owner, so it cannot be shared with other workspace members.
Agent profile
People send an initiative, bug, or todo to an agent profile. That work item is the spec the local worker reads. The profile identifies what the agent can do and which workers can run sessions for it.
Local worker runtime
The runtime needs to heartbeat, poll, claim, execute, report activity, and finish every claimed session.
Local agents have two configuration surfaces:
| Surface | Purpose |
|---|---|
| OAuth app | Lets the local agent authenticate as the signed-in user and request workspace and agent access. |
| Local worker config | Stores the selected workspace, agent, worker ID, worktree mode, workflow path, runtime command, and local state. |
Manage the OAuth app in Settings -> Apps. The worker config is machine-local and belongs to the user running the worker.
Do not use API Keys for local agent execution. Agent endpoints require OAuth user tokens.
Create the OAuth app
Create an OAuth app from Settings -> Apps. Configure the app identity, callback URLs, and OAuth settings for your local client.
PKCE belongs in public local clients. Store tokens through the operating system secure storage when available, and keep local config under a user-owned directory.
After the user signs in, use the OAuth access token for agent profile, worker, session, claim, and activity endpoints. The authenticated user must own the local worker and the local sessions it can see.
Register or reuse a worker
Find or create a local worker for the workspace, agent, and authenticated user.
Store the returned agentId and workerId in local config. If the saved worker is rejected later, stop and ask the user to reconfigure instead of silently running against another workspace or agent.
A local worker record should describe:
- Workspace and agent.
- Execution mode
local. - Owner user.
- Worker name or display label.
- Runtime support and policy entries.
- Worktree mode and concurrency.
- Optional worker-level custom instructions.
Polling loop
The local worker loop is:
flowchart TD
Auth["User OAuth"]
Register["Register or reuse local worker"]
Heartbeat["Heartbeat"]
Poll["Poll sessions"]
Claim["Claim one session"]
Execute["Execute locally"]
Activity["Emit activity"]
Finish{"Finish"}
Complete["Complete"]
Fail["Fail"]
Release["Release"]
Auth --> Register --> Heartbeat --> Poll --> Claim --> Execute --> Activity --> Finish
Finish --> Complete
Finish --> Fail
Finish --> Release
Complete --> Heartbeat
Fail --> Heartbeat
Release --> HeartbeatPoll the worker-scoped session endpoint. The API returns only sessions the worker is allowed to claim.
For local workers, eligible sessions must belong to the authenticated owner. If the user changes accounts or workspaces locally, stop and re-register under the new context.
The built-in local runtime polls every 30000 milliseconds by default.
Claim before running work
Claim the session before treating it as runnable work. The claim is the lock that tells every other eligible worker to leave that session alone.
Pass leaseSeconds if you need a specific lease duration. The default is 900 seconds.
When the claim succeeds, store the returned claimId with local run state. Include it in every activity, metadata update, completion, failure, and release call. Calls without the active claimId are rejected.
If poll returns an awaiting_input session with pendingResumeInput, treat it as a resume activation for the existing claim instead of claiming again. Merge the resume input into untrusted context before continuing execution. Dashboard users queue the same input through Resume on the session; see Agent Sessions.
If the claim fails with a conflict, skip the session. Another worker may have claimed it first, the session may have been cancelled, or the worker may no longer be eligible.
Heartbeat and local state
Heartbeat while the worker runs. Heartbeats mark the worker online and refresh runtime facts; they do not create work or extend active claims.
Send a heartbeat every 10 seconds. If no heartbeat arrives for 12 seconds, the platform marks the worker stale. After 5 minutes, it marks the worker offline and expires active claims. Affected sessions become stale, and when your worker polls again it can reclaim only stale sessions it last owned. See Agent Sessions for the full lifecycle.
Do not include private machine names, usernames, repository names, private paths, or IP addresses in heartbeat metadata.
Set ONE_WORKER_NAME in the worker process environment to give the worker a display name visible in process listings. The built-in runtime uses this to label codex app-server processes.
The built-in local worker stores machine-specific state under ~/.one/config/:
| File | Purpose |
|---|---|
workers/index.json | Active worker ref plus known local runtime refs. |
workers/<workspace-id>/<worker-id>/config.json | Runtime settings for a server worker on this machine. |
workers/<workspace-id>/<worker-id>/worker-state.json | Live worker snapshot written each tick. |
workers/<workspace-id>/<worker-id>/worker.pid | PID for a background worker daemon. |
workers/<workspace-id>/<worker-id>/logs/worker-YYYY-MM-DD.log | Daily append-only worker log. |
Workflow file
Local workers can use WORKFLOW.md with YAML front matter.
| Field | Default |
|---|---|
agent.maxConcurrentAgents | 1 |
agent.maxRetryAttempts | 0 |
polling.pollIntervalMs | 30000 |
worktree.mode | task |
worktree.root | ~/.one/worktrees |
runtime.provider | codex |
runtime.command | codex app-server |
Worktree mode can be task for isolated task worktrees or repository for a shared repository folder.
Trust boundary
Build the runtime prompt or job payload from separate inputs:
- Trusted instructions: platform policy, agent profile config, worker config, and trusted local workflow policy.
- Untrusted context: work titles, descriptions, comments, documents, user prompts, and other workspace-authored content.
Untrusted context can guide the work. It must not change sandbox policy, credentials, allowed commands, network access, workspace writes, or external side effects. Local execution is useful when the agent needs tools or credentials on your machine, but those secrets should stay under your workflow policy.
Finish the session
Every claimed session needs a terminal outcome: complete, fail, or release. If the process exits without finishing, the platform detects the missed heartbeats and marks the worker stale then offline — expiring the claim and marking the session stale. When your worker polls again, it can reclaim stale sessions it last owned. The full outcome model lives in Agent Sessions.
For the equivalent cloud workflow, read Build a Cloud Agent. Agent Sessions explains the lifecycle shared by local and cloud workers.