harn-stdlib 0.8.34

Embedded Harn standard library source catalog
Documentation
/**
 * std/agent/resume_by — explicit resume-responsibility callbacks (S-14, harn#1864).
 *
 * Replaces the legacy "infer resume responsibility from `conditions`"
 * dispatch with a callback-first surface (per epic #1853). Each preset
 * is a pure closure with signature `(harness, suspension) -> dict` so it
 * composes cleanly with every `std/lifecycle/combinators` helper.
 *
 * Import:
 *   import { ResumeBy, default_resume_by } from "std/agent/resume_by"
 *
 * Conventions:
 *   - Callbacks return a result dict shaped
 *     `{handled: bool, mechanism: string, reason?: string}`. The
 *     `mechanism` string is propagated to the suspension envelope as
 *     `resume_by_mechanism` so transcripts, replay, and audits all
 *     reference the same canonical name.
 *   - `handled: false` means the callback declined (e.g.
 *     `ResumeBy.cloud_harness` with no cloud session); composed chains
 *     such as `first_available(...)` continue to the next entry.
 *   - The runtime emits a `resume_by_dispatched` audit through
 *     `harness.emit_audit` on every handled invocation so behaviour is
 *     observable from `lifecycle_audit_log_take()`.
 *
 * Tracking: harn#1864 (S-14), harn#1836 (epic), harn#1853 (callback-first).
 */
/**
 * first_handled walks `callbacks` in order with `(harness, suspension)`
 * and returns the first result whose `handled` field is `true`. This is
 * a thin specialization of `std/lifecycle/combinators::first_available`
 * tuned for `ResumeBy.*` callbacks, where declined entries (e.g.
 * `cloud_harness` with no cloud session) still return a non-nil
 * `{handled: false, reason: ...}` dict for audit visibility. The
 * combinator preserves the decline result as the chain's return value
 * when every entry declines so callers can observe the last reason.
 *
 * @effects: []
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: first_handled([ResumeBy().cloud_harness, ResumeBy().local_runtime])
 */
pub fn first_handled(callbacks) {
  return { harness, suspension ->
    if type_of(callbacks) != "list" || len(callbacks) == 0 {
      return nil
    }
    var last = nil
    for cb in callbacks {
      let result = cb(harness, suspension)
      if result != nil && result?.handled {
        return result
      }
      last = result
    }
    return last
  }
}

fn __resume_by_payload(suspension, mechanism, extra) {
  let base = {mechanism: mechanism, handle: suspension?.handle, reason: suspension?.reason}
  if extra == nil {
    return base
  }
  return base + extra
}

fn __resume_by_emit(harness, suspension, mechanism, extra = nil) {
  if harness == nil {
    return
  }
  harness.emit_audit("resume_by_dispatched", __resume_by_payload(suspension, mechanism, extra))
}

fn __resume_by_emit_declined(harness, suspension, mechanism, reason) {
  if harness == nil {
    return
  }
  harness
    .emit_audit("resume_by_declined", __resume_by_payload(suspension, mechanism, {reason: reason}))
}

fn __resume_by_has_conditions(suspension) {
  let conditions = suspension?.conditions
  if conditions == nil {
    return false
  }
  if type_of(conditions) == "dict" {
    return len(keys(conditions)) > 0
  }
  return true
}

fn __resume_by_cloud_session(harness) {
  if harness == nil {
    return nil
  }
  let session = try {
    harness.cloud_session_id()
  }
  if is_err(session) {
    return nil
  }
  return session
}

/**
 * resume_by_parent_llm dispatches the suspension to the parent agent's
 * transcript so the parent's LLM is responsible for resuming the child.
 * Always returns `{handled: true, mechanism: "ResumeBy.parent_llm"}` —
 * use it as the terminal entry in a `first_available(...)` chain.
 *
 * Emits a `resume_by_dispatched` audit with the suspension handle and
 * reason; downstream wiring to actually inject the parent-side reminder
 * lands when the harness exposes `emit_to_parent_transcript`
 * (tracked alongside harn#1853).
 *
 * @effects: [host]
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: resume_by_parent_llm(harness, suspension)
 */
pub fn resume_by_parent_llm(harness, suspension) {
  __resume_by_emit(harness, suspension, "ResumeBy.parent_llm")
  return {handled: true, mechanism: "ResumeBy.parent_llm"}
}

/**
 * resume_by_local_runtime registers the suspension's resume conditions
 * with the in-process trigger dispatcher (#152). Declines with
 * `{handled: false, reason: "no_conditions"}` when no conditions are
 * present — the parent LLM must resume in that case.
 *
 * @effects: [host]
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: resume_by_local_runtime(harness, suspension)
 */
pub fn resume_by_local_runtime(harness, suspension) {
  if !__resume_by_has_conditions(suspension) {
    __resume_by_emit_declined(harness, suspension, "ResumeBy.local_runtime", "no_conditions")
    return {handled: false, mechanism: "ResumeBy.local_runtime", reason: "no_conditions"}
  }
  __resume_by_emit(
    harness,
    suspension,
    "ResumeBy.local_runtime",
    {conditions: suspension?.conditions},
  )
  return {handled: true, mechanism: "ResumeBy.local_runtime"}
}

