Skip to content

CD-1284: stream vault downloads to disk; add vault_read_text tool#20

Merged
teddy-paris merged 3 commits into
mainfrom
agent/CD-1284-stream-download-vault-read-text
Jul 11, 2026
Merged

CD-1284: stream vault downloads to disk; add vault_read_text tool#20
teddy-paris merged 3 commits into
mainfrom
agent/CD-1284-stream-download-vault-read-text

Conversation

@teddy-paris

Copy link
Copy Markdown
Contributor

Problem

downloadCaseDevVaultObject buffers the whole object in memory before writing (casedev-vault-api.tsnew Uint8Array(await response.arrayBuffer())). Downloading a 2.18GB scanned PDF gets the linc process OOM-SIGKILLed ~73s in — confirmed twice in prod session logs (bridge.rpc_exit {code: null, signal: "SIGKILL"} immediately after vault_download start, run dead with the UI spinning). Linear: CD-1280/CD-1284.

And even when a giant download succeeds, the raw original is usually a scanned PDF the sandbox can't read — the content agents actually need is the extracted text, which case.dev now serves page-numbered per object (GET /vault/{id}/objects/{objectId}/text, casedotdev-mono CD-1282, verified live).

Changes

  1. Stream downloads to disk with pipeline(Readable.fromWeb(response.body), createWriteStream(...)) — constant memory at any size; bytes from stat.
  2. New vault_read_text / casedev_vault_read_text tools — write the extracted, page-numbered text into the workspace as .txt, return {path, chars, pageMarkers, note}; content never enters model context. Contrasting descriptions on the download tools (originals = raw bytes / content = read_text), plus a note in download results for ingested objects.

Tests

  • streams a multi-chunk ReadableStream body to disk, verifies content + byte count
  • readCaseDevVaultObjectText writes the file with page-marker stats and hits the right endpoint
  • rejects text reads when no extracted text exists
  • existing suites green (7/7 api, 17/17 vault-matter); npm run check clean (biome, tsgo, shrinkwrap, browser-smoke)

Rollout

After merge + release, casedotdev-mono bumps @casemark/linc and rebuilds the Daytona snapshots (CD-1284 tracks the chain). Post-deploy verification: the production giant-doc chat that currently dies at ~73s should read one 23MB page-numbered text file and answer with page citations.

🤖 Generated with Claude Code

Two changes to the native Case.dev vault tools:

1. downloadCaseDevVaultObject buffered the entire object in memory
   (new Uint8Array(await response.arrayBuffer())) before writing. On
   multi-GB originals the sandbox OOM killer SIGKILLs the linc process
   ~73s in — confirmed twice in production against a 2.18GB scanned
   PDF (bridge.rpc_exit {signal: SIGKILL} in the session logs, run dead
   with no terminal event). Stream the response body straight to disk
   with pipeline(); memory stays constant at any file size.

2. New vault_read_text / casedev_vault_read_text tools: fetch the
   object's full extracted text (GET /vault/{id}/objects/{objectId}/text,
   page-numbered when the source is paginated) into a workspace .txt
   and return path + char/page-marker stats. Raw originals are often
   scanned images the sandbox cannot read; the extracted text is the
   right surface for reading, grepping, or comprehensively analyzing
   document content. Download tool descriptions now carry the
   originals-vs-content contrast, and downloads of ingested objects
   include a note pointing at vault_read_text.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 11, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
linc-docs Ready Ready Preview, Comment Jul 11, 2026 10:57pm

const filename = baseName.endsWith(".txt") ? baseName : `${baseName}.txt`;
await mkdir(params.outDir, { recursive: true });
const outputPath = safeOutputPath(params.outDir, filename);
await writeFile(outputPath, text, "utf8");

@vercel vercel Bot Jul 11, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Streaming download leaves a truncated/corrupt file at outputPath when pipeline fails mid-transfer, which a later read can treat as a complete document.

Fix on Vercel

CI regenerates models.generated.ts from the live gateway during Build,
and the catalog has moved since the last sync (Jun 23) — the committed
file and the live catalog disagreed, so local checks passed while CI
failed on the full-control example's claude-sonnet-4-20250514 (dropped
upstream). Sync the generated catalog and move the example to
claude-sonnet-5.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The catalog sync raised claude-sonnet-4-5's context window from 200K to
1M, stranding the test's hardcoded 190K usage far below the compaction
threshold. Derive the near-limit usage from model.contextWindow so
future catalog growth can't strand it again.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@Thurgood-AI Thurgood-AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Approved. The download path now streams instead of buffering multi-GB originals, the extracted-text tool is wired through both native tool names with page-marker metadata, and the model-catalog/threshold updates are internally consistent.

Verification:

  • Focused vault API suite: 7/7 passed locally
  • CI build/check/test: green
  • Vercel checks: green

Non-blocking follow-up: streaming to a temporary file and atomically renaming on success would prevent a failed transfer from leaving a partial file at the final path. The current behavior fails the tool call loudly and a retry truncates/replaces the destination, so I don't consider that a merge blocker.

@teddy-paris teddy-paris merged commit 6bf547c into main Jul 11, 2026
4 checks passed
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.

2 participants