hitlservice

package
v0.28.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 7, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package hitlservice evaluates approval policies for tool calls. Policy decisions (allow / deny / approve) are returned to the caller; the caller (typically a ToolsRepo decorator like localtools.HITLWrapper) is responsible for actually pausing execution and sourcing the human decision.

Index

Constants

View Source
const (
	ReasonMatchedRule   = "matched_rule"
	ReasonDefaultAction = "default_action"
)

Reason constants used in EvaluationResult.Reason.

Variables

This section is empty.

Functions

This section is empty.

Types

type Action

type Action string

Action is the outcome of policy evaluation for a tool call.

const (
	// ActionAllow passes the tool call through without any approval step.
	ActionAllow Action = "allow"
	// ActionApprove blocks execution and requests human approval before proceeding.
	ActionApprove Action = "approve"
	// ActionDeny rejects the tool call immediately with a soft message to the LLM.
	ActionDeny Action = "deny"
)

type ApprovalRequest

type ApprovalRequest struct {
	ToolCallID string
	ToolsName  string
	ToolName   string
	Args       map[string]any
	Diff       string
	DiffOld    string
	DiffNew    string
}

ApprovalRequest describes a tool invocation that requires human review. Diff is populated for file-mutation tools (write_file, sed) to show the unified diff of what would change.

type Condition

type Condition struct {
	Key   string      `json:"key"`
	Op    ConditionOp `json:"op"`
	Value string      `json:"value"`
}

Condition is a single key/op/value predicate applied to the args of a tool call.

type ConditionOp

type ConditionOp string

ConditionOp is the comparison operator for a rule condition.

const (
	// OpEq requires the argument value to equal the condition value exactly.
	OpEq ConditionOp = "eq"
	// OpGlob matches the argument value against a glob pattern.
	// Both value and pattern are normalized with path.Clean before matching,
	// preventing path-traversal bypass (e.g. ./src/../etc/passwd → etc/passwd).
	// Supports * (within a path component), ? (single char), and ** (across separators).
	OpGlob ConditionOp = "glob"
	// OpHost treats the argument value as a URL and matches its host against a
	// comma-separated list of host patterns in Value. It is the policy-native
	// way to express SSRF-style host denial (e.g. webtools to localhost or a
	// cloud-metadata endpoint): the host is parsed out of the URL, so a trailing
	// :port or path cannot evade the match the way a raw URL glob can. IP
	// literals match exactly; bare names match the host and any subdomain
	// (api.example.com matches example.com).
	OpHost ConditionOp = "host"
)

type EvaluationResult

type EvaluationResult struct {
	Action      Action
	MatchedRule *int   // nil when DefaultAction was applied (no rule matched)
	Reason      string // ReasonMatchedRule or ReasonDefaultAction
	TimeoutS    int
	OnTimeout   Action
}

EvaluationResult carries the policy decision plus introspection data.

type KVReader

type KVReader interface {
	GetKV(ctx context.Context, key string, out interface{}) error
}

type Policy

type Policy struct {
	DefaultAction Action `json:"default_action,omitempty"`
	Rules         []Rule `json:"rules"`
}

Policy is the top-level document stored as hitl-policy.json in the VFS. Rules are evaluated in order; the first matching rule wins. DefaultAction is applied when no rule matches; it is fail-closed to "approve" when absent so an unaccounted-for tool pauses for a human.

type PolicyEvaluator

type PolicyEvaluator interface {
	Evaluate(ctx context.Context, toolsName, toolName string, args map[string]any) (EvaluationResult, error)
}

type PolicySource

type PolicySource interface {
	ReadPolicy(ctx context.Context, tenantID, name string) ([]byte, error)
}

PolicySource reads a named HITL policy document for a tenant. Any error (including not-found) makes the evaluator fall back to the built-in default policy, so implementations need not special-case absence.

func NewFSPolicySource

func NewFSPolicySource(dirs ...string) PolicySource

NewFSPolicySource returns a PolicySource that looks up "<dir>/<name>" in each dir in order, returning the first hit. tenantID is ignored: the OSS runtime is single-tenant. Empty dirs are skipped. Tenant-scoped builds inject their own PolicySource (e.g. backed by the VFS) instead.

type Rule

type Rule struct {
	Tools  string      `json:"tools"`
	Tool   string      `json:"tool"`
	When   []Condition `json:"when,omitempty"`
	Action Action      `json:"action"`
	// TimeoutS is the number of seconds to wait for a human response when Action is
	// ActionApprove. Zero means no timeout (block indefinitely until ctx is cancelled).
	TimeoutS int `json:"timeout_s,omitempty"`
	// OnTimeout is the fallback action when the approval window expires.
	// Only "deny" and "approve" are valid (allow would silently bypass approval).
	OnTimeout Action `json:"on_timeout,omitempty"`
}

Rule matches a tools+tool pair (with optional conditions) and assigns an action. When contains zero conditions the name match alone is sufficient. All conditions in When must hold for the rule to match (AND semantics).

type Service

type Service interface {
	PolicyEvaluator

	RequestApproval(ctx context.Context, req ApprovalRequest, sink taskengine.TaskEventSink) (bool, error)

	Respond(approvalID string, approved bool) bool
}

func New

func New(src PolicySource, tenantID string, store KVReader, tracker libtracker.ActivityTracker) Service

New constructs a hitlservice bound to a tenant. The tenantID is forwarded to every policy lookup the service performs.

func NewWithDefaultPolicy

func NewWithDefaultPolicy(src PolicySource, tenantID string, store KVReader, tracker libtracker.ActivityTracker, fallbackPolicy string) Service

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL