Class: Ask::AppServer::Server
- Inherits:
-
Object
- Object
- Ask::AppServer::Server
- Defined in:
- lib/ask/app_server/server.rb
Overview
JSON-RPC server implementing the ZCode/Codex app-server protocol over stdio.
Communicates via NDJSON (Newline-Delimited JSON) over stdin/stdout. Supports session management, streaming events, and mid-execution injection.
Protocol methods:
session/create, session/list, session/resume, session/subscribe,
session/send, session/events, session/abort, workspace/readState
Notifications (server → client):
session/event, interaction/requestPermission, interaction/requestUserInput
The server also handles incoming responses to its outgoing requests (e.g., client responses to interaction/requestPermission).
Instance Method Summary collapse
-
#initialize(session_manager: nil) ⇒ Server
constructor
A new instance of Server.
-
#register_permission_handler(handler) ⇒ Object
Register a PermissionHandler so the server can wire its protocol sender.
-
#running? ⇒ Boolean
Whether the server is running.
-
#send_request(method, params, &block) ⇒ Object
Send an outgoing JSON-RPC request to the client.
-
#start ⇒ Object
Start the server — reads from stdin in a background thread.
-
#stop ⇒ Object
Stop the server.
Constructor Details
#initialize(session_manager: nil) ⇒ Server
Returns a new instance of Server.
22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/ask/app_server/server.rb', line 22 def initialize(session_manager: nil) @session_manager = session_manager || SessionManager.new @running = false @started_at = nil @input_queue = Queue.new @response_handlers = {} # outgoing request_id => Proc @outgoing_id = 0 @logger = Logger.new($stdout, level: ENV["DEBUG"] ? Logger::DEBUG : Logger::WARN) # Register the protocol method handlers @handlers = {} register_default_handlers end |
Instance Method Details
#register_permission_handler(handler) ⇒ Object
Register a PermissionHandler so the server can wire its protocol sender. The server will set up the handler's on_request callback to send interaction/requestPermission messages and route responses back.
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/ask/app_server/server.rb', line 109 def (handler) handler.on_request do |request_id, tool_name, arguments| send_request("interaction/requestPermission", { requestId: request_id, toolName: tool_name, input: arguments, riskLevel: blocked_tool_risk_level(tool_name), reason: "Tool '#{tool_name}' requires approval" }) do |result, error| if result decision = result["decision"] || result[:decision] || "deny" handler.handle_response(request_id, decision) else handler.handle_response(request_id, "deny", reason: error&.dig("message")) end end end end |
#running? ⇒ Boolean
Whether the server is running.
92 93 94 |
# File 'lib/ask/app_server/server.rb', line 92 def running? @running end |
#send_request(method, params, &block) ⇒ Object
Send an outgoing JSON-RPC request to the client. If a block is given, it will be called with (result, error) when the client responds.
99 100 101 102 103 104 |
# File 'lib/ask/app_server/server.rb', line 99 def send_request(method, params, &block) id = next_outgoing_id @response_handlers[id] = block if block write_line({ id: id, method: method, params: params }) id end |
#start ⇒ Object
Start the server — reads from stdin in a background thread. The main thread handles event pushing and shutdown.
38 39 40 41 42 43 44 45 46 47 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 |
# File 'lib/ask/app_server/server.rb', line 38 def start @running = true @started_at = Time.now # Reader thread: reads NDJSON lines from stdin @reader = Thread.new do while @running begin line = $stdin.gets break unless line line = line.strip next if line.empty? @input_queue << JSON.parse(line) rescue JSON::ParserError => e send_error(nil, -32700, "Parse error: #{e.}") rescue => e @logger.error("Reader error: #{e.}") break end end @input_queue << nil # signal shutdown end # Event push thread: polls subscribed sessions and pushes notifications @pusher = Thread.new do while @running push_session_events sleep 0.1 # 100ms poll interval end end # Main loop: processes incoming messages while @running msg = @input_queue.pop break if msg.nil? (msg) end rescue => e @logger.error("Server error: #{e.}") raise ensure @running = false @reader&.kill rescue nil @pusher&.kill rescue nil end |
#stop ⇒ Object
Stop the server.
87 88 89 |
# File 'lib/ask/app_server/server.rb', line 87 def stop @running = false end |