Skip to content

fix(core): bound tool structured output - #37968

Closed
kitlangton wants to merge 8 commits into
v2from
bound-tool-output
Closed

kitlangton wants to merge 8 commits into
v2from
bound-tool-output

Conversation

@kitlangton

@kitlangton kitlangton commented Jul 20, 2026 •

Copy link
Copy Markdown
Contributor

What

Bound every durably published tool structured value to 16 KiB. Oversized values spill to managed tool-output storage and persist as compact receipts instead of expanding SQLite rows and global session events.

Built-in payload tools now project compact metadata while preserving model-visible output. Consumer-critical metadata remains available: edit and patch retain bounded, parseable files[].patch previews; question retains bounded answers; subagent retains child-session identity and status.

Before / After

Before: ToolOutputStore.bound capped model content but persisted structured without measuring it. Read, fetch, search, skill, subagent, plugin, and provider-executed results could duplicate megabytes into session history. Post-execution hooks and progress events could bypass the local settlement guard entirely.

After: structured values are normalized to their durable record shape before a strict UTF-8 16 KiB check. Oversized values spill once and persist as { _truncated: true, _bytes, _outputPath }; structured-only results remain model-visible through bounded content. Local settlement, execute.after, provider-executed success, and progress publication all pass through the guard. Storage failures produce terminal tool/step failure state instead of leaving tools running.

How

  • packages/core/src/tool-output-store.ts serializes structured values, enforces the byte ceiling, writes managed spill files, and builds bounded model previews.
  • packages/core/src/session/runner/{llm,publish-llm-event}.ts applies the guard at durable success and progress boundaries, including provider-executed results.
  • packages/core/src/tool/registry.ts runs redaction hooks before retention, then bounds final output and limits producer-specific truncation propagation to explicit receipt definitions.
  • glob, grep, read, skill, subagent, webfetch, and websearch emit compact typed receipts.
  • edit, patch, and question preserve their UI-consumed structured shapes while bounding payload strings; large patch previews remain balanced unified-diff summaries.
  • CodeMode payload tools explicitly expose their typed internal output so compact persistence receipts do not starve model-authored code. Other plugins retain their declared structured projection by default.

Scope

This targets the V2 tool stack on the v2 branch. Raw provider-executed result remains verbatim for provider-history replay; the separate encoded-event budget is out of scope. V2 has no todowrite built-in or corresponding TUI renderer, so legacy todo receipt changes are intentionally absent.

Testing

  • cd packages/core && bun typecheck
  • Focused changed-area suite: 297 passed, 0 failed
  • Full core suite: 1371 passed, 6 skipped, 2 failed; both failures reproduce independently in the existing LocationServiceMap catalog isolation and user-global plugin discovery tests
  • cd packages/plugin && bun typecheck && bun run test: 7 passed
  • cd packages/session-ui && bun typecheck && bun run test: 60 passed
  • cd packages/tui && bun typecheck && bun run test: 475 passed, 5 skipped
  • Repository typecheck: bun turbo typecheck --concurrency=3 (32 packages passed)
  • Changed-file Oxlint: 0 errors
  • git diff --check
  • GitHub Actions: standards, compliance, typecheck, Linux/Windows unit, and Linux/Windows e2e all pass

Flow

flowchart TD
  A[Tool result or progress] --> B[Normalize durable structured record]
  B --> C[Apply producer receipt projection]
  C --> D{Serialized UTF-8 at most 16 KiB?}
  D -- Yes --> E[Persist bounded structured metadata]
  D -- No --> F[Write managed spill file]
  F --> G[Persist spill receipt]
  E --> H[Publish session event]
  G --> H
  H --> I[TUI replay and next model step]
Loading

@kitlangton
kitlangton changed the base branch from dev to v2 July 20, 2026 18:09
@github-actions

Copy link
Copy Markdown
Contributor

Automated PR Cleanup

Thank you for contributing to opencode.

Due to the high volume of PRs from users and AI agents, we periodically close older PRs using automated criteria so maintainers can focus review time on the most active and community-supported contributions.

This PR was closed because it matched the following cleanup criteria:

  • The PR was created more than 1 month ago
  • The PR had fewer than 2 positive reactions
  • Positive reactions are counted as thumbs-up, heart, celebration, or rocket reactions on the PR

PRs created within the last month are not affected by this cleanup.

If you believe this PR was closed incorrectly, or if you are still actively working on it, please leave a comment explaining why it should be reopened. A maintainer can review and reopen it if appropriate.

Thanks again for taking the time to contribute.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant