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
|
# 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
|
## 0.0.1.alpha4 - 2026-07-12
|
||||||
|
|
||||||
### Fixed
|
### 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
|
## 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)
|
### Streaming (the headline)
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
@@ -166,7 +187,7 @@ bundle install
|
|||||||
bundle exec rake test
|
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
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,24 @@ module Opencode
|
|||||||
@workspace = workspace
|
@workspace = workspace
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_session(title: nil, permissions: nil)
|
def create_session(
|
||||||
body = { title: title, permission: permissions }.compact
|
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)
|
post("/session", body)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -476,6 +492,14 @@ module Opencode
|
|||||||
{ providerID: provider, modelID: model_id }
|
{ providerID: provider, modelID: model_id }
|
||||||
end
|
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)
|
def post(path, body)
|
||||||
uri = build_uri(path)
|
uri = build_uri(path)
|
||||||
request = Net::HTTP::Post.new(uri)
|
request = Net::HTTP::Post.new(uri)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Opencode
|
module Opencode
|
||||||
VERSION = "0.0.1.alpha4"
|
VERSION = "0.0.1.alpha5"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class SmokeTest < Minitest::Test
|
|||||||
|
|
||||||
def test_create_session_returns_session_id
|
def test_create_session_returns_session_id
|
||||||
stub_request(:post, "#{BASE}/session")
|
stub_request(:post, "#{BASE}/session")
|
||||||
|
.with(body: { title: "smoke", permission: [] }.to_json)
|
||||||
.to_return(status: 200, body: { id: SESSION_ID, title: "smoke" }.to_json,
|
.to_return(status: 200, body: { id: SESSION_ID, title: "smoke" }.to_json,
|
||||||
headers: { "Content-Type" => "application/json" })
|
headers: { "Content-Type" => "application/json" })
|
||||||
|
|
||||||
@@ -53,6 +54,51 @@ class SmokeTest < Minitest::Test
|
|||||||
assert_equal SESSION_ID, response[:id]
|
assert_equal SESSION_ID, response[:id]
|
||||||
end
|
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
|
def test_update_session_patches_permissions_and_returns_the_updated_session
|
||||||
permissions = [
|
permissions = [
|
||||||
{ permission: "skill", pattern: "*", action: "deny" },
|
{ permission: "skill", pattern: "*", action: "deny" },
|
||||||
|
|||||||
Reference in New Issue
Block a user