opencode-ruby 0.0.1.alpha6 → 0.0.1.alpha7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ebac8d73ce010347ee830b23a1280b45a6ff71effd1109aad787743ec60fae45
4
- data.tar.gz: c815940b8e87bcd5724535e987833e360ee9a725081f753cd451f5ae6938f70d
3
+ metadata.gz: 7a4ddecebacda21e25ac50bd14d6d36c8ad61b35d23dee985b620d82a2b94b36
4
+ data.tar.gz: 3aa46267d464d9f95ddc96cb0a7d59cff04a9559c1dffb8d5c6d99c84f46b7b5
5
5
  SHA512:
6
- metadata.gz: 902785c4c78de0f30affcb638bec979844f2e1481cabbb4ce3a2a0fc23ca8d384ed6e2cdbd1e49da7050599728dfb619b1eb7b7c3f011e7b18a793a00e00ca40
7
- data.tar.gz: 7e7cc7a596386dd3846dbe1baa91f3cbeba049e46bc8748e05823375ddd07b145849a217ef249d97ff732d3e9f90c3e3e2de1ee7b6ba2021ef30e8cd04bf362d
6
+ metadata.gz: 2b7a4ef58b44d2d179c3dd389a0b3a2593557188fe03e62d8e63fa19b766de22f75cef35309f2155d3eb6af80bb79c4f44f3251448602aade9fc928761561a4e
7
+ data.tar.gz: 141450c28378875563cb27742eea0ae60d08ef325c37b40923bb0b4103e26f9ce82dd603dc5db313578a12e8a7a160bbbea5aefdeed7e5f87751acb364410cd6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.0.1.alpha7 - 2026-07-18
4
+
5
+ ### Fixed
6
+
7
+ - Add an at-most-once `on_subscribed` hook to the lower-level
8
+ `Opencode::Client#stream_events` API. Higher-level orchestrators can now
9
+ wait for `server.connected` before submitting `prompt_async` without
10
+ abandoning their own Reply observers, persistence, or recovery pipeline.
11
+ - Propagate subscription-hook failures directly and never invoke the hook
12
+ again on SSE reconnect. Ambiguous prompt transport failures therefore
13
+ cannot silently become duplicate turns.
14
+
3
15
  ## 0.0.1.alpha6 - 2026-07-18
4
16
 
5
17
  ### Fixed
data/README.md CHANGED
@@ -119,6 +119,23 @@ client.stream_events(session_id: session_id) do |event|
119
119
  end
120
120
  ```
121
121
 
122
+ Orchestrators that submit their own async prompt must do so through
123
+ `on_subscribed`; the callback runs only after the first `server.connected`
124
+ frame and at most once across automatic reconnects:
125
+
126
+ ```ruby
127
+ client.stream_events(
128
+ session_id: session_id,
129
+ on_subscribed: -> { client.send_message_async(session_id, prompt) }
130
+ ) do |event|
131
+ reply.apply(event)
132
+ end
133
+ ```
134
+
135
+ If the callback raises, `stream_events` propagates that error and does not
136
+ retry it. This is intentional: a timed-out prompt response is ambiguous, so
137
+ reposting could duplicate the model turn and its cost.
138
+
122
139
  ### Interactive prompts
123
140
 
124
141
  When the agent uses the `question` or `permission` tools, opencode emits `question.asked` / `permission.asked` events. Answer them via:
@@ -285,6 +285,13 @@ module Opencode
285
285
  # without nuking real reasoning. Callers that know their agent is
286
286
  # short-prompt + fast can pass a lower value.
287
287
  #
288
+ # on_subscribed: optional callable invoked at most once, after the first
289
+ # `server.connected` frame proves the SSE response body is flowing. This
290
+ # is the safe place for higher-level orchestrators to submit prompt_async;
291
+ # reconnects wait for their own connected frame but never invoke it again.
292
+ # A raised callback error is propagated directly and is never treated as
293
+ # a reconnectable SSE transport failure.
294
+ #
288
295
  # idle_stream_timeout: seconds to wait BETWEEN meaningful events once
289
296
  # the session has started producing them. Default nil = no check
290
297
  # (preserves the overall `timeout` ceiling behavior). Opt-in heartbeat
@@ -298,7 +305,7 @@ module Opencode
298
305
  # translate into a user-visible error / retry affordance.
299
306
  def stream_events(session_id:, timeout: 600, first_event_timeout: 120,
300
307
  idle_stream_timeout: nil,
301
- reply: nil, on_activity_tick: nil, &block)
308
+ reply: nil, on_activity_tick: nil, on_subscribed: nil, &block)
302
309
  consume_event_stream(
303
310
  session_id: session_id,
304
311
  timeout: timeout,
@@ -306,6 +313,7 @@ module Opencode
306
313
  idle_stream_timeout: idle_stream_timeout,
307
314
  reply: reply,
308
315
  on_activity_tick: on_activity_tick,
316
+ on_subscribed: on_subscribed,
309
317
  &block
310
318
  )
311
319
  end
@@ -318,6 +326,7 @@ module Opencode
318
326
  first_event_deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + first_event_timeout
319
327
  received_session_event = false
320
328
  last_meaningful_event_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
329
+ subscription_callback_attempted = on_subscribed.nil?
321
330
 
322
331
  loop do
323
332
  now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
@@ -390,14 +399,22 @@ module Opencode
390
399
  # available readiness handshake before prompting.
391
400
  next unless event[:type] == "server.connected"
392
401
 
393
- begin
394
- turn_started = on_subscribed.call
395
- rescue StandardError => error
396
- # Prompt submission happens inside the open SSE response.
397
- # Do not mistake its transport failure for an SSE disconnect
398
- # and hide it behind a reconnect/first-event timeout.
399
- subscription_callback_error = error
400
- raise
402
+ turn_started = false
403
+ unless subscription_callback_attempted
404
+ # Mark the attempt before invoking the callback. A timeout
405
+ # can mean the prompt reached OpenCode even though its HTTP
406
+ # response did not reach us; retrying would duplicate the
407
+ # turn. The caller receives the original error instead.
408
+ subscription_callback_attempted = true
409
+ begin
410
+ turn_started = on_subscribed.call
411
+ rescue StandardError => error
412
+ # Prompt submission happens inside the open SSE response.
413
+ # Do not mistake its transport failure for an SSE disconnect
414
+ # and hide it behind a reconnect/first-event timeout.
415
+ subscription_callback_error = error
416
+ raise
417
+ end
401
418
  end
402
419
  if turn_started
403
420
  # Before this fix stream_events began only after the prompt
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Opencode
4
- VERSION = "0.0.1.alpha6"
4
+ VERSION = "0.0.1.alpha7"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opencode-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.alpha6
4
+ version: 0.0.1.alpha7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ajay Krishnan