Skip to content

feat(project-files): let Chat read and write Project files - #9034

Draft
mzxchandra wants to merge 10 commits into
stagingfrom
codex/project-files-product-v2
Draft

mzxchandra wants to merge 10 commits into
stagingfrom
codex/project-files-product-v2

Conversation

@mzxchandra

@mzxchandra mzxchandra commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Lets Chat read, search, write, upload and copy Project files, on top of the Project files backend that #9015 through #9019 merged.
Everything stays behind the projects flag, which turns on only after the project-first redesign ships its Project files UI.

Scope: backend and Chat transport only.
The Project file browser and other Project UI were removed from this PR, because the redesign will build that surface project-first rather than inside the workspace Files page.
The removed UI is preserved at the project-files-ui-seed tag for that work.
The only client change left is the shared ChatContext wire type (Project contexts and file owners) and its kind registry entry.

What Chat can do with Project files

  • Discover the user's Projects (projects list) and target one explicitly per command with --project <id>.
  • Read, view and grep Project files, write and upload them from the workbench sandbox, and copy files between workspaces and Projects.
  • Accept Project and Project file tags in a user's message.

Secret handling matches workspace files

  • Project text reads and grep now redact secrets the way workspace text reads do, instead of refusing any file that contains one.
  • One text extraction core is shared by workspace text reads and private file observation: parse, redact before a line window can cut a secret, then slice.
  • readProjectFileText admits the source and every rendered input as complete text and returns text only, so bytes never leave under a complete label.
  • readProjectFileArtifact still admits opaque bytes, so visual and byte reads of a file with a secret are still refused.

Flag gating

  • Project sandbox callbacks now run under the same workbench content gate as workspace callbacks, since staging treats resource-delegated Chat principals as Chat.
  • The chat resources API refuses new Project tabs while Projects are off; removing or reordering stored tabs still works.
  • Every other Chat path to Project data already checks the flag: worker hints, list_user_projects, Project file use cases, and sandbox callbacks.
  • A flag check in list_user_projects that could never run was removed.

Pairing with simstudioai/mothership#594
This PR and the worker companion merge back to back, Sim first.
Sim sends file-owner hints only when the worker's health check advertises the file-owner protocol, so merging Sim first changes nothing at runtime.
The worker byte-checks Sim's copies of the generated contracts, so the pair must land together; the companion pin will point at this PR's head on staging.

Type of Change

  • New feature
  • Other: removes the Project files UI from scope

