Repository navigation
feat(opencode-agents)!: honor AI Config tools - #216
Conversation
The OpenCode handler previously deleted tool_handlers and never read config["tools"], so the AI Config's tool catalog did nothing and every caller silently inherited whatever opencode.json already configured. That broke the tool-handling contract every other handler follows and gave a judge (tool_handlers=None) a full, unsandboxed OpenCode agent. The offered set is now the intersection of config["tools"] and the caller's tool_handlers, same as the other handlers. A NativeTool (see the new OpenCode* built-in sentinels) is allowed on the session by its own provider name. A plain callable is served from a per-call loopback MCP bridge the handler registers on the running opencode serve with POST /mcp and disconnects when the call ends -- there is no OpenCode route that accepts a tool definition directly on a message. The session's permission ruleset now always denies everything, then allows only the offered names, so a variation with no tools gets a model with no tools (including every judge). chat and execute_tool spans are opened and closed from the same bus events the AG-UI stream already derives from, named after the AI Config key. Nothing here has been run against a real OpenCode MCP client; the bridge's own protocol behavior is unit-tested directly, and the handler's side of the contract (ruleset, registration, teardown) is tested against the existing fake opencode server. Co-authored-by: alexis <alexis@launchdarkly.com>
|
Heads up for review: while wiring observability's Vega runtime to this commit (git pin,
So This will affect any consumer trying to git-pin |
packages/client is still version 0.4.0. Requiring >=0.4.1 made a git pin of this commit uninstallable: Poetry and pip resolve the sibling client package as 0.4.0 and reject it. The handler is written against the client source in the same commit, so the floor matches that declared version. Co-authored-by: alexis <alexis@launchdarkly.com>
|
Follow-up on the install failure: |
jeffdupont
left a comment
There was a problem hiding this comment.
The allow-list does not do what it says for edit-family tools: OpenCodeEdit also grants write and apply_patch, and OpenCodeWrite on its own grants nothing. The new dependency floor also lets PyPI install a client version this code cannot import.
PR title
This changes behaviour for current users. Today a call with no tool_handlers (including every judge) gets the tools in opencode.json. After this PR it gets none, because the session ruleset always denies *. Anyone who relies on host-configured tools loses them without warning. Since the package is 0.x, please make the title feat(opencode-agents)!: ... so release-please flags the break.
Spec (launchdarkly/ai-sdks-monorepo TESTING.md)
The handler is Python-only, so JS needs nothing. The spec still has nothing on this contract: the offered set as the intersection, deny-all for an empty set and for judges, and the OpenCode* sentinels. Other handlers' tool rules are written there, for example the Claude sentinels at TESTING.md:2792. Please add a short OpenCode section in the monorepo alongside this PR.
parameter_forwarding is not in the published 0.4.0 tag, and every handler depends on launchdarkly-ai-server>=0.4.1. This sets the package version string to 0.4.1 and does not publish to PyPI. The release-please manifest stays at 0.4.0, the last released version, so release-please still owns the real publish. Co-authored-by: alexis <alexis@launchdarkly.com>
…MCP connected OpenCode remaps write and apply_patch onto edit, so the session ruleset sends edit for any of the three. Drop OpenCodeList: v1.18.4 has no list tool. A CancelledError marks launchdarkly.run.cancelled instead of stream.abandoned. POST /mcp must report the server as connected. A second completed message.updated does not open another chat span. Bridge shutdown runs off the event loop. The dependency floor is >=0.4.1 again. Co-authored-by: alexis <alexis@launchdarkly.com>
jeffdupont
left a comment
There was a problem hiding this comment.
e879883 covers everything from my first pass, and I've replied on each thread. Still needed before I approve:
- Drop 928b977. Hand-bumping
packages/clientto 0.4.1 (pyproject.toml:3,__init__.py:6) conflicts with release PR #206 and mislabels$ld:ai:sdk:infofor unreleased builds. It also movesuv.lockto revision 5, so please regenerate it with the repo's uv. test_handler.py:1576fails on macOS (/varvs/private/var). ComparePath(...).resolve()on both sides.- Tests the spec (ai-sdks-monorepo#89) asks for that aren't here yet:
- offering only
OpenCodeEdit, and onlyOpenCodeApplyPatch, each sends exactly[deny *, allow edit](only theOpenCodeWritecase is covered,test_tool_contract.py:391); - a judge session is sent the deny-all ruleset (
test_a_judge_opens_its_own_session,test_handler.py:1183, doesn't checkpermission); skillis denied unless offered.
- offering only
- Could someone do one live run against
opencode servewith a callable tool?
The hand-written version conflicted with the release PR and made $ld:ai:sdk:info report 0.4.1 before that version exists. The package metadata is 0.4.0 again. opencode-agents still depends on launchdarkly-ai-server>=0.4.1. uv.lock was regenerated with uv 0.13.0; the lockfile format revision stays 5 because that is what this uv writes. Co-authored-by: alexis <alexis@launchdarkly.com>
Offering only OpenCodeEdit or OpenCodeApplyPatch sends deny-all plus allow edit. A judge session is deny-all. skill is allowed only when it is offered. Recorded cwd and config are compared with Path.resolve on both sides so /var and /private/var match. Co-authored-by: alexis <alexis@launchdarkly.com>
OpenCode's MCP client (confirmed live against the opencode 1.18.4 linux-x64 binary) opens a GET right after initialize and marks the server connection failed on anything but a live text/event-stream, never sending tools/list. The bridge answered that GET with a bare 405, which the spec allows but this client does not treat as optional. The bridge now opens the stream and holds it until stop(), writing an immediate connected comment and periodic keepalives. A live run with one callable tool against the real binary now completes: the model calls the bridged tool, the callable runs, and the final answer reflects its result. Co-authored-by: alexis <alexis@launchdarkly.com>
…er-ai-config-tools-1b7f Co-authored-by: alexis <alexis@launchdarkly.com> # Conflicts: # packages/opencode-agents/tests/test_handler.py
HTTP/1.1 kept the GET connection open, so after the stream ended the handler thread blocked in readline until the client dropped the socket. stop() never joined that thread. The GET now sends Connection: close and sets close_connection, so the thread exits while the client is still connected. Co-authored-by: alexis <alexis@launchdarkly.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c0b5872. Configure here.
…kens _usage_ids is filled only when a completion carries a tokens mapping, so a second message.updated with time.completed and no tokens opened another chat span. Closed message ids are recorded on every completion. Later tokens still count once on the run. Co-authored-by: alexis <alexis@launchdarkly.com>
jeffdupont
left a comment
There was a problem hiding this comment.
Thanks — f73110e, 767f500, c0b5872 and 594734e cover the version revert, the edit-grant/judge/skill tests, macOS paths, and both Bugbot findings. Two things left: uv.lock is still revision = 5 (main is 3), so please regenerate it with the repo's uv; and has anyone done the live run against opencode serve with a callable tool? Once those are in, I'll approve.
|
Live run, against the OpenCode 1.18.4 linux-x64 binary, after the SSE fix in 8e84277: A handler call with one plain callable, Before that fix, the same binary's |
uv 0.12.21 is the last release that writes lockfile revision 3, which is the revision on main. uv 0.12.22 and later write revision 5. uv lock --check agrees with this file. Co-authored-by: alexis <alexis@launchdarkly.com>
🤖 I have created a release *beep* *boop* --- <details><summary>launchdarkly-ai-server: 0.5.0</summary> ## [0.5.0](launchdarkly-ai-server-0.4.0...launchdarkly-ai-server-0.5.0) (2026-10-27) ### ⚠ BREAKING CHANGES * **skills:** promote Agent Skills to core ([#210](#210)) * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Experimental * **evaluations:** export each offline row as one trace ([#170](#170)) ([fc241df](fc241df)) * **evaluations:** normalize text behaviors ([7cee8c8](7cee8c8)) ### Features * **skills:** export the Claude Agents skills handler from the package root ([#217](#217)) ([5d46dbb](5d46dbb)) * **skills:** promote Agent Skills to core ([#210](#210)) ([715815f](715815f)) ### Bug Fixes * **client:** show a judge an output of 0 or false, and render non-string values as JSON ([7cee8c8](7cee8c8)) * **client:** strip whitespace from LD_BASE_URI, LD_STREAM_URI and LD_EVENTS_URI in init_client ([1629123](1629123)) * **judges:** a judge config's outputFormat must not reach the provider ([#86](#86)) ([d8b893d](d8b893d)) * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) * **skills:** coalesce a watcher's debounce window into one reconcile ([#215](#215)) ([b214bbd](b214bbd)) * **skills:** default FDv2SkillStore endpoints from LD_BASE_URI and LD_STREAM_URI ([1629123](1629123)) </details> <details><summary>launchdarkly-ai-python: 0.4.0</summary> ## [0.4.0](launchdarkly-ai-python-0.3.0...launchdarkly-ai-python-0.4.0) (2026-10-27) ### ⚠ BREAKING CHANGES * **skills:** promote Agent Skills to core ([#210](#210)) ### Features * **skills:** promote Agent Skills to core ([#210](#210)) ([715815f](715815f)) </details> <details><summary>launchdarkly-ai-claude-agents: 0.3.0</summary> ## [0.3.0](launchdarkly-ai-claude-agents-0.2.6...launchdarkly-ai-claude-agents-0.3.0) (2026-10-27) ### ⚠ BREAKING CHANGES * **skills:** promote Agent Skills to core ([#210](#210)) * **claude-agents:** switch the built-ins off by default and load no host settings ([#201](#201)) * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Features * **skills:** export the Claude Agents skills handler from the package root ([#217](#217)) ([5d46dbb](5d46dbb)) * **skills:** promote Agent Skills to core ([#210](#210)) ([715815f](715815f)) ### Bug Fixes * **claude-agents:** switch the built-ins off by default and load no host settings ([#201](#201)) ([f99d538](f99d538)) * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-claude-messages: 0.3.0</summary> ## [0.3.0](launchdarkly-ai-claude-messages-0.2.5...launchdarkly-ai-claude-messages-0.3.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-openai-agents: 0.3.0</summary> ## [0.3.0](launchdarkly-ai-openai-agents-0.2.6...launchdarkly-ai-openai-agents-0.3.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-vercel-agents: 0.2.0</summary> ## [0.2.0](launchdarkly-ai-vercel-agents-0.1.1...launchdarkly-ai-vercel-agents-0.2.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-litellm-agents: 0.2.0</summary> ## [0.2.0](launchdarkly-ai-litellm-agents-0.1.1...launchdarkly-ai-litellm-agents-0.2.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-openai-messages: 0.3.0</summary> ## [0.3.0](launchdarkly-ai-openai-messages-0.2.5...launchdarkly-ai-openai-messages-0.3.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-vercel-messages: 0.2.0</summary> ## [0.2.0](launchdarkly-ai-vercel-messages-0.1.1...launchdarkly-ai-vercel-messages-0.2.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-litellm-messages: 0.2.0</summary> ## [0.2.0](launchdarkly-ai-litellm-messages-0.1.1...launchdarkly-ai-litellm-messages-0.2.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-langchain-agents: 0.3.0</summary> ## [0.3.0](launchdarkly-ai-langchain-agents-0.2.6...launchdarkly-ai-langchain-agents-0.3.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-langchain-messages: 0.3.0</summary> ## [0.3.0](launchdarkly-ai-langchain-messages-0.2.5...launchdarkly-ai-langchain-messages-0.3.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-opencode-agents: 0.3.0</summary> ## [0.3.0](launchdarkly-ai-opencode-agents-0.2.0...launchdarkly-ai-opencode-agents-0.3.0) (2026-10-27) ### ⚠ BREAKING CHANGES * **opencode-agents:** honor AI Config tools ([#216](#216)) * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Experimental * **evaluations:** normalize text behaviors ([7cee8c8](7cee8c8)) ### Features * **opencode-agents:** honor AI Config tools ([#216](#216)) ([b5a63ec](b5a63ec)) ### Bug Fixes * **client:** show a judge an output of 0 or false, and render non-string values as JSON ([7cee8c8](7cee8c8)) * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> <details><summary>launchdarkly-ai-typesafe: 0.2.0</summary> ## [0.2.0](launchdarkly-ai-typesafe-0.1.1...launchdarkly-ai-typesafe-0.2.0) (2026-10-27) ### ⚠ BREAKING CHANGES * handlers now forward the AI Config's model parameters to the provider, limited to each handler's list in TESTING.md 1.12. Configs that set parameters which used to be ignored now take effect. ### Bug Fixes * pass AI Config model parameters through to every provider handler ([#107](#107)) ([e3afb3f](e3afb3f)) </details> --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Overview** > This is a **Release Please** cut that bumps versions across the monorepo (notably `launchdarkly-ai-server` **0.5.0**, `launchdarkly-ai-python` **0.4.0**, and matching minor bumps on provider adapters). The edits are manifest entries, `pyproject.toml` / `__version__` strings, and new **CHANGELOG** sections for 2026-10-27. > > The release notes capture several **breaking** runtime changes already on `main`: **Agent Skills** move from experimental to core (`launchdarkly_ai_server.skills`), **AI Config model parameters** are now forwarded to every provider handler (configs that previously ignored them will behave differently), **Claude Agents** defaults built-in tools off and stops loading host settings, and **OpenCode Agents** now honor AI Config tools. The core client also documents judge fixes (`outputFormat` not sent to providers), URI trimming in `init_client`, skills watcher/store fixes, and experimental evaluation trace export. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 4138892. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

What
The OpenCode handler now honors
config["tools"]andtool_handlers, matching the contract every other handler follows. Previously_stream_impldiddel tool_handlersand never readconfig["tools"], so the AI Config's tool catalog did nothing and every caller, including a judge, silently inherited whateveropencode.jsonalready configured.This is a behavior change for current callers: a variation with no tools, including every judge, now gets a model with no tools. The
!in the title is that break. The package is still 0.x, so release-please treats it as a minor.How
config["tools"]and the keys present intool_handlers. ANativeToolis allowed by its provider tool name. A plain callable is served from a per-call loopback MCP bridge registered withPOST /mcp. HTTP 200 is not enough: the body is the status map, and registration raises unlessresponse.json()[name]["status"]is"connected".GETopen as a livetext/event-streaminstead of answering405. OpenCode's MCP client (confirmed live against theopencode1.18.4 linux-x64 binary) opens thatGETright afterinitializeand marks the server connection"failed"on anything but a live stream, never sendingtools/list. The spec allows405there; this client does not treat it as optional.*, then allows only the offered permissions.writeandapply_patchare not their own permissions. OpenCode remaps both ontoedit, soOpenCodeWriteandOpenCodeApplyPatchare the same grant asOpenCodeEdit, and the ruleset sendseditonce. An empty offered set still denies everything.skillis denied unless it is offered.chatper assistant message, oneexecute_toolper tool call, named after the AI Config key. A second completedmessage.updatedfor an id already recorded does not open anotherchatspan. A consumer who stops reading markslaunchdarkly.stream.abandoned. ACancelledErrormarkslaunchdarkly.run.cancelledinstead.OpenCodeBash,OpenCodeRead,OpenCodeEdit,OpenCodeWrite,OpenCodeGlob,OpenCodeGrep,OpenCodeTask,OpenCodeSkill,OpenCodeTodoWrite,OpenCodeWebFetch,OpenCodeWebSearch,OpenCodeApplyPatch. There is noOpenCodeList.packages/clientstays at0.4.0; the earlier hand-bump to0.4.1is reverted so it does not fight release PR chore: release main #206 and$ld:ai:sdk:infodoes not report a version that was never published.launchdarkly-ai-opencode-agentsstill depends onlaunchdarkly-ai-server>=0.4.1, so a git pin of this SHA is not installable by a strict resolver until the client is actually released.Live OpenCode 1.18.4
The
opencode1.18.4 linux-x64 binary runs here. A handler call with one plain callable,get_weather, against that binary now completes: the model called the bridged tool (CALLED [{'city': 'Oslo'}]), and the final answer is "The weather in Oslo is sunny." The earlier run failed withPOST /mcpreporting{"status": "failed", "error": "SSE error: Non-200 status code (405)"}because the bridge answered itsGETwith a bare405; OpenCode's client treats that as a dead connection and never sendstools/list. Traced live with a logging stand-in server: OpenCode sendsPOST initialize,POST notifications/initialized, thenGETwithAccept: text/event-stream— and only sendsPOST tools/listonce thatGETcomes back as a live stream. The bridge now opens that stream (200,Content-Type: text/event-stream, an immediate: connectedcomment, then periodic keepalives) and holds it untilstop().Verification
test_tool_bridge.py::test_get_opens_a_live_sse_stream_not_405reads a live chunk from theGET(not just the status line) and would have caught the405regression.