feat: support claude agent SDK-style structured outputs in the OpenCode SDK #8161
feat: support claude agent SDK-style structured outputs in the OpenCode SDK #8161thdxr merged 12 commits intoanomalyco:devfrom
Conversation
Add outputFormat option to session.prompt() for requesting structured JSON output. When type is 'json_schema', injects a StructuredOutput tool that validates model output against the provided schema. - Add OutputFormat schema types (text, json_schema) to message-v2.ts - Add structured_output field to AssistantMessage - Add StructuredOutputError for validation failures - Implement createStructuredOutputTool helper in prompt.ts - Integrate structured output into agent loop with retry support - Regenerate OpenAPI spec with new types - Add unit tests for schema validation
Regenerate SDK types to include outputFormat and structured_output fields.
- Fix loop exit condition to check processor.message.finish instead of result === "stop" (processor.process() returns "continue" on normal exit) - Add system prompt instruction when json_schema mode is enabled to ensure model calls the StructuredOutput tool - Add integration tests for structured output functionality - Fix test Session.messages call to use object parameter format
Document the outputFormat feature for requesting structured JSON output: - Basic usage example with JSON schema - Output format types (text, json_schema) - Schema configuration options (type, schema, retryCount) - Error handling for StructuredOutputError - Best practices for using structured output
When structured output is requested, the model must call the StructuredOutput tool instead of responding with plain text. Changes: - Add toolChoice parameter to LLM.StreamInput - Pass toolChoice: "required" to streamText when outputFormat is json_schema - This forces the model to call a tool, ensuring structured output is captured
Merge upstream changes while preserving structured output feature: - Keep tools deprecation notice from upstream - Keep bypassAgentCheck parameter from upstream - Keep variant field on user messages from upstream - Preserve outputFormat and StructuredOutput tool injection Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
cc @thdxr :) |
|
The following comment was made by an LLM, it may be inaccurate: No duplicate PRs found |
|
Happy to clean up merge conflicts if this PR is on the right track implementation-wise |
There was a problem hiding this comment.
This is a solid start! I have some questions around the implementation.
question: retry logic
Maybe I'm missing something, but where is the retry logic? I see there is a retryCount in the OutputFormat (with the json_schema discriminant); however, I can't determine where this is used. The error seems to be hard coded to 0 retries when created.
discussion: architecture
Is this the best path to take? I understand this is reverse-engineered from Claude Code, but let's be honest: I'm here because Claude Code was pissing me off. They couldn't even figure out how to remove MCP tools from their prompts when the tools are disabled for subagents.
Important
Actually, I researched the claude agents SDK and I'm fairly certain (by reading through their code) they use native output_format as defined here. This may not have always been the case, but they do seem to be using API enforced structured outputs now.
That said, what do you think about an approach that utilizes the provider-level structured outputs? Almost all providers implement something for their models, and I think we could likely provide a clean abstraction that utilizes those features.
I personally built a system similar to this PR this inside my own plugin. Instead of a tool call, it simply injected the expected response format and requested the model reply in an object that matched. It does work, there's no doubt of that, but it does have it's limitations. In some scenarios, assuming the retry logic is implemented, you end up with a few extra round trips to correct the outputs. It's easy enough to enforce this at the tool layer, but different models have different levels of "willingness to cooperate".
Regarding my implementation, I injected the Zod schemas directly (in a format the LLM could understand) and asked it to just generate that output, instead of calling a tool. I found this method to be slightly more reliable than requesting a tool call, because many models see tool calls and "non-terminal" to their execution loops. Or at least, this is a working theory I have based off how many times I've seen OpenAI models get sassy when I request they end with tool calls.
Anyway, food for thought.
TLDR
- Is there retry logic?
- Should we look into first-class provider support instead of prompt injection + output validation + retries?
Fascinating, do you have evals or some reproduction setup that demonstrates this? The architectural approach is similar to Claude code's because (a) OpenCode's harness is largely based on Claude code's, (b) not all inference providers support structured outputs so this output is more generalizable, and (c) I had conversations with one of the OpenCode maintainers to validate the approach before I started Will double-check retry logic once I'm back at my machine |
Unfortunately no, this was just me messing with it across different models. I do think it's worth doing some research on before merging into OpenCode core though. I wouldn't mind trying that out and reporting my findings, since I could get access to both methodologies quite easily.
I don't think that's true (at least not currently). I just pulled the latest from the claude agents SDK and they are using the
That I totally agree with. That's also why I'm fairly certain Claude Code does NOT do this because they only call into their APIs which support this. It would be nonsensical for them to not use it, actually. For us though...we don't have that luxury. It's why I implemented something similar in my plugin. That said, I wonder if there is a two stage approach we could take? We could attempt to use the provider support directly or fall back to this injection/tool method if not available. I think that actually opens the door for doing this, then later adding provider support (maybe even per-provider) as it becomes available. |
|
BTW @K-Mistele want to move the "architecture" talk back to the issue? It might be larger than just this PR, especially if we do a two-staged thing (this PR + some other stuff). |
Good to know, let's set aside guesswork for now then.
This is a straightforward feature and a research project before merging it seems unwarranted, happy to leave that decision up to the maintainers though.
Unfortunately, this is guesswork, and it is incorrect. Perhaps I should've been more clear - the tool injection approach is precisely how Claude code implements this feature, based on analysis of proxied network traffic from the harness. It does not use the output_format feature in the API, it injects a tool called StructuredOutput. The prompts I provided are very similar to what the claude harness uses for that StructuredOutput tool that it injects for this purpose.
See above. I am precisely certain this is exactly the approach the harness uses. Verifying this is trivial.
Correct, retries are a tactical implementation decision to compensate for smaller models or lower-precision inference providers which OpenCode supports and which many users may wish to use |
I attempted to do just that with a proxy and didn't see that tool. Granted, I also don't know how I would specifically get Claude Code to trigger a structured output. I got it to ask a question, but that just called it's The only reason I could see Claude Code using a tool like that at the end of the generation would be to ensure streaming responses (which return chunks of JSON) result in the properly formatted result at the end. But I feel like that would be best done in the actual harness, not relying on a non-deterministic LLM to hopefully call the tool.
I think you misunderstood me, or maybe I am misunderstanding you... I get the value of retries and I see that you provided a way to configure a number of retries. The issue I'm seeing is that the logic to perform those retries, increment, and and check for retry limits seems to be missing. If you pass in Am I missing something? How does this implementation handle the retries? |
|
Checking on the retries right now.
yes, use the claude agent SDK (https://platform.claude.com/docs/en/agent-sdk/typescript) with link to more details about how to use structured outputs in the agent SDK is given in the PR description (or here) Do that and put a proxy in front of it. You'll see something like this (note that the input parameters / descriptions are from your specified JSON schema)
|
Sounds good!
Ahh, that's very interesting. Thanks for providing that! What's interesting is I know that the Anthropic Client SDK uses the underlying API The tool/validate/retry loop you proposed here is good for all the reasons you mentioned. So just to be clear, I'm not against using this approach at all. I think it's a good method to ensure it "always" (within reason) works regardless of the underlying provider/model capability/etc. I also want it to be clear I'm not against using a tool to do the validation. My anecdotal experience was just that: anecdotal. I simply wanted to provide context to see if that was tested before hand. I certainly didn't do a thorough analysis of the best method to use and I assume Anthropic verified their approach, but only against their models. When it comes to relying on output from just prompt vs requiring a tool, I would much rather rely on a tool. The only reason I opted to do something else in my plugin was because certain low powered OpenAI models didn't reliably call tools. I wanted to use a tool and am now thinking I'll try that method again. It's possible I didn't implement it fully by requesting the model always call a tool. The only real concern I have for this PR is the retry loop. The discussion about using provider APIs directly is not material to this work, as it would be a follow up regardless. |
Add 5 new unit tests that verify the retry mechanism for structured output validation: - Multiple validation failures trigger multiple onError calls - Success after failures correctly calls onSuccess - Error messages guide model to fix issues and retry - Simulates retry state tracking matching prompt.ts logic - Simulates successful retry after initial failures Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Thanks for adding the tests! I want to make sure I'm following the implementation correctly. Looking at the test Here's the specific flow I'm trying to trace:
From what I can see in
For retries to work, I'd expect the code to:
Without steps 2-3, the LLM never gets another chance. It doesn't know validation failed because the loop already exited. Could you point me to where this happens? I might be looking at the wrong place, but I've traced through The tests verify that Happy to hop on a call if that's easier! I just want to make sure users get the retry behavior they're expecting. |
00637c0 to
71e0ba2
Compare
f1ae801 to
08fa7f7
Compare
Corrects the docs to match the `format` attribute introduced in anomalyco#8161
Corrects the docs to match the `format` attribute introduced in anomalyco#8161
…de SDK (anomalyco#8161) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Dax Raad <d@ironbay.co>
…de SDK (anomalyco#8161) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Dax Raad <d@ironbay.co>
…de SDK (anomalyco#8161) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Dax Raad <d@ironbay.co>
…192) * release: v1.1.55 * fix(docs): locale translations * fix(app): don't scroll code search input * chore: generate * fix(app): don't close sidebar on session change (anomalyco#13013) * zen: log error * fix(desktop): open apps with executables on Windows (anomalyco#13022) * fix(docs): invalid markdown * fix(app): task tool rendering * release: v1.1.56 * chore: update website stats * wip: zen * fix(docs): locale translations * wip: zen * wip: zen * wip: zen * wip: zen * wip: zen * Update VOUCHED list anomalyco#12841 (comment) * fix(tui): improve amazon-bedrock check to include container credentials (anomalyco#13037) * fix(tui): default session sidebar to auto (anomalyco#13046) * tweak: /review prompt to look for behavior changes more explicitly (anomalyco#13049) * fix(core): ensure compaction is more reliable, add reserve token buffer to ensure that input window has enough room to compact (anomalyco#12924) Co-authored-by: James Lal <james@littlebearlabs.io> * chore: generate * docs: remove 'Migrating to 1.0' documentation section (anomalyco#13076) * wip: zen * fix: add additional context overflow cases, remove overcorrecting ones (anomalyco#13077) * feat(desktop): add WSL backend mode (anomalyco#12914) * fix(desktop): read wayland preference from store (anomalyco#13081) * fix(desktop): server spawn resilience (anomalyco#13028) Co-authored-by: Brendan Allan <git@brendonovich.dev> * fix(docs): avoid footer language selector truncation (anomalyco#13124) * docs(ko): improve translations for intro, cli, and commands (anomalyco#13094) * chore: generate * feat(desktop): enhance Windows app resolution and UI loading states (anomalyco#13084) * fix: encode non-ASCII directory paths in v1 SDK HTTP headers (anomalyco#13131) * desktop: use tracing for logging (anomalyco#13135) * fix(web): prevent language select label truncation (anomalyco#13100) * fix(app): copy path button styles * fix(app): terminal copy/paste * release: v1.1.57 * fix(tui): prevent home wordmark corruption in height-constrained terminals (anomalyco#13069) * feat(prompt): mode-specific input placeholders (anomalyco#12388) * fix(tui): keep /share available to copy existing link (anomalyco#12532) * fix(tui): dismiss dialogs with ctrl+c (anomalyco#12884) * fix(app): terminal resize * fix(console): translations * fix(app): terminal PTY buffer carryover * fix(app): notifications on child sessions * Revert "feat(desktop): add WSL backend mode (anomalyco#12914)" This reverts commit 213a872. * release: v1.1.58 * fix(app): sidebar remount * test(app): more e2e tests (anomalyco#13162) * Fix/reverception (anomalyco#13166) Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com> * fix(app): translations * release: v1.1.59 * chore: generate * fix(app):workspace reset (anomalyco#13170) Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> * chore: generate * fix(app): translations * chore: update docs sync workflow * add square logo variants to brand page * feat: adjust read tool so that it can handle dirs too (anomalyco#13090) * tweak: make read tool offset 1 indexed instead of 0 to avoid confusion that could be caused by line #s being 1 based (anomalyco#13198) * Update VOUCHED list anomalyco#13204 (comment) * tweak: compaction check (anomalyco#13214) * upgrade opentui to 0.1.79 (anomalyco#13036) * chore: update nix node_modules hashes * chore: upgrade bun to 1.3.9 (anomalyco#13223) * chore: update nix node_modules hashes * tweak: use promise all for mcp listTools calls (anomalyco#13229) * docs(ar): second-pass localization cleanup * fix(docs): locale translations for nav elements and headings * fix(app): guard randomUUID in insecure browser contexts (anomalyco#13237) Co-authored-by: Selim <31136147+selimerunkut@users.noreply.github.com> * feat(opencode): Venice Add automatic variant generation for Venice models (anomalyco#12106) * feat(tui): add toggle to hide session header (anomalyco#13244) * Update VOUCHED list anomalyco#13076 (comment) * support custom api url per model * chore: generate * feat: support claude agent SDK-style structured outputs in the OpenCode SDK (anomalyco#8161) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Dax Raad <d@ironbay.co> * chore: generate * fix(win32): use ffi to get around bun raw input/ctrl+c issues (anomalyco#13052) * release: v1.1.60 * zen: return cost * tweak: tool outputs to be more llm friendly (anomalyco#13269) * desktop: sqlite migration progress bar (anomalyco#13294) * Testing SignPath Integration (anomalyco#13308) * ci: move test-sigining policy * chore: style loading screen * wip(ui): diff virtualization (anomalyco#12693) * chore: update nix node_modules hashes * feat: update to not post comment on workflows when no duplicates found (anomalyco#13238) * wip: zen * core: allow model configurations without npm/api provider details Makes npm and api fields optional in the provider schema so model definitions can be more flexible when provider package details aren't needed. * release: v1.1.61 * chore: generate * wip: zen * core: expose tool arguments in shell hook for plugin visibility * chore: refactor packages/app files (anomalyco#13236) Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Co-authored-by: Frank <frank@anoma.ly> * fix(app): more defensive session context metrics * fix(app): more defensive code component * zen: minimax m2.5 * wip: zen * fix(docs): correct `format` attribute in `StructuredOutputs` (anomalyco#13340) * fix: downgrade bun to 1.3.5 (anomalyco#13347) * chore: update nix node_modules hashes * feat(tool): return image attachments from webfetch (anomalyco#13331) * ignore: document test fixtures for agents * chore(app): refactor for better solidjs hygiene (anomalyco#13344) * fix(app): preserve undo history for plain-text paste (anomalyco#13351) * fix(app): project icons unloading * release: v1.1.62 * improve codex model list * release: v1.1.63 * test: add more test cases for project.test.ts (anomalyco#13355) * fix(app): remote http server connections * fix(app): suggestion active state broken * chore: cleanup * chore: cleanup * chore: cleanup * chore: cleanup * chore: cleanup * fix(app): normalize oauth error messages * feat(app): option to turn off sound effects * fix(app): terminal pty isolation * docs: update STACKIT provider documentation with typo fix (anomalyco#13357) Co-authored-by: amankalra172 <aman.kalra@st.ovgu.de> * chore: generate * do not open console on error (anomalyco#13374) * feat: windows selection behavior, manual ctrl+c (anomalyco#13315) * fix: baseline CPU detection (anomalyco#13371) * chore: update nix node_modules hashes * fix: look for recent model in fallback in cli (anomalyco#12582) * chore: generate * chore: cleanup flag code (anomalyco#13389) * fix: token substitution in OPENCODE_CONFIG_CONTENT (anomalyco#13384) * release: v1.1.64 * fix: resolve ACP hanging indefinitely in thinking state on Windows (anomalyco#13222) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: LukeParkerDev <10430890+Hona@users.noreply.github.com> Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Aiden Cline <aidenpcline@gmail.com> * desktop: only show loading window if sqlite migration is necessary * fix(app): failed to create store * fix(app): worktree delete * chore: cleanup * fix(app): prompt input quirks * fix(app): notification should navigate to session * fix(app): emoji as avatar * cleanup desktop loading page * feat(app): toggle all provider models * fix(app): reconnect event stream on disconnect * ci: remove signpath policy * feat(hook): add tool.definition hook for plugins to modify tool description and parameters (anomalyco#4956) * fix: ensure @-ing a dir uses the read tool instead of dead list tool (anomalyco#13428) * Revert "fix: token substitution in OPENCODE_CONFIG_CONTENT" (anomalyco#13429) * release: v1.1.65 * feat: update some ai sdk packages and uuse adaptive reasoning for opus 4.6 on vertex/bedrock/anthropic (anomalyco#13439) * feat(cli): add --dir option to run command (anomalyco#12443) * chore: update nix node_modules hashes * fix(desktop): performance optimization for showing large diff & files (anomalyco#13460) * fix(web): sync docs locale cookie on alias redirects (anomalyco#13109) * fix(app): remount SDK/sync tree when server URL changes (anomalyco#13437) * docs: Fix zh-cn translation mistake in tools.mdx (anomalyco#13407) * chore: generate * fix(web): use prompt_async endpoint to avoid timeout over VPN/tunnel (anomalyco#12749) * fix(app): terminal resize * chore: cleanup * docs(ko): polish Korean phrasing in acp, agents, config, and custom-tools docs (anomalyco#13446) * docs: add pacman installation option for Arch Linux alongside AUR (anomalyco#13293) * fix(test): move timeout config to CLI flag (anomalyco#13494) Co-authored-by: 严浩 <h_mini2024@oo1.dev> * fix: standardize zh-CN docs character set and terminology (anomalyco#13500) * fix(ui): support cmd-click links in inline code (anomalyco#12552) * fix: prevent opencode run crash on malformed tool inputs (anomalyco#13051) Co-authored-by: 0xK3vin <kevin@git-pu.sh> * sqlite again (anomalyco#10597) Co-authored-by: Github Action <action@github.com> Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Co-authored-by: Brendan Allan <git@brendonovich.dev> * chore: generate * desktop: remote OPENCODE_SQLITE env (anomalyco#13545) * chore: update nix node_modules hashes * desktop: fix rust * release: v1.2.0 * zen: minimax m2.5 & glm5 * fix: tweak websearch tool description date info to avoid cache busts (anomalyco#13559) * tui: show all project sessions from any working directory Previously sessions were only listed if they were created in the current working directory or its subdirectories. Users can now view and switch to any session in the project regardless of which directory they're in. * release: v1.2.1 * ci: test-signing signpath policy * ci: move signpath policy * bump vertex ai packages (anomalyco#13625) * fix vercel gateway variants (anomalyco#13541) Co-authored-by: Benjamin Woodruff <github@benjam.info>" * chore: update nix node_modules hashes * core: filter sessions at database level to improve session list loading performance * core: add comprehensive test coverage for Session.list() filters Adds test cases for filtering sessions by directory, root sessions only, start time, search terms, and result limits to ensure the listing functionality works correctly for all filter combinations. * release: v1.2.2 * fix: ensure vercel variants pass amazon models under bedrock key (anomalyco#13631) * chore: bump nixpkgs to get bun 1.3.9 (anomalyco#13302) * fix: add WAL checkpoint on database open (anomalyco#13633) * fix: ensure anthropic models on OR also have variant support (anomalyco#13498) * chore: update nix node_modules hashes * release: v1.2.3 * fix(app): stack overflow in filetree (anomalyco#13667) Co-authored-by: adamelmore <2363879+adamdottv@users.noreply.github.com> * feat(app): clear notifications action (anomalyco#13668) Co-authored-by: adamelmore <2363879+adamdottv@users.noreply.github.com> * fix: derive all IDs from file paths during json migration Earlier migrations moved data to new directories without updating JSON fields. Now consistently derives all IDs from file paths: - Projects: id from filename - Sessions: id from filename, projectID from parent directory - Messages: id from filename, sessionID from parent directory - Parts: id from filename, messageID from parent directory This ensures migrated data matches the actual file layout regardless of stale values in JSON content. * test: add tests for path-derived IDs in json migration Tests verify that file paths are used for IDs even when JSON contains different values - ensuring robustness against stale JSON content. * core: add db command for database inspection and querying * release: v1.2.4 * fix: ensure sqlite migration logs to stderr instead of stdout (anomalyco#13691) * feat: Add GeistMono Nerd Font to available mono font options (anomalyco#13720) * fix(desktop): focus window after update/relaunch (anomalyco#13701) * docs: add Ukrainian README translation (anomalyco#13697) * fix(app): keybind [shift+tab] (anomalyco#13695) * fix(app): only navigate prompt history at input boundaries (anomalyco#13690) * fix(desktop): issue viewing new files opened from the file tree (anomalyco#13689) * feat(app): localize "free usage exceeded" error & "Add credits" clickable link (anomalyco#13652) * release: v1.2.5 * feat(opencode): add `cljfmt` formatter support for Clojure files (anomalyco#13426) * fix(website): correct zh-CN translation of proprietary terms in zen.mdx (anomalyco#13734) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> * chore: generate * desktop: use process-wrap instead of manual job object (anomalyco#13431) * feat(opencode): Add Venice support in temperature, topP, topK and smallOption (anomalyco#13553) * fix(desktop): normalize Linux Wayland/X11 backend and decoration policy (anomalyco#13143) Co-authored-by: Brendan Allan <brendonovich@outlook.com> * feat: add openai-compatible endpoint support for google-vertex provider (anomalyco#10303) Co-authored-by: BlueT - Matthew Lien - 練喆明 <BlueT@BlueT.org> Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Aiden Cline <aidenpcline@gmail.com> * chore: generate * fix: google vertex var priority (anomalyco#13816) * fix(docs): correct critical translation errors in Russian zen page (anomalyco#13830) * fix(core): remove unnecessary per-message title LLM calls (anomalyco#13804) * chore: cleanup * fix (tui): Inaccurate tips (anomalyco#13845) * fix: bump GitLab provider and auth plugin for mid-session token refresh (anomalyco#13850) * chore: update nix node_modules hashes * feat(cli): add --continue and --fork flags to attach command (anomalyco#13879) * feat(cli): add db migrate command for JSON to SQLite migration (anomalyco#13874) * ignore: rm random comment on opencode.jsonc * fix(tui): make use of server dir path for file references in prompts (anomalyco#13781) * feat(opencode): add `dfmt` formatter support for D language files (anomalyco#13867) * release: v1.2.6 * feat(cli): add session delete command (anomalyco#13571) * fix(opencode): ACP sessions never get LLM-generated titles (anomalyco#13095) * feat(acp): add opt-in flag for question tool (anomalyco#13562) Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Aiden Cline <aidenpcline@gmail.com> * tweak: drop ids from attachments in tools, assign them in prompt.ts instead (anomalyco#13890) * fix(website): fix site in safari 18 (anomalyco#13894) * ci: fixed apt cache not working in publish.yml (anomalyco#13897) * ci: use `useblacksmith/stickydisk` on linux runners only (anomalyco#13909) * core: keep message part order stable when files resolve asynchronously (anomalyco#13915) * ci: fixed Rust cache for 'cargo install' in publish.yml (anomalyco#13907) * ci: fixed missing if condition (anomalyco#13934) * Hide server CLI on windows (anomalyco#13936) * zen: glm 5 free * wip: zen * ci: update triage workflow (anomalyco#13944) * fix(app): keep Escape handling local to prompt input on macOS desktop (anomalyco#13963) * docs(ko): improve Korean translation accuracy and clarity in Zen docs (anomalyco#13951) * docs: improve zh-cn and zh-tw documentation translations (anomalyco#13942) * fix(docs): correct reversed meaning in Korean plugins logging section (anomalyco#13945) * chore: generate * fix(opencode): resolve conflicts from upstream sync * fix(tool): remove spurious validateDirectory introduced during sync (#191) --------- Co-authored-by: opencode <opencode@sst.dev> Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com> Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Co-authored-by: Frank <frank@anoma.ly> Co-authored-by: Filip <34747899+neriousy@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: OpeOginni <107570612+OpeOginni@users.noreply.github.com> Co-authored-by: Dax <mail@thdxr.com> Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: James Lal <james@littlebearlabs.io> Co-authored-by: Ariane Emory <97994360+ariane-emory@users.noreply.github.com> Co-authored-by: Brendan Allan <brendonovich@outlook.com> Co-authored-by: Brendan Allan <git@brendonovich.dev> Co-authored-by: webwww123 <1876948535@qq.com> Co-authored-by: Jun <87404676+Seungjun0906@users.noreply.github.com> Co-authored-by: Jack <740172898@qq.com> Co-authored-by: Shintaro Jokagi <61367823+taroj1205@users.noreply.github.com> Co-authored-by: Kit Langton <kit.langton@gmail.com> Co-authored-by: Dax Raad <d@ironbay.co> Co-authored-by: Sebastian <hasta84@gmail.com> Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com> Co-authored-by: Selim <31136147+selimerunkut@users.noreply.github.com> Co-authored-by: dpuyosa <dpuyosa@users.noreply.github.com> Co-authored-by: Kyle Mistele <kyle@humanlayer.dev> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Ryan Vogel <ryan@inbound.new> Co-authored-by: Dylan Fiedler <dtfiedler@users.noreply.github.com> Co-authored-by: Rasheed <99349713+gitRasheed@users.noreply.github.com> Co-authored-by: Aman Kalra <49478659+amankalra172@users.noreply.github.com> Co-authored-by: amankalra172 <aman.kalra@st.ovgu.de> Co-authored-by: Smit Chaudhary <smitchaudhary10@gmail.com> Co-authored-by: projectArtur <155688912+ASidorenkoCode@users.noreply.github.com> Co-authored-by: Aiden Cline <aidenpcline@gmail.com> Co-authored-by: Spoon <212802214+spoons-and-mirrors@users.noreply.github.com> Co-authored-by: Rahul Mishra <blankparticle@gmail.com> Co-authored-by: Chris Yang <18487241+ysm-dev@users.noreply.github.com> Co-authored-by: Annopick <veater@qq.com> Co-authored-by: eytans <eytans@users.noreply.github.com> Co-authored-by: G36maid <53391375+G36maid@users.noreply.github.com> Co-authored-by: 严浩 <37316281+yanhao98@users.noreply.github.com> Co-authored-by: 严浩 <h_mini2024@oo1.dev> Co-authored-by: Niu Shuai <guisu2010@gmail.com> Co-authored-by: Kevin <kevin@apthos.net> Co-authored-by: 0xK3vin <kevin@git-pu.sh> Co-authored-by: Github Action <action@github.com> Co-authored-by: Alberto Valverde <alberto@toscat.net> Co-authored-by: adamelmore <2363879+adamdottv@users.noreply.github.com> Co-authored-by: Brandon Julio Thenaro <brandon.julio.t@icloud.com> Co-authored-by: zerone0x <hi@trine.dev> Co-authored-by: Denys <dector@dector.space> Co-authored-by: Shoubhit Dash <shoubhit2005@gmail.com> Co-authored-by: Shane Bishop <71288697+shanebishop1@users.noreply.github.com> Co-authored-by: Alex Yaroshuk <34632190+alexyaroshuk@users.noreply.github.com> Co-authored-by: Salam Elbilig <finalfantasia@users.noreply.github.com> Co-authored-by: Pan Kaixin <pan_kaixin@qq.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: bnema <brice@briceamen.com> Co-authored-by: Jhin Lee <leehack@gmail.com> Co-authored-by: BlueT - Matthew Lien - 練喆明 <BlueT@BlueT.org> Co-authored-by: Chujiang <110hqc@gmail.com> Co-authored-by: Rafi Khardalian <rafi@actualyze.ai> Co-authored-by: Imanol Maiztegui <imanol.mzd@gmail.com> Co-authored-by: Vladimir Glafirov <vglafirov@gitlab.com> Co-authored-by: Robert Schadek <rburners@gmail.com> Co-authored-by: Zhiyuan Zheng <zhzy0077@hotmail.com> Co-authored-by: ImmuneFOMO <60671130+ImmuneFOMO@users.noreply.github.com> Co-authored-by: James Long <longster@gmail.com> Co-authored-by: Goni Zahavy <goni1993@gmail.com> Co-authored-by: Ganesh <179367536+itskritix@users.noreply.github.com> Co-authored-by: vynn <163614291+vynnlee@users.noreply.github.com> Co-authored-by: chenmi <jack_chenyuana@163.com> Co-authored-by: Minung Han <hmu332233@gmail.com> Co-authored-by: Janni Turunen <janni@Jannis-MacBook-Air.local>

What does this PR do?
This PR adds support for structured generation in the OpenCode SDK (v2) by implementing an injected
StructuredOutputtool with an arbitrary user-defined JSON schema.This implementation approach follows Claude Code's approach as indicated by debugging and proxy analysis.
When set,
toolChoiceis forced to"required"and the agent loop is exited once the input to the tool call passes validation against the user-supplied JSON Schema.Retries are supported, and retry count is customizable (important for smaller models or providers which have poor support for structured generation!).
Prompts and descriptions are based on those used in the Claude Agent SDK
You can learn more about structured outputs in the Claude Agent SDK here
Closes #5639
How did you verify your code works?
This PR contains both unit tests and integration tests.