Module: Ask::SessionProtocol::Interactions
- Defined in:
- lib/ask/session_protocol/interactions.rb
Overview
Resolvable interactions — the human-in-the-loop surface of the protocol.
An interaction is created by the host (emitted as an interaction event: approval.required, plan.proposed) with a unique id. Any client may resolve it by id through the interaction/* and plan/* methods; the host routes the resolution to the session and tombstones delivery so each subscriber sees it exactly once.
approval.required ──▶ interaction/approve | interaction/reject
plan.proposed ──▶ plan/approve | plan/reject
user input ──▶ interaction/respond (host → client request)
Defined Under Namespace
Classes: Interaction
Constant Summary collapse
- KINDS =
The interaction kinds.
%w[approval plan user_input].freeze
- STATUSES =
Resolution statuses. A resolved interaction never returns to pending.
%w[pending approved rejected responded expired].freeze
- APPROVAL_PAYLOAD =
Payload shape for kind "approval" (from the approval.required event).
{ "toolName" => { type: :string, required: true }, "args" => { type: :any, required: false }, "message" => { type: :string, required: false }, "autoApprovable" => { type: :boolean, required: false } }.freeze
- PLAN_PAYLOAD =
Payload shape for kind "plan" (from the plan.proposed event).
{ "plan" => { type: :string, required: true } }.freeze
- USER_INPUT_PAYLOAD =
Payload shape for kind "user_input" (host → client request asking the human for input; resolve via interaction/respond).
{ "prompt" => { type: :string, required: true, description: "What the agent is asking the human." }, "options" => { type: :array, required: false, description: "Suggested answers, when the host offers any." } }.freeze
- PAYLOADS =
Payload spec per kind. The single source of truth for interaction payload validation and schema generation.
{ "approval" => APPROVAL_PAYLOAD, "plan" => PLAN_PAYLOAD, "user_input" => USER_INPUT_PAYLOAD }.freeze
Class Method Summary collapse
-
.from_h(hash) ⇒ Interaction
Rebuild an interaction from its wire shape.
-
.interaction(id:, kind:, status: "pending", payload: {}) ⇒ Interaction
Build an interaction, validating the payload against its kind.
-
.validate_payload!(kind, payload) ⇒ true
Validate a payload against its kind spec.
Class Method Details
.from_h(hash) ⇒ Interaction
Rebuild an interaction from its wire shape.
103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/ask/session_protocol/interactions.rb', line 103 def self.from_h(hash) hash = hash.transform_keys(&:to_s) raise ArgumentError, "interaction must be a Hash" unless hash.is_a?(Hash) interaction( id: hash["id"], kind: hash["kind"], status: hash["status"] || "pending", payload: hash["payload"] || {} ) end |
.interaction(id:, kind:, status: "pending", payload: {}) ⇒ Interaction
Build an interaction, validating the payload against its kind.
94 95 96 97 |
# File 'lib/ask/session_protocol/interactions.rb', line 94 def self.interaction(id:, kind:, status: "pending", payload: {}) validate_payload!(kind, payload) Interaction.new(id: id, kind: kind, status: status, payload: payload) end |
.validate_payload!(kind, payload) ⇒ true
Validate a payload against its kind spec. Unknown extra fields are allowed (forward compatibility).
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/ask/session_protocol/interactions.rb', line 121 def self.validate_payload!(kind, payload) spec = PAYLOADS[kind] raise ArgumentError, "unknown interaction kind: #{kind.inspect}" unless spec raise ArgumentError, "payload for #{kind} must be a Hash" unless payload.is_a?(Hash) spec.each do |field, field_spec| value = payload[field] if field_spec[:required] && value.nil? raise ArgumentError, "interaction #{kind} missing required payload field #{field.inspect}" end next if value.nil? Events.validate_field!(kind, field, field_spec, value) end true end |