harn-stdlib 0.10.25

Embedded Harn standard library source catalog
Documentation
/**
 * Merge annotations carried directly on a tool registry entry. Entry-local
 * metadata overrides entry policy defaults.
 *
 * @effects: []
 * @errors: []
 * @api_stability: experimental
 */
pub fn agent_tool_entry_annotations(entry) {
  if type_of(entry) != "dict" {
    return {}
  }
  let annotations = {}
  const policy = entry?.policy
  if type_of(policy) == "dict" {
    annotations = annotations + policy
  }
  const direct = entry?.annotations
  if type_of(direct) == "dict" {
    annotations = annotations + direct
  }
  const func = entry?.function
  if type_of(func) == "dict" && type_of(func?.policy) == "dict" {
    annotations = annotations + func.policy
  }
  if type_of(func) == "dict" && type_of(func?.annotations) == "dict" {
    annotations = annotations + func.annotations
  }
  return annotations
}

/**
 * Return policy-level annotations for one tool name. Empty or malformed
 * policy annotation maps are ignored.
 *
 * @effects: []
 * @errors: []
 * @api_stability: experimental
 */
pub fn agent_tool_policy_annotations(policy, tool_name) {
  if type_of(policy) != "dict" {
    return {}
  }
  const registry = policy?.tool_annotations ?? policy?.toolAnnotations ?? {}
  if type_of(registry) != "dict" {
    return {}
  }
  const annotations = registry[tool_name]
  if type_of(annotations) == "dict" {
    return annotations
  }
  return {}
}

/**
 * Merge policy-level annotations with registry-entry annotations. Policy
 * annotations provide defaults; direct registry metadata wins.
 *
 * @effects: []
 * @errors: []
 * @api_stability: experimental
 */
pub fn agent_tool_annotations(entry, policy, tool_name) {
  return agent_tool_policy_annotations(policy, tool_name) + agent_tool_entry_annotations(entry)
}

const __AGENT_READ_ONLY_TOOL_KINDS = ["read", "search", "think", "fetch"]

const __AGENT_READ_ONLY_SIDE_EFFECTS = ["none", "read_only"]

/**
 * Classify one registry entry through the ACP-aligned annotation vocabulary.
 * Missing or unknown annotations fail closed as mutating.
 *
 * @effects: []
 * @errors: []
 * @api_stability: experimental
 */
pub fn agent_tool_is_read_only(entry, policy = nil, tool_name = "") -> bool {
  const annotations = agent_tool_annotations(entry, policy, tool_name)
  if annotations?.destructiveHint == true {
    return false
  }
  const kind = lowercase(to_string(annotations?.kind ?? annotations?.tool_kind ?? annotations?.toolKind ?? ""))
  const level = lowercase(
    to_string(
      annotations?.side_effect_level ?? annotations?.sideEffectLevel ?? annotations?.side_effect
        ?? annotations?.sideEffect
        ?? "",
    ),
  )
  const read_only_hint = annotations?.readOnlyHint ?? annotations?.read_only_hint
  if kind != "" && !contains(__AGENT_READ_ONLY_TOOL_KINDS, kind) {
    return false
  }
  if level != "" && !contains(__AGENT_READ_ONLY_SIDE_EFFECTS, level) {
    return false
  }
  if read_only_hint != nil && !(type_of(read_only_hint) == "bool" && read_only_hint) {
    return false
  }
  return kind != "" || level != "" || (type_of(read_only_hint) == "bool" && read_only_hint)
}

fn __agent_tool_registry_entries(registry) {
  if type_of(registry) == "dict" {
    return registry?.tools ?? []
  }
  if type_of(registry) == "list" {
    return registry
  }
  return []
}

fn __agent_tool_entry_name(entry) {
  return to_string(entry?.function?.name ?? entry?.name ?? "")
}

fn __agent_tool_call_name(call) {
  return to_string(call?.name ?? call?.tool_name ?? call?.function?.name ?? "")
}

