Class: Ask::CodingHarness::DemoAdapter
- Inherits:
-
Ask::CodingProviders::Adapter
- Object
- Ask::CodingProviders::Adapter
- Ask::CodingHarness::DemoAdapter
- Defined in:
- lib/ask/coding_harness/demo_adapter.rb
Overview
A scripted coding agent for trying the harness without API keys.
Walks through a realistic turn — todos, tool calls with output, a diff, and a tool call queued for human approval — waiting on the approval queue exactly like the real ask_agent adapter. The text and file contents are generated from the workspace it is pointed at, so every run feels like the real thing.
ach demo # serve the web UI against the demo agent
Class Method Summary collapse
- .from_config(**config) ⇒ Object
-
.install! ⇒ Object
── Registration ──.
Instance Method Summary collapse
- #abort(_session_id) ⇒ Object
-
#approve_action(_session_id, action_id) ⇒ Object
── Approval controls (same duck-typed surface as ask_agent) ──.
- #approve_all(_session_id) ⇒ Object
- #create_session(workspace_path, mode: nil, model: nil, system_prompt: nil) ⇒ Object
-
#initialize(approval: :require) ⇒ DemoAdapter
constructor
A new instance of DemoAdapter.
- #pending_approvals(_session_id) ⇒ Object
- #reject_action(_session_id, action_id) ⇒ Object
- #send_and_stream(session_id, content, turn_timeout: 600.0, &block) ⇒ Object
- #start ⇒ Object
- #stop ⇒ Object
Constructor Details
#initialize(approval: :require) ⇒ DemoAdapter
Returns a new instance of DemoAdapter.
23 24 25 26 27 28 29 30 31 |
# File 'lib/ask/coding_harness/demo_adapter.rb', line 23 def initialize(approval: :require, **) @approval = approval.to_sym @pending = {} @next_action_id = 1 @mutex = Mutex.new @cv = ConditionVariable.new @aborted = false @sessions = {} end |
Class Method Details
.from_config(**config) ⇒ Object
33 34 35 |
# File 'lib/ask/coding_harness/demo_adapter.rb', line 33 def self.from_config(**config) new(**config) end |
.install! ⇒ Object
── Registration ──
183 184 185 186 187 188 189 190 |
# File 'lib/ask/coding_harness/demo_adapter.rb', line 183 def self.install! existing = begin Ask::CodingProviders.resolve_adapter(:demo) rescue Ask::CodingProviders::ConfigurationError nil end Ask::CodingProviders.register_adapter(:demo, self) unless existing end |
Instance Method Details
#abort(_session_id) ⇒ Object
177 178 179 |
# File 'lib/ask/coding_harness/demo_adapter.rb', line 177 def abort(_session_id) @mutex.synchronize { @aborted = true; @cv.broadcast } end |
#approve_action(_session_id, action_id) ⇒ Object
── Approval controls (same duck-typed surface as ask_agent) ──
146 147 148 149 150 151 152 153 |
# File 'lib/ask/coding_harness/demo_adapter.rb', line 146 def approve_action(_session_id, action_id) @mutex.synchronize do action = @pending[action_id] next [] unless action @pending[action_id] = action.merge("status" => "approved") @cv.broadcast end end |
#approve_all(_session_id) ⇒ Object
164 165 166 167 168 169 |
# File 'lib/ask/coding_harness/demo_adapter.rb', line 164 def approve_all(_session_id) @mutex.synchronize do @pending.each_value { |a| a["status"] = "approved" if a["status"] == "pending" } @cv.broadcast end end |
#create_session(workspace_path, mode: nil, model: nil, system_prompt: nil) ⇒ Object
42 43 44 45 46 |
# File 'lib/ask/coding_harness/demo_adapter.rb', line 42 def create_session(workspace_path, mode: nil, model: nil, system_prompt: nil, **) sid = "demo_#{@sessions.size + 1}" @sessions[sid] = workspace_path sid end |
#pending_approvals(_session_id) ⇒ Object
171 172 173 174 175 |
# File 'lib/ask/coding_harness/demo_adapter.rb', line 171 def pending_approvals(_session_id) @mutex.synchronize do @pending.values.select { |a| a["status"] == "pending" } end end |
#reject_action(_session_id, action_id) ⇒ Object
155 156 157 158 159 160 161 162 |
# File 'lib/ask/coding_harness/demo_adapter.rb', line 155 def reject_action(_session_id, action_id) @mutex.synchronize do action = @pending[action_id] next [] unless action @pending[action_id] = action.merge("status" => "rejected") @cv.broadcast end end |
#send_and_stream(session_id, content, turn_timeout: 600.0, &block) ⇒ Object
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/ask/coding_harness/demo_adapter.rb', line 48 def send_and_stream(session_id, content, turn_timeout: 600.0, &block) workspace = @sessions[session_id] || Dir.pwd emit(block, "turn.started", {}) emit(block, "model.streaming", { "delta" => "Alright, let me take a look at #{File.basename(workspace)}. " }) emit(block, "model.streaming", { "delta" => "I'll start by mapping the project structure.\n\n" }) emit(block, "todos.updated", { "todos" => [ { "id" => "1", "title" => "Inspect the project structure", "status" => "in_progress" }, { "id" => "2", "title" => "Find the main entry point", "status" => "pending" }, { "id" => "3", "title" => "Propose improvements", "status" => "pending" } ] }) files = list_files(workspace) emit(block, "tool.use", { "toolName" => "bash", "toolCallId" => "call_1", "input" => { "command" => "find . -type f | head -30" } }) emit(block, "tool.result", { "toolName" => "bash", "toolCallId" => "call_1", "output" => files.join("\n"), "isError" => false, "durationMs" => 84 }) readme = read_first(workspace, %w[README.md readme.md]) if readme emit(block, "tool.use", { "toolName" => "read", "toolCallId" => "call_2", "input" => { "path" => readme[0] } }) emit(block, "tool.result", { "toolName" => "read", "toolCallId" => "call_2", "output" => readme[1], "isError" => false, "durationMs" => 12 }) end emit(block, "todos.updated", { "todos" => [ { "id" => "1", "title" => "Inspect the project structure", "status" => "completed" }, { "id" => "2", "title" => "Find the main entry point", "status" => "completed" }, { "id" => "3", "title" => "Propose improvements", "status" => "in_progress" } ] }) emit(block, "model.streaming", { "delta" => "I can see the project has #{files.size} files. Here's a diff I'd like to apply — it needs your approval first:\n\n" }) emit(block, "tool.use", { "toolName" => "apply_patch", "toolCallId" => "call_3", "input" => { "patch" => "diff --git a/README.md b/README.md\nindex 8f1a2b3..c9d4e5f 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,3 +1,4 @@\n # #{File.basename(workspace)}\n \n+Generated by the ask-coding-harness demo agent.\n" } }) # Queue an approval — the demo pauses here until the user decides # (or auto-approves in headless mode). action_id = queue_action(block, "apply_patch", { "patch" => "…" }, "Applying a patch to README.md requires approval") outcome = if %i[off auto].include?(@approval) @mutex.synchronize do @pending[action_id] = @pending[action_id].merge("status" => "approved") end :approved else wait_for_decision(action_id) end case outcome when :approved emit(block, "approval.updated", { "actionId" => action_id, "status" => "approved" }) emit(block, "tool.result", { "toolName" => "apply_patch", "toolCallId" => "call_3", "output" => "Patch applied to README.md", "isError" => false, "durationMs" => 5 }) emit(block, "model.streaming", { "delta" => "Done — README.md now documents that this workspace was demoed by the harness. " \ "Try asking me to do something real: point the harness at a project, add an API key, " \ "and I'll work through it with tools, approvals, and diffs just like this.\n\n" \ "Things to try: run tests, refactor a file, explain an error.\n" }) emit(block, "turn.completed", { "response" => "Demo turn completed." }) when :rejected emit(block, "approval.updated", { "actionId" => action_id, "status" => "rejected" }) emit(block, "model.streaming", { "delta" => "Understood — I won't touch anything. That's the approval flow working: " \ "nothing mutates without your say-so.\n" }) emit(block, "turn.completed", { "response" => "Demo turn completed (rejected)." }) when :aborted emit(block, "turn.aborted", {}) end end |
#start ⇒ Object
37 |
# File 'lib/ask/coding_harness/demo_adapter.rb', line 37 def start; end |
#stop ⇒ Object
38 39 40 |
# File 'lib/ask/coding_harness/demo_adapter.rb', line 38 def stop @mutex.synchronize { @aborted = true; @cv.broadcast } end |