harn-stdlib 0.10.128

Embedded Harn standard library source catalog
Documentation
/**
 * Registry value produced by `tool_registry()` / `tool_define(...)`.
 *
 * Packages that accept or forward registries through typed public APIs
 * should import this alias from `std/tools` instead of `dict` or a local
 * structural copy.
 */
pub type ToolRegistryInfo = {name: string, version?: string, description?: string}

/** Draft 2020-12 schema document, including the standard boolean form. */
pub type JsonSchema202012 = bool | dict<string, unknown>

/** Reusable named JSON Schemas shared by every adapter projection. */
pub type ToolComponents = {schemas: dict<string, JsonSchema202012>}

pub type ToolCliValueHint = "file" \
  | "directory" \
  | "path" \
  | "url" \
  | "email" \
  | "username" \
  | "hostname" \
  | "command" \
  | "other"

pub type ToolCliBooleanStyle = "value" | "set_true" | "set_false"

pub type ToolCliArgumentSpec = {
  long?: string,
  short?: string,
  aliases?: list<string>,
  position?: int,
  value_name?: string,
  help?: string,
  value_hint?: ToolCliValueHint,
  boolean_style?: ToolCliBooleanStyle,
  repeatable?: bool,
  hidden?: bool,
  completions?: list<string>,
  display_order?: int,
  help_group?: string,
}

pub type ToolCliCommandSpec = {
  command: list<string>,
  title?: string,
  description?: string,
  aliases?: list<string>,
  hidden?: bool,
  display_order?: int,
}

pub type ToolCliTreeSpec = {commands: list<ToolCliCommandSpec>}

pub type ToolRegistryOptions = {
  info?: ToolRegistryInfo,
  components?: ToolComponents,
  cli?: ToolCliTreeSpec,
}

pub type ToolRegistry = {
  _type: "tool_registry",
  tools: list,
  info?: ToolRegistryInfo,
  components?: ToolComponents,
  cli?: ToolCliTreeSpec,
}

/** Deterministic command path projected by `harn tool run`. */
pub type ToolCliSpec = {
  command: list<string>,
  aliases?: list<string>,
  hidden?: bool,
  arguments?: dict<string, ToolCliArgumentSpec>,
}

/** Closed set of adapters that may discover and invoke a tool. */
pub type ToolAudience = "cli" | "mcp" | "catalog" | "dashboard" | "agent"

/** Adapter exposure policy projected with the tool catalog. */
pub type ToolGovernance = {audiences: list<ToolAudience>}

/** Origin coordinates retained across generated adapters. */
pub type ToolSource = {kind: string, id?: string, binding?: dict}

/** Harn-owned execution classification used by approval and scheduling policy. */
pub type ToolPolicy = {
  kind: "read" | "edit" | "delete" | "move" | "search" | "execute" | "think" | "fetch" | "other",
  side_effect_level: "none" \
    | "read_only" \
    | "workspace_write" \
    | "process_exec" \
    | "network" \
    | "desktop_control",
}

/** MCP task execution contract retained by the shared tool catalog. */
pub type ToolExecution = {taskSupport: "forbidden" | "optional" | "required"}

/** Portable icon metadata used by MCP and future presentation adapters. */
pub type ToolIcon = {src: string, mimeType?: string, sizes?: list<string>, theme?: "light" | "dark"}

/** Static, handler-free projection of one executable registry entry. */
pub type ToolCatalogEntry = {
  name: string,
  title?: string,
  description?: string,
  inputSchema: JsonSchema202012,
  outputSchema?: JsonSchema202012,
  errorSchema?: JsonSchema202012,
  annotations?: dict,
  icons?: list<ToolIcon>,
  execution?: ToolExecution,
  governance: ToolGovernance,
  cli: ToolCliSpec,
  namespace?: string,
  deferLoading: bool,
  source?: ToolSource,
  policy?: ToolPolicy,
  _meta?: dict,
}

/** Versioned static projection shared by generated presentation adapters. */
pub type ToolCatalog = {
  schema_version: "harn-tools/2.0",
  info?: ToolRegistryInfo,
  cli?: ToolCliTreeSpec,
  tools: list<ToolCatalogEntry>,
  components?: ToolComponents,
}