fn __agent_tool_find_entry(registry, name) {
  for entry in __agent_tool_registry_entries(registry) {
    if __agent_tool_entry_name(entry) == name {
      return entry
    }
  }
  return nil
}

fn __agent_tool_is_workspace_write(entry, policy, name) -> bool {
  const annotations = agent_tool_annotations(entry, policy, name)
  if annotations?.destructiveHint == true || annotations?.readOnlyHint == true {
    return false
  }
  const kind = lowercase(to_string(annotations?.kind ?? ""))
  const level = lowercase(to_string(annotations?.side_effect_level ?? ""))
  if kind != "" && kind != "edit" {
    return false
  }
  if level != "" && level != "workspace_write" {
    return false
  }
  return kind == "edit" || level == "workspace_write"
}

/**
 * True only when a non-empty turn consists entirely of explicitly read-only
 * calls. Unknown tools fail closed, matching `agent_tool_is_read_only`.
 *
 * @effects: []
 * @errors: []
 * @api_stability: experimental
 */
pub fn agent_tool_calls_are_read_only(calls, registry, policy = nil) -> bool {
  if type_of(calls) != "list" || len(calls) == 0 {
    return false
  }
  for call in calls {
    const name = __agent_tool_call_name(call)
    const entry = __agent_tool_find_entry(registry, name)
    if name == "" || entry == nil || !agent_tool_is_read_only(entry, policy, name) {
      return false
    }
  }
  return true
}

/**
 * Collapse byte-identical read-only calls when text/JSON parsing repeats a
 * block. Native calls retain provider identities and are never collapsed;
 * unknown or mutating tools fail closed and remain in source order.
 *
 * @effects: []
 * @errors: []
 * @api_stability: experimental
 */
pub fn agent_collapse_duplicate_read_calls(calls, registry, options = nil) -> list {
  const format = lowercase(to_string(options?.tool_format ?? ""))
  if type_of(calls) != "list" {
    return []
  }
  if format != "text" && format != "json" {
    return calls
  }
  let kept = []
  let seen = []
  for call in calls {
    const name = __agent_tool_call_name(call)
    const entry = __agent_tool_find_entry(registry, name)
    const read_only = entry != nil && agent_tool_is_read_only(entry, options?.policy, name)
    const args = call?.arguments ?? call?.tool_args ?? {}
    const signature = if type_of(call) == "dict" {
      sha256(json_stringify({name: name, args: args}))
    } else {
      nil
    }
    if read_only && signature != nil && contains(seen, signature) {
      continue
    }
    kept = kept.push(call)
    if read_only && signature != nil {
      seen = seen.push(signature)
    }
  }
  return kept
}

/**
 * Return the sole non-destructive workspace-write tool, or an empty string
 * when the annotated surface has none or more than one. This lets generic
 * policy request a write without guessing among product-specific tool names.
 *
 * @effects: []
 * @errors: []
 * @api_stability: experimental
 */
pub fn agent_tool_single_workspace_write_name(registry, policy = nil) -> string {
  let found = ""
  for entry in __agent_tool_registry_entries(registry) {
    const name = __agent_tool_entry_name(entry)
    if name != "" && __agent_tool_is_workspace_write(entry, policy, name) {
      if found != "" {
        return ""
      }
      found = name
    }
  }
  return found
}

/**
 * Resolve a non-destructive workspace-write tool from the active registry. A
 * host may name its product-specific primary writer; Harn validates that entry
 * against the same annotation contract. Without a preference, the surface must
 * expose exactly one eligible writer.
 *
 * @effects: []
 * @errors: []
 * @api_stability: experimental
 */
pub fn agent_tool_resolve_workspace_write_name(registry, policy = nil, preferred = "") -> string {
  const name = trim(preferred)
  if name == "" {
    return agent_tool_single_workspace_write_name(registry, policy)
  }
  const entry = __agent_tool_find_entry(registry, name)
  if entry == nil || !__agent_tool_is_workspace_write(entry, policy, name) {
    return ""
  }
  return name
}