feat: include agent metadata in workspace list responses (#27934)

Closes #27933. Related: #27897 (single-agent GET).

Agent metadata is only readable via a per-agent watch stream, so reading
it across N workspaces costs N+1 requests. This adds a batch read to the
list endpoint:

```text
GET /api/v2/workspaces?q=param:"pool=demo" include_agent_metadata:task_status
```

- New `include_agent_metadata` search key, repeatable and key-scoped. It
expands the response, it does not filter workspaces.
- `GetWorkspaces` aggregates the requested keys as JSON behind a `CASE`:
without opt-in the response is unchanged and the subquery never runs.
Runs only for the returned page, inside the same authorized query.
- Agents in the response gain `metadata`
(`[]codersdk.WorkspaceAgentMetadata`, `omitempty`), mapped by the
`workspace_agent_id` each element carries. The collection script is
omitted; it can be long.
- `codersdk.WorkspaceFilter` gains `IncludeAgentMetadata []string`.
- No wildcard, no schema change, no migration.

---

Authored by Coder Agents on behalf of @Emyrk.
This commit is contained in:
Steven Masley
2026-08-10 08:13:32 -05:00
committed by GitHub
parent a3a51228ee
commit 9a57dfa642
21 changed files with 1507 additions and 588 deletions
+215 -37
View File
@@ -10192,6 +10192,23 @@ Only certain features set these fields: - FeatureManagedAgentLimit - FeatureAgen
],
"logs_length": 0,
"logs_overflowed": true,
"metadata": [
{
"description": {
"display_name": "string",
"interval": 0,
"key": "string",
"script": "string",
"timeout": 0
},
"result": {
"age": 0,
"collected_at": "2019-08-24T14:15:22Z",
"error": "string",
"value": "string"
}
}
],
"name": "string",
"operating_system": "string",
"parent_id": {
@@ -11544,6 +11561,23 @@ Only certain features set these fields: - FeatureManagedAgentLimit - FeatureAgen
],
"logs_length": 0,
"logs_overflowed": true,
"metadata": [
{
"description": {
"display_name": "string",
"interval": 0,
"key": "string",
"script": "string",
"timeout": 0
},
"result": {
"age": 0,
"collected_at": "2019-08-24T14:15:22Z",
"error": "string",
"value": "string"
}
}
],
"name": "string",
"operating_system": "string",
"parent_id": {
@@ -15243,6 +15277,23 @@ If the schedule is empty, the user will be updated to use the default schedule.|
],
"logs_length": 0,
"logs_overflowed": true,
"metadata": [
{
"description": {
"display_name": "string",
"interval": 0,
"key": "string",
"script": "string",
"timeout": 0
},
"result": {
"age": 0,
"collected_at": "2019-08-24T14:15:22Z",
"error": "string",
"value": "string"
}
}
],
"name": "string",
"operating_system": "string",
"parent_id": {
@@ -15528,6 +15579,23 @@ If the schedule is empty, the user will be updated to use the default schedule.|
],
"logs_length": 0,
"logs_overflowed": true,
"metadata": [
{
"description": {
"display_name": "string",
"interval": 0,
"key": "string",
"script": "string",
"timeout": 0
},
"result": {
"age": 0,
"collected_at": "2019-08-24T14:15:22Z",
"error": "string",
"value": "string"
}
}
],
"name": "string",
"operating_system": "string",
"parent_id": {
@@ -15566,43 +15634,44 @@ If the schedule is empty, the user will be updated to use the default schedule.|
### Properties
| Name | Type | Required | Restrictions | Description |
|------------------------------|----------------------------------------------------------------------------------------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `api_version` | string | false | | |
| `apps` | array of [codersdk.WorkspaceApp](#codersdkworkspaceapp) | false | | |
| `architecture` | string | false | | |
| `connection_timeout_seconds` | integer | false | | |
| `created_at` | string | false | | |
| `directory` | string | false | | |
| `disconnected_at` | string | false | | |
| `display_apps` | array of [codersdk.DisplayApp](#codersdkdisplayapp) | false | | |
| `environment_variables` | object | false | | |
| » `[any property]` | string | false | | |
| `expanded_directory` | string | false | | |
| `first_connected_at` | string | false | | |
| `health` | [codersdk.WorkspaceAgentHealth](#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. |
| `id` | string | false | | |
| `instance_id` | string | false | | |
| `last_connected_at` | string | false | | |
| `latency` | object | false | | Latency is mapped by region name (e.g. "New York City", "Seattle"). |
| » `[any property]` | [codersdk.DERPRegion](#codersdkderpregion) | false | | |
| `lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](#codersdkworkspaceagentlifecycle) | false | | |
| `log_sources` | array of [codersdk.WorkspaceAgentLogSource](#codersdkworkspaceagentlogsource) | false | | |
| `logs_length` | integer | false | | |
| `logs_overflowed` | boolean | false | | |
| `name` | string | false | | |
| `operating_system` | string | false | | |
| `parent_id` | [uuid.NullUUID](#uuidnulluuid) | false | | |
| `ready_at` | string | false | | |
| `resource_id` | string | false | | |
| `scripts` | array of [codersdk.WorkspaceAgentScript](#codersdkworkspaceagentscript) | false | | |
| `started_at` | string | false | | |
| `startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](#codersdkworkspaceagentstartupscriptbehavior) | false | | Startup script behavior is a legacy field that is deprecated in favor of the `coder_script` resource. It's only referenced by old clients. Deprecated: Remove in the future! |
| `status` | [codersdk.WorkspaceAgentStatus](#codersdkworkspaceagentstatus) | false | | |
| `subsystems` | array of [codersdk.AgentSubsystem](#codersdkagentsubsystem) | false | | |
| `troubleshooting_url` | string | false | | |
| `updated_at` | string | false | | |
| `version` | string | false | | |
| Name | Type | Required | Restrictions | Description |
|------------------------------|----------------------------------------------------------------------------------------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `api_version` | string | false | | |
| `apps` | array of [codersdk.WorkspaceApp](#codersdkworkspaceapp) | false | | |
| `architecture` | string | false | | |
| `connection_timeout_seconds` | integer | false | | |
| `created_at` | string | false | | |
| `directory` | string | false | | |
| `disconnected_at` | string | false | | |
| `display_apps` | array of [codersdk.DisplayApp](#codersdkdisplayapp) | false | | |
| `environment_variables` | object | false | | |
| » `[any property]` | string | false | | |
| `expanded_directory` | string | false | | |
| `first_connected_at` | string | false | | |
| `health` | [codersdk.WorkspaceAgentHealth](#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. |
| `id` | string | false | | |
| `instance_id` | string | false | | |
| `last_connected_at` | string | false | | |
| `latency` | object | false | | Latency is mapped by region name (e.g. "New York City", "Seattle"). |
| » `[any property]` | [codersdk.DERPRegion](#codersdkderpregion) | false | | |
| `lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](#codersdkworkspaceagentlifecycle) | false | | |
| `log_sources` | array of [codersdk.WorkspaceAgentLogSource](#codersdkworkspaceagentlogsource) | false | | |
| `logs_length` | integer | false | | |
| `logs_overflowed` | boolean | false | | |
| `metadata` | array of [codersdk.WorkspaceAgentMetadata](#codersdkworkspaceagentmetadata) | false | | Metadata is only populated on the workspaces list endpoint when the request opts in with the include_agent_metadata search key, and it only carries the requested keys. The description's script is always empty here: it can be long, and list consumers want values. |
| `name` | string | false | | |
| `operating_system` | string | false | | |
| `parent_id` | [uuid.NullUUID](#uuidnulluuid) | false | | |
| `ready_at` | string | false | | |
| `resource_id` | string | false | | |
| `scripts` | array of [codersdk.WorkspaceAgentScript](#codersdkworkspaceagentscript) | false | | |
| `started_at` | string | false | | |
| `startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](#codersdkworkspaceagentstartupscriptbehavior) | false | | Startup script behavior is a legacy field that is deprecated in favor of the `coder_script` resource. It's only referenced by old clients. Deprecated: Remove in the future! |
| `status` | [codersdk.WorkspaceAgentStatus](#codersdkworkspaceagentstatus) | false | | |
| `subsystems` | array of [codersdk.AgentSubsystem](#codersdkagentsubsystem) | false | | |
| `troubleshooting_url` | string | false | | |
| `updated_at` | string | false | | |
| `version` | string | false | | |
## codersdk.WorkspaceAgentContainer
@@ -16005,6 +16074,75 @@ If the schedule is empty, the user will be updated to use the default schedule.|
| `id` | string | false | | |
| `workspace_agent_id` | string | false | | |
## codersdk.WorkspaceAgentMetadata
```json
{
"description": {
"display_name": "string",
"interval": 0,
"key": "string",
"script": "string",
"timeout": 0
},
"result": {
"age": 0,
"collected_at": "2019-08-24T14:15:22Z",
"error": "string",
"value": "string"
}
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
|---------------|------------------------------------------------------------------------------------------|----------|--------------|-------------|
| `description` | [codersdk.WorkspaceAgentMetadataDescription](#codersdkworkspaceagentmetadatadescription) | false | | |
| `result` | [codersdk.WorkspaceAgentMetadataResult](#codersdkworkspaceagentmetadataresult) | false | | |
## codersdk.WorkspaceAgentMetadataDescription
```json
{
"display_name": "string",
"interval": 0,
"key": "string",
"script": "string",
"timeout": 0
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
|----------------|---------|----------|--------------|-------------|
| `display_name` | string | false | | |
| `interval` | integer | false | | |
| `key` | string | false | | |
| `script` | string | false | | |
| `timeout` | integer | false | | |
## codersdk.WorkspaceAgentMetadataResult
```json
{
"age": 0,
"collected_at": "2019-08-24T14:15:22Z",
"error": "string",
"value": "string"
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
|----------------|---------|----------|--------------|-----------------------------------------------------------------------------------------------------------------------------------------|
| `age` | integer | false | | Age is the number of seconds since the metadata was collected. It is provided in addition to CollectedAt to protect against clock skew. |
| `collected_at` | string | false | | |
| `error` | string | false | | |
| `value` | string | false | | |
## codersdk.WorkspaceAgentPortShare
```json
@@ -16488,6 +16626,23 @@ If the schedule is empty, the user will be updated to use the default schedule.|
],
"logs_length": 0,
"logs_overflowed": true,
"metadata": [
{
"description": {
"display_name": "string",
"interval": 0,
"key": "string",
"script": "string",
"timeout": 0
},
"result": {
"age": 0,
"collected_at": "2019-08-24T14:15:22Z",
"error": "string",
"value": "string"
}
}
],
"name": "string",
"operating_system": "string",
"parent_id": {
@@ -16958,6 +17113,23 @@ If the schedule is empty, the user will be updated to use the default schedule.|
],
"logs_length": 0,
"logs_overflowed": true,
"metadata": [
{
"description": {
"display_name": "string",
"interval": 0,
"key": "string",
"script": "string",
"timeout": 0
},
"result": {
"age": 0,
"collected_at": "2019-08-24T14:15:22Z",
"error": "string",
"value": "string"
}
}
],
"name": "string",
"operating_system": "string",
"parent_id": {
@@ -17308,6 +17480,12 @@ If the schedule is empty, the user will be updated to use the default schedule.|
],
"logs_length": 0,
"logs_overflowed": true,
"metadata": [
{
"description": {},
"result": {}
}
],
"name": "string",
"operating_system": "string",
"parent_id": {