Support configured child sessions
Some checks failed
Test / test (3.3) (push) Failing after 9m35s
Test / test (3.4) (push) Failing after 9m37s
Test / test (3.2) (push) Failing after 9m37s
Some checks failed
Test / test (3.3) (push) Failing after 9m35s
Test / test (3.4) (push) Failing after 9m37s
Test / test (3.2) (push) Failing after 9m37s
This commit is contained in:
10
CHANGELOG.md
10
CHANGELOG.md
@@ -1,5 +1,15 @@
|
||||
# Changelog
|
||||
|
||||
## 0.0.1.alpha5 - 2026-07-15
|
||||
|
||||
### Added
|
||||
|
||||
- Extend `Opencode::Client#create_session` with OpenCode's native parent,
|
||||
agent, model, metadata, and workspace fields while preserving the existing
|
||||
title and permission call shape. Session model strings are encoded with the
|
||||
session endpoint's `{ providerID, id }` shape rather than the message
|
||||
endpoint's `{ providerID, modelID }` shape.
|
||||
|
||||
## 0.0.1.alpha4 - 2026-07-12
|
||||
|
||||
### Fixed
|
||||
|
||||
23
README.md
23
README.md
@@ -48,6 +48,27 @@ Multi-tenant apps construct multiple clients with different `base_url`s — each
|
||||
|
||||
## Core API
|
||||
|
||||
### Configured and parent-linked sessions
|
||||
|
||||
OpenCode can create a session under an existing parent and select its agent,
|
||||
model, metadata, workspace, and permission policy in the same request:
|
||||
|
||||
```ruby
|
||||
child = client.create_session(
|
||||
title: "Destination curator",
|
||||
parent_id: parent_session_id,
|
||||
agent: "destination-list-curator",
|
||||
model: "openai/gpt-5.5",
|
||||
metadata: { run_id: "9" },
|
||||
workspace_id: workspace_id,
|
||||
permissions: permission_rules
|
||||
)
|
||||
```
|
||||
|
||||
Model strings use OpenCode's `provider/model` form; a preformatted model hash
|
||||
with `providerID` and `id` keys is also accepted. These configured-session
|
||||
fields require OpenCode 1.16.1 or newer.
|
||||
|
||||
### Streaming (the headline)
|
||||
|
||||
```ruby
|
||||
@@ -166,7 +187,7 @@ bundle install
|
||||
bundle exec rake test
|
||||
```
|
||||
|
||||
16-test smoke covers Client end-to-end against WebMock-stubbed OpenCode endpoints.
|
||||
The smoke suite covers Client end-to-end against WebMock-stubbed OpenCode endpoints.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -25,8 +25,24 @@ module Opencode
|
||||
@workspace = workspace
|
||||
end
|
||||
|
||||
def create_session(title: nil, permissions: nil)
|
||||
body = { title: title, permission: permissions }.compact
|
||||
def create_session(
|
||||
title: nil,
|
||||
permissions: nil,
|
||||
parent_id: nil,
|
||||
agent: nil,
|
||||
model: nil,
|
||||
metadata: nil,
|
||||
workspace_id: nil
|
||||
)
|
||||
body = {
|
||||
title: title,
|
||||
permission: permissions,
|
||||
parentID: parent_id,
|
||||
agent: agent,
|
||||
model: format_session_model(model),
|
||||
metadata: metadata,
|
||||
workspaceID: workspace_id
|
||||
}.compact
|
||||
post("/session", body)
|
||||
end
|
||||
|
||||
@@ -476,6 +492,14 @@ module Opencode
|
||||
{ providerID: provider, modelID: model_id }
|
||||
end
|
||||
|
||||
def format_session_model(model)
|
||||
return nil unless model
|
||||
return model if model.is_a?(Hash)
|
||||
|
||||
provider, model_id = model.split("/", 2)
|
||||
{ providerID: provider, id: model_id }
|
||||
end
|
||||
|
||||
def post(path, body)
|
||||
uri = build_uri(path)
|
||||
request = Net::HTTP::Post.new(uri)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Opencode
|
||||
VERSION = "0.0.1.alpha4"
|
||||
VERSION = "0.0.1.alpha5"
|
||||
end
|
||||
|
||||
@@ -46,6 +46,7 @@ class SmokeTest < Minitest::Test
|
||||
|
||||
def test_create_session_returns_session_id
|
||||
stub_request(:post, "#{BASE}/session")
|
||||
.with(body: { title: "smoke", permission: [] }.to_json)
|
||||
.to_return(status: 200, body: { id: SESSION_ID, title: "smoke" }.to_json,
|
||||
headers: { "Content-Type" => "application/json" })
|
||||
|
||||
@@ -53,6 +54,51 @@ class SmokeTest < Minitest::Test
|
||||
assert_equal SESSION_ID, response[:id]
|
||||
end
|
||||
|
||||
def test_create_session_sends_native_child_and_configuration_fields
|
||||
permissions = [ { permission: "skill", pattern: "*", action: "deny" } ]
|
||||
expected_body = {
|
||||
title: "curator",
|
||||
permission: permissions,
|
||||
parentID: "ses_parent",
|
||||
agent: "destination-list-curator",
|
||||
model: { providerID: "openrouter", id: "anthropic/claude-sonnet-4" },
|
||||
metadata: { run: "9" },
|
||||
workspaceID: "wrk_1"
|
||||
}
|
||||
|
||||
stub_request(:post, "#{BASE}/session")
|
||||
.with(body: expected_body.to_json)
|
||||
.to_return(status: 200, body: { id: SESSION_ID }.to_json,
|
||||
headers: { "Content-Type" => "application/json" })
|
||||
|
||||
response = @client.create_session(
|
||||
title: "curator",
|
||||
permissions: permissions,
|
||||
parent_id: "ses_parent",
|
||||
agent: "destination-list-curator",
|
||||
model: "openrouter/anthropic/claude-sonnet-4",
|
||||
metadata: { run: "9" },
|
||||
workspace_id: "wrk_1"
|
||||
)
|
||||
|
||||
assert_equal SESSION_ID, response[:id]
|
||||
assert_requested :post, "#{BASE}/session", body: expected_body.to_json, times: 1
|
||||
end
|
||||
|
||||
def test_create_session_preserves_a_preformatted_model
|
||||
model = { providerID: "openai", id: "gpt-5.5", variant: "high" }
|
||||
|
||||
stub_request(:post, "#{BASE}/session")
|
||||
.with(body: { model: model }.to_json)
|
||||
.to_return(status: 200, body: { id: SESSION_ID }.to_json,
|
||||
headers: { "Content-Type" => "application/json" })
|
||||
|
||||
response = @client.create_session(model: model)
|
||||
|
||||
assert_equal SESSION_ID, response[:id]
|
||||
assert_requested :post, "#{BASE}/session", body: { model: model }.to_json, times: 1
|
||||
end
|
||||
|
||||
def test_update_session_patches_permissions_and_returns_the_updated_session
|
||||
permissions = [
|
||||
{ permission: "skill", pattern: "*", action: "deny" },
|
||||
|
||||
Reference in New Issue
Block a user