ask-coding-providers 0.3.1 → 0.3.3

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: f1e380717ee69fa2d0da0df5a2c706e2b6e8ef3509ea6f6baad0c96d170d62f3
4
- data.tar.gz: 21d327fadd79f5fbbdce88c71e1009b2f45b03431bd7cf29e258f4bcb07b3b05
3
+ metadata.gz: 546ff8509486604af6cc4f8fa7060bb91797a147b77336de0c71f4ca340d9ebf
4
+ data.tar.gz: 544b15d771c6c03bef753c3ee349b755a2bf484b07a58e3cb034f6e812fd3848
5
5
  SHA512:
6
- metadata.gz: 975feb731eb168bd1a291c395e6421fd882ca9b0a23766602dde39352a187cb79c354df70d531e4516eb6050854e2086bbe3c629185e7751ea6d54c6554eca3a
7
- data.tar.gz: 23cf6fe197707a8a0d456fbc8299f004cbb87134b17393a634305d227d1ada59b1bf476458ad482cc734317be8b87ae0972e240f6bc87a5d2a08ee1576707c57
6
+ metadata.gz: bb40d05d7c73881d57333164f48ac7becad1e973448e5b29c7e3404de7bc57fb1204598303561ebe35e0f5b34da1921a03eec253d56e2c7958b4c74df0b2af4e
7
+ data.tar.gz: 2ecef478830899e6e1d8192a6de02e75df7d28900e9132c9eeb34e408aa1fa2adce1cce0854c4a7318254b82de3dcc02be66705c0b6c328d89570d7360b96405
data/CHANGELOG.md ADDED
@@ -0,0 +1,78 @@
1
+ # Changelog
2
+
3
+ ## [0.3.2] - 2026-08-10
4
+
5
+ ### Added
6
+
7
+ - **Declarative agent sessions.** `create_session` accepts an `agent:`
8
+ name — the ask-agent convention (`agents/<name>/agent.rb` +
9
+ `instructions.md`, discovered from the working directory). The session
10
+ is built via `Ask::Agent.new`, so the definition's tools, skills
11
+ (agent_dir), and instructions apply, while harness-level options
12
+ (model, system prompt, approval, plan mode, todos) still win. The
13
+ emitting approval queue is passed through, so approval events stream
14
+ exactly like the plain path.
15
+
16
+
17
+
18
+ ### Added
19
+
20
+ - **Per-session system prompts.** `create_session` accepts a
21
+ `system_prompt:` override for the session (takes precedence over the
22
+ adapter-level session option), so hosts like
23
+ ask-coding-harness can give every workspace its own instructions.
24
+
25
+
26
+
27
+ ### Fixed
28
+
29
+ - **Claude adapter streamed nothing** — the inner `each do |block|` loop
30
+ shadowed the method's `&block` parameter, so `block.call` invoked the
31
+ content Hash and every streaming turn failed. The loop variable is now
32
+ `content_block`; `model.streaming` events stream correctly.
33
+ - **Codex session store** — `find_sessions` bound 2 parameters for 3 SQL
34
+ placeholders (silently returned `[]`); `find_recent_tui_session` passed
35
+ bind variables as separate arguments to `get_first_row` (silently
36
+ returned `nil`). Both now bind correctly.
37
+
38
+ ### Added
39
+
40
+ - **External adapter tests** — Claude Code CLI streaming (stubbed
41
+ processes), Codex app-server client lifecycle, and read-only ZCode/Codex
42
+ session-store queries against temp SQLite fixtures. Full suite passes
43
+ the 70% coverage gate.
44
+
45
+
46
+ ### Added
47
+
48
+ - **Rich event stream from the `ask_agent` adapter.** Sessions now emit
49
+ `model.thinking`, `tool.use`, `tool.delta`, `tool.result` (with
50
+ `isError`/`durationMs`), `approval.required`, `approval.updated`,
51
+ `plan.proposed`, `plan.approved`, `plan.rejected`, `todos.updated`,
52
+ `turn.aborted`, and `error` events in addition to the existing
53
+ `model.streaming`/`turn.completed`/`turn.failed`. Existing consumers keep
54
+ working unchanged.
55
+ - **Persistent in-process sessions.** Each `create_session` id maps to one
56
+ `Ask::Agent::Session` instance whose conversation history accumulates
57
+ across turns.
58
+ - **Approval controls.** `approve_action`, `reject_action`, `approve_all`,
59
+ `reject_all`, `pending_approvals`, plus `approve_plan`/`reject_plan`/
60
+ `pending_plan` for plan mode. `approval:` accepts `:off`, `:require`
61
+ (tools listed in `approval_required:` queue for human review), or `:auto`
62
+ (queue exists, never blocks).
63
+ - **Plan mode and todos.** `plan_mode:` and `todos:` pass through to the
64
+ session and their events stream to subscribers.
65
+ - **Abort support.** `abort(session_id)` stops the current turn, which
66
+ emits `turn.aborted`.
67
+ - **Turn completion waits for approvals.** `send_and_stream` keeps the
68
+ stream open until the turn fully settles (follow-up turns included),
69
+ bounded by `turn_timeout`.
70
+
71
+ ### Fixed
72
+
73
+ - `turn.started` is emitted after subscribing, so subscribers always
74
+ receive it.
75
+ - Sessions created but never run no longer crash `list_sessions` /
76
+ `session_history`.
77
+ - Approval mode is validated at adapter construction (fail fast).
78
+ - Session listing sorts deterministically.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kaka Ruto
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -64,7 +64,7 @@ module Ask
64
64
  @client&.running? || false