/**
 * resume_by_cloud_harness registers the suspension with the harn-cloud
 * webhook receiver for durability across process restarts. Declines
 * with `{handled: false, reason: "no_cloud_session"}` when no cloud
 * session is bound — the local runtime or parent LLM must resume.
 *
 * @effects: [host]
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: resume_by_cloud_harness(harness, suspension)
 */
pub fn resume_by_cloud_harness(harness, suspension) {
  let session = __resume_by_cloud_session(harness)
  if session == nil {
    __resume_by_emit_declined(harness, suspension, "ResumeBy.cloud_harness", "no_cloud_session")
    return {handled: false, mechanism: "ResumeBy.cloud_harness", reason: "no_cloud_session"}
  }
  __resume_by_emit(
    harness,
    suspension,
    "ResumeBy.cloud_harness",
    {cloud_session: session, conditions: suspension?.conditions},
  )
  return {handled: true, mechanism: "ResumeBy.cloud_harness"}
}

/**
 * resume_by_pipeline_drain defers the suspension to the enclosing
 * pipeline's drain step (#1853). Always returns
 * `{handled: true, mechanism: "ResumeBy.pipeline_drain"}` so the
 * drain step owns the resume responsibility from there on.
 *
 * @effects: [host]
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: resume_by_pipeline_drain(harness, suspension)
 */
pub fn resume_by_pipeline_drain(harness, suspension) {
  __resume_by_emit(harness, suspension, "ResumeBy.pipeline_drain")
  return {handled: true, mechanism: "ResumeBy.pipeline_drain"}
}

/**
 * ResumeBy bundles the four canonical resume-responsibility callbacks
 * so callers can spell access as `ResumeBy().parent_llm`,
 * `ResumeBy().local_runtime`, `ResumeBy().cloud_harness`, or
 * `ResumeBy().pipeline_drain`. Each entry is a `(harness, suspension)`
 * closure, so they compose with `first_available`, `compose`,
 * `with_telemetry`, and the rest of `std/lifecycle/combinators`.
 *
 * Convention: bind the namespace once at the top of a pipeline
 * (`let ResumeBy = ResumeBy()`) and reference fields by dotted access
 * — the same shape as `Backpressure()` in `std/lifecycle/pool`.
 *
 * @effects: []
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: let ResumeBy = ResumeBy()
 */
pub fn ResumeBy() {
  return {
    parent_llm: resume_by_parent_llm,
    local_runtime: resume_by_local_runtime,
    cloud_harness: resume_by_cloud_harness,
    pipeline_drain: resume_by_pipeline_drain,
  }
}

/**
 * default_resume_by picks a sensible callback when the caller passes
 * `resume_by = nil` to `agent_await_resumption`. Mirrors the spec:
 *
 *   - `conditions == nil` → `ResumeBy.parent_llm`
 *   - `conditions != nil`, no cloud session → `ResumeBy.local_runtime`
 *   - `conditions != nil`, cloud session → `first_available([
 *       ResumeBy.cloud_harness, ResumeBy.local_runtime,
 *     ])`
 *
 * Callers can override the default by passing any `(harness,
 * suspension) -> dict` callback (often a `first_available` chain).
 *
 * @effects: [host]
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: default_resume_by({conditions: cond, harness: harness})
 */
pub fn default_resume_by(opts = nil) {
  let conditions = opts?.conditions
  let harness = opts?.harness
  let has_conditions = if conditions == nil {
    false
  } else if type_of(conditions) == "dict" {
    len(keys(conditions)) > 0
  } else {
    true
  }
  if !has_conditions {
    return resume_by_parent_llm
  }
  let cloud_session = __resume_by_cloud_session(harness)
  if cloud_session == nil {
    return resume_by_local_runtime
  }
  return first_handled([resume_by_cloud_harness, resume_by_local_runtime])
}

/**
 * invoke_resume_by runs `callback(harness, suspension)` defensively and
 * returns the dispatch result. When the callback returns `{handled:
 * false}` (or nil), the runtime falls back to `ResumeBy.parent_llm` so
 * the parent agent is always the safety net. Exceptions from the
 * callback are caught and surfaced through the fallback path.
 *
 * @effects: [host]
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: invoke_resume_by(callback, harness, suspension)
 */
pub fn invoke_resume_by(callback, harness, suspension) {
  let cb = if callback == nil {
    resume_by_parent_llm
  } else {
    callback
  }
  let outcome = try {
    cb(harness, suspension)
  }
  let resolved = if is_err(outcome) {
    if harness != nil {
      harness
        .emit_audit(
        "resume_by_errored",
        __resume_by_payload(suspension, "callback", {error: to_string(outcome)}),
      )
    }
    nil
  } else {
    outcome
  }
  if resolved == nil || !resolved?.handled {
    if harness != nil {
      harness
        .emit_audit(
        "resume_by_fallback",
        __resume_by_payload(suspension, "ResumeBy.parent_llm", {previous: resolved?.mechanism}),
      )
    }
    return resume_by_parent_llm(harness, suspension)
  }
  return resolved
}