harn-stdlib 0.8.51

Embedded Harn standard library source catalog
Documentation
import { agent_emit_event, agent_session_inject_feedback } from "std/agent/state"

type AgentRequiredToolRequirement = string | list<string>

type AgentRequiredToolsEnvelope = {
  schema_version: int,
  kind: "missing_required_tools",
  reason: string,
  missing: list<string>,
  successful_tool_names: list<string>,
  iterations: int,
}

fn __agent_required_tool_satisfied(requirement: AgentRequiredToolRequirement, successful: list) -> bool {
  if type_of(requirement) == "list" {
    for candidate in requirement {
      if contains(successful, candidate) {
        return true
      }
    }
    return false
  }
  return contains(successful, requirement)
}

fn __agent_required_tool_missing(requirement: AgentRequiredToolRequirement, successful: list) -> bool {
  return !__agent_required_tool_satisfied(requirement, successful)
}

fn __agent_required_tool_label(requirement) -> string {
  if type_of(requirement) == "list" {
    return join(requirement, "|")
  }
  return to_string(requirement)
}

fn __agent_required_tools_from_options(opts) -> list {
  let required = opts?.require_successful_tools
  if required == nil {
    return []
  }
  return required
}

/**
 * agent_required_tools_missing_from_result returns missing required tools.
 *
 * @effects: []
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: agent_required_tools_missing_from_result(result, opts)
 */
pub fn agent_required_tools_missing_from_result(result: dict, opts) -> list<string> {
  let required = __agent_required_tools_from_options(opts)
  if len(required) == 0 {
    return []
  }
  let successful = result?.tools?.successful ?? []
  var missing = []
  for requirement in required {
    if !__agent_required_tool_satisfied(requirement, successful) {
      missing = missing.push(__agent_required_tool_label(requirement))
    }
  }
  return missing
}

/**
 * agent_required_tools_missing_from_session returns missing accumulated tools.
 *
 * @effects: []
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: agent_required_tools_missing_from_session(opts, successful_tools_seen)
 */
pub fn agent_required_tools_missing_from_session(opts, successful_tools_seen: list) -> list<string> {
  let required = __agent_required_tools_from_options(opts)
  if len(required) == 0 {
    return []
  }
  var missing = []
  for requirement in required {
    if __agent_required_tool_missing(requirement, successful_tools_seen) {
      missing = missing.push(__agent_required_tool_label(requirement))
    }
  }
  return missing
}

fn __agent_required_tools_friction_event(result: dict, opts, missing: list<string>) -> dict {
  let session_id = to_string(result?.session_id ?? "")
  var recurrence = ["missing_required_tools=" + to_string(len(missing))]
  if opts?.persona != nil {
    recurrence = recurrence.push("persona=" + to_string(opts.persona))
  }
  return {
    kind: "tool_gap",
    source: "agent_loop.require_successful_tools",
    actor: opts?.persona,
    run_id: if session_id == "" {
      nil
    } else {
      session_id
    },
    redacted_summary: "agent_loop completed without invoking required tool(s): "
      + join(missing, ", "),
    recurrence_hints: recurrence,
    metadata: {
      missing_required_tools: missing,
      successful_tool_names: result?.tools?.successful ?? [],
      iterations: result?.llm?.iterations ?? 0,
      preset_kind: opts?._preset_kind,
    },
  }
}

fn __agent_required_tools_envelope(result: dict, missing: list<string>, iterations: int) -> AgentRequiredToolsEnvelope {
  return {
    schema_version: 1,
    kind: "missing_required_tools",
    reason: "Required tool(s) did not succeed",
    missing: missing,
    successful_tool_names: result?.tools?.successful ?? [],
    iterations: iterations,
  }
}

fn __agent_required_tools_emit_friction(result: dict, opts, missing: list<string>) {
  let event = __agent_required_tools_friction_event(result, opts, missing)
  let _ = try {
    friction_record(event)
  }
  let session_id = to_string(result?.session_id ?? "")
  if session_id != "" {
    let _ = try {
      agent_emit_event(session_id, "require_successful_tools_violation", event)
    }
  }
}

/**
 * agent_required_tools_enforce fails results that missed required tools.
 *
 * @effects: [agent]
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: agent_required_tools_enforce(result, opts)
 */
pub fn agent_required_tools_enforce(result: dict, opts) -> dict {
  let missing = agent_required_tools_missing_from_result(result, opts)
  if len(missing) == 0 {
    return result
  }
  let iterations = result?.llm?.iterations ?? 0
  let envelope = __agent_required_tools_envelope(result, missing, iterations)
  __agent_required_tools_emit_friction(result, opts, missing)
  return result
    + {
    status: "failed",
    final_status: "failed",
    stop_reason: "missing_required_tools",
    missing_required_tools: missing,
    error: "Required tools did not succeed: " + join(missing, ", "),
    error_envelope: envelope,
  }
}

/**
 * agent_required_tools_feedback builds feedback for missing required tools.
 *
 * @effects: []
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: agent_required_tools_feedback(["run_tests"])
 */
pub fn agent_required_tools_feedback(missing: list<string>) -> string {
  return "Required tool(s) have not succeeded yet: "
    + join(missing, ", ")
    + ". Continue working and call the missing required tool(s) before finalizing."
}

/**
 * agent_required_tools_inject_feedback injects missing-tool feedback.
 *
 * @effects: [agent]
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: agent_required_tools_inject_feedback(session_id, missing)
 */
pub fn agent_required_tools_inject_feedback(session_id: string, missing: list<string>) {
  agent_session_inject_feedback(session_id, "required_tools", agent_required_tools_feedback(missing))
}