From aabe9b418b272c4744aad2adfce7ebcb1f410d5b Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Sat, 19 Sep 2026 19:34:54 -0700 Subject: [PATCH 1/8] feat(files): surface file versions in the File block and logs, with conditional writes --- apps/docs/content/docs/integrations/file.mdx | 8 + .../file-download/file-download.tsx | 7 +- apps/sim/blocks/blocks/file.ts | 9 + apps/sim/executor/types.ts | 6 + apps/sim/lib/api/contracts/primitives.ts | 2 + apps/sim/lib/api/contracts/tools/file.ts | 27 +++ apps/sim/lib/core/security/redaction.test.ts | 20 +++ apps/sim/lib/core/utils/user-file.ts | 10 +- apps/sim/lib/internal/file/operations.test.ts | 10 ++ apps/sim/lib/internal/file/operations.ts | 51 +++++- apps/sim/lib/logs/execution/logger.ts | 41 +++-- .../workspace/workspace-file-manager.ts | 15 +- .../workspace/workspace-file-versions.ts | 7 +- apps/sim/lib/workflows/types.ts | 3 + .../edit-workspace-file-content.test.ts | 52 +++++- .../edit-workspace-file-content.ts | 27 ++- .../application/file-revision.ts | 28 +++ .../read-workspace-file-metadata.ts | 3 +- .../update-workspace-file-content.test.ts | 161 ++++++++++++++++++ .../update-workspace-file-content.ts | 18 +- apps/sim/tools/file/append.ts | 6 + apps/sim/tools/file/edit.ts | 18 ++ apps/sim/tools/file/get.ts | 5 + apps/sim/tools/file/write.ts | 15 ++ apps/sim/tools/generated/tool-metadata.ts | 2 +- apps/sim/tools/generated/tool-outputs.ts | 2 +- apps/sim/tools/index.ts | 3 + 27 files changed, 501 insertions(+), 55 deletions(-) create mode 100644 apps/sim/lib/workspace-files/application/file-revision.ts create mode 100644 apps/sim/lib/workspace-files/application/update-workspace-file-content.test.ts diff --git a/apps/docs/content/docs/integrations/file.mdx b/apps/docs/content/docs/integrations/file.mdx index cee24c8b1c2..0fc151aad4e 100644 --- a/apps/docs/content/docs/integrations/file.mdx +++ b/apps/docs/content/docs/integrations/file.mdx @@ -139,6 +139,7 @@ Create a new workspace file, either from text content or from an existing file. | `fileInput` | file | No | An existing file to store in the workspace, such as one produced by an earlier tool. Use this for anything that is not text — PDFs, images, audio, archives. Provide exactly one of content or fileInput. | | `contentType` | string | No | MIME type for new files \(e.g., "text/plain"\). Auto-detected from the file extension, or taken from the stored file, if omitted. | | `overwrite` | boolean | No | Replace the contents of an existing file at the exact target path \(folder and name\) instead of creating a suffixed copy. Creates the file when that path does not exist yet. | +| `expectedRevision` | string | No | Refuse the write unless the file still holds the content this revision names, as returned by Get File or an earlier write. Use it so an edit computed from what you read cannot overwrite someone else’s change. | #### Output @@ -148,6 +149,8 @@ Create a new workspace file, either from text content or from an existing file. | `name` | string | File name | | `size` | number | File size in bytes | | `url` | string | URL to access the file | +| `version` | number | Version number of the content this write recorded | +| `revision` | string | Opaque token for the content this write produced. Pass it back as expectedRevision to make a later write conditional on nothing having changed since. | ### File Append @@ -171,6 +174,8 @@ Append content to an existing workspace file. The file must already exist. Conte | `name` | string | File name | | `size` | number | File size in bytes | | `url` | string | URL to access the file | +| `version` | number | Version number of the content this write recorded | +| `revision` | string | Opaque token for the content this write produced. Pass it back as expectedRevision to make a later write conditional on nothing having changed since. | ### Apply File Edit @@ -194,6 +199,7 @@ Apply one precise edit to an existing text file without rewriting it. Use search | `startAnchor` | string | No | For delete_between, the complete first line to delete. The start anchor is removed. | | `endAnchor` | string | No | For delete_between, the complete ending boundary line. The end anchor remains in the file. | | `occurrence` | number | No | For anchored edits, which matching anchor occurrence to use, starting at 1. Defaults to 1. | +| `expectedRevision` | string | No | Refuse the edit unless the file still holds the content this revision names, as returned by Get File or an earlier write. Use it so an edit computed from what you read cannot overwrite someone else’s change. | #### Output @@ -203,6 +209,8 @@ Apply one precise edit to an existing text file without rewriting it. Use search | `name` | string | File name | | `size` | number | File size in bytes | | `lineCount` | number | Lines in the file after the edit | +| `version` | number | Version number of the content this edit recorded | +| `revision` | string | Opaque token for the content this edit produced. Pass it back as expectedRevision to make a later write conditional on nothing having changed since. | ### File Compress diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/file-download/file-download.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/file-download/file-download.tsx index 2cb8e023541..8d310f38b29 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/file-download/file-download.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/file-download/file-download.tsx @@ -17,6 +17,8 @@ interface FileData { url: string storageProvider?: 's3' | 'blob' | 'gcs' | 'local' bucketName?: string + /** Workspace file version these bytes came from; absent on runs recorded before versioning. */ + version?: number } interface FileCardsProps { @@ -100,7 +102,10 @@ function FileCard({ file, isExecutionFile = false, workspaceId }: FileCardProps)
- {file.type} + + {file.type} + {file.version === undefined ? '' : ` · v${file.version}`} +