diff --git a/CHANGELOG.md b/CHANGELOG.md index 09083b8..7bf44ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.0.1.alpha3 - 2026-07-10 + +### Added + +- `Opencode::Client#update_session` for applying permission rules through + OpenCode's session PATCH endpoint. OpenCode appends these rules, so hosts + should fingerprint their ordered policy and call this only when it changes. + ## 0.0.1.alpha2 — 2026-05-20 ### Added diff --git a/README.md b/README.md index 83bf856..e8e938a 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,17 @@ result = client.send_message(session_id, "Quick yes/no: is Ruby fun?") # result is the OpenCode response hash; see API docs for fields. ``` +### Updating session permissions + +```ruby +client.update_session(session_id, permissions: permission_rules) +``` + +OpenCode appends PATCHed permission rules and evaluates the last matching +rule. Hosts should send a complete ordered policy and fingerprint it so the +same policy is not appended on every turn. This endpoint requires OpenCode +1.16.1 or newer; the rest of the client remains compatible with 1.15. + ### Lower-level event firehose If you need raw SSE events (every server tick, todo update, prompt asked/replied), use `stream_events` directly: @@ -155,7 +166,7 @@ bundle install bundle exec rake test ``` -12-test smoke covers Client end-to-end against WebMock-stubbed OpenCode endpoints. +16-test smoke covers Client end-to-end against WebMock-stubbed OpenCode endpoints. ## License diff --git a/lib/opencode/client.rb b/lib/opencode/client.rb index 5f8f706..6a26732 100644 --- a/lib/opencode/client.rb +++ b/lib/opencode/client.rb @@ -144,6 +144,10 @@ module Opencode execute(request) end + def update_session(session_id, permissions:) + patch("/session/#{session_id}", { permission: permissions }) + end + def children(session_id) uri = build_uri("/session/#{session_id}/children") request = Net::HTTP::Get.new(uri) @@ -434,6 +438,13 @@ module Opencode execute(request) end + def patch(path, body) + uri = build_uri(path) + request = Net::HTTP::Patch.new(uri) + request.body = body.to_json + execute(request) + end + def build_uri(path, scoped: true) uri = @uri.dup uri.path = path diff --git a/lib/opencode/version.rb b/lib/opencode/version.rb index 538607c..e9ec7c5 100644 --- a/lib/opencode/version.rb +++ b/lib/opencode/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Opencode - VERSION = "0.0.1.alpha2" + VERSION = "0.0.1.alpha3" end diff --git a/test/opencode/smoke_test.rb b/test/opencode/smoke_test.rb index a1e4109..335b03e 100644 --- a/test/opencode/smoke_test.rb +++ b/test/opencode/smoke_test.rb @@ -53,6 +53,24 @@ class SmokeTest < Minitest::Test assert_equal SESSION_ID, response[:id] end + def test_update_session_patches_permissions_and_returns_the_updated_session + permissions = [ + { permission: "skill", pattern: "*", action: "deny" }, + { permission: "skill", pattern: "core-details", action: "allow" } + ] + + stub_request(:patch, "#{BASE}/session/#{SESSION_ID}") + .with(body: { permission: permissions }.to_json) + .to_return(status: 200, body: { id: SESSION_ID, permission: permissions }.to_json, + headers: { "Content-Type" => "application/json" }) + + response = @client.update_session(SESSION_ID, permissions: permissions) + + assert_equal SESSION_ID, response[:id] + assert_equal permissions, response[:permission] + assert_requested :patch, "#{BASE}/session/#{SESSION_ID}", times: 1 + end + def test_send_message_async_returns_empty_body stub_request(:post, "#{BASE}/session/#{SESSION_ID}/prompt_async") .to_return(status: 204, body: "")