Skip to content
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,17 @@ caller also receives it directly. `node.status.online` / `node.status.offline`
durably record node liveness transitions (offline carries a `reason` such as
`liveness_timeout` | `disconnected` | `deregistered`).

Agent roster presence is lease-based, not a write-once registration flag.
`active` means Relaycast observed authenticated agent activity within the last
five minutes (`last_seen`); an older persisted `active` (or legacy `online`)
record is reported and durably swept as `offline`. Registration or subsequent
authenticated activity renews the lease. Releasing an agent dispatches to its
live host when one exists. If the host is absent or offline, a normal release
fails explicitly with `agent_host_unavailable` instead of creating an ownerless
pending invocation. A `delete_agent` request can be completed locally in that
case: Relaycast deactivates bindings and deletes the record and its implicit
direct node.

Fleet node presence is also published to workspace-key observer streams as the
ephemeral `node.online`, `node.heartbeat`, and `node.offline` events. Each
carries a `node` payload matching the `GET /nodes` roster entry (capabilities,
Expand Down Expand Up @@ -567,6 +578,8 @@ immediate delivery and receive a rejected capability result.
Queue/cron-backed adapters that own node dispatch outside the Node adapter should call
`drainNodeInvocations` after node reconnect/register/heartbeat and
`sweepTimedOutInvocations` from cron via `@relaycast/engine/node-invocations`.
They should also call `sweepStaleAgents` from `@relaycast/engine` to persist
agent lease expiry; roster reads derive the same status even before that sweep.

Actions are async fire-and-forget: invoking an action returns an ack with
`invocation_id` and dispatches an `action.invoke` frame to the handler's node. Agent
Expand Down
36 changes: 29 additions & 7 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ components:
status:
type: string
enum: [active, idle, blocked, waiting, offline, online]
description: >-
Presence-aware lifecycle status. `active` means the engine observed
authenticated activity within the five-minute agent liveness TTL;
a persisted active/legacy-online row older than that is reported and
durably swept as `offline`.
token:
type: string
description: Agent token (only returned on registration)
Expand Down Expand Up @@ -226,7 +231,10 @@ components:
additionalProperties: true
status:
type: string
description: Invocation dispatch state, typically `pending` or `dispatched`.
description: >-
Invocation lifecycle state. `pending` or `dispatched` means a live
host owns the request; `completed` can be returned immediately when
release reaps an agent that has no live host.
created_at:
type: string
format: date-time
Expand Down Expand Up @@ -1695,7 +1703,11 @@ paths:

get:
summary: List agents
description: List all agents in the workspace. Observer tokens require `agents:read`.
description: >-
List all agents in the workspace. Active presence is derived from
server-observed `last_seen` using a five-minute TTL, so stale persisted
active rows are returned as offline and swept durably. Observer tokens
require `agents:read`.
tags:
- Agents
security:
Expand All @@ -1706,7 +1718,7 @@ paths:
in: query
schema:
type: string
enum: [online, offline]
enum: [active, idle, blocked, waiting, offline, online, all]
responses:
'200':
description: List of agents
Expand Down Expand Up @@ -1875,9 +1887,13 @@ paths:
post:
summary: Request agent release
description: |
Request the node that owns an agent to release it by dispatching the
built-in `release` action. The engine marks or deletes the agent only
after the node reports successful completion.
If the agent has a live host, request release by dispatching the built-in
`release` action and apply the lifecycle change after the host confirms.
If no host is bound or the hosting connection is not live, a normal
release fails explicitly with `503 agent_host_unavailable`; it never
creates an ownerless pending invocation. With `delete_agent`, the engine
can reap the database record directly and returns a completed invocation,
deleting the agent and any implicit direct node.
tags:
- Agents
security:
Expand All @@ -1904,7 +1920,7 @@ paths:
description: If true, permanently delete the agent
responses:
'201':
description: Release action invoked
description: Release dispatched to a live host or completed locally
content:
application/json:
schema:
Expand All @@ -1920,6 +1936,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'503':
description: Agent has no live host to receive a normal release
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'

/channels:
post:
Expand Down
Loading
Loading