Skip to content

Commit ca4f245

Browse files
committed
Chat uploads resolve by uploads/<name> for reads, sandbox mounts, and image references (never listings or writes); an unresolved reference image fails the call instead of rendering without it; copilot session-sandbox calls are priced like Function-block sandboxes and report the raw cost beside the billed one; CLI docs regenerated
1 parent 60744d4 commit ca4f245

32 files changed

Lines changed: 1146 additions & 156 deletions

apps/docs/content/docs/cli/blocks.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ sim blocks list [options]
3939
| `--category <value>` | No | Restrict to one toolbar category. Accepted values: `blocks`, `tools`, `triggers`. |
4040
| `--capability <value>` | No | Restrict to blocks that can start a workflow — the `triggers` category, blocks declaring `triggerAllowed`, and blocks with trigger-mode fields. Accepted values: `trigger`. |
4141
| `--source <value>` | No | Restrict to shipped blocks or to this workspace’s deployed custom blocks. Accepted values: `builtin`, `custom`. |
42+
| `--include-sunset` | No | Include `legacy` and `deprecated` blocks. Off by default: a sunset block keeps executing where it is already placed, but it is not offered for new authoring. Each returned entry carries `sunset.replacedBy`, the block to build with instead. |
43+
| `--no-include-sunset` | No | Send --include-sunset as false. |
4244
| `--sort-by <value>` | No | Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order. Accepted values: `id`, `name`, `category`. |
4345
| `--sort-order <value>` | No | Sort direction. Accepted values: `asc`, `desc`. |
4446
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |

apps/docs/content/docs/cli/files.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ sim files read <fileId> [options]
299299

300300
| Argument | Required | Description |
301301
| --- | --- | --- |
302-
| `fileId` | Yes | File identifier. |
302+
| `fileId` | Yes | File identifier, or the file’s VFS path: `files/&lt;folder&gt;/&lt;name&gt;`, or `uploads/&lt;name&gt;` for a Chat upload. |
303303

304304
</CommandTable>
305305

apps/docs/content/docs/cli/logs.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ sim logs stats [options]
5151
| `--folder <value...>` | No | Folder path as shown in the app; the leading / is optional (space-separated, or @path / @- with one value per line; @@value for a literal leading @). |
5252
| `--trigger <value...>` | No | Comma-separated trigger types to include. An empty entry is rejected. The vocabulary is open, so an unrecognized member selects no runs; the literal `all` disables this filter. (space-separated, or @path / @- with one value per line; @@value for a literal leading @). |
5353
| `--level <value>` | No | Severity level to include. Accepted values: `info`, `error`. |
54+
| `--include-handled-errors` | No | Whether runs with a handled block error are counted as `handledErrorRuns`, and whether `level=error` also selects them. Off by default: counting them scans each run’s stored trace. |
55+
| `--no-include-handled-errors` | No | Send --include-handled-errors as false. |
5456
| `--start-date <value>` | No | Only include runs started at or after this UTC ISO 8601 timestamp, e.g. `2026-08-06T00:00:00Z`. A date without a time, or a timestamp carrying a UTC offset instead of `Z`, is rejected, as is year `0000`, which names no storable instant. |
5557
| `--end-date <value>` | No | Only include runs started at or before this UTC ISO 8601 timestamp, e.g. `2026-08-06T00:00:00Z`. A date without a time, or a timestamp carrying a UTC offset instead of `Z`, is rejected, as is year `0000`, which names no storable instant. |
5658
| `--segment-count <value>` | No | Number of equal time buckets to divide the window into, from 1 to 500. It is the ceiling on how many buckets a series carries: with `includeEmpty=true` exactly this many are returned, otherwise only the buckets holding at least one run. Buckets are never narrower than one minute, so on a short window the series extends past the end of the window rather than being compressed, and the trailing buckets are empty. |
@@ -84,6 +86,8 @@ sim logs list [options]
8486
| `--include-trace-spans` | No | Include trace spans in JSON or YAML output (implies full detail). |
8587
| `--include-final-output` | No | Include final output in JSON or YAML output (implies full detail). |
8688
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |
89+
| `--include-handled-errors` | No | Whether `level=error` also selects runs that finished at `info` after a block error was recovered by an error path. Off by default: such a run succeeded, so it is an error only to a caller auditing error handling. Every row reports `hasHandledErrors` whether or not this is set. Job runs carry no block trace, so the flag never widens that branch. |
90+
| `--no-include-handled-errors` | No | Send --include-handled-errors as false. |
8791
| `--status <value>` | No | Comma-separated execution statuses to include, from `pending` \| `running` \| `paused` \| `redacting` \| `completed` \| `failed` \| `cancelled`. An empty entry is rejected. ANDed with `level`, which reports severity rather than lifecycle. |
8892
| `--workflow-name <value>` | No | Case-insensitive substring match against the run's workflow name. Runs whose workflow has been deleted match nothing, because the name is no longer joinable. |
8993
| `--include-job-runs` | No | Whether Chat and Sim-agent job runs join the sequence alongside workflow runs. Job runs report `kind: "job"`, carry no `workflow` summary, and never carry a cost ledger. They are dropped entirely — not partially matched — whenever a filter they cannot answer is set: by workflow, workflow name, folder, model, or status. A filter therefore never means two different things across the union. Accepted only when sorting by `startedAt`: job runs record cost as a document and no comparable status, so they cannot participate in the other orderings. |

