Skip to content

Commit 48d6ef5

Browse files
mzxchandracubic-dev-ai[bot]icecrasher321
authored
feat(files): scoped search, in-place edits, and ranged reads for agent memory filesystems (#7393)
* feat(files): folder operations for the File block, and folders as scope Adds folder operations to file_v5 and the agent tool surface, and makes a folder a scope on the file operations that already existed rather than a second set of operations beside them. New operations: List, Create Folder, Move Folder, Delete Folder, Restore Folder, and Move File. List answers "what is in here" — subfolders and files together, direct children by default, the whole subtree under Recursive, subject to Max Depth and Search. Entries are a discriminated union on kind, and the listing is capped with a truncated flag rather than unbounded now that it includes files. Read, Get Content, Compress and Append gain an optional Folder above their file picker. It narrows what the picker offers; on the three read operations it also stands for that folder's files when none are picked, resolved when the workflow runs so a file added later is included. Append only narrows the picker, but the folder does travel when the advanced entry supplies a name rather than an id, because a name is only unique inside a folder. Write gains a folder destination, placed above File Name because it names where before it names what. Two things are worth a reviewer's attention. Path handling. Two spellings circulate: the stored display path, which backslash-escapes a slash inside a folder name, and the canonical percent-encoded path the tools take. A folder genuinely named "Q3/Q4" is one level in both and two if either is split on "/". folderPathSegments picks the parser by the leading slash the canonical form always carries, and resolveFolderIdsForPaths, isFileInFolderScope and selectDirectoryEntries are pure and tested against exactly that case. Where a folder is known, the code resolves by id and never builds a path-shaped reference at all. Delete Folder's recursive flag is a guard, not a scope. Without it, deleting a non-empty folder fails, and it is user-only so a model asked to clean up a folder cannot set it on a guess. This is deliberately the inverse of the read family's Include Subfolders, which is a scope and defaults on. Five file operations widen from copilot-only delegation to admit the executor, each justified by a tool in this change: files.list and files.folders.list for file_list, and files.folders.update / delete / restore for their tools. Principal kinds are unchanged and copilot keeps every operation it had. * Update apps/sim/tools/file/compress.ts Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * Update apps/sim/lib/workspace-files/application/workspace-file-folders.test.ts Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * Update apps/sim/lib/internal/file/operations.ts Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * feat(files): confine content search to a folder scope * feat(files): edit and insert file content in place * feat(files): ranged content reads and v2 surfaces for search and edit * Update apps/docs/openapi-v2-files-audit.json Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * Update apps/docs/content/docs/integrations/file.mdx Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * Update apps/sim/lib/workspace-files/application/edit-workspace-file-content.test.ts Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * Update apps/docs/content/docs/cli/files.mdx Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * refactor(files): share one folder-scope module with the table branch * test(files): cover both non-text buffer shapes, and regenerate the spec * fix(cli): stop claiming a line limit caps a --filter match * fix(files): connect the scoped-search, edit and ranged-read fields end to end * test(files): give the session principal fixture its required sessionId * fix(files): keep folder creation working under a slash-named parent * chore(files): sort the folder-scope import * fix(realtime): count room subscribers so one unmount cannot cut off another * test(realtime): type the room fixture instead of casting it * fix(files): close the round-2 review findings * fix(files): scope the picker from either half of the folder pair * docs(files): say that a folder constrains a canonical id too * fix(files): keep the root scope on a named append, edit or insert * fix(files): keep multi-folder search serializable and reclaim a stranded lock * fix(files): normalize a slash-omitted folder path instead of discarding it * docs(files): say what the search coverage flag actually covers * Update apps/sim/lib/api/contracts/v2/openapi/files-audit.ts Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * refactor(files): make the folder scope one advanced field, always recursive * chore(docs): regenerate the spec after the conflict-example wording change * fix(files): let an exact file id win over a lookalike name * fix(files): refuse an out-of-scope file id instead of matching a lookalike * feat(files): bring back Include Subfolders, in the advanced fields * Update apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-upload/file-upload.tsx Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * feat(files): complete folder scoping across surfaces * test(files): align generated API and folder scope coverage * fix(files): close folder scope review gaps * feat(files): add anchor-based file edits * fix(files): harden scoped edit operations * fix(files): preserve scoped listing semantics * fix(files): bound expanded edit output * fix(files): keep replacement content literal --------- Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
1 parent f050dc0 commit 48d6ef5

137 files changed

Lines changed: 32788 additions & 657 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ Also available as `sim files folders ls`.
108108
| `--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: `name`, `createdAt`, `updatedAt`. |
109109
| `--sort-order <value>` | No | Sort direction. Accepted values: `asc`, `desc`. |
110110
| `--scope <value>` | No | Which lifecycle set to list: `active` (default) returns live folders only; `archived` returns folders a recursive delete soft-deleted, which is how a caller finds a path to hand to the folder restore. Authorization is identical for both. Accepted values: `active`, `archived`. |
111+
| `--recursive <value>` | No | Whether parentPath includes every descendant instead of direct children only. Accepted values: `true`, `1`, `yes`, `on`, `y`, `enabled`, `false`, `0`, `no`, `off`, `n`, `disabled`. |
112+
| `--depth <value>` | No | Deepest level below parentPath to include when recursive is true. |
111113

112114
</CommandTable>
113115

@@ -172,6 +174,32 @@ sim files delete <fileId> [options]
172174

173175
</CommandTable>
174176

177+
## Apply one exact or anchor-based edit to a text file
178+
179+
```bash
180+
sim files edit <fileId> [options]
181+
```
182+
183+
**Arguments**
184+
185+
<CommandTable>
186+
187+
| Argument | Required | Description |
188+
| --- | --- | --- |
189+
| `fileId` | Yes | File identifier. |
190+
191+
</CommandTable>
192+
193+
**Options**
194+
195+
<CommandTable>
196+
197+
| Option | Required | Description |
198+
| --- | --- | --- |
199+
| `--edit <json\|@file>` | Yes | One edit object: &#123;"mode":"search_replace","search":"old","content":"new","replaceAll":false&#125;, &#123;"mode":"replace_between","beforeAnchor":"start line","afterAnchor":"end line","content":"new"&#125;, &#123;"mode":"insert_after","anchor":"line","content":"new"&#125;, or &#123;"mode":"delete_between","startAnchor":"first line deleted","endAnchor":"ending line kept"&#125;. Anchored modes also accept occurrence starting at 1 (JSON, or @path / @- to read a file or stdin). |
200+
201+
</CommandTable>
202+
175203
## Show file metadata and sharing status
176204

177205
```bash
@@ -310,6 +338,8 @@ sim files read <fileId> [options]
310338
| Option | Required | Description |
311339
| --- | --- | --- |
312340
| `--max-bytes <value>` | No | Optional ceiling on the source bytes fed to the parser, lowering but never raising the server limit. |
341+
| `--offset <value>` | No | First line to return, 1-based. Absent starts at the first line. |
342+
| `--limit <value>` | No | How many lines to return from `offset`. Absent reads to the end. |
313343

314344
</CommandTable>
315345

@@ -355,6 +385,27 @@ sim files restore <fileId>
355385

356386
</CommandTable>
357387

388+
## Search file content
389+
390+
```bash
391+
sim files search [options]
392+
```
393+
394+
**Options**
395+
396+
<CommandTable>
397+
398+
| Option | Required | Description |
399+
| --- | --- | --- |
400+
| `--query <value>` | Yes | Regular expression, or exact text when `mode` is `exact`. |
401+
| `--mode <value>` | No | How `query` is read. Accepted values: `exact`, `regex`. |
402+
| `--max-results <value>` | No | Maximum matching lines to return. |
403+
| `--folder <value...>` | No | Folders to search, by path as shown in the app; omit to search the whole workspace (space-separated, or @path / @- with one value per line; @@value for a literal leading @). |
404+
| `--include-subfolders` | No | Whether each folder scope includes nested folders; on by default. |
405+
| `--no-include-subfolders` | No | Send --include-subfolders as false. |
406+
407+
</CommandTable>
408+
358409
## Unzip an archive into a new folder beside it
359410

360411
```bash

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

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,8 @@ Also available as `sim files folders ls`.
788788
| `--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: `name`, `createdAt`, `updatedAt`. |
789789
| `--sort-order <value>` | No | Sort direction. Accepted values: `asc`, `desc`. |
790790
| `--scope <value>` | No | Which lifecycle set to list: `active` (default) returns live folders only; `archived` returns folders a recursive delete soft-deleted, which is how a caller finds a path to hand to the folder restore. Authorization is identical for both. Accepted values: `active`, `archived`. |
791+
| `--recursive <value>` | No | Whether parentPath includes every descendant instead of direct children only. Accepted values: `true`, `1`, `yes`, `on`, `y`, `enabled`, `false`, `0`, `no`, `off`, `n`, `disabled`. |
792+
| `--depth <value>` | No | Deepest level below parentPath to include when recursive is true. |
791793

792794
</CommandTable>
793795

@@ -858,6 +860,34 @@ sim files delete <fileId> [options]
858860

859861
</CommandTable>
860862

863+
### sim files edit
864+
865+
Apply one exact or anchor-based edit to a text file
866+
867+
```bash
868+
sim files edit <fileId> [options]
869+
```
870+
871+
**Arguments**
872+
873+
<CommandTable>
874+
875+
| Argument | Required | Description |
876+
| --- | --- | --- |
877+
| `fileId` | Yes | File identifier. |
878+
879+
</CommandTable>
880+
881+
**Options**
882+
883+
<CommandTable>
884+
885+
| Option | Required | Description |
886+
| --- | --- | --- |
887+
| `--edit <json\|@file>` | Yes | One edit object: &#123;"mode":"search_replace","search":"old","content":"new","replaceAll":false&#125;, &#123;"mode":"replace_between","beforeAnchor":"start line","afterAnchor":"end line","content":"new"&#125;, &#123;"mode":"insert_after","anchor":"line","content":"new"&#125;, or &#123;"mode":"delete_between","startAnchor":"first line deleted","endAnchor":"ending line kept"&#125;. Anchored modes also accept occurrence starting at 1 (JSON, or @path / @- to read a file or stdin). |
888+
889+
</CommandTable>
890+
861891
### sim files describe
862892

863893
Show file metadata and sharing status
@@ -1006,6 +1036,8 @@ sim files read <fileId> [options]
10061036
| Option | Required | Description |
10071037
| --- | --- | --- |
10081038
| `--max-bytes <value>` | No | Optional ceiling on the source bytes fed to the parser, lowering but never raising the server limit. |
1039+
| `--offset <value>` | No | First line to return, 1-based. Absent starts at the first line. |
1040+
| `--limit <value>` | No | How many lines to return from `offset`. Absent reads to the end. |
10091041

10101042
</CommandTable>
10111043

@@ -1055,6 +1087,29 @@ sim files restore <fileId>
10551087

10561088
</CommandTable>
10571089

1090+
### sim files search
1091+
1092+
Search File Content
1093+
1094+
```bash
1095+
sim files search [options]
1096+
```
1097+
1098+
**Options**
1099+
1100+
<CommandTable>
1101+
1102+
| Option | Required | Description |
1103+
| --- | --- | --- |
1104+
| `--query <value>` | Yes | Regular expression, or exact text when `mode` is `exact`. |
1105+
| `--mode <value>` | No | How `query` is read. Accepted values: `exact`, `regex`. |
1106+
| `--max-results <value>` | No | Maximum matching lines to return. |
1107+
| `--folder <value...>` | No | Folders to search, by path as shown in the app; omit to search the whole workspace (space-separated, or @path / @- with one value per line; @@value for a literal leading @). |
1108+
| `--include-subfolders` | No | Whether each folder scope includes nested folders; on by default. |
1109+
| `--no-include-subfolders` | No | Send --include-subfolders as false. |
1110+
1111+
</CommandTable>
1112+
10581113
### sim files unzip
10591114

10601115
Unzip an archive into a new folder beside it

0 commit comments

Comments
 (0)