harn-stdlib 0.9.13

Embedded Harn standard library source catalog
Documentation
// Overlays: a flag-free, data-driven prompt-nudge overlay generalizing
// burin-code's `mode_overlay_lines` (`lib/runtime/agent-loop.harn`) —
// mode-specific (and optionally lane-specific) guidance lines appended to
// the outbound system prompt.
//
// Burin's real mechanism is a `match active_mode` returning a base line list
// with independently-gated extra lines APPENDED after it — never replacing
// or removing base content. This module reuses that same "append, never
// override" shape but expresses it through the EXISTING per-turn
// prompt-fragment channel `agent_loop` already folds into every outbound
// request: `context_profile.prompt_fragments` (harn#2631,
// `std/agent/preflight::agent_build_turn_system_fragments`). No new hook
// surface.
//
// "Fill nil at a lower-priority seam, never override explicit input" here
// means two things, both enforced below:
//   1. An overlay ONLY ever ADDS a fragment alongside whatever the caller
//      already set (`agent_options.system`, `system_prefix`, other
//      `context_profile.prompt_fragments` entries, ...) — it never touches
//      or replaces them.
//   2. Within the overlay's own content, an explicit `options.overrides`
//      entry for a `(mode, lane)` slot wins over that slot's row default —
//      the row only fills the slot when the caller left it nil.
/**
 * A single overlay row: `lines` render for `mode` (and, when `lane` is set,
 * only when the active lane also matches). `enabled: false` disables a row
 * without removing it from the table (still data, not an `if`).
 */
pub type OverlayRow = {mode: string, lane?: string, lines: list<string>, enabled?: bool}

/**
 * default_overlay_rows() — a small starter table generalizing burin's
 * per-mode nudge lines: a base `agent`-mode economy nudge, a narrower
 * reminder for the `explicit_patch` lane (see `std/agent/lanes`), and a
 * `plan`-mode nudge to produce a concrete plan before editing.
 *
 * @effects: []
 * @errors: []
 * @api_stability: experimental
 */
pub fn default_overlay_rows() {
  return [
    {
      lines: [
        "Batch related read-only tool calls together before acting; avoid narrating between them.",
        "Prefer acting on information the task and current tools already provide over asking a clarifying question.",
      ],
      mode: "agent",
    },
    {
      lane: "explicit_patch",
      lines: [
        "This turn is scoped to a narrow patch lane: stay inside the named target files and avoid broad workspace exploration.",
      ],
      mode: "agent",
    },
    {lines: ["Produce a concrete, reviewable plan before making any edits."], mode: "plan"},
  ]
}

fn __overlay_validate_rows(rows) {
  if type_of(rows) != "list" || len(rows) == 0 {
    throw "overlay_policy: rows must be a non-empty list of overlay rows"
  }
  for row in rows {
    if type_of(row) != "dict" {
      throw "overlay_policy: each overlay row must be a dict"
    }
    if to_string(row?.mode ?? "") == "" {
      throw "overlay_policy: each overlay row needs a non-empty `mode`"
    }
    if type_of(row?.lines) != "list" {
      throw "overlay_policy: overlay row for mode \"" + to_string(row.mode) + "\" needs a `lines` list"
    }
  }
  return rows
}

fn __overlay_row_enabled(row) {
  return row?.enabled ?? true
}

fn __overlay_row_key(mode, lane) {
  let mode_text = to_string(mode ?? "")
  let lane_text = to_string(lane ?? "")
  if lane_text == "" {
    return mode_text
  }
  return mode_text + ":" + lane_text
}

/**
 * agent_overlay_match(rows, mode, lane?) — pick the most specific enabled
 * row for `(mode, lane)`: a row whose `lane` equals the given `lane` wins
 * over a `lane`-less row for the same `mode`; returns `nil` when nothing
 * matches (silence is valid — an overlay never invents content).
 *
 * @effects: []
 * @errors: []
 * @api_stability: experimental
 */
pub fn agent_overlay_match(rows, mode, lane = nil) {
  let mode_text = to_string(mode ?? "")
  let lane_text = to_string(lane ?? "")
  var mode_only = nil
  for row in rows {
    if !__overlay_row_enabled(row) {
      continue
    }
    if to_string(row?.mode ?? "") != mode_text {
      continue
    }
    let row_lane = row?.lane
    if row_lane != nil && lane_text != "" && to_string(row_lane) == lane_text {
      return row
    }
    if row_lane == nil && mode_only == nil {
      mode_only = row
    }
  }
  return mode_only
}

/**
 * overlay_context_fragment(rows, mode, lane?, overrides?) — render the
 * matched overlay row (or an explicit `overrides` entry, keyed by
 * `"<mode>"` or `"<mode>:<lane>"`) as a `context_profile` prompt fragment
 * `{body, id, source}`, or `nil` when there is nothing to add. An
 * `overrides` entry fills the SAME slot a row would fill and wins over it —
 * it never stacks with the row.
 *
 * @effects: []
 * @errors: []
 * @api_stability: experimental
 */
pub fn overlay_context_fragment(rows, mode, lane = nil, overrides = nil) {
  let key = __overlay_row_key(mode, lane)
  let override_table = if type_of(overrides) == "dict" {
    overrides
  } else {
    {}
  }
  let override_lines = override_table?[key] ?? override_table?[to_string(mode ?? "")]
  let lines = if type_of(override_lines) == "list" {
    override_lines
  } else {
    let row = agent_overlay_match(rows, mode, lane)
    row?.lines
  }
  if type_of(lines) != "list" || len(lines) == 0 {
    return nil
  }
  let body = trim(join(lines, "\n"))
  if body == "" {
    return nil
  }
  return {body: body, id: "overlay:" + key, source: "overlay_policy"}
}

/**
 * overlay_policy(rows, mode, agent_options?, options?) — layer a
 * mode/lane-specific prompt-nudge overlay onto `agent_options` via the
 * existing `context_profile.prompt_fragments` channel, preserving any
 * fragments the caller already set. `rows` is DATA (see `OverlayRow`);
 * `options.lane` narrows matching to a `(mode, lane)` row (see
 * `std/agent/lanes`); `options.overrides` supplies caller lines that fill
 * (and win over) a row's slot — see `overlay_context_fragment`. Never
 * touches `agent_options.system` or any other explicit system-prompt
 * option; it only adds a fragment alongside them.
 *
 * @effects: []
 * @errors: [runtime]
 * @api_stability: experimental
 * @example: agent_loop(task, nil, overlay_policy(default_overlay_rows(), "agent", {provider: "anthropic"}))
 */
pub fn overlay_policy(rows, mode, agent_options = nil, options = nil) {
  let validated = __overlay_validate_rows(rows)
  let opts = if type_of(agent_options) == "dict" {
    agent_options
  } else {
    {}
  }
  let extra = options ?? {}
  let fragment = overlay_context_fragment(validated, mode, extra?.lane, extra?.overrides)
  if fragment == nil {
    return opts
  }
  let profile = if type_of(opts?.context_profile) == "dict" {
    opts.context_profile
  } else {
    {}
  }
  let existing = if type_of(profile?.prompt_fragments) == "list" {
    profile.prompt_fragments
  } else {
    []
  }
  return opts + {context_profile: profile + {prompt_fragments: existing + [fragment]}}
}