DeepSeekBot

Docs

Open files on the Host

Open Memory, Workspace and message files in native applications.

Click a displayed Memory Repository or authorized Workspace path to choose a detected application. Memory file rows offer a context menu and a More button. Current files can be revealed in the file manager, opened in an editor, copied as a Host path, or downloaded to this device.

New message attachments use the same actions: click a file chip, right-click a file or image, or use its More button. Clicking an image still opens its preview. The menu names the Host computer explicitly. With Tailscale or Cloudflare Tunnel, an editor or file manager opens on the computer running DSH; downloading transfers the current bytes to the browser's device. Editing a download does not write back to the Host. If native applications are unavailable, download and copy remain available.

Sending transfers the file to one profile-managed destination. Opening and saving that attachment edits this same destination; the original message's next read, preview or download uses its current bytes, filename, MIME and size. Refresh the Channel after saving to refresh displayed metadata. The upload-source file stays independent. Two independent uploads remain separate even when their bytes match; explicit reuse of one attachment identity shares the same file across messages.

External saves create no attachment versions, notifications, Source Revisions, Inbox admissions or Bot wakes. A missing destination reports unavailable and is never restored from original upload bytes. Retained old hash-addressed attachments are converted at Host startup; successfully migrated chips offer the same editor menu, while failed conversions retain their old readable state. Memory keeps its existing Git behavior.

Storage and integration

New references are {fileId,name,mime,size}; legacy references are {hash,name,mime,size}. Exactly one identity is allowed. The immutable message envelope keeps the sent reference; message queries project current metadata. channel_read_image takes attachment_id with the returned fileId, or hash for a legacy image. The Host validates current Bot membership and message ownership before reading current, size-limited image bytes. channel_read_image returns image content without a Host path; explicit original file access below returns only its source-validated path. A cached legacy image hash with its owning message resolves that message’s migrated file when unambiguous; a hash without message ownership, ambiguous hashes within one message, and legacy references on new sends are explicitly refused. Refresh the message to obtain its canonical fileId. Trusted Channel forwarding reuses the same validated reference contract; destination confirmation remains #570.

Under $DSH_HOME/botharness/attachments/files/<uuid>/, data/<safe filename> is the real destination and record.json is the durable identity/transfer receipt. The receipt holds a checksum for upload retry, with no archived file bytes. Composer retries reuse one upload key without overwriting an edited destination. New sends validate profile ownership, and native actions/downloads resolve channelId + messageId + fileId afresh. The authenticated download uses no-store and server-sniffed MIME with nosniff.

Staged transfer cleanup remains separate. Reference-aware cleanup protects file identities from all retained Source Event envelopes, including removed Channels, before removing old unreferenced destinations and their records; automatic retention remains disabled. Future Profile Backup, selected Export and explicit Purge must include current referenced destinations and their records. They must preserve shared identity, never purge a file still reachable from retained facts, and capture current bytes rather than create a version archive. Those products are not added by this slice.

The design is ADR-0100. The runnable verification entry is scripts/e2e-real-attachment-files.mjs: prepare through the real composer, save through an actual external editor, verify the original and independent/shared messages, restart the same Profile, then verify missing-file refusal. Its private fixture and login URL stay outside Git; only synthetic screenshots are published.

Existing-message migration

Messaging owns schema generation 39's attachment_file_bindings, keyed by immutable Source Event ID and attachment ordinal. Host startup scans all retained message envelopes, including removed Channels. Each legacy occurrence reserves a durable independent UUID in pending, streams and integrity-checks its old CAS object into the real-file owner, rereads and verifies the converted bytes, then atomically marks the binding ready. Queries apply this binding before projecting current metadata; original Source Event envelopes, text, placements and Inbox Admissions are unchanged. No file copy is created when a Human opens the menu.

The old representation recorded content hashes without a trustworthy upload identity or reuse provenance. Equal hashes therefore receive independent destinations, even within one message; historical sharing cannot be reconstructed from a hash match. New explicit reuse of a canonical fileId remains shared. Concurrent startup calls share one migration run. Interruption preserves the reserved UUID; restart resumes incomplete conversions without duplicating destinations. A ready binding is never recopied, even if its current file is missing or externally edited. Failed conversions expose the readable legacy object until repair, with bounded attachment-migration diagnostics naming the occurrence and repair/restart action. A corrupt source remains unavailable rather than being accepted.