apps/docs/content/docs/cli/reference.mdx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,8 @@ sim blocks list [options]
310310
| `--category <value>` | No | Restrict to one toolbar category. Accepted values: `blocks`, `tools`, `triggers`. |
311311
| `--capability <value>` | No | Restrict to blocks that can start a workflow — the `triggers` category, blocks declaring `triggerAllowed`, and blocks with trigger-mode fields. Accepted values: `trigger`. |
312312
| `--source <value>` | No | Restrict to shipped blocks or to this workspace’s deployed custom blocks. Accepted values: `builtin`, `custom`. |
313+
| `--include-sunset` | No | Include `legacy` and `deprecated` blocks. Off by default: a sunset block keeps executing where it is already placed, but it is not offered for new authoring. Each returned entry carries `sunset.replacedBy`, the block to build with instead. |
314+
| `--no-include-sunset` | No | Send --include-sunset as false. |
313315
| `--sort-by <value>` | No | Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order. Accepted values: `id`, `name`, `category`. |
314316
| `--sort-order <value>` | No | Sort direction. Accepted values: `asc`, `desc`. |
315317
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |
@@ -997,7 +999,7 @@ sim files read <fileId> [options]
997999

9981000
| Argument | Required | Description |
9991001
| --- | --- | --- |
1000-
| `fileId` | Yes | File identifier. |
1002+
| `fileId` | Yes | File identifier, or the file’s VFS path: `files/&lt;folder&gt;/&lt;name&gt;`, or `uploads/&lt;name&gt;` for a Chat upload. |
10011003

10021004
</CommandTable>
10031005

@@ -2441,6 +2443,8 @@ sim logs stats [options]
24412443
| `--folder <value...>` | No | Folder path as shown in the app; the leading / is optional (space-separated, or @path / @- with one value per line; @@value for a literal leading @). |
24422444
| `--trigger <value...>` | No | Comma-separated trigger types to include. An empty entry is rejected. The vocabulary is open, so an unrecognized member selects no runs; the literal `all` disables this filter. (space-separated, or @path / @- with one value per line; @@value for a literal leading @). |
24432445
| `--level <value>` | No | Severity level to include. Accepted values: `info`, `error`. |
2446+
| `--include-handled-errors` | No | Whether runs with a handled block error are counted as `handledErrorRuns`, and whether `level=error` also selects them. Off by default: counting them scans each run’s stored trace. |
2447+
| `--no-include-handled-errors` | No | Send --include-handled-errors as false. |
24442448
| `--start-date <value>` | No | Only include runs started at or after this UTC ISO 8601 timestamp, e.g. `2026-08-06T00:00:00Z`. A date without a time, or a timestamp carrying a UTC offset instead of `Z`, is rejected, as is year `0000`, which names no storable instant. |
24452449
| `--end-date <value>` | No | Only include runs started at or before this UTC ISO 8601 timestamp, e.g. `2026-08-06T00:00:00Z`. A date without a time, or a timestamp carrying a UTC offset instead of `Z`, is rejected, as is year `0000`, which names no storable instant. |
24462450
| `--segment-count <value>` | No | Number of equal time buckets to divide the window into, from 1 to 500. It is the ceiling on how many buckets a series carries: with `includeEmpty=true` exactly this many are returned, otherwise only the buckets holding at least one run. Buckets are never narrower than one minute, so on a short window the series extends past the end of the window rather than being compressed, and the trailing buckets are empty. |
@@ -2476,6 +2480,8 @@ sim logs list [options]
24762480
| `--include-trace-spans` | No | Include trace spans in JSON or YAML output (implies full detail). |
24772481
| `--include-final-output` | No | Include final output in JSON or YAML output (implies full detail). |
24782482
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |
2483+
| `--include-handled-errors` | No | Whether `level=error` also selects runs that finished at `info` after a block error was recovered by an error path. Off by default: such a run succeeded, so it is an error only to a caller auditing error handling. Every row reports `hasHandledErrors` whether or not this is set. Job runs carry no block trace, so the flag never widens that branch. |
2484+
| `--no-include-handled-errors` | No | Send --include-handled-errors as false. |
24792485
| `--status <value>` | No | Comma-separated execution statuses to include, from `pending` \| `running` \| `paused` \| `redacting` \| `completed` \| `failed` \| `cancelled`. An empty entry is rejected. ANDed with `level`, which reports severity rather than lifecycle. |
24802486
| `--workflow-name <value>` | No | Case-insensitive substring match against the run's workflow name. Runs whose workflow has been deleted match nothing, because the name is no longer joinable. |
24812487
| `--include-job-runs` | No | Whether Chat and Sim-agent job runs join the sequence alongside workflow runs. Job runs report `kind: "job"`, carry no `workflow` summary, and never carry a cost ledger. They are dropped entirely — not partially matched — whenever a filter they cannot answer is set: by workflow, workflow name, folder, model, or status. A filter therefore never means two different things across the union. Accepted only when sorting by `startedAt`: job runs record cost as a document and no comparable status, so they cannot participate in the other orderings. |
@@ -4108,7 +4114,7 @@ sim tables delete <tableId> [options]
41084114

