Repository navigation
fix(mothership): admit stored content to Chat per item through one gate - #8941
waleedlatif1 wants to merge 5 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
All reported issues were addressed across 197 files
Heads up: you’ve reached your flex budget. Increase your flex budget or wait for usage to reset.
Turn on auto-fix | Re-trigger cubic
|
3bb1031 to
c77ebc3
Compare
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 207 files
Heads up: you’ve reached your flex budget. Increase your flex budget or wait for usage to reset.
Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.
Turn on auto-fix | Re-trigger cubic
c77ebc3 to
335f1d9
Compare
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 212 files
Heads up: you’ve reached your flex budget. Increase your flex budget or wait for usage to reset.
Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.
Turn on auto-fix | Re-trigger cubic
…before gating Seeds one clean, secret-bearing, unknown and unrecorded item per content store and snapshots every shared content route as an API-key caller sees it, before any Chat delivery gate changes.
Content from workspace files, run files, knowledge and table rows now reaches Chat's model only after each item is classified and admitted by the gate of the surface it leaves through: the registry gate (Chat tools, CLI, mounts) or the session gate (run_code sandbox proxy). Unknown items are excluded per item instead of withholding whole results; items with recorded secrets are redacted where text, refused where opaque. Non-Chat callers are unchanged. Every operation that admits Copilot declares contentDelivery, and a sourced use case must account for what it returns, or Chat's transports refuse the response. Closes the bulk-download, search, run-file, base64, knowledge, share, export, table and tools-execute paths, and redacts grep lines before clipping them.
Committed writes answer saved-not-shown instead of a retryable error, keep their panel refresh, and account per operation. Chat streams pin inline bytes off, taint needs a registry, compile admits before its cache, owned files bind to their context, and count/truncation oracles are closed for Chat in knowledge, files search and row search. Table selection cells are projected before escaping and coerced import cells keep their taint.
…ent delivery Chat may share or see links only to clean files whatever the surface, text is redacted before a line window cuts it, accounting follows a decision, cached compiles and knowledge metadata bind to the versions they show, row-search matches classify at their matched revision, and table counts follow admitted rows.
A table read whose rows, counts, cursor or affected count depend on cell values is refused for Chat, before the predicate runs, when the table has an unvouched row or a secret in a referenced column. Inline run-file bytes are collected per storage key, and table-selection fallbacks are projected.
335f1d9 to
612fb47
Compare
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
| ...(filters.search && isCopilotPrincipal(principal) | ||
| ? { pagination: { ...result.pagination, total: result.pagination.offset + chunks.length } } | ||
| : {}), |
There was a problem hiding this comment.
listKnowledgeChunks removes refused chunks but keeps nextCursorKeys from the raw search. With search=<guess> and limit=1, two unknown chunks matching the guess produce an empty page with a cursor; no matches produce an empty page without one. Chat can use that difference to test guesses against hidden text.
listKnowledgeDocuments has the same problem with the unchanged pagination.hasMore. Rewriting total does not protect either route because their responses use the cursor fields instead. Build paging from admitted matches, or refuse searches over content Chat cannot inspect before running them.
How this was verified: The queries search stored text before admission, and the Chat-reachable routes return cursors computed from those unfiltered matches.
| for (const [columnId, raw] of Object.entries(source ?? {})) { | ||
| const stored = row[columnId] | ||
| if (stored === null || stored === undefined || Object.is(stored, raw)) continue | ||
| const rawProvenance = registry.exportCommittedProvenanceForValue(raw) | ||
| if (!rawProvenance.complete || rawProvenance.entries.length > 0) { | ||
| return createUnknownTableRowSecretProvenance() | ||
| } | ||
| } | ||
| return createTableRowSecretProvenanceFromRegistry(row, registry) |
There was a problem hiding this comment.
CSV imports lose secret tracking
resolveImportedRowProvenance checks cells only after CSV parsing has already changed them. For a recorded secret such as sk_live_example123 in an unquoted CSV cell:
trim: trueremoves the spaces before bothrowSourcesand the stored string are built.- Those values compare equal, so the guard skips the check.
- The exact matcher no longer finds the original secret and stamps the row with no secret entries.
Chat can then read the trimmed secret without redaction. Carry evidence from before CSV parsing, or mark affected rows unknown when parsing changes secret text.
How this was verified: CSV parsing trims the input before rowSources is captured, while the import matcher checks the original decrypted secret literally.
Summary
Content derived from workspace files, run files, knowledge and table rows now reaches Chat's model only after each item it came from is classified and admitted by the gate of the surface it leaves through. One primitive replaces the file and row delivery observers, the hand-maintained table route list, the sandbox catalog routes and the per-surface checks.
lib/core/application/content-delivery.ts): a use case describes what it returns as sources (kind,id,provenance,view:complete|{derived}|opaque) and callsadmitContent/admitEach/requireAdmittedContentbefore loading bytes. Only Copilot principals are classified; every other caller is admitted without a provenance read.lib/mothership/content-delivery/): the registry gate (Chat tools, CLI, run_code mounts) imports entries into the registry egress projects with, staged per item so one bad item never latches the shared registry. The session gate (run_code sandbox proxy) records into the workbench history. Neither ever admitsunknown.contentDelivery: 'sourced' | 'none'at definition. Asourceduse case that returns without accounting throws for Chat. Both transports refuse an unaccounted successful response for every HTTP method (the old CLI forward and proxy paths failed open).update_content, share update, chunk/row/document writes) keep their audit and effects when the returned item is refused or the gate fails. They then answer403"saved, not shown", or502from the proxy.copilot-route-inventory.test.tsfails on any undeclared Chat-reachable route and publishes the 32 sourced routes tocopilot-content-routes.json.content-delivery.integration.tsfails when a listed route is not driven through Chat's real transports..claude/rules/sim-content-delivery.md, indexed inapps/sim/AGENTS.md.Policy (before → after)
U = unknown, E = exact with secret entries, R/C = unrecorded or clean.
files search(P1, misroute)files getexcludedInputsPATCHbase64base64removed, descriptor kepttable_selectionfiles read knowledge/…tools executewith file/knowledge/table/memory/function/custom/MCP toolsDecisions
Compatibility
content-delivery-compat.integration.tscalls ~35 shared routes as an API key. It compares their responses against a baseline recorded on the base commit, which is the first commit of this PR.Migrations
None. Every provenance shape already exists. Table import resolves provenance by storage key, so in-flight jobs are covered and there is no payload change.
Performance
Type of Change
Testing
bun run type-check,bun run lint,bun run check:audits(59 audits)scripts/test-integration.tson throwaway Postgres/Redis: 1,875 + 125 testscontent-delivery.integration.ts: 27 cases over every sourced route, each against clean, secret-bearing, unknown and unrecorded items, through the real CLI transport and sandbox proxyChecklist
test-auditauthoring gate)