65
65
  end
66
66
 
67
- def create_session(workspace_path, mode: nil)
67
+ def create_session(workspace_path, mode: nil, system_prompt: nil, **)
68
68
  ensure_running
69
69
  params = { cwd: workspace_path || @cwd }
70
70
  session = @client.session_new(**params)
@@ -14,7 +14,7 @@ module Ask
14
14
  # @param workspace_path [String] the working directory
15
15
  # @param mode [String, nil] permission mode
16
16
  # @return [String] the session ID
17
- def create_session(workspace_path, mode: nil)
17
+ def create_session(workspace_path, mode: nil, system_prompt: nil, **)
18
18
  raise NotImplementedError, "#{self.class} must implement #create_session"
19
19
  end
20
20
 
@@ -149,7 +149,13 @@ module Ask
149
149
  # @param model [String, nil] model override for this session
150
150
  # @param system_prompt [String, nil] system prompt override for this
151
151
  # session (takes precedence over any system_prompt in session_opts)
152
- def create_session(workspace_path, mode: nil, model: nil, system_prompt: nil)
152
+ # @param agent [String, nil] declarative agent name (ask-agent
153
+ # convention: agents/<name>/agent.rb + instructions.md, discovered
154
+ # from the workspace's working directory). When given, the session
155
+ # is built via Ask::Agent.new so the definition's tools, skills,
156
+ # and instructions apply; the harness-level options (model,
157
+ # system_prompt, approval, plan mode, todos) still win.
158
+ def create_session(workspace_path, mode: nil, model: nil, system_prompt: nil, agent: nil)
153
159
  ensure_started
154
160
  sid = "sess_#{SecureRandom.uuid}"
155
161
  @mutex.synchronize do
@@ -158,6 +164,7 @@ module Ask
158
164
  mode: mode,
159
165
  model: model || @model_id,
160
166
  system_prompt: system_prompt,
167
+ agent: agent,
161
168
  created_at: Time.now,
162
169
  session: nil,
163
170
  subscribers: [],
@@ -367,36 +374,71 @@ module Ask
367
374
  # ── Session construction ──
368
375
 
369
376
  def build_session(entry)
370
- prompt = entry[:system_prompt] || @session_opts[:system_prompt]
371
- chat = build_chat(entry[:model], prompt)
377
+ session = entry[:agent] ? build_agent_session(entry) : build_plain_session(entry)
378
+ session.on_event { |event| translate_event(entry, session, event) }
379
+ session
380
+ end
372
381
 
373
- queue = EmittingApprovalQueue.new(
374
- on_submit: ->(a) { emit_approval(entry, a, :pending) },
375
- on_status: ->(a) { emit_approval(entry, a, a.status) }
376
- )
382
+ # Build a session from a declarative agent definition (ask-agent
383
+ # convention). The definition's tools, skills (agent_dir), and
384
+ # instructions apply; harness-level options win where set. Agents
385
+ # are discovered from the working directory (the harness runs the
386
+ # session inside its workspace).
387
+ #
388
+ # The emitting approval queue is passed through Ask::Agent.new's
389
+ # opts, so Session#build_approval wires it with the session's
390
+ # apply/reject/register callbacks — approval events stream exactly
391
+ # like the plain path.
392
+ def build_agent_session(entry)
393
+ queue = emitting_queue(entry)
394
+ opts = {
395
+ approval: approval_config(queue),
396
+ plan_mode: @plan_mode,
397
+ todos: @todos,
398
+ max_turns: @max_turns,
399
+ # Caller options win over the definition; a nil model lets the
400
+ # definition's own model apply.
401
+ model: (entry[:model] unless entry[:model] == @model_id)
402
+ }.compact
403
+ opts[:system_prompt] = entry[:system_prompt] if entry[:system_prompt]
404
+ Ask::Agent.new(entry[:agent], **opts)
405
+ end
377
406
 
378
- approval_cfg =
379
- case @approval
380
- when APPROVAL_REQUIRE then { queue: queue, require_approval: @approval_required }
381
- when APPROVAL_AUTO then { queue: queue }
382
- when APPROVAL_OFF then nil
383
- else
384
- raise ArgumentError, "approval must be one of #{APPROVAL_MODES.inspect}, got #{@approval.inspect}"
385
- end
407
+ # Build the default session (no declarative agent).
408
+ def build_plain_session(entry)
409
+ prompt = entry[:system_prompt] || @session_opts[:system_prompt]
410
+ chat = build_chat(entry[:model], prompt)
386
411
 
387
412
  Ask::Agent::Session.new(
388
413
  model: chat,
389
414
  tools: @tools,
390
415
  max_turns: @max_turns,
391
- approval: approval_cfg,
416
+ approval: approval_config(emitting_queue(entry)),
392
417
  plan_mode: @plan_mode,
393
418
  todos: @todos,
394
419
  **@session_opts
395
- ).tap do |session|
396
- session.on_event { |event| translate_event(entry, session, event) }
420
+ )
421
+ end
422
+
423
+ def emitting_queue(entry)
424
+ EmittingApprovalQueue.new(
425
+ on_submit: ->(a) { emit_approval(entry, a, :pending) },
426
+ on_status: ->(a) { emit_approval(entry, a, a.status) }
427
+ )
428
+ end
429
+
430
+ def approval_config(queue)
431
+ case @approval
432
+ when APPROVAL_REQUIRE then { queue: queue, require_approval: @approval_required }
433
+ when APPROVAL_AUTO then { queue: queue }
434
+ when APPROVAL_OFF then nil
435
+ else
436
+ raise ArgumentError, "approval must be one of #{APPROVAL_MODES.inspect}, got #{@approval.inspect}"
397
437
  end
398
438
  end
399
439
 
440
+
441
+
400
442
  def build_chat(model_id, system_prompt = nil)
401
443
  chat = Ask::Agent::Chat.new(
402
444
  model: model_id,
@@ -38,7 +38,7 @@ module Ask
38
38
  @started
39
39
  end
40
40
 
41
- def create_session(workspace_path, mode: nil)
41
+ def create_session(workspace_path, mode: nil, system_prompt: nil, **)
42
42
  ensure_started
43
43
  sid = "sess_#{SecureRandom.uuid}"
44
44
  @sessions[sid] = { workspace: workspace_path, mode: mode, created_at: Time.now }
@@ -42,7 +42,7 @@ module Ask
42
42
  @client&.running? || false
43
43
  end
44
44
 
45
- def create_session(workspace_path, mode: nil)
45
+ def create_session(workspace_path, mode: nil, system_prompt: nil, **)
46
46
  ensure_running
47
47
  thread = @client.thread_start(cwd: workspace_path)
48
48
  tid = thread["id"] || thread[:id]
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ask
4
4
  module CodingProviders
5
- VERSION = "0.3.1"
5
+ VERSION = "0.3.3"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ask-coding-providers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kaka Ruto
@@ -73,6 +73,8 @@ executables: []
73
73
  extensions: []
74
74
  extra_rdoc_files: []
75
75
  files:
76
+ - CHANGELOG.md
77
+ - LICENSE
76
78
  - README.md
77
79
  - lib/ask-coding-providers.rb
78
80
  - lib/ask/coding_providers.rb
@@ -110,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
110
112
  - !ruby/object:Gem::Version
111
113
  version: '0'
112
114
  requirements: []
113
- rubygems_version: 4.0.3
115
+ rubygems_version: 4.0.18
114
116
  specification_version: 4
115
117
  summary: Coding agent adapters for the ask-rb ecosystem
116
118
  test_files: []