41094115
### sim tables enrichment get
41104116

4111-
Get Enrichment Run Detail
4117+
Get Row Group Run
41124118

41134119
```bash
41144120
sim tables enrichment get <tableId> <rowId> <groupId>

apps/docs/content/docs/cli/tables.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1041,7 +1041,7 @@ sim tables delete <tableId> [options]
10411041

10421042
</CommandTable>
10431043

1044-
## Get enrichment run detail
1044+
## Get row group run
10451045

10461046
```bash
10471047
sim tables enrichment get <tableId> <rowId> <groupId>

apps/docs/openapi-v2-files-audit.json

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -728,20 +728,19 @@
728728
"get": {
729729
"operationId": "readFileText",
730730
"summary": "Read File Text",
731-
"description": "Return a file's text content, parsed out of the stored bytes. This reads the file; it writes nothing — `POST /api/v2/files/{fileId}/unzip` is the endpoint that unzips an archive into the workspace. Answers `400` for a type no parser supports, naming the raw-bytes download as the escape hatch, and `413` for a file above the extraction ceiling. A generated document is extracted from its compiled artifact rather than its generation source, so one still compiling answers `409` and is worth retrying. **`degraded: true` means text extraction did not fully succeed and the returned text may be incomplete or synthesized from the file's raw bytes. Do not treat it as authoritative content.** The legacy `.doc` and `.ppt` parsers deliberately return best-effort content rather than failing, so this flag — not an error status — is how a partial extraction is reported. `truncated` separately reports that a parser limit stopped extraction early.",
731+
"description": "Return a file's text content, parsed out of the stored bytes. `{fileId}` is the file's id or its VFS path (`files/<folder>/<name>`; `uploads/<name>` for a Chat upload, which no listing shows), and the response echoes the canonical `path` that was read. This reads the file; it writes nothing — `POST /api/v2/files/{fileId}/unzip` is the endpoint that unzips an archive into the workspace. Answers `400` for a type no parser supports, naming the raw-bytes download as the escape hatch, and `413` for a file above the extraction ceiling. A generated document is extracted from its compiled artifact rather than its generation source, so one still compiling answers `409` and is worth retrying. **`degraded: true` means text extraction did not fully succeed and the returned text may be incomplete or synthesized from the file's raw bytes. Do not treat it as authoritative content.** The legacy `.doc` and `.ppt` parsers deliberately return best-effort content rather than failing, so this flag — not an error status — is how a partial extraction is reported. `truncated` separately reports that a parser limit stopped extraction early.",
732732
"tags": ["Files"],
733733
"parameters": [
734734
{
735735
"name": "fileId",
736736
"in": "path",
737737
"required": true,
738-
"description": "File identifier.",
738+
"description": "File identifier, or the file’s VFS path: `files/<folder>/<name>`, or `uploads/<name>` for a Chat upload.",
739739
"schema": {
740740
"type": "string",
741741
"minLength": 1,
742-
"maxLength": 128,
743-
"pattern": "^[A-Za-z0-9_-]+$",
744-
"description": "File identifier."
742+
"maxLength": 4096,
743+
"description": "File identifier, or the file’s VFS path: `files/<folder>/<name>`, or `uploads/<name>` for a Chat upload."
745744
}
746745
},
747746
{
@@ -3357,6 +3356,10 @@
33573356
"type": "string",
33583357
"description": "File name, including its extension."
33593358
},
3359+
"path": {
3360+
"type": "string",
3361+
"description": "Canonical VFS path of the file that was read: `files/…`, or `uploads/<name>` for a Chat upload."
3362+
},
33603363
"type": {
33613364
"type": "string",
33623365
"description": "Stored MIME type of the source file."
@@ -3400,6 +3403,7 @@
34003403
"required": [
34013404
"fileId",
34023405
"name",
3406+
"path",
34033407
"type",
34043408
"text",
34053409
"truncated",

apps/sim/app/api/v2/files/[fileId]/text/route.test.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ describe('GET /api/v2/files/[fileId]/text', () => {
7777
data: {
7878
fileId: FILE_ID,
7979
name: 'notes.txt',
80+
path: 'files/notes.txt',
8081
type: 'text/plain',
8182
text: 'hello there!',
8283
truncated: false,
@@ -133,11 +134,46 @@ describe('GET /api/v2/files/[fileId]/text', () => {
133134

134135
expect(mocks.readText).toHaveBeenCalledWith(
135136
expect.objectContaining({
136-
input: { fileId: FILE_ID, assertedWorkspaceId: WORKSPACE_ID, maxBytes: 4096 },
137+
input: { workspaceId: WORKSPACE_ID, reference: FILE_ID, maxBytes: 4096 },
137138
})
138139
)
139140
})
140141

142+
/**
143+
* A Chat upload is absent from every listing, so the `uploads/<name>` path its
144+
* upload notice names is the only handle the model has. The path parameter
145+
* therefore carries a VFS reference, not only an id, and the response echoes
146+
* the canonical path that was read so the model sees the name it was told.
147+
*/
148+
it('accepts a VFS path as the file reference and echoes the path read', async () => {
149+
mocks.readText.mockResolvedValueOnce(
150+
result({
151+
file: { id: 'wf_upload', name: 'face (2).png', type: 'image/png', vfsNamespace: 'uploads' },
152+
})
153+
)
154+
155+
const response = await GET(textRequest(), {
156+
params: Promise.resolve({ fileId: 'uploads/face%20(2).png' }),
157+
})
158+
const body = await response.json()
159+
160+
expect(response.status).toBe(200)
161+
expect(mocks.readText).toHaveBeenCalledWith(
162+
expect.objectContaining({
163+
input: {
164+
workspaceId: WORKSPACE_ID,
165+
reference: 'uploads/face%20(2).png',
166+
maxBytes: undefined,
167+
},
168+
})
169+
)
170+
expect(body.data).toMatchObject({
171+
fileId: 'wf_upload',
172+
name: 'face (2).png',
173+
path: 'uploads/face%20(2).png',
174+
})
175+
})
176+
141177
it('rejects a query with an undeclared key', async () => {
142178
const response = await GET(textRequest(`workspaceId=${WORKSPACE_ID}&format=html`), context)
143179

apps/sim/app/api/v2/files/[fileId]/text/route.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { v2ReadFileTextContract } from '@/lib/api/contracts/v2/files'
22
import { defineV2JsonRoute, v2ApiKeyAuth, v2RateLimits } from '@/lib/api/server/routes'
3+
import { workspaceFileVfsPath } from '@/lib/uploads/contexts/workspace/workspace-file-manager'
34
import { v2FileErrorPolicies } from '@/lib/workspace-files/api'
45
import { fileOperations } from '@/lib/workspace-files/application/operations'
56
import { readWorkspaceFileText } from '@/lib/workspace-files/application/read-workspace-file-text'
@@ -9,6 +10,10 @@ export const dynamic = 'force-dynamic'
910
/**
1011
* GET /api/v2/files/[fileId]/text — extract a file's text.
1112
*
13+
* `[fileId]` is a file id or the file's VFS path, so a Chat upload — absent from
14+
* every listing — is readable by the `uploads/<name>` path its upload notice
15+
* names. The response echoes the canonical path that was read.
16+
*
1217
* Runs on the existing `files.read_content` operation: extracting text reads
1318
* exactly the bytes that operation already authorizes.
1419
*
@@ -28,15 +33,16 @@ export const GET = defineV2JsonRoute({
2833
rateLimit: v2RateLimits.publicApi,
2934
errorPolicy: v2FileErrorPolicies.concealResourceAuthorization,
3035
mapInput: ({ params, query }) => ({
31-
fileId: params.fileId,
32-
assertedWorkspaceId: query.workspaceId,
36+
workspaceId: query.workspaceId,
37+
reference: params.fileId,
3338
maxBytes: query.maxBytes,
3439
}),
3540
useCase: readWorkspaceFileText,
3641
present: ({ file, text, truncated, degraded, degradedReason, byteCount }) => ({
3742
data: {
3843
fileId: file.id,
3944
name: file.name,
45+
path: workspaceFileVfsPath(file),
4046
type: file.type,
4147
text,
4248
truncated,

0 commit comments

Comments
 (0)