The project is in a healthy, maintained state
The contract between the ask session host and every client (terminal TUI, web console, bots, IDE) — plus the shared Client and Host. Defines the canonical session event vocabulary, the resolvable interaction types (approvals, plan proposals, user input), the client-to-host method surface, protocol versioning, and generated JSON Schema artifacts. No runtime, no transports — just the contract.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

~> 5.25
~> 3.1
~> 13.0
~> 0.22

Runtime

>= 0
 Project Readme

ask-session-protocol

The canonical wire protocol for ask agent sessions.

The contract between the ask session host (ask-app-server) and every client: terminal TUI, web console, bots, IDE integrations, CI scripts. One runtime, many thin clients — all speaking the same versioned vocabulary.

This gem is a definition, not an implementation. It contains no runtime, no transports, and no session logic. It is the single authoritative, versioned artifact for what a session event looks like on the wire and which methods a host exposes — so the host and every client evolve independently against the same contract.

Why this exists

The ask ecosystem previously translated Ask::Agent runtime events into three incompatible wire shapes (app-server events, harness SSE events, adapter events). ask-session-protocol replaces that drift with one canonical, versioned vocabulary, generated JSON Schema artifacts, and strict validation at the boundaries.

                    ┌───────────────────────────────────────────┐
                    │  ask-agent (session runtime)              │
                    └──────────────┬────────────────────────────┘
                                   │
              ┌────────────────────▼────────────────────┐
              │  ask-session-protocol  ← this gem       │
              │  events · interactions · methods        │
              │  versions · JSON Schema                 │
              └────────────────────┬────────────────────┘
                                   │  the only crossing artifact
              ┌────────────────────▼────────────────────┐
              │  ask-app-server (THE HOST)              │
              │  transports: in-process | stdio |       │
              │  unix socket | SSE                      │
              └───┬───────────┬───────────┬─────────────┘
            terminal TUI  web console   bots/IDE

The event envelope

Every event on the wire has the same shape:

{ "type": "model.streaming", "seq": 12, "payload": { "delta": "Hello" } }
  • type — one of the canonical dot-names below (additive growth only; clients must tolerate unknown types within a major protocol version)
  • seq — monotonically increasing per session; clients use it for ordering, dedup, and replay
  • payload — the event body, string-keyed, validated against the registry

Event vocabulary (19 types)

Category Events
Session lifecycle session.created, session.ended
Turn lifecycle turn.started, turn.completed, turn.failed, turn.aborted
Model output model.streaming, model.thinking
Tool execution tool.use, tool.delta, tool.result
Interactions approval.required ✱, approval.updated, plan.proposed ✱, plan.approved, plan.rejected
Session state todos.updated, file.changed
Errors error

✱ — interaction events are resolvable by id from any client through the interaction/* and plan/* methods. A terminal, web console, or bot resolves the same pending interaction; the host tombstones delivery so each subscriber sees it exactly once.

Method surface (21 methods)

Group Methods
Handshake ping, initialize
Session lifecycle session/create, session/list, session/resume, session/subscribe, session/events, session/send, session/abort, session/close
Artifacts session/artifacts, session/artifact/get
Interactions interaction/list, interaction/approve, interaction/reject, interaction/approve-all, interaction/reject-all, interaction/respond
Plan mode plan/approve, plan/reject
Workspace workspace/readState

Host → client: the session/event notification carries canonical event envelopes. Reverse requests interaction/requestPermission and interaction/requestUserInput are defined for interop with the external app-server protocol standard.

Versioning

  • Ask::SessionProtocol::PROTOCOL_VERSION — the wire version, negotiated in the initialize handshake. Bump the minor on additive changes (new events, new methods); bump the major on breaking changes.
  • Ask::SessionProtocol::VERSION — the gem version.

Validation

The registries validate everything at the boundary:

event = Ask::SessionProtocol::Events.event(
  type: "approval.required",
  seq: 3,
  payload: { "id" => "act_1", "toolName" => "bash", "args" => { "command" => "ls" } }
)
event.to_h # => { "type" => "approval.required", "seq" => 3, "payload" => { ... } }

Ask::SessionProtocol::Methods.validate_params!("session/send", { "sessionId" => "s1", "content" => "hi" })
# => true

Unknown payload fields are allowed (forward compatibility); missing required fields, wrong types, and out-of-enum values raise ArgumentError.

Payloads are string-keyed on the wire. Ruby callers may pass Symbol keys — they are normalized to Strings at the boundary, and a Symbol/String collision on the same name raises. Events.filter_snapshot consumes a replayed snapshot (session/subscribe, session/events): canonical entries become Events, host-internal or unknown entries are filtered out.

JSON Schema

docs/ask-session-protocol.schema.json (draft 2020-12) is generated from the registries with rake schema and committed, so non-Ruby clients (bots, IDEs, ) can validate against the contract without loading this gem.

Installation

gem "ask-session-protocol"

Development

bundle install
rake test        # minitest suite
rake schema      # regenerate docs/ask-session-protocol.schema.json

License

MIT