harn-stdlib 0.7.59

Embedded Harn standard library source catalog
Documentation
// @harn-entrypoint-category agent.stdlib
import { filter_nil } from "std/collections"

fn __agent_option_keys() {
  return [
    "provider",
    "model",
    "thinking",
    "tools",
    "max_iterations",
    "tool_format",
    "structural_experiment",
    "context_callback",
    "context_filter",
    "tool_retries",
    "tool_backoff_ms",
  ]
}

/** agent creates an agent spec dict. */
pub fn agent(name, config = nil) {
  if type_of(name) != "string" {
    throw "agent: name must be a string"
  }
  let cfg = config ?? {}
  if type_of(cfg) != "dict" {
    throw "agent: second argument must be a config dict"
  }
  return cfg + {_type: "agent", name: name}
}

/** agent_name reads an agent spec name. */
pub fn agent_name(agent) {
  if type_of(agent) != "dict" {
    throw "agent_name: argument must be an agent"
  }
  return agent?.name
}

/** agent_config builds prompt/system/options for an agent spec. */
pub fn agent_config(agent, prompt) {
  if type_of(agent) != "dict" || agent?._type != "agent" {
    throw "agent_config: first argument must be an agent (created with agent())"
  }
  var options = {}
  for key in __agent_option_keys() {
    if agent[key] != nil {
      options = options + {[key]: agent[key]}
    }
  }
  return {prompt: prompt, system: agent?.system, options: options}
}

fn __worker_dict(value) {
  if type_of(value) == "dict" {
    return value
  }
  return {}
}

fn __worker_list(value) {
  if type_of(value) == "list" {
    return value
  }
  return []
}

fn __worker_options(value, label) {
  if value == nil {
    return {}
  }
  if type_of(value) == "dict" {
    return value
  }
  throw label + ": options must be a dict or nil"
}

fn __worker_mode(config) {
  if config?.graph != nil {
    return "workflow"
  }
  return "stage"
}

fn __worker_workflow_config(config) {
  let options = __worker_dict(config?.options)
  return filter_nil(
    config
      + {graph: workflow_graph(config.graph), artifacts: __worker_list(config?.artifacts), options: options},
  )
}

fn __worker_stage_config(config) {
  return filter_nil(
    config + {artifacts: __worker_list(config?.artifacts), options: __worker_dict(config?.options)},
  )
}

fn __worker_config(config) {
  let cfg = __worker_dict(config)
  let mode = __worker_mode(cfg)
  if mode == "workflow" {
    return __worker_workflow_config(cfg)
  }
  return __worker_stage_config(cfg)
}

/** spawn_agent. */
pub fn spawn_agent(config) {
  return __host_worker_spawn(__worker_config(config))
}

/** send_input. */
pub fn send_input(worker, task) {
  return __host_worker_send_input(worker, task)
}

/** worker_trigger. */
pub fn worker_trigger(worker, payload) {
  return __host_worker_trigger(worker, payload)
}

/** wait_agent. */
pub fn wait_agent(worker_or_workers) {
  return __host_worker_wait(worker_or_workers)
}

/** close_agent. */
pub fn close_agent(worker) {
  return __host_worker_close(worker)
}

/** resume_agent. */
pub fn resume_agent(worker_or_snapshot) {
  return __host_worker_resume(worker_or_snapshot)
}

/** list_agents. */
pub fn list_agents() {
  return __host_worker_list()
}

fn __sub_agent_string(value, fallback = "") {
  if type_of(value) != "string" {
    return fallback
  }
  let text = trim(value)
  if text == "" {
    return fallback
  }
  return value
}

fn __sub_agent_string_list(value, label) {
  if value == nil {
    return []
  }
  if type_of(value) != "list" {
    throw label + ": expected a list of strings"
  }
  var out = []
  for item in value {
    if type_of(item) != "string" {
      throw label + ": expected a list of strings"
    }
    let text = trim(item)
    if text != "" && !contains(out, text) {
      out = out.push(text)
    }
  }
  return out
}

fn __sub_agent_base_tools(opts) {
  if opts?.tools != nil {
    return opts.tools
  }
  return __host_current_tool_registry()
}

fn __sub_agent_selected_tools(opts, allowed_tools) {
  let base = __sub_agent_base_tools(opts)
  if len(allowed_tools) == 0 {
    return base
  }
  if base == nil {
    return nil
  }
  return tool_select(base, allowed_tools)
}

fn __sub_agent_options(opts, session_id, selected_tools) {
  var out = {}
  let internal = [
    "background",
    "carry",
    "returns",
    "allowed_tools",
    "name",
    "execution",
    "system",
    "session_id",
    "policy",
  ]
  for key in opts.keys() {
    if !contains(internal, key) {
      out = out + {[key]: opts[key]}
    }
  }
  out = out + {session_id: session_id}
  if selected_tools != nil {
    out = out + {tools: selected_tools}
  }
  return out
}

/** sub_agent_request builds the Harn-owned child-agent request envelope. */
pub fn sub_agent_request(task, options = nil) {
  let task_text = __sub_agent_string(task)
  if task_text == "" {
    throw "sub_agent_run: task is required"
  }
  let opts = __worker_options(options, "sub_agent_run")
  let allowed_tools = __sub_agent_string_list(opts?.allowed_tools, "sub_agent_run.allowed_tools")
  let selected_tools = __sub_agent_selected_tools(opts, allowed_tools)
  let session_id = __sub_agent_string(opts?.session_id, "sub_agent_session_" + uuid_v7())
  return filter_nil(
    {
      _type: "sub_agent_request",
      name: __sub_agent_string(opts?.name, "sub-agent"),
      task: task_text,
      system: __sub_agent_string(opts?.system, nil),
      options: __sub_agent_options(opts, session_id, selected_tools),
      returns_schema: opts?.returns?.schema,
      session_id: session_id,
      background: opts?.background ? true : false,
      carry: opts?.carry,
      execution: opts?.execution,
      policy: opts?.policy,
      allowed_tools: allowed_tools,
    },
  )
}

/** sub_agent_run. */
pub fn sub_agent_run(task, options = nil) {
  return __host_sub_agent_run(sub_agent_request(task, options))
}