protocol: file-transfer contract (send_message.files[] + send_file directive) - #342
Merged
Conversation
…irective Add a non-image files[] attachment field to send_message (host lands each in the agent workspace so read_file/bash can use it; distinct from vision images[]), and document the send_file host directive convention on eventual_response.directive for agent->user file delivery. Spec-only; per-language server behavior follows. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vzh8t76aDVgeoiwC9KEKZZ
🦋 Changeset detectedLatest commit: 8f08306 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
brentrager
added a commit
that referenced
this pull request
Aug 11, 2026
…st parity (#344) Bring the Python smooth-operator server to parity with the Rust reference on the file-transfer contract (spec PR #342): - images[]: attached to the model's user message as OpenAI image_url content parts (multimodal turns), in-tree in turn_runner against the pinned published core — no core release needed. Guarded fail-soft: falls back to text-only when a knowledge base is wired (the pinned core reuses the message for retrieval). - files[]: parsed fail-soft onto a new per-turn TurnContext (the Python analog of Rust's ToolProviderContext), surfaced for a host tool to land in the workspace; never sent to the model. - directive sink: TurnContext carries a last-write-wins directive a host tool writes (e.g. the send_file convention); drained after the turn onto TurnResult.directive and emitted on eventual_response.directive (omitted when none — back-compat). - Regenerated the SDK types from the updated spec (files[] + directive docs). - Tests: images-attach, files-on-context, tool->directive->eventual_response, fail-soft parsing, dispatcher forwarding + attachment parsing. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
brentrager
added a commit
that referenced
this pull request
Aug 11, 2026
…arity (#345) Bring the Go smooth-operator server to parity with Rust on the file-transfer contract (spec #342): send_message.images[] / files[] attachments and the send_file directive convention on eventual_response.directive. - Regen go/protocol/types_gen.go from the #342 spec: adds Files (and the previously-unregenerated Skill field). Generated file, not hand-edited. - Parse images[] + files[] fail-soft in handleSendMessage (a malformed array is dropped wholesale, never rejecting the turn — matching Rust's from_value(...).ok().unwrap_or_default()). - New TurnContext (go/server/turn_context.go): the Go analog of the Rust ToolProviderContext file-transfer fields. Carries the turn's images + files and a directive sink, attached to the turn's context.Context so a host tool reads it via TurnContextFrom(ctx) (the engine dispatches every tool with that ctx). Directive is last-write-wins, concurrency-safe under ParallelToolCalls. - Drain the directive sink after the turn onto eventual_response.directive (omitted when no tool wrote one — back-compat), mirroring the Rust drain. - Tests: images/files reach a tool, a tool's directive lands on eventual_response, absent-directive omission, fail-soft on malformed images/files, directive last-write-wins. Known gap (documented in turn_context.go): images are NOT attached to the model turn as OpenAI image_url content parts. The pinned Go engine core (smooth-operator-core/go) ChatMessage.Content is a plain string with no multimodal content and no AgentOptions user-images option; unlike Rust's core (ImageContent + with_user_images), that support does not exist in the Go core, which is an external, pinned dependency from a separate repo. Attaching images to the model requires a smooth-operator-core/go release adding multimodal ChatMessage content + AgentOptions.UserImages + openai.go serialization, then a pin bump — out of scope for this single-repo PR. Until then images are surfaced to host tools only. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
brentrager
added a commit
that referenced
this pull request
Aug 11, 2026
…arity (#346) Implement the PR #342 file-transfer contract in the TypeScript smooth-operator server, to parity with the Rust reference: - Regenerate the client SDK protocol types so send_message gains images[]/files[] and eventual_response gains directive. - images[] are attached to the turn's user message as OpenAI image_url content parts. The published core takes a plain-string message and reuses it for retrieval, so a withUserImages() wrapper rewrites the outgoing request body's current user message instead (retrieval/memory stay text-based). - files[] are surfaced on a new per-turn ToolContext (never sent to the model), mirroring the Rust ToolProviderContext. - New optional toolProvider seam (mirrors Rust ToolProvider): host tools bound to the turn's context can read the attachments and write ctx.directive; the dispatcher drains it onto eventual_response.directive (last-write-wins). - All attachment parsing is fail-soft. - Tests: withUserImages/parse unit tests + end-to-end images-attach, files-on-context, tool->directive->eventual_response, and fail-soft. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This was referenced Aug 11, 2026
Merged
brentrager
added a commit
that referenced
this pull request
Aug 11, 2026
…st parity (#348) Bring the C# smooth-operator server to Rust parity on the PR #342 file-transfer contract. send_message was text-only; it now handles images[], files[], and the send_file directive. - Regenerate Types.cs from the updated spec (adds SendMessageRequest.Files, Skill). - images[]: parsed fail-soft, attached as OpenAI image_url content parts. The engine builds the live user turn from a string, so images ride an image-only ChatMessage seated on the thread just before the text turn (no empty/duplicate message). data: -> DataContent, http(s) -> UriContent; detail on AdditionalProperties. Malformed/unsupported entries dropped. - files[]: parsed fail-soft, surfaced on a new per-turn TurnContext (AsyncLocal) so host tools can read them; never sent to the model. - Directive sink: a host tool writes onto TurnContext.Current.Directive; drained after the turn onto eventual_response.directive (last-write-wins, omitted when none for back-compat). - Tests: FileTransferTests covers images-attach, malformed fail-soft, files-on- context, tool->directive->eventual_response, and directive omission. Source-only; the engine stays the published NuGet (no publish). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This was referenced Aug 11, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Spec-level contract for bidirectional file transfer across all faces of Big Smooth.
send_message.files[]—{name, mimeType?, url(data:)}. Unlike visionimages[], these are surfaced to the host on the tool-provider context so the host lands them in the agent workspace (read_file/bash can then use them). Fail-soft, back-compatible.send_fileconvention on the existing opaqueeventual_response.directive:{type:'send_file', files:[{name, mimeType?, url}]}. A hostsend_filetool writes it onto the turn's directive sink; faces render each file as a download/share.Scope of this PR
Spec-only (additive, optional fields — no conformance fixture change, so all four generated-type lanes stay green). Per-language server behavior (parse
files; wire the directive sink in Go/TS/Python/.NET to Rust's parity) lands in follow-up per-language PRs that regen types from this spec.Part of the Big Smooth bidirectional file-transfer epic (agent send + user attach, all faces: th code, th web, iOS, Android).
🤖 Generated with Claude Code
https://claude.ai/code/session_01Vzh8t76aDVgeoiwC9KEKZZ