Repository navigation
📓 feat: Author Files in Attached Workspaces - #15548
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 13e00464bf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 91f80633ad
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Live cross-repo verification passed against Code Interpreter #104 through Code API on port 23114, Redis, and an outbound worker. Verified exact final-content preview, stale revision rejection, fresh fenced commit persistence, and the final BOM-normalized result schema. Current head includes only the review fixes plus the isolated-declaration annotation and BOM result-shape alignment after the acknowledged review request; no additional Codex request was made. |
|
Codex Review: Didn't find any major issues. Keep it up! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Pull request overview
This PR enables create_file / edit_file to safely mutate files inside a selected attached (BYOM) workspace by extending the workspace-tools protocol and routing file-authoring calls through the attached worker while preserving the canonical tool identities and approval behavior.
Changes:
- Extend the workspace-tools protocol adapter with
write_file,edit_file, andpreview_editrequest/result schemas plus bounded validation. - Route host-side
create_file/edit_fileto attached workspace mutations (with conflict handling, atomic edits, and protected-content preview fencing). - Wire new mutation functions through the server Code process + agent deps and add targeted unit tests.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/api/src/code/workspace.ts | Adds protocol types + validation for workspace write/edit/preview operations. |
| packages/api/src/code/workspace.spec.ts | Adds adapter-level tests for write/edit/preview validation and fencing. |
| packages/api/src/agents/tools.ts | Adds attached-workspace tool definitions/descriptions and registration toggles. |
| packages/api/src/agents/tools.spec.ts | Tests attached-workspace descriptions/semantics are registered as expected. |
| packages/api/src/agents/initialize.ts | Plumbs workspaceTools into tool registration during agent init. |
| packages/api/src/agents/handlers.ts | Implements attached-workspace create/edit routing, limits, preview fence, and abort propagation. |
| packages/api/src/agents/handlers.spec.ts | Adds behavioral tests for attached create/edit, conflicts, limits, and protected-content blocking. |
| api/server/services/Files/Code/process.js | Adds Code API forwarding functions for workspace mutations + preview. |
| api/server/services/Files/Code/process.spec.js | Tests forwarding behavior and headers for new workspace mutation calls. |
| api/server/services/Endpoints/agents/skillDeps.js | Exposes new mutation functions to agent handlers via deps wiring. |
| api/server/services/Endpoints/agents/skillDeps.spec.js | Tests deps wiring exposes the new mutation functions. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| it('creates a file atomically in an attached workspace', async () => { | ||
| const writeWorkspaceFile = jest.fn(async () => ({ | ||
| protocolVersion: 1 as const, | ||
| operation: 'write_file' as const, | ||
| workspaceId: 'primary', | ||
| path: 'src/new.ts', | ||
| created: true, | ||
| bytesWritten: 20, | ||
| })); |
| function attachedWorkspaceAuthoringPath( | ||
| tc: ToolCallRequest, | ||
| filePath: string, | ||
| ): { filePath: string } | ToolExecuteResult { | ||
| if (!filePath.startsWith('workspace/')) { | ||
| return errorResult(tc, 'Attached environment file paths must use "workspace/{relativePath}".'); | ||
| } | ||
| const relativePath = filePath.slice('workspace/'.length); | ||
| const pathError = invalidSandboxAuthoringPath(relativePath); | ||
| return pathError ? errorResult(tc, pathError) : { filePath: relativePath }; | ||
| } |
| const action = result.created ? 'Created' : 'Updated'; | ||
| return successResult(tc, `${action} workspace/${path.filePath} (${content.length} chars).`, { | ||
| path: `workspace/${path.filePath}`, | ||
| [HOST_FILE_AUTHORING_ARTIFACT_KEY]: true, | ||
| bytes_written: result.bytesWritten, | ||
| created: result.created, | ||
| }); |
| function isCodeOnlyFileAuthoringDef(def: LCTool | undefined): boolean { | ||
| if (def?.name === CREATE_FILE_TOOL_NAME) { | ||
| return def.description === CODE_CREATE_FILE_DESCRIPTION; | ||
| return ( | ||
| def.description === CODE_CREATE_FILE_DESCRIPTION || | ||
| def.description === ATTACHED_CODE_CREATE_FILE_DESCRIPTION | ||
| ); | ||
| } | ||
| if (def?.name === EDIT_FILE_TOOL_NAME) { | ||
| return def.description === CODE_EDIT_FILE_DESCRIPTION; | ||
| return ( | ||
| def.description === CODE_EDIT_FILE_DESCRIPTION || | ||
| def.description === ATTACHED_CODE_EDIT_FILE_DESCRIPTION | ||
| ); | ||
| } | ||
| return false; | ||
| } |
| if (request.operation === 'preview_edit') { | ||
| const content = typeof value.content === 'string' ? value.content : null; | ||
| return ( | ||
| hasOnlyKeys(value, PREVIEW_EDIT_RESULT_KEYS) && | ||
| value.path === request.path && | ||
| content != null && | ||
| Buffer.from(content).toString('utf8') === content && | ||
| typeof value.hasUtf8Bom === 'boolean' && | ||
| /^[a-f0-9]{64}$/.test(typeof value.baseSha256 === 'string' ? value.baseSha256 : '') && | ||
| value.replacements === request.edits.length && | ||
| Number.isSafeInteger(value.bytesWritten) && | ||
| Number(value.bytesWritten) === | ||
| new TextEncoder().encode(content).byteLength + (value.hasUtf8Bom ? 3 : 0) && | ||
| Number(value.bytesWritten) <= WORKSPACE_WRITE_MAX_BYTES | ||
| ); | ||
| } |
| function areValidWorkspaceEdits(edits: unknown): edits is WorkspaceTextEdit[] { | ||
| if (!Array.isArray(edits)) return false; | ||
| if (edits.length < 1 || edits.length > WORKSPACE_EDIT_MAX_COUNT) return false; | ||
| let bytes = 0; | ||
| for (const edit of edits) { | ||
| if ( | ||
| !isRecord(edit) || | ||
| !hasOnlyKeys(edit, TEXT_EDIT_KEYS) || | ||
| !isUtf8StringWithinBytes(edit.oldText, WORKSPACE_WRITE_MAX_BYTES) || | ||
| edit.oldText.length === 0 || | ||
| !isUtf8StringWithinBytes(edit.newText, WORKSPACE_WRITE_MAX_BYTES) | ||
| ) { | ||
| return false; | ||
| } | ||
| bytes += | ||
| new TextEncoder().encode(edit.oldText).byteLength + | ||
| new TextEncoder().encode(edit.newText).byteLength; | ||
| if (bytes > WORKSPACE_WRITE_MAX_BYTES) return false; | ||
| } | ||
| return true; | ||
| } |
709c411 to
50a4d58
Compare
Summary
I routed the existing
create_fileandedit_filetools through a selected attached BYOM workspace while preserving their canonical names for approval hooks and managed-environment compatibility. This PR depends on LibreChat #15546 and Code Interpreter #102–#103.write_fileand atomic batchedit_filevalidation to LibreChat's worker protocol adapter.workspace/{relativePath}namespace without exposing the worker's absolute host path.overwrite: false, and surface deterministic conflict guidance instead of replacing files silently.create_fileandedit_fileidentities so the default permission layer and future customizable approval hooks govern BYOM exactly like managed code environments.Change Type
Testing
@librechat/apitests covering workspace protocol, agent handlers, tool registration, and initialization.git diff --checkacross all changed files.@librechat/codeworker attached to a new empty non-Git directory. Verified create-only conflict (409), two-edit atomic commit, no partial mutation after a later failed batch, persistent readback, traversal rejection, and cleared mutation quarantine.Test Configuration:
danny-avila/code-workspace-create678ce9bd25d8836086277facbb85a95375a6747a127.0.0.1:23114Checklist
Dependency