///|
/// The request format version this module writes and reads.
pub const RunRequestVersion : Int = 1
///|
/// One structured request for `openseek run --input-format json`: what to run
/// and on what. See `docs/run-result.md` for how it is framed on stdin.
pub(all) struct RunRequest {
/// The caller's name for this request, echoed in the result so a parent can
/// tell which request a result answers.
request_id : String?
/// The preset to run (`explore`, `review`, …) or `general`; `None` means
/// general. The command line's `--kind`, when given, must agree.
kind : String?
/// What the run works on: `{"task": String}` for a general run, the
/// preset's own input otherwise.
input : Json
/// The step ceiling the caller asks for (`limits.max_steps`). An explicit
/// `--max-steps` on the command line wins over it.
max_steps : Int?
} derive(Eq, Debug)
///|
/// Why a JSON value is not a request this module can run.
pub(all) suberror RunRequestError {
/// A request from a newer (or unknown) format version.
UnsupportedVersion(Int)
/// A field is missing or mistyped; names the field.
Malformed(String)
/// A field this engine recognizes but does not implement; names it. A
/// request is refused rather than run without what it asked for.
Unsupported(String)
} derive(Eq, Debug)
///|
pub impl ToJson for RunRequest with fn to_json(self) {
let fields : Map[String, Json] = {
"version": RunRequestVersion.to_json(),
"input": self.input,
}
if self.request_id is Some(id) {
fields["request_id"] = id.to_json()
}
if self.kind is Some(kind) {
fields["kind"] = kind.to_json()
}
if self.max_steps is Some(steps) {
fields["limits"] = { "max_steps": steps.to_json() }
}
Json::object(fields)
}
///|
/// Read a request back into a `RunRequest`: the inverse of `to_json`. Unknown
/// fields are ignored so that a newer caller can add optional ones; `schema`
/// is refused, because this engine cannot constrain a report to a caller's
/// schema and must not pretend to.
pub fn parse_run_request(json : Json) -> RunRequest raise RunRequestError {
guard json is Object(fields) else { raise Malformed("request") }
guard int(fields, "version") is Some(version) else {
raise Malformed("version")
}
guard version == RunRequestVersion else { raise UnsupportedVersion(version) }
if fields.contains("schema") {
raise Unsupported("schema")
}
fn optional_text(key) raise RunRequestError {
match fields.get(key) {
None => None
Some(String(value)) if !value.is_blank() => Some(value)
Some(_) => raise Malformed(key)
}
}
let request_id = optional_text("request_id")
let kind = optional_text("kind")
guard fields.get("input") is Some(input) else { raise Malformed("input") }
let max_steps = match fields.get("limits") {
None => None
Some(Object(limits)) =>
match limits.get("max_steps") {
None => None
Some(_) =>
match int(limits, "max_steps") {
Some(steps) if steps >= 1 => Some(steps)
_ => raise Malformed("limits.max_steps")
}
}
Some(_) => raise Malformed("limits")
}
{ request_id, kind, input, max_steps, }
}
///|
pub extend RunRequest with Debug::{to_repr}
///|
pub extend RunRequest with Eq::{equal, not_equal}
///|
pub extend RunRequest with ToJson::{to_json}
///|
pub extend RunRequestError with Debug::{to_repr}
///|
pub extend RunRequestError with Eq::{equal, not_equal}