Testing

  • Typecheck, repository audits and the test-types ratchet pass.
  • Unit suites for the touched areas pass (427 files).
  • Project and content integration suites pass against disposable PostgreSQL, including Project reads, grep, write transport, account deletion and private purge.
  • New integration coverage checks that a Project file with a secret is masked at the model egress for full reads, never leaks through a line-range read, still matches grep on its other lines, and is refused for byte reads.
  • Locally, nine generated-document archive tests time out because the isolated document worker is unavailable; they time out identically on the commit before this change.
  • Staging verification with Projects off: pre-deploy fixtures are seeded and the old release passes all three Chat checks (grep masks a workspace secret, a ranged read shows the secret's name instead of its value, a sandbox read succeeds); the post-deploy run follows once this pair is serving.
  • E2B and Daytona acceptance of the Project workbench flow is still outstanding and gates the flag flip, not this merge.

Reviewers: focus on readProjectFileText and the artifact view factory in artifacts.ts, the shared extractFileTextFromBytes, and the Project sandbox callback gate.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

🤖 Generated with Claude Code

@vercel

vercel Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
docs Ready Ready Preview Oct 16, 2026 12:00am UTC

Request Review

@mzxchandra
mzxchandra force-pushed the codex/project-files-product-v2 branch from dc1b9e6 to d66d69d Compare October 15, 2026 04:23
@github-actions github-actions Bot added the requires-mothership-merge Has a companion PR on the mothership/copilot side — merge in lockstep label Oct 15, 2026
- Keep prepareProjectsForAccountDeletion returning the Project IDs to delete.
  The object return added an unused storageCleanupEventIds field; the
  durable cleanup outbox already processes those events, so restore the
  staging contract and its stronger integration assertions.
- Exempt lib/mothership/generated from the relative-import convention.
  contracts:sync copies those wire contracts verbatim and byte-checks them,
  so their sibling imports must stay relative; drop their baseline rows.
- Re-record the files page module-count baseline for the Project browser.
- Type the test fixtures that regressed, including a queue-recovery
  fixture that persisted the invalid completion status 'success'.
Staging now holds resource-delegated Chat principals to content
admission, so a Project callback read with no gate was refused and the
sandbox got a 503. Run Project callbacks under the same workbench
session gate as workspace callbacks (the gate's workspace is optional,
since Projects have none) and share the withheld-result handling.

The Project CLI read suite now runs reads under the tool executor's
registry gate, as Chat does.
The chat resources API stored a Project-owned tab without checking the
projects flag. Refuse new Project tabs while Projects are off; removing
or reordering stored tabs stays possible so they can be cleaned up.

Also drop the projects flag check in list_user_projects that could never
run (listProjects already refuses while the flag is off) and the content
delivery barrel export left unused by the shared sandbox delivery helper.
The Project files UI will be built for the project-first redesign, so
this PR keeps only the backend and Chat transport behind the projects
flag. Restore the workspace Files page, file viewer, Chat resource
panel, add-resource and mention entries, and their client query hooks
and owner adapters to staging. The previous tip is tagged
project-files-ui-seed for that UI work.

Kept: the shared ChatContext wire type (Project contexts and file owners
reach the Chat server) and its exhaustive kind registry entry.

Also drop the UI-only browser constants and response types nothing uses
now, restore the files page module-count baseline, and type the
use-chat fixtures that returned with the staging test.
Project grep and text reads admitted every Project artifact as opaque
bytes, so any file carrying a secret was refused to Chat instead of
having the secret masked, unlike the same file in a workspace.

Share one text extraction core between workspace text reads and private
file observation: parse, redact before a line window can cut a secret,
then slice. Project text reads now go through readProjectFileText, which
admits the source and every rendered input as complete text and returns
text only. The complete artifact read stays private to that use case, so
bytes never leave under a complete label; readProjectFileArtifact keeps
admitting opaque bytes, and visual reads of a secret still refuse.
…s-product-v2

# Conflicts:
#	apps/sim/lib/mothership/request/application/recover-stream.ts
@mzxchandra

Copy link
Copy Markdown
Contributor Author

@greptile

@mzxchandra

Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@mzxchandra I have started the AI code review. It will take a few minutes to complete.

@mzxchandra mzxchandra changed the title feat(files): Project file browser and Mothership integration feat(project-files): let Chat read and write Project files Oct 15, 2026
@github-actions github-actions Bot removed the requires-mothership-merge Has a companion PR on the mothership/copilot side — merge in lockstep label Oct 15, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 116 files

Confidence score: 3/5

  • In workspace.ts, organization-target workbench receipts may not be scoped and audited to the selected workspace when the chat provides only chatOrganizationId. Pass organizationId: context.chatOrganizationId ?? context.organizationId.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/lib/mothership/agent-cli/workspace.ts">

<violation number="1" location="apps/sim/lib/mothership/agent-cli/workspace.ts:59">
P2: Organization-target workbench receipts are scoped and audited to the selected workspace when the chat provides only `chatOrganizationId`. Pass `organizationId: context.chatOrganizationId ?? context.organizationId` so receipts retain organization scope across targets and audit records identify the owning organization.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Turn on auto-fix | Re-trigger cubic

Comment thread apps/sim/lib/mothership/agent-cli/index.ts
Comment thread apps/sim/lib/mothership/agent-cli/project-file-grep.ts
Comment thread apps/sim/stores/panel/types.ts
Comment thread apps/sim/stores/panel/types.ts
Comment thread apps/sim/stores/panel/types.ts
Comment thread apps/sim/stores/panel/types.ts
Comment thread apps/sim/lib/mothership/agent-cli/workspace.ts
Comment thread apps/sim/lib/mothership/agent-cli/project-file-read.ts Outdated
Comment thread apps/sim/lib/mothership/tools/sandbox-file-copy.ts
Comment thread apps/sim/lib/mothership/agent-cli/project-file-provenance.ts
…tory

#9057 hid the projects group from --help and `sim cli` search until
launch, and the inventory printer dropped every hidden command, so the
Mothership worker lost the Project file commands it routes --project to.

List a hidden top-level group that is only unpublished, marked
`unpublished`, so the worker can route to it once Sim enables Projects
for a turn; other hidden commands stay out, and --help is unchanged.
@greptile-apps

greptile-apps Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 4/5

[High impact] Adds file read and write access to the chat system.

Fix the workspace file-tab regression and satisfy the test rule before merging.

Findings

  1. P1 Workspace tabs lose their address ▶
  2. P2 Tests skip saved tab behavior ▶

Summary

Adds Chat transport for discovering Projects and reading, searching, writing, uploading, and copying their files.

  • Chat can discover Projects and work with their files.
  • Chat masks secrets in Project text before it slices or searches files.
  • Chat keeps each file's Project or workspace owner attached to its resources and requests.
  • Project file access and new Project tabs wait for the Projects rollout.
  • Repository conventions and generated baselines track the new Project file paths.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Chat command] --> B{File owner}
  B --> C[Workspace adapter]
  B --> D[Project adapter]
  D --> E[Check current Project access]
  E --> F{Read type}
  F --> G[Complete text admission]
  G --> H[Extract text]
  H --> I[Mask before line selection]
  I --> J[Final model projection]
  F --> K[Opaque byte admission]
  K --> L[Refuse secret-bearing model bytes]
Loading

