feat: expose authorized Worker Drive tools and native WIP entries

Use T-722 identity-bound API for paged discovery, bounded text read/create/write/edit, folders, relocate/delete, revision-fixed images, Workdir artifact publication, and mutation receipts. Keep IDs as Workspace-bound decimal strings, observed CAS preconditions and Client validators hidden from model arguments, and never substitute revisions or blindly replay uncertain effects.

Activate via host-bound Feature/Profile/schema and centralized instructions. Publish only the Drive entrance as indexable; resolve selected stable IDs directly with coherent self-scoped WIP publication. Internal SubWorkers and Reviewer children deny undelegated Drive access at feature and client boundaries; no child grant or local storage authority is synthesized.

T-723 validation: root cargo check; manifest 190; tools 78 unit +39 integration +1 doctest; Worker 918 unit +110 integration; Runtime 348 unit +34 binary; Server 708 library +11 binary including 17 real DB+LocalFileSystem signed-API adapter tests. Native image capture/replay, multichunk revision bounds, Workdir import, concurrent CAS, current grants, response-loss reconciliation and dependent config WIP0.2 regressions pass. WIP crates.io 0.2.0 resolution verifier, fmt and diff checks pass.
This commit is contained in:
2026-10-09 03:01:41 +09:00
parent 8affa46bce
commit 61afdb7773
30 changed files with 4609 additions and 71 deletions
+167
View File
@@ -0,0 +1,167 @@
# Worker Drive tools and WIP
Drive is Workspace-owned DB hierarchy and local blob storage exposed through the
authorized Server API. It is **not** a Workdir, mount, Git checkout, or a Session
attachment store. Worker code never accesses the Server's physical blob root.
## Enablement and authority
Set `feature.drive = { enabled = true; };` in a DCDL Profile (or
`[feature.drive] enabled = true` in TOML). Only activation is accepted; Profile
fields cannot specify Workspace, grant, actor, access, or host paths. Default,
Coder, Companion, Intake, Orchestrator and Ticket Worker builtin Profiles select
the feature. Standalone, Reviewer and Job defaults do not. Host must inject an
available Workspace-bound `WorkspaceClient`; standalone activation is inert.
An authorized Workspace owner must independently grant the **registered Runtime
Worker** whole-Drive `read_only` or `read_write` access through the T-722 grant API.
Every read, metadata resolution, mutation, chunk and receipt query passes current
Backend authorization. An enabled feature, Subject, Ticket assignment, path, ID,
or observed validator is not permission. A read-only grant is never upgraded.
The current API has no read-only capability descriptor endpoint: an authorized
WIP inspect describes the operations for the entry kind, while Backend rejects
writes without a write grant. Tool permission configuration also applies to
native Invoke, including fail-closed `ask` where approval cannot be obtained.
Internal SubWorkers (including Reviewer children) have no separate Backend Drive
identity/grant contract today. Spawn suppresses Drive activation **after every
selector resolution**, including `inherit` and explicit builtin Profiles, and
wraps the Workspace client to deny Drive JSON/binary operations. They cannot use
the parent's shared client to inherit Drive read or write. Perform such work in
the authorized parent, or a separately registered Runtime Worker with its own
current Backend grant. Any future internal delegation must introduce an explicit
parent-bounded scope and Backend reauthorization, not remove this fence based on
Profile activation. The wrapper preserves non-Drive Workspace operations and
Reviewer attestation.
## Normal Tools
`DriveRoot {}` discovers the authorized root and returns `{path, metadata}`.
Public entry inputs are **Workspace-bound** references:
```json
{"workspace_id":"workspace-id","node_id":"9007199254740993"}
```
Node IDs and revisions remain canonical positive decimal **strings**, including
values above JavaScript's safe-integer range. Renaming/moving preserves IDs and
authenticated latest URLs. Deleting/recreating the same name allocates a new ID.
| Tool | Input/use |
| --- | --- |
| `DriveMetadata` | `entry`: one live entry and its observed revision |
| `DriveList` | optional `parent` (default root), `limit` 1–200, opaque `after` |
| `DriveSearch` | `query`, optional `include_text`, `limit` 1–128, opaque `after` |
| `DriveRead` | `entry`, optional `max_bytes` 1–65,536; reports `truncated` |
| `DriveCreateFolder` | `parent`, logical `name` |
| `DriveCreateText` | `parent`, `name`, `content` <=64 KiB, optional `content_type` (Markdown by default) |
| `DriveWrite` | observed `entry`, complete replacement `content` <=64 KiB |
| `DriveEdit` | observed `entry`, `old_string`, `new_string`, optional `replace_all` |
| `DriveRelocate` | observed `entry`, destination `parent`, new/current `name` |
| `DriveDelete` | observed `entry`; folder deletion follows Backend rules |
| `DriveViewImage` | `entry`; bounded revision-fixed image bytes, not a URL |
| `DriveSaveWorkdir` | explicit `target_workdir` alias, relative `path`, destination `parent`, `name`, optional `content_type` (octet-stream by default) |
| `DriveRequestStatus` | `request_id` from an uncertain mutation |
List/search are paged discovery only, not implicit mutation observations. Read or
metadata-inspect an entry before replacing, editing, moving or deleting it. The
Tools adapter retains at most 256 observations, scoped to its injected Workspace.
Eviction requires a new explicit observation; it never substitutes another entry.
A successful mutation removes the prior Tools observation. This is a bounded
precondition cache, not a grant cache, second hierarchy DB, or durable history of
all Drive versions. Restored workers observe again before mutating.
`DriveEdit` uses the same pure `fs_operation::text` rules as Workdir and Workspace
config: nonempty `old_string`, different replacement, exactly one nonoverlapping
match unless `replace_all=true`, and bounded resulting text. It fetches a full
<=64 KiB preimage and verifies the **previously observed** revision. It cannot edit
truncated content. The Backend's atomic CAS still decides the first winner.
## Native WIP 0.2.0
Start with `Inspect {"path":"/drive"}`. `/drive` offers root/metadata, list/search,
creation, Workdir save and receipt query. Invoke selects the structured Interface
reference shown by Inspect:
```json
{
"path":"/drive",
"interface":{"scope":"/drive","name":"yoi.drive/entry/v1"},
"operation":"list",
"arguments":{"limit":20}
}
```
Direct entry paths returned by discovery are:
```text
/drive/<hex-encoded UTF-8 Workspace ID>/<canonical decimal node ID>
```
Use the returned path rather than hand-constructing it. A file exposes metadata,
read, write/edit, relocate/delete and view_image; a folder exposes metadata,
list, create_folder/create_text, save_workdir and relocate/delete where valid.
A native target binds its entry or creation parent: no ID or revision argument is
needed. Relocate's `parent` is a WIP typed entry path. Each entry is self-scoped;
Host captures target, scope object, descriptor, validator and handler together.
Client retains and supplies metadata automatically. Do not copy validators or
`scope_ref` through model arguments.
Drive has **no indexable entry children**, regardless of the size of the Drive or
the Client's already-inspected entries. Tree shows the Drive entrance, not a file
inventory. List/search return bounded metadata and paths; only inspect the
selected path. No Worker index/embedding or eager body injection is added.
## Conflicts, unknown outcomes and publication
Mutations use a Host-generated request identity, observed node ID and exact
revision. Conflict never causes an adapter to replace the expected revision with
latest and retry. Observe explicitly, reconsider the edit and submit a deliberate
new operation. Native Client stale handling likewise does not replay dispatch.
Timeout, lost response, oversized/invalid mutation response or invalid completion
may mean **OutcomeUnknown**. The adapter queries the *same* request receipt once;
if committed it returns that confirmed DB result. Otherwise it preserves unknown
and the `request_id`. Query `DriveRequestStatus`; an `uncommitted` snapshot is not
proof that an in-flight request cannot later commit. Do not retry blindly with a
fresh request ID. Backend grants also protect receipt lookup/replay. Safe typed
Denied/NotFound/Conflict/Invalid/Limit/StorageUnavailable/OutcomeUnknown errors are
classified without reflecting provider paths, SQL, transport URLs or secrets.
Saving a Workdir artifact reads through the selected alias's scoped session
`read_bytes`, checks path/offset/size/hash on every bounded chunk and verifies the
assembled SHA256. Source read and destination write are independent. Unknown
aliases, outside-scope paths, traversal/absolute host paths, changed sources,
source read denial, oversized bodies and destination denial cannot publish a
success. Upload transfers raw bytes internally with size/SHA256 metadata; the
model never has to use Bash, a raw host path, or generate base64. Success is the
Backend's **DB-publication commit**, not merely blob transfer completion.
## Images and historical observations
ViewImage reads <=10 MiB PNG/JPEG/GIF/WebP bytes in <=64 KiB Drive chunks at the
metadata revision. Every chunk is authorized and revision-fixed. An intervening
update expires the read; no mixed-generation image is attached. Media is detected
from bytes using the existing image helper, not the filename or a download URL.
Tools and native Invoke return `ImageAttachment` through `ToolOutput`. Native
attachments accompany—not replace or bypass validation of—the WIP return value.
Engine history, durable Session capture/log replay and normal ToolResult pruning
own those bytes. Later Drive updates/deletion/revocation do not rewrite the text
or image already recorded in a Session; restore reproduces that observation until
normal pruning/correction removes details append-only. This is not full Drive
version history and does not use unrecorded per-request message insertion.
## Validation seams
- `cargo test -p worker --lib feature::builtin::drive`
- `cargo test -p worker --lib native_invoke`
- `cargo test -p worker --lib native_attachments_do_not_bypass`
- `cargo test -p worker-runtime --lib binary`
- `cargo test -p yoi-workspace-server --lib worker_drive_tests`
Server adapter tests use isolated DB + LocalFileSystem + production typed HTTP
router and fresh Runtime proofs/current Worker grants. Worker tests cover
bounded observations, exact CAS inputs, no blind resend, Tools/WIP schemas,
conditional registration, child denial and native image capture/replay. See
`AGENTS.md` for root check, changed-crate tests, format and diff requirements.