type ToolDefinitionConfig = {
  /** Legacy per-parameter shorthand. Prefer input_schema for new adapters. */
  parameters?: dict,
  /** Complete caller-owned object schema, normalized directly into inputSchema. */
  input_schema?: dict<string, unknown>,
  returns?: JsonSchema202012,
  output_schema?: JsonSchema202012,
  error_schema?: JsonSchema202012,
  handler?: fn(dict) -> any,
  executor?: string,
  host_capability?: string,
  mcp_server?: string,
  defer_loading?: bool,
  namespace?: string,
  title?: string,
  annotations?: dict,
  cli?: ToolCliSpec,
  source?: ToolSource,
  policy?: dict,
  execution_policy?: ToolPolicy,
  execution?: ToolExecution,
  governance?: ToolGovernance,
  icons?: list<ToolIcon>,
  meta?: dict,
  guidance?: string,
}

/**
 * Declarative tool spec accepted by `tool_define_many` / `tool_registry_from`.
 *
 * System-prompt `guidance` is co-located with the tool. When this tool is
 * present in the active tool set, the runtime auto-injects this text as a
 * capability-gated system-prompt fragment (gated on the tool's own
 * presence), so instruction and tool can never drift. Omit it and the
 * instruction never appears. See docs/src/prompt-assembly.md.
 */
pub type ToolDefinitionSpec = {
  name: string,
  description?: string,
  desc?: string,
  config?: ToolDefinitionConfig,
  /** Legacy per-parameter shorthand. Prefer input_schema for new adapters. */
  parameters?: dict,
  /** Complete caller-owned object schema, normalized directly into inputSchema. */
  input_schema?: dict<string, unknown>,
  returns?: JsonSchema202012,
  output_schema?: JsonSchema202012,
  error_schema?: JsonSchema202012,
  handler?: fn(dict) -> any,
  executor?: string,
  host_capability?: string,
  mcp_server?: string,
  defer_loading?: bool,
  namespace?: string,
  title?: string,
  annotations?: dict,
  cli?: ToolCliSpec,
  source?: ToolSource,
  policy?: dict,
  execution_policy?: ToolPolicy,
  execution?: ToolExecution,
  governance?: ToolGovernance,
  icons?: list<ToolIcon>,
  meta?: dict,
  guidance?: string,
}

type PathScopeMatcherOptions = {
  scope?: string,
  arg_keys?: list<string>,
  mount_modes?: list<string>,
  on_violation?: string,
  patterns?: list<string>,
}?

/**
 * path_scope returns a dynamic-permissions matcher that checks path args
 * against the active session workspace anchor.
 *
 * @effects: []
 * @errors: []
 * @api_stability: experimental
 * @example: path_scope({mount_modes: ["extend"]})
 */
pub fn path_scope(options: PathScopeMatcherOptions = nil) -> dict {
  const opts = options ?? {}
  let matcher = {
    type: "path_scope",
    scope: opts?.scope ?? "anchor_plus_mounted",
    arg_keys: opts?.arg_keys ?? ["path", "destination", "source", "file"],
    on_violation: opts?.on_violation ?? "deny",
  }
  if opts?.mount_modes != nil {
    matcher = matcher + {mount_modes: opts.mount_modes}
  }
  if opts?.patterns != nil {
    matcher = matcher + {patterns: opts.patterns}
  }
  return matcher
}

fn __tool_spec_config(spec: ToolDefinitionSpec) -> ToolDefinitionConfig {
  if type_of(spec) != "dict" {
    throw "tool_define_many: each spec must be a dict"
  }
  let config = spec.config ?? {}
  for key in spec.keys() {
    if !contains(["name", "description", "desc", "config"], key) {
      config = config + {[key]: spec[key]}
    }
  }
  return config
}

/**
 * tool_define_many adds a list of tool specs to a registry.
 *
 * @effects: []
 * @errors: []
 */
pub fn tool_define_many(registry: ToolRegistry, specs: list<ToolDefinitionSpec>) -> ToolRegistry {
  if type_of(specs) != "list" {
    throw "tool_define_many: specs must be a list"
  }
  let tools = registry ?? tool_registry()
  for spec in specs {
    const name = spec.name
    const description = spec.description ?? spec.desc
    if name == nil || name == "" {
      throw "tool_define_many: each spec needs a non-empty name"
    }
    if description == nil || description == "" {
      throw "tool_define_many: each spec needs a non-empty description"
    }
    tools = tool_define(tools, name, description, __tool_spec_config(spec))
  }
  return tools
}

/**
 * tool_registry_from creates a registry from a list of tool specs.
 *
 * @effects: []
 * @errors: []
 */
pub fn tool_registry_from(
  specs: list<ToolDefinitionSpec>,
  options: ToolRegistryOptions? = nil,
) -> ToolRegistry {
  const opts = options ?? {}
  return tool_define_many(tool_registry(opts.info, opts.components, opts.cli), specs)
}