Class: Insika::Server::App
- Inherits:
-
Object
- Object
- Insika::Server::App
- Defined in:
- lib/insika/server/app.rb
Overview
Rack app. Transports ONLY
translate requests into Commands — the server holds no business
logic. It parses JSON, builds Command.build(...), dispatches on the
CommandBus and projects the Event Stream to SSE. Reads are NOT Commands: they are
direct reads from the stores.
AUDITABLE constitutional rule: server/ does not import the Executor,
store WRITE methods, or RubyLLM. Requires: json, rack, async and the
core types (Command/Event/errors) already loaded by the composition root.
Constant Summary collapse
- SSE_HEADERS =
{ "content-type" => "text/event-stream", "cache-control" => "no-cache", "connection" => "keep-alive" }.freeze
Instance Method Summary collapse
-
#call(env) ⇒ Object
Explicit routing, NO framework: ~10 routes in a
case. -
#initialize(command_bus:, event_stream:, session_store:, task_store:, config:, pending_action_store: nil, a2a: nil, provisioner: nil, workflow_registry: nil, onboarding: nil, profiles: nil, channels: nil) ⇒ App
constructor
The operator control UI now lives in the Studio (§12 G5); server/ is a pure transport surface (/v1, /a2a).
Constructor Details
#initialize(command_bus:, event_stream:, session_store:, task_store:, config:, pending_action_store: nil, a2a: nil, provisioner: nil, workflow_registry: nil, onboarding: nil, profiles: nil, channels: nil) ⇒ App
The operator control UI now lives in the Studio (§12 G5); server/ is a pure transport surface (/v1, /a2a). The constitutional rule holds: server/ only READS stores and never imports the Executor, store writes, or RubyLLM.
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 |
# File 'lib/insika/server/app.rb', line 47 def initialize(command_bus:, event_stream:, session_store:, task_store:, config:, pending_action_store: nil, a2a: nil, provisioner: nil, workflow_registry: nil, onboarding: nil, profiles: nil, channels: nil) @command_bus = command_bus @event_stream = event_stream @session_store = session_store @task_store = task_store @config = config @pending_action_store = pending_action_store # read for GET /v1/tasks/:id @a2a = a2a # A2A edge. nil = server does not expose A2A (parity). @provisioner = provisioner # PackImporter. nil = provisioning not exposed. # Item 22 / §4.4: READ-ONLY registry, injected only where workflows are # exposed (the minimal wiring). nil = no /v1/workflows routes (parity — the # deployment does not expose workflows). Reading a catalog is a READ, like a # store read: the constitutional rule (no Executor/store-writes/RubyLLM) holds. @workflow_registry = workflow_registry # Item 20 / §5.6: LLM-first onboarding surface (start.md + models.json + # docs). PUBLIC (no auth — the whole point of the "read <base>/start.md" trick # is that the developer's coding agent can fetch it), and READ-ONLY, so the # constitutional rule holds. nil = routes not exposed (parity — the production # deployment opts in). Reading files/masked stores is a READ, like a store read. @onboarding = onboarding # RFC-0014 §3.2: READ-ONLY ProfileSource, so `GET /v1/agents/:id` can answer # what an agent has — the eval is a client and cannot read a store. Same # constitutional footing as the workflow registry: reading a catalog is a # READ. nil = the route 404s (parity). @profiles = profiles # RFC-0011 §4.4: ONE generic route family for every channel, opt-in by # injecting the registry (nil ⇒ the routes do not exist, parity with @a2a). # The channel does the translating; this class keeps doing only transport. @channels = channels @heartbeat = config.fetch(:heartbeat, 15) @sync_timeout = config.fetch(:sync_timeout, 10) # synchronous control end |
Instance Method Details
#call(env) ⇒ Object
Explicit routing, NO framework: ~10 routes in a case. A single
rescue centralizes the error->status mapping. Only SYNCHRONOUS
errors (before the fiber) become HTTP status; a task failure travels as
an event on the stream and lands in GET /v1/tasks/:id.
87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/insika/server/app.rb', line 87 def call(env) req = Rack::Request.new(env) route(req) rescue JSON::ParserError => e error_response(400, e) # malformed JSON, before any dispatch rescue Insika::ValidationError => e error_response(422, e) rescue Insika::NotFoundError => e error_response(404, e) rescue Async::TimeoutError => e error_response(504, e) # synchronous control request exceeded the ceiling rescue StandardError => e error_response(500, e) end |