Skip to content
Open
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
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,15 @@ This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Packages without a separate changelog are covered by the cross-package notes below.

## [Unreleased]
## [Unreleased - Minor]

### Added

- Agent tokens can read the agent roster (`GET /v1/agents`, `GET /v1/agents/:name`) and release or delete the agents they spawned, so spawned agents can work without the workspace key. Agents report who spawned them as `spawned_by`.

### Changed

- `POST /v1/agents/release` and `POST /v1/agents/release-exact` with an agent token accept only the caller itself or agents it spawned; other targets return `403 agent_not_spawned_by_caller` and need a workspace key.

## [8.13.0] - 2026-09-25

Expand Down
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,9 @@ hosted usage view, backfill boundary, and cost model.

- Workspace: isolated environment for one project/team
- Workspace key (`rk_live_*`): admin token for managing workspace resources
- Agent token (`at_live_*`): REST identity token an individual agent uses to participate
- Agent token (`at_live_*`): REST identity token an individual agent uses to participate. It can
also read the agent roster and node fleet, and release or delete the agents it spawned
(`spawned_by`), so a spawned agent never needs the workspace key
- Node token (`nt_live_*`): realtime transport token for direct or broker nodes on `/v1/node/ws`
- Observer token (`ot_live_*`): scoped read-only token for workspace realtime and read-only REST
- Identity types: `agent` (AI worker), `human` (person), `system` (automation/service actor)
Expand Down Expand Up @@ -725,6 +727,15 @@ send its SHA-256 hash as `expected_token_hash`; Relaycast then rejects a stale
release with `agent_release_generation_conflict` before dispatch or completion,
so a same-name takeover is left untouched.

When an agent token invokes the spawn action (`POST /actions/spawn/invoke` or `POST /agents/spawn`),
the agent the node registers for that invocation records the caller as `spawned_by`. That agent
token may then release (`POST /agents/release`, `POST /agents/release-exact`) or delete
(`DELETE /agents/:name`) it; releasing or deleting any other agent returns
`403 agent_not_spawned_by_caller` (an agent may still release itself). The workspace key keeps full
rights. Agents registered directly, spawned with a workspace key, or created before ownership was
recorded have `spawned_by: null`. Registering identities, observer tokens, webhooks, directory
writes, node enrollment, and workspace deletion stay workspace-key only.

`GET /nodes` pushes `capability`, `name`, and a liveness `status` selector
into its SQL query instead of fetching the full roster and filtering in JS.
Without `history`, the response stays the legacy bare array every existing
Expand Down
42 changes: 37 additions & 5 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,13 @@ components:
type: string
format: date-time
description: Last presence update timestamp.
spawned_by:
type: string
nullable: true
description: >-
Id of the agent whose spawn created this agent. That agent's token
may release or delete it. Null when registered directly, spawned
with a workspace key, or created before ownership was recorded.
channels:
type: array
description: Channels this agent belongs to. Present on agent detail responses.
Expand Down Expand Up @@ -2398,11 +2405,13 @@ paths:
active or legacy online rows are returned as offline. Reads do not write
to the database; durable cleanup runs separately. Status filters are
applied in SQL against derived presence, with online aliasing active.
Observer tokens require `agents:read`.
Agent tokens may read the roster. Observer tokens require `agents:read`
and see only the agents their filters allow.
tags:
- Agents
security:
- workspaceKey: []
- agentToken: []
- observerToken: []
parameters:
- name: status
Expand Down Expand Up @@ -2530,11 +2539,13 @@ paths:
summary: Get agent
description: >-
Get agent by name. Presence uses the same five-minute last_seen TTL as
the roster, without database writes. Observer tokens require `agents:read`.
the roster, without database writes. Agent tokens may read any agent.
Observer tokens require `agents:read`.
tags:
- Agents
security:
- workspaceKey: []
- agentToken: []
- observerToken: []
parameters:
- name: name
Expand Down Expand Up @@ -2605,11 +2616,13 @@ paths:
description: >-
Tombstone an agent, remove memberships, and dead-letter its active deliveries
in one atomic write. Database adapters without transaction or atomic batch
support are refused before these changes.
support are refused before these changes. An agent token may delete only
agents it spawned (`spawned_by`); other agents need a workspace key.
tags:
- Agents
security:
- workspaceKey: []
- agentToken: []
parameters:
- name: name
in: path
Expand All @@ -2619,6 +2632,14 @@ paths:
responses:
'204':
description: Agent deleted
'403':
description: agent_not_spawned_by_caller; the agent token did not spawn this agent
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: Agent not found

/agents/{name}/subscription-channel:
post:
Expand Down Expand Up @@ -2950,7 +2971,9 @@ paths:
tombstoning the agent and deleting any implicit direct node. Irreversible
release removes memberships and dead-letters active deliveries in the
same atomic write. Database adapters without transaction or atomic batch
support are refused before these changes.
support are refused before these changes. An agent token may release
itself or agents it spawned (`spawned_by`); releasing any other agent
needs a workspace key or node token.
tags:
- Agents
security:
Expand Down Expand Up @@ -3000,6 +3023,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'403':
description: agent_not_spawned_by_caller; the agent token did not spawn this agent
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: Agent not found
content:
Expand Down Expand Up @@ -3031,7 +3060,8 @@ paths:
replacement is never released by this operation. Replaying a key
returns the original terminal invocation and does not enqueue a second
`action.invoked` webhook, while reusing it with another payload
conflicts.
conflicts. An agent token may release itself or agents it spawned
(`spawned_by`).
tags: [Agents]
security:
- workspaceKey: []
Expand Down Expand Up @@ -3074,6 +3104,8 @@ paths:
data: { $ref: '#/components/schemas/LifecycleActionInvocation' }
'400':
description: Missing or invalid identity/key
'403':
description: agent_not_spawned_by_caller; the agent token did not spawn this agent
'409':
description: agent_identity_mismatch or idempotency_key_reused; no replacement mutation occurred
'503':
Expand Down
12 changes: 11 additions & 1 deletion packages/engine/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,17 @@ See the [root changelog](../../CHANGELOG.md) for cross-package release highlight
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [Unreleased - Minor]

### Added

- `GET /v1/agents` and `GET /v1/agents/:name` accept agent tokens (read-only); observer tokens keep their scope and filters.
- Migration `0062_agent_spawned_by.sql` adds `agents.spawned_by`. A node's `agent.register` for a spawn invocation dispatched to it (matching the invocation's agent name) records the invoking agent; workspace-key spawns and existing rows stay `null`. Agent resources expose it as `spawned_by`.
- `DELETE /v1/agents/:name` accepts an agent token for agents it spawned.

### Changed

- `POST /v1/agents/release` and `POST /v1/agents/release-exact` with an agent token accept only the caller itself or agents it spawned; other targets return `403 agent_not_spawned_by_caller`. Workspace keys and node tokens are unchanged.

## [8.13.0] - 2026-09-25

Expand Down
Loading
Loading