Skip to content
Kward Search API index

Class: Kward::RPC::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/kward/rpc/server.rb

Overview

Experimental JSON-RPC backend for UI clients.

The server speaks LSP-style Content-Length framing over stdin/stdout, exposes capabilities during initialize, redacts secrets in errors and notifications, and coordinates auth, config, sessions, turns, tools, memory, commands, and startup resources.

Server should stay focused on protocol concerns: framing, JSON-RPC error codes, method dispatch, capability reporting, and redaction at the wire boundary. Delegate stateful product behavior to manager objects such as SessionManager, AuthManager, and ConfigManager. When adding an RPC feature, update dispatch, capabilities, docs, and tests together so clients can trust initialize as the source of supported behavior.

Constant Summary collapse

PROTOCOL_VERSION =
1
JSONRPC_VERSION =
"2.0"
BUILTIN_SLASH_COMMAND_NAMES =
PromptCommands::BUILTIN_RESERVED_COMMAND_NAMES
ERROR_CODES =
{
  parse_error: -32_700,
  invalid_request: -32_600,
  method_not_found: -32_601,
  invalid_params: -32_602,
  internal_error: -32_603
}.freeze
PROTOCOL_METHODS =
["initialize", "shutdown"].freeze
WORKSPACE_METHODS =
["workspace/validate", "workspace/info"].freeze
TOOL_METHODS =
["tools/list"].freeze
PROMPT_METHODS =
["prompts/list", "prompts/expand"].freeze
SESSION_METHODS =
[
  "sessions/create", "sessions/resume", "sessions/list", "sessions/rename",
  "sessions/clone", "sessions/compact", "sessions/forkMessages", "sessions/fork",
  "sessions/tree", "sessions/tree/setLabel", "sessions/tree/navigate",
  "sessions/export", "sessions/delete", "sessions/close", "sessions/transcript"
].freeze
TURN_METHODS =
["turns/start", "turns/cancel", "turns/status", "turns/events"].freeze
MODEL_METHODS =
["models/list", "models/current", "models/set", "reasoning/set"].freeze
RUNTIME_METHODS =
["runtime/state", "runtime/stats"].freeze
RUNTIME_SETTING_METHODS =
["runtime/updateSetting", "runtime/reload"].freeze
AUTH_METHODS =
[
  "auth/status", "auth/providers", "auth/loginWithApiKey", "auth/logoutProvider",
  "auth/loginWithOAuth", "auth/startOpenAILogin", "auth/submitOpenAICode", "auth/loginStatus"
].freeze
MEMORY_METHODS =
[
  "memory/status", "memory/enable", "memory/disable", "memory/autoSummary/enable",
  "memory/autoSummary/disable", "memory/list", "memory/add", "memory/addCore",
  "memory/forget", "memory/promote", "memory/relax", "memory/inspect",
  "memory/why", "memory/summarize"
].freeze
WORKER_METHODS =
["workers/list", "workers/show"].freeze
COMMAND_METHODS =
["commands/list", "commands/run"].freeze
STARTUP_RESOURCE_METHODS =
["resources/startup"].freeze
CONFIG_METHODS =
["config/read", "config/update"].freeze
LOGGING_METHODS =
["logging/stats", "logging/tokenCsv"].freeze
UI_METHODS =
["ui/answerQuestion"].freeze
SESSION_EVENT_NOTIFICATION =
"session/event"
SESSION_UPDATED_NOTIFICATION =
"session/updated"
TURN_EVENT_NOTIFICATION =
"turn/event"
UI_QUESTION_NOTIFICATION =
"ui/question"
"ui/footer"
METHOD_GROUPS =
{
  protocol: PROTOCOL_METHODS,
  workspace: WORKSPACE_METHODS,
  tools: TOOL_METHODS,
  prompts: PROMPT_METHODS,
  sessions: SESSION_METHODS,
  turns: TURN_METHODS,
  models: MODEL_METHODS,
  runtime: RUNTIME_METHODS,
  runtime_settings: RUNTIME_SETTING_METHODS,
  auth: AUTH_METHODS,
  memory: MEMORY_METHODS,
  workers: WORKER_METHODS,
  commands: COMMAND_METHODS,
  startup_resources: STARTUP_RESOURCE_METHODS,
  config: CONFIG_METHODS,
  logging: LOGGING_METHODS,
  ui: UI_METHODS
}.freeze
RPC_METHODS =
METHOD_GROUPS.values.flatten.freeze

Instance Method Summary collapse

Constructor Details

#initialize(input: $stdin, output: $stdout, error_output: $stderr, client: Client.new, experimental_workers: false) ⇒ Server

Creates the RPC server and its stateful managers.



104
105
106
107
108
109
110
111
112
113
114
# File 'lib/kward/rpc/server.rb', line 104

def initialize(input: $stdin, output: $stdout, error_output: $stderr, client: Client.new, experimental_workers: false)
  @transport = Transport.new(input: input, output: output)
  @error_output = error_output
  @client = client
  @config_manager = ConfigManager.new
  @session_manager = SessionManager.new(server: self, client: client, config_manager: @config_manager)
  @auth_manager = AuthManager.new(server: self, config_manager: @config_manager)
  @worker_store = Workers::Store.new
  @experimental_workers = experimental_workers
  @shutdown = false
end

Instance Method Details

#error_payload(error) ⇒ Hash

Builds redacted diagnostics suitable for JSON-RPC error data.

Parameters:

  • error (Exception)

Returns:

  • (Hash)


148
149
150
151
152
153
154
# File 'lib/kward/rpc/server.rb', line 148

def error_payload(error)
  Redactor.redact({
    code: error.class.name,
    message: error.message,
    backtrace: Array(error.backtrace).first(8)
  })
end

#log_error(error) ⇒ Object



156
157
158
# File 'lib/kward/rpc/server.rb', line 156

def log_error(error)
  @error_output.puts("Kward RPC error: #{Redactor.redact_string(error.message)}") if @error_output
end

#notify(method, params = {}) ⇒ Object

Sends a redacted JSON-RPC notification to the client.

Parameters:

  • method (String)

    notification method name

  • params (Hash) (defaults to: {})

    notification params



140
141
142
# File 'lib/kward/rpc/server.rb', line 140

def notify(method, params = {})
  @transport.write_message({ jsonrpc: JSONRPC_VERSION, method: method, params: Redactor.redact(params) })
end

#runvoid

This method returns an undefined value.

Reads framed JSON-RPC messages until shutdown or EOF.



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/kward/rpc/server.rb', line 119

def run
  until @shutdown
    begin
      message = @transport.read_message
      break unless message

      handle_message(message)
    rescue JSON::ParserError => e
      write_error(nil, ERROR_CODES[:parse_error], "Parse error", e)
    rescue StandardError => e
      write_error(nil, ERROR_CODES[:invalid_request], e.message, e)
    end
  end
ensure
  @session_manager.shutdown_sessions
end