Files
opencode-ruby/CHANGELOG.md
Ajay Krishnan e3e7b69c7f Initial opencode-ruby v0.0.1.alpha1 — hand-rolled HTTP+SSE client for OpenCode
Headline API:

  reply = client.stream(session_id, "Explain monads") do |part|
    print part["content"] if part["type"] == "text"
  end
  reply.full_text   # final accumulated text

Sources ported from ajaynomics/ajent-rails lib/opencode/client/ after
the Phase-1+2 tier carve + Phase-2.5 boundary cleanup (see ajent-rails
PRs #840 and #843). Rails-runtime coupling stripped:

  - Defaults read from ENV[OPENCODE_BASE_URL/SERVER_PASSWORD/TIMEOUT]
    instead of Rails.application.config.x.opencode_blackline.*
  - EventTraceable.timed_event(...) calls swapped for
    Opencode::Instrumentation.instrument(...) — pluggable adapter
    (default no-op) that callers wire to ActiveSupport::Notifications,
    OpenTelemetry, stdout, etc.

Runtime dependency: activesupport (>= 6.1, < 9.0) for the small
core_ext surface (blank?/present?/presence/truncate/duplicable?/
megabytes). ActiveSupport is NOT Rails — it's a standalone helpers
gem that most Ruby apps already have transitively.

What's in the gem:

  Opencode::Client          HTTP + SSE client; #stream block-form API
  Opencode::Reply           SSE-event accumulator with observer protocol
  Opencode::Reply::Result   typed Struct value object
  Opencode::ReplyObserver   observer protocol module (no-op defaults)
  Opencode::Prompts         per-Reply pending question/permission registry
  Opencode::Tracer          callable that prefixes event names
  Opencode::Instrumentation pluggable adapter
  Opencode::ResponseParser  wire-format extractors
  Opencode::ToolPart        canonical tool-part hash shape
  Opencode::PartSource      wire-vs-stream-only discriminator
  Opencode::Todo            todo status canonicalization
  Opencode::Error (+ 7 subclasses)

What's out (per design D18 — wait for demand signal):

  - acts_as_opencode_session concern
  - ActiveRecord-backed session lifecycle
  - rails generators
  - opencode-rails as a separate gem

Instead, examples/conversation_recipe.rb ships as a ~140-line
plain-ActiveRecord blueprint demonstrating session lifecycle,
with_lock, update_columns mid-stream pattern, and CAS-safe finalize.

Tests: 12 runs, 25 assertions, 0 failures (smoke test against
WebMock-stubbed OpenCode endpoints — covers the postcard, error
model, Instrumentation, and Reply::Result shape).

Authored against ajent-rails commit 02954eeb (opencode-gem/phase-3-prep).
2026-05-19 20:06:40 -07:00

2.6 KiB

Changelog

0.0.1.alpha1 — Unreleased

First public alpha. HTTP + SSE client for OpenCode REST API.

What's in

  • Opencode::Client — Net::HTTP-based HTTP client with SSE streaming + automatic reconnection.
    • #create_session(title:, permissions:), #get_messages(session_id), #list_sessions, #delete_session(id), #abort_session(id).
    • #send_message(session_id, text, model:, ...) — synchronous send-and-poll.
    • #send_message_async(session_id, text, ...) — async send.
    • #stream(session_id, text, ...) { |part| ... } → Opencode::Reply::Resultthe headline. Block-form streaming with internal Reply accumulation and final-exchange merge.
    • #stream_events(session_id:, ...) { |event| ... } — lower-level SSE event firehose for power users.
    • #reply_question(request_id:, answers:) / #reply_permission(request_id:, reply:) — answer interactive prompts.
  • Opencode::Reply — live state machine accumulating SSE events into the assistant's reply. Documented observer protocol (Opencode::ReplyObserver).
  • Opencode::Reply::Result — typed Struct value object returned by Client#stream and Reply#result. Fields: :parts_json, :full_text, :reasoning_text, :tool_parts.
  • Opencode::Instrumentation — pluggable adapter (default no-op). Plug in ActiveSupport::Notifications, OpenTelemetry, stdout, etc.
  • Opencode::ResponseParser, Opencode::ToolPart, Opencode::PartSource, Opencode::Todo — wire-format helpers used by Reply and reusable by callers building their own SSE handling.
  • Opencode::Prompts — per-Reply registry of pending question/permission prompts (used by Reply internally; exposed for callers that need to peek).
  • Opencode::Tracer — callable that prefixes event names before forwarding to a host emitter.
  • Error hierarchy: Opencode::Error and seven subclasses (ConnectionError, TimeoutError, SessionNotFoundError, StaleSessionError, IdleStreamError, ServerError, BadRequestError).

What's out

  • ActiveRecord-backed session lifecycle, acts_as_opencode_session, generators — deferred to opencode-rails if external demand materializes. See examples/conversation_recipe.rb for the canonical Rails wiring pattern.
  • Multi-tenant per-user Docker container orchestration — application glue, not a gem's concern.

Compatibility

  • Ruby ≥ 3.2
  • OpenCode server ≥ 1.15 (tested against the message bus schema in packages/opencode/src/session/message-v2.ts)
  • Runtime dependency: activesupport (>= 6.1) for blank?/present?/presence/truncate/duplicable?/megabytes. ActiveSupport is not Rails — it's a standalone helpers gem.