fix(tasks): omit params from the list-tasks response#377
Conversation
The list endpoint returned the full task object, including `params`: the
arbitrary, caller-supplied create-time payload. On a broadly-scoped list that
exposes whatever callers store there (per-caller bearer tokens, PII, ...) in
bulk. Return a lean `TaskSummary` from `GET /tasks` instead, and keep the full
record (including `params`) on the single-task `GET /tasks/{id}`.
- Add `TaskSummary` (list shape) that omits `params`; point `list_tasks` at it.
- Update the dev UI task-name fallback to use the task `name` instead of
`params.description`, and regenerate `openapi.yaml`.
- Tests: list omits `params`; single-task GET still includes it.
Breaking change: typed SDK consumers that read `params` off a list item must
fetch the task by id instead.
✱ Stainless preview buildsThis PR will update the openapi python typescript Edit this comment to update them. They will appear in their respective SDK's changelogs. ✅ agentex-sdk-python studio · code · diff
✅ agentex-sdk-openapi studio · code · diff
✅ agentex-sdk-typescript studio · code · diff
This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push. |
Summary
GET /tasks(list) returned the full task object, the sameTaskResponsethe single-task GET returns, so every item includedparams: the arbitrary, caller-supplied create-time payload. Callers routinely stash sensitive material there (credentials, tokens, PII). Because the list is broadly scoped, that turned a single list call into a bulk read of every task'sparams.This makes the list a lean summary (
TaskSummary, noparams) and keeps the full record on the single-task fetch. A list response should carry only what a collection view needs; the full record belongs on the item fetch.Scope: the list response only. Behavior of
GET /tasks/{id},GET /tasks/name/{name},POST/PATCH, and the domain/repository layers is unchanged.Endpoint contract (after)
flowchart TD Client(["Client"]) Client -->|"GET /tasks (list)"| Summary["list of TaskSummary<br/>id, name, status, status_reason,<br/>created_at, updated_at, cleaned_at,<br/>task_metadata, agents"] Client -->|"GET /tasks/{id} (detail)"| Full["TaskResponse<br/>(all summary fields) + params"] Summary -. "need params for one task?" .-> Client Client ==>|"fetch that id"| Full classDef lean fill:#e8f5e9,stroke:#43a047,color:#1b5e20; classDef full fill:#fdecea,stroke:#e53935,color:#b71c1c; class Summary lean; class Full full;params, and is fetched one id at a time (and is subject to the same per-request authorization as before).Schema
TaskSummaryis a deliberate allowlist. It is a standalone model (not a subclass ofTask) soparamscannot leak in via inheritance, and becauseBaseModelignores extra fields, validating aTaskEntity(which still carriesparams) into aTaskSummarydropsparamsat serialization.classDiagram class TaskSummary { +id +name +status +status_reason +created_at +updated_at +cleaned_at +task_metadata +agents } class TaskResponse { +id +name +status +status_reason +created_at +updated_at +cleaned_at +task_metadata +agents +params } note for TaskSummary "list response, omits params" note for TaskResponse "single-task detail, includes params (may carry credentials or PII)"The two share the same safe fields; only
TaskResponseexposesparams.Before / after
What changes across a whole list call:
flowchart LR subgraph Before["Before: GET /tasks"] direction TB b["N tasks × full object<br/>N × params (creds/PII)"] end subgraph After["After: GET /tasks"] direction TB a["N tasks × lean summary<br/>0 × params"] end Before --> After classDef bad fill:#fdecea,stroke:#e53935,color:#b71c1c; classDef good fill:#e8f5e9,stroke:#43a047,color:#1b5e20; class Before bad; class After good;Field reference
TaskSummary)TaskResponse)idnamestatus/status_reasoncreated_at/updated_at/cleaned_attask_metadataagents?relationships=agentsparamsBreaking change and migration
Removing
paramsfrom the list is an intentional, security-motivated breaking change for typed consumers that readparamsoff a list item.flowchart TD Q{"Reading params off a list item?"} Q -->|No| OK["No change needed"] Q -->|Yes| Fix["Fetch the task by id, then read params"] classDef ok fill:#e8f5e9,stroke:#43a047,color:#1b5e20; class OK ok;The SDKs are generated from
agentex/openapi.yaml(Stainless), so the list-item type regenerates automatically on merge; consumers pick up the change when they bump the SDK. This PR regenerates and commitsopenapi.yaml(thelist_tasks200 now referencesTaskSummary), which theopenapi-specCI check enforces.Consumers touched in this PR
agentex-ui):createTaskNamefell back toparams.descriptionfor the task label; switched to the taskname(task_metadata.display_name → name → "Unnamed task"). No other code readsparamsoff a listed task.Implementation
src/api/schemas/tasks.py: addTaskSummary(allowlist, omitsparams).src/api/routes/tasks.py:list_tasksresponse_model=list[TaskSummary], returnsTaskSummary.model_validate(...)per entity. Single-task routes unchanged.agentex-ui/lib/task-utils.ts+ test: name-derivation fallback.agentex/openapi.yaml: regenerated.Follow-ups / known limitations
task_metadatais still free-form (dict[str, Any]) and partly caller-influenced. By convention it holds non-secret display/routing/filter metadata (and the list can filter on it), so it stays in the summary. If we want the list to be strict rather than convention-safe, a follow-up could givetask_metadataa typed shape so callers cannot stash sensitive data that the list would surface.Test plan
flowchart LR subgraph list["GET /tasks"] L1["task present in list"] L2["params NOT in item"] end subgraph get["GET /tasks/{id}"] G1["params present + correct"] end list --> gettests/integration/api/tasks/test_tasks_api.py): list omitsparamsfor both populated- and null-paramstasks; the schema test assertsparamsabsent on list items; single-task GET (by id and by name) still returnsparams.agentex-ui/lib/task-utils.test.ts): task-name derivation fromdisplay_name/name.openapi.yamlregenerated and committed.TaskSummary.model_validate({... "params": {...}})dropsparamswhileTaskResponsekeeps it;ruffclean.