Owner-qualified legacy downloads and image reads resolve the migrated current destination, never a frozen old CAS substitute. If occurrences in one message resolve to different destinations for an old hash, that old identifier is ambiguous: use the current fileId. Duplicate occurrences still pointing to the same unconverted CAS object remain readable. The production download always requires message ownership and uses no-store, including mixed-state legacy reads. Raw legacy references remain an input decoder for retained facts and owner-qualified compatibility, not a way to create new mutable message files.

Reference-aware cleanup marks ready fileIds and all pending reserved fileIds; legacy objects remain reachable while any retained occurrence is unconverted. Only after all retained dependencies are converted can the existing explicit sweep release an obsolete shared CAS object. Migration does not enable automatic deletion or affect SoulSnapshots or other CAS stores. Future Profile Backup/Export/Purge must include the Operational Database's binding records and current file destinations/receipts. This one-time conversion and its obsolete compatibility storage provide no attachment-history API.

Schema activation is a one-way upgrade: older hash-only releases cannot consume generation 39 or the managed file identities. Recover forward, retaining the database, bindings and current files. Before upgrading, any separately supported backup remains its owner's responsibility; this feature does not implement a new backup or restore product. Runtime proof uses scripts/e2e-legacy-attachment-migration.mjs: actual old-version composer sends, a private SQLite backup into a new isolated Profile, real native editor save, owner-qualified current reads, source/independent-file separation, unchanged durable attention, restart and missing-file refusal.

Let a PersonaBot process a received file

In the local Bot Channel, expand Workspace Grants, add a work folder and expand its row. Allow Bot to write in this folder starts off; enable it only for the folder where the Bot should save and produce files. Existing read-only Grants remain read-only. Turning it off blocks future Orchestrator writes without deleting files or changing Assignment permissions. Shell calls still require Human approval or a matching saved rule. A permission change invalidates earlier saved-rule scope.

Send an arbitrary-format attachment, such as a ZIP of CSV records, and request a new result. The Orchestrator saves a separate working file through channel_attachment_save, uses native file tools and approved Shell calls, explicitly imports the generated file through channel_attachment_import, and replies with its independent download. Parent directories must exist and save-as never overwrites an existing destination. Original references remain unchanged; the attachment owner retains the existing 25 MiB transfer limit. Download the returned result to inspect its contents.

To inspect an original, ask the Bot to read the specified attachment. To change it, explicitly say “edit this original”, for example “change status=pending to status=approved in this original status.txt”. The Bot selects the exact message/file through channel_attachment_open; read only permits native inspection, while edit-original uses the existing Human tool approval or matching saved rule. Approve only the intended original. The Bot uses ordinary native read/edit/write on the returned path, without creating a copy or changing its Memory cwd.

Refresh the Channel, then download or reopen the original message's attachment to inspect current contents. Explicitly shared references to the same fileId show the edit, even after Host restart; identical independent uploads and the uploader's local source file remain separate. Native access expires at the end of the turn and rechecks current source membership on each operation. Leaving the source Channel or losing the original file prevents further access; a missing original is never recreated from upload bytes. File edits produce no Source Revision, file-change Inbox Admission or automatic wake; any conversational confirmation is an explicit reply. Native checks and Shell approval remain, with no BotHarness lock or file-version archive.

These local slices are #632 and #633. See ADR-0105.

Process a Lark ZIP in its original topic

Use the isolated qualified IM Profile described in the connection guide. Bind one QA Bot account, authorize the test group and enable mention reception. Only one Host may own that account's receiving connection. In the Bot's Workspace Grants, explicitly allow writing to the folder where files should be processed.

Upload a small CSV ZIP to the authorized Lark group/topic. Reply to that file using ordinary text, select the real Bot from the @ member picker, and request a new ZIP result. The Bot Inbox's External message detail shows the filename; Download file acquires the original on demand. Download errors remain visible and do not imply a successful transfer.

The Bot uses bridge_read and bridge_attachment_save to save an independent working copy, then native files and approved Shell to process it. It explicitly selects the new result with channel_attachment_import and uses bridge_reply_file to return it in the exact original topic under its own identity. Text and file replies share one reply intent per source; requesting a file does not send a preliminary automatic text acknowledgement. Download the received result in Lark and check its contents; Platform accepted alone is not delivery or read evidence.

The original stays unchanged. Transfers use the existing 25 MiB limit; revoking source access blocks future provider reads/replies, while a completed independent copy remains under its own Workspace Grant. Restart retains source and file identity and never repeats uncertain sends. This is the #657 temporary-provider slice, not production enablement, Slack support or general message history. See ADR-0107.

View the source on GitHub