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 +4 -4
- data/CHANGELOG.md +78 -0
- data/LICENSE +21 -0
- data/lib/ask/coding_providers/acp/adapter.rb +1 -1
- data/lib/ask/coding_providers/adapter.rb +1 -1
- data/lib/ask/coding_providers/ask_agent/adapter.rb +60 -18
- data/lib/ask/coding_providers/claude/adapter.rb +1 -1
- data/lib/ask/coding_providers/codex/adapter.rb +1 -1
- data/lib/ask/coding_providers/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 546ff8509486604af6cc4f8fa7060bb91797a147b77336de0c71f4ca340d9ebf
|
|
4
|
+
data.tar.gz: 544b15d771c6c03bef753c3ee349b755a2bf484b07a58e3cb034f6e812fd3848
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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
|
-
|
|
371
|
-
|
|
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
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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:
|
|
416
|
+
approval: approval_config(emitting_queue(entry)),
|
|
392
417
|
plan_mode: @plan_mode,
|
|
393
418
|
todos: @todos,
|
|
394
419
|
**@session_opts
|
|
395
|
-
)
|
|
396
|
-
|
|
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]
|
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.
|
|
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.
|
|
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: []
|