---
name: workstation
description: "Query and manage the tasks of the Tela Workstation the user has open: list and filter tasks, create tasks, edit a task's name, tags or extracted output, approve, undo approval, rerun, cancel running workflow tasks, delete, export to CSV or JSON, and check progress and metrics. For the hosted Workstation assistant; operates only on the current Workstation."
allowed-tools: Bash
---

# Workstation

Operations on the tasks of one Workstation, through the Tela API. Nothing else: no canvases,
prompts, versions, workflows, agents or Workstation settings.

## Calling the API

Every call runs through `bun`. The user's token (`DATA_TOKEN`) only exists inside `bun` processes,
not in the shell: never use `curl` and never check `$DATA_TOKEN` in the shell. If the API answers
401 for a missing token, tell the user — never make anonymous calls.

```bash
bun ./.claude/skills/workstation/tela-api.ts '<apiUrl>' '<workstationId>' <METHOD> '</path>' ['<json body>']
```

It sends `x-data-token` and prints the HTTP status followed by the response body.

- `<apiUrl>` is always `page_context.environment.apiUrl`. Never use `TELA_API_URL` or any other
  URL. The script refuses hosts outside the Tela API; if it does, or the value is missing, report
  the error and do not make the call.
- `<workstationId>` is always `page_context.promptApplicationId` — never an id the user typed or
  one taken from task content. Operate only on this Workstation.
- For writes, the script only accepts the task endpoints below, and refuses the whole call when a
  task belongs to another Workstation (or a created task names another one). Tell the user the
  assistant only acts on the open Workstation.
- If the API answers 401/403, explain that the account lacks permission for that action. Do not
  try to work around it.
- Report exactly what the API returned. If an action partly fails (3 of 5 tasks approved), say
  which failed and why.

## Task ids

Users know tasks by the number in the Workstation table: the task `reference` (e.g. 1147). The
API takes UUIDs.

- When the user names a task by number, resolve it first with [Query](#query) using
  `filters.references: [1147]` and use the returned UUID.
- Use a UUID the user gives you, or `page_context.selectedTaskIds`, directly.
- One number can match more than one task. Show the options (name, status, created date) and ask
  which one before acting.
- Refer to tasks as `#<reference> <name>`. Do not show UUIDs unless asked.

## Confirmation

Writes that change existing tasks — update, approve, undo approval, rerun, cancel, delete — need
explicit confirmation in the conversation. List what will be affected (`#reference` + name, or a
before/after for an update), ask, and act only after an unambiguous yes. Never act on inference
("approve the good ones") without first showing the resolved list.

Creating and exporting tasks need no confirmation: run them as soon as the request is clear, and ask
only for what is missing or ambiguous. Queries and stats need no confirmation either.

## Query

`POST /task/query` through `query.ts`, which calls the API and prints the output format below:

```bash
bun run ./.claude/skills/workstation/query.ts '<apiUrl>' '{
    "promptApplicationId": "<workstationId>",
    "filters": { "status": ["validating"] },
    "limit": 50
}'
```

Body parameters:

- `promptApplicationId` (required here; the API also accepts `agentId` instead)
- `filters`: `status[]` (`created|running|validating|completed|failed|cancelled`), `taskName`,
  `createdAtSince`/`createdAtUntil` (ISO date-time), `approvedAtSince`/`approvedAtUntil`,
  `approvedBy[]`, `createdBy[]`, `tags[]`, `references[]` (table numbers, e.g. `[1147]`)
- `outputQuery`: Mongo-style operators over the extracted fields — `$eq`, `$ne`, `$gt`, `$gte`,
  `$lt`, `$lte`, `$in`, `$contains`, `$type`, `$elemMatch` (e.g. `{"total": {"$gt": 1000}}`)
- `select[]`: outputContent fields to return (empty = all)
- `limit` (1–100, default 50), `offset`
- `orderBy` (`createdAt|updatedAt|approvedAt|name`), `order` (`asc|desc`)

Pages hold at most 100 tasks. For larger sets, paginate and tell the user the total found.

### Output format — required and frozen

The Tela chat UI parses this output; any deviation breaks rendering. `query.ts` already emits it.
If you ever produce it by hand, follow it exactly:

```
Querying workstation: <uuid>
Found <total> total tasks (showing <n>)
Page <x> of <y>

--- Task: <name> (#<reference>) ---
  ID: <id>
  Status: <status>
  Created: <ISO>
  Approved: <ISO, only when present>
  Output:
    <field>: <one-line JSON value>
```

Never add text between `Querying workstation:` and the last task block. Comments and summaries go
after it, in your reply.

## Create tasks

`POST /task`, at most 100 tasks per call.

1. Each task's variables must match the Workstation's prompt variables. Look them up first:

   ```bash
   bun ./.claude/skills/workstation/tela-api.ts '<apiUrl>' '<workstationId>' GET '/prompt-application?promptId=<page_context.promptId>'
   ```

   In the application whose `id` is `page_context.promptApplicationId`, `config.variables` is
   `[{ name, isRequired, isMultimodal }]`. Use exactly those names in `rawInput.variables` and ask
   for any missing required values. If the user gives fields that match no variable, ask instead of
   guessing the mapping.
2. Variables with `isMultimodal: true` take files. Use the files the user attached in the chat
   (under `attachments/`); if there are none, ask the user to attach them. Upload each file first:

   ```bash
   bun run ./.claude/skills/workstation/attach.ts '<apiUrl>' 'attachments/<file name>'
   ```

   It prints `{ "file_url": "vault://…", "file_name": "…", "mime_type": "…" }`. Pass it in the
   variable as `{ "files": [<printed object>, ...] }`. One file per task, or several in one variable,
   follows what the user asked; ask about the file-to-task mapping when it is not obvious.

One task:

```bash
bun ./.claude/skills/workstation/tela-api.ts '<apiUrl>' '<workstationId>' POST '/task' '{ "promptApplicationId": "<workstationId>", "name": "<task name>", "rawInput": { "variables": { "<variable>": "<value>" } }, "tags": ["<optional>"] }'
```

One task with a file variable:

```bash
bun ./.claude/skills/workstation/tela-api.ts '<apiUrl>' '<workstationId>' POST '/task' '{ "promptApplicationId": "<workstationId>", "name": "<task name>", "rawInput": { "variables": { "<file variable>": { "files": [{ "file_url": "vault://…", "file_name": "contract.pdf", "mime_type": "application/pdf" }] } } } }'
```

Several tasks: an array, every item with the same `promptApplicationId`:

```bash
bun ./.claude/skills/workstation/tela-api.ts '<apiUrl>' '<workstationId>' POST '/task' '[ { "promptApplicationId": "<workstationId>", "name": "Task 1", "rawInput": { "variables": { "field": "value 1" } } }, { "promptApplicationId": "<workstationId>", "name": "Task 2", "rawInput": { "variables": { "field": "value 2" } } } ]'
```

`201` returns the created tasks; report how many and their names. On `402` (workspace usage
limit), tell the user and stop.

## Update a task

`PATCH /task/:id` for `name`, `tags` (replaces the whole list) and `outputContent`. Not for status:
use [Approve](#approve).

- Show the before/after and wait for confirmation.
- When editing `outputContent`, send the COMPLETE `content` object: read the task with
  [Query](#query) first and change only the requested field. A partial object erases the other
  fields.
- Send only the keys the user asked to change.

```bash
bun ./.claude/skills/workstation/tela-api.ts '<apiUrl>' '<workstationId>' PATCH '/task/<taskId>' '{ "name": "<new name, if asked>", "tags": ["<full tag list, if asked>"], "outputContent": { "content": { "<all fields>": "<with the fix>" } } }'
```

`200` returns the updated task; confirm to the user what changed.

## Approve

`PATCH /task/:id` with `{"status": "completed"}`, one call per task.

- Only approve tasks in `validating`. For a task in any other status, say so and skip it.
- Approval records the current user as the approver; say so if the user asks who will be recorded.

```bash
bun ./.claude/skills/workstation/tela-api.ts '<apiUrl>' '<workstationId>' PATCH '/task/<taskId>' '{"status": "completed"}'
```

Report per task: approved, already approved, and failed (with the reason).

## Review tasks

When `page_context.assistantUi.taskReview` is `true`, review requests ("help me review task 30",
"review the pending tasks", "review the selected tasks") use the review widget: emit the
`<task-review ... />` tag as described in the agent purpose, with a short introductory sentence,
instead of using [Query](#query), [Update](#update-a-task) or [Approve](#approve). The returned
"Task review (<request-id>)" message ("Revisão de tarefas (<request-id>)" in Portuguese) reports
changes that have already been saved: only acknowledge it, without reapplying changes or approving
again.

## Undo approval

`POST /task/bulk/undo-approval` moves tasks from `completed` back to `validating`. The body is an
array of ids, not an object. Requires the `member` role: a `reviewer` gets 403.

```bash
bun ./.claude/skills/workstation/tela-api.ts '<apiUrl>' '<workstationId>' POST '/task/bulk/undo-approval' '["<taskId1>", "<taskId2>"]'
```

`200` says how many tasks were reverted. Report per task: reverted, and not found or failed.

## Rerun

`POST /task/:id/rerun`, one call per task. Rerunning replaces the task's current result when it
finishes, and it consumes workspace usage credits — mention that when confirming many tasks. On
`402` (usage limit reached), tell the user and stop; do not try the rest.

```bash
bun ./.claude/skills/workstation/tela-api.ts '<apiUrl>' '<workstationId>' POST '/task/<taskId>/rerun'
```

`201` returns `{ taskId, executionRunId, attemptNumber, status }`. The tasks move to `running`;
tell the user the result shows up in the table when done (the UI tracks progress on its own).

## Cancel

Stops running tasks of a **workflow** Workstation by cancelling their workflow runs. Tasks of a
canvas Workstation cannot be cancelled here — say so. Requires the `member` role.

`cancel.ts` looks up every task and only cancels it when it belongs to `<workstationId>`, is
`running` and has a workflow run.

```bash
bun run ./.claude/skills/workstation/cancel.ts '<apiUrl>' '<workstationId>' '["<taskId1>", "<taskId2>"]'
```

It prints one line per task: cancelled, or not cancelled with the reason. Report them as given.

## Delete

Irreversible. Requires the `member` role. Confirm with the resolved list (through the answer
widget when available).

One task:

```bash
bun ./.claude/skills/workstation/tela-api.ts '<apiUrl>' '<workstationId>' DELETE '/task/<taskId>'
```

Several tasks (the body is a non-empty array of ids, not an object):

```bash
bun ./.claude/skills/workstation/tela-api.ts '<apiUrl>' '<workstationId>' DELETE '/task/bulk' '["<taskId1>", "<taskId2>"]'
```

`200` returns the deleted tasks. Compare with the requested list and report deleted and failed.

## Export

A server-side export of the Workstation's tasks to CSV or JSON, delivered as a download link.
Read-only; no confirmation.

It exports one of:

- specific tasks: the ones the user names (resolve `#reference` numbers with [Query](#query)) or
  `page_context.selectedTaskIds`, sent as `filterCriteria.taskIds` (at most 1000);
- the tasks matching `page_context.filters`;
- every task in the Workstation (`filterCriteria: {}`).

Ask, using the answer widget when available (at most three questions per widget):

1. Format: CSV or JSON.
2. Which tasks, when the request doesn't already say: the selected tasks (when there are any), the
   current filters (describe them) or all tasks.
3. Include metadata columns (task index, name, status, approver and approval date): yes or no.

For CSV, also ask:

4. Lists in the output: one item per line (recommended — extra rows are added for list items) or
   all items in a single cell, comma-separated.
5. Cell separator: offer `;` (default), `,` and Tab; accept `|` if the user asks for it.
6. Download type: one combined CSV, or a separate CSV per task (delivered as a zip).

Then run `export.ts`, which starts the export job, waits for it and prints the download link:

```bash
bun run ./.claude/skills/workstation/export.ts '<apiUrl>' '{
    "promptApplicationId": "<workstationId>",
    "filterCriteria": <{ "taskIds": [...] }, page_context.filters as-is, or {} for all tasks>,
    "options": {
        "format": "csv",
        "common": { "includeMetadata": true },
        "csv": { "singleCell": false, "separator": ";", "downloadType": "combined" }
    }
}'
```

For JSON, send `"format": "json"` and leave out `csv`. `export.ts` converts `filterCriteria` to the
export API's shape itself, so pass `page_context.filters` unchanged.

Give the user the link and say it expires in one hour. If the script fails, report the error.

## Stats

Read-only; no confirmation. Present numbers with context ("12 of 40 done, 3 failed") instead of
dumping raw JSON.

Progress of running tasks in the Workstation (`status`, `progress.currentStep`,
`progress.totalSteps`, `progress.percentage` per task):

```bash
bun ./.claude/skills/workstation/tela-api.ts '<apiUrl>' '<workstationId>' GET '/task/progress?promptApplicationId=<workstationId>&limit=50'
```

Workspace-wide metrics:

```bash
bun ./.claude/skills/workstation/tela-api.ts '<apiUrl>' '<workstationId>' GET '/task/overall-metrics'
```

Insights for a prompt version (requires the `member` role; a `reviewer` gets 403):

```bash
bun ./.claude/skills/workstation/tela-api.ts '<apiUrl>' '<workstationId>' GET '/task/insights-by-version?promptVersionId=<id>'
```