Reviews (2) · Last reviewed commit: "Merge remote-tracking branch 'origin/sta..." · Reviewed by Greptile

Comment thread apps/sim/lib/mothership/tools/server/open-resource.ts
Comment thread apps/sim/lib/mothership/resources/store.ts
Comment thread apps/sim/lib/mothership/file-owners/workspace.ts Outdated
Workspace regressions while Projects are off:
- Sandbox file copies between workspaces keep the workspace route when
  the lease has not negotiated the file-owner protocol; only pairs that
  name a Project owner need it.
- Chat upload contexts keep their uploads/ path in the workspace owner
  adapter.

Project transport:
- Project CLI requests refuse mutations during benchmark reference
  generation, as workspace requests do.
- Project grep defaults an omitted scope to files.
- Project files with unrecorded or empty exact provenance are readable
  when the caller holds a registry; a missing registry is still refused.

Mothership inventory:
- Unpublished Project commands carry response shapes from the
  launch-ready Project OpenAPI definitions.
- An operation whose delegated services exclude Chat no longer mints a
  Chat principal, so the refused-route list now matches runtime for
  Project file version history.
@mzxchandra

Copy link
Copy Markdown
Contributor Author

@greptile

@mzxchandra

Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@mzxchandra I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 121 files

Confidence score: 3/5

  • project-file-read.ts treats undecodable files as text because it checks for null while workspaceFileTextFormat returns undefined; files read therefore fails on non-visual binary Project files. Check for undefined as well.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/lib/mothership/agent-cli/project-file-read.ts">

<violation number="1" location="apps/sim/lib/mothership/agent-cli/project-file-read.ts:85">
P2: This check compares against `null`, but `workspaceFileTextFormat` returns `undefined` for undecodable files. As a result, `files read` on a non-visual binary Project file always attempts text extraction and fails instead of returning its binary metadata. Check for `undefined` (or use a truthiness check).</violation>
</file>

Reply with feedback, questions, or to request a fix.

Turn on auto-fix | Re-trigger cubic

*/
const readsText =
!explicitVisual &&
workspaceFileTextFormat({ name: resolved.file.name, type: resolvedType }) !== null &&

@cubic-dev-ai cubic-dev-ai Bot Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This check compares against null, but workspaceFileTextFormat returns undefined for undecodable files. As a result, files read on a non-visual binary Project file always attempts text extraction and fails instead of returning its binary metadata. Check for undefined (or use a truthiness check).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/lib/mothership/agent-cli/project-file-read.ts, line 85:

<comment>This check compares against `null`, but `workspaceFileTextFormat` returns `undefined` for undecodable files. As a result, `files read` on a non-visual binary Project file always attempts text extraction and fails instead of returning its binary metadata. Check for `undefined` (or use a truthiness check).</comment>

<file context>
@@ -0,0 +1,184 @@
+     */
+    const readsText =
+      !explicitVisual &&
+      workspaceFileTextFormat({ name: resolved.file.name, type: resolvedType }) !== null &&
+      (textRange || !isDefaultVisual(resolvedType))
+    let file: typeof resolved.file
</file context>
Suggested change
workspaceFileTextFormat({ name: resolved.file.name, type: resolvedType }) !== null &&
workspaceFileTextFormat({ name: resolved.file.name, type: resolvedType }) !== undefined &&
Fix with cubic

Comment on lines +110 to +113
const { workspaceId, ...rest } = resource
return {
...rest,
owner: resource.owner ?? { entityType: 'workspace', entityId: workspaceId },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Workspace tabs lose their address

normalizeChatResource removes workspaceId, but the browser also runs this helper through sanitizeChatResources and mergeChatResource. In organization Chat, workspace file tabs then lose the address their reader needs. Closing them sends neither workspaceId nor owner, so the saved tab does not match the DELETE request and returns after reload.

The HTTP and SSE presenter cannot fix this because the browser strips the restored address again. Keep the browser-facing address until its readers and close callbacks support owner, or keep this normalization server-only.

Comment on lines +92 to +98
it('stores a Project tab once Projects are on', async () => {
featureFlagsMockFns.mockIsFeatureEnabled.mockImplementation(async (flag) => flag === 'projects')
await changeChatResources.execute({
principal,
input: { chatId: 'chat', change: { kind: 'upsert', resources: [projectFile] } },
})
expect(mocks.store).toHaveBeenCalledWith('chat', { kind: 'upsert', resources: [projectFile] })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Tests skip saved tab behavior

The new successful add and remove tests mock the surrounding authorization and storage calls, then only assert toHaveBeenCalledWith on storage. CLAUDE.md explicitly forbids tests that mock every collaborator and assert calls on those mocks. These tests do not show that a tab was saved or removed.

Cover these cases through the existing database-backed resource tests instead. This repository requirement must be satisfied before merging.

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

This branch was successfully deployed

1 active deployment
Preview — 999c1a34 Deployed Oct 16, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant