tools

package module
v0.12.0 Latest Latest
Warning

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

Go to latest
Published: Aug 25, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

README

tools

github.com/looprig/tools provides optional standard tools for looprig Loops. The harness defines the contracts. This module provides implementations that consumers can select individually, plus the deliberate related-family Tasks bundle.

loop.WithTools(
	tools.ReadFileDefinition(readGuard),
	tools.GlobDefinition(readGuard),
	tools.GrepDefinition(readGuard),
	tools.TaskDefinitions(),
)

TaskDefinitions() produces the four model-facing tools TaskCreate, TaskUpdate, TaskGet, and TaskList. They are one deliberate bundle because the four operations must share one bounded, Loop-local task graph. Each definition build creates a fresh graph; parent and child Loops are isolated, while modes within one Loop share the graph. The Harness owns and injects the Subagent control tool for delegated Loops; consumers must not add it from this module.

There is no bundled file-tool definition. A read-only Loop can receive ReadFile without also constructing WriteFile or EditFile. Consumers can mix these tools with their own definitions or use no standard tools at all.

The module root is intentionally a small definition facade. Each concrete tool has a focused package, such as readfile, writefile, grep, bash, and websearch. The task package owns the four related task operations. The permission package is the shared workspace rule library, and shared containment and mutation mechanics remain private under internal.

All README snippets are compiled by example_readme_test.go at the module root.

Tool preparation

Every tool is a tool.CallPreparer. PrepareCall owns the whole preparation boundary: it decodes and validates the untrusted arguments once, normalizes commands, URLs, and paths, resolves canonical resource identities, and emits one typed tool.Request listing every capability Requirement the call needs. Invalid input fails during preparation and never reaches the permission gate. Execution consumes the typed prepared artifact bound to the call — the raw arguments are never reparsed — and a prepared tool that runs without its artifact fails closed.

Tools classify capabilities; they never decide Deny, Gated, or Allow. That three-state decision belongs to the harness gate evaluator, which consumes requests structurally without any tool-specific field extraction.

The permission package

permission implements the single hardened workspace permission store of the access-profile specification. It stores capability rules — kind (command.execute, network, filesystem.read, filesystem.write), effect (allow or deny, deny always beats allow), enforcement class, and match — under the strict schema-version-2 JSON codec.

store, diagnostics, err := permission.NewWorkspaceStore(permission.Config{
	Path: permissionFilePath, // one explicit absolute path; never discovered
})

Hardening: the store serves exactly one explicit permission-file path (it never computes HOME-relative or implicit locations), requires owner-only 0600 files, bounds file size, re-reads the file per query in interactive mode (so concurrent processes observe each other's atomically renamed updates), loads one immutable snapshot in read-only headless mode, and persists approved allow candidates atomically under an interprocess lock. Any load failure fails closed as an error.

Bash command rules come in three enforcement classes: an exact normalized command, the wildcard Bash(*), and the token-prefix family Bash(git log:*). Family matching is per shell segment: the normalized command is split at &&, ||, ;, |, |&, &, newline, and subshell boundaries, and a family covers a segment only when the segment is a provably simple command whose leading bare literal tokens equal the family tokens exactly — token equality, never string prefix. Anything the conservative grammar cannot prove simple (substitution, redirection, dynamic expansion, ambiguous quoting, …) is matchable only by a wildcard or an exact rule. Everything fails closed.

The automatic-family eligibility catalog is injected by the consumer (Config.FamilyEligible); a manually authored allow family outside the catalog stays authoritative but produces a non-fatal Diagnostic the consumer must surface. Deny families never warn.

The harness gate consumes the store structurally as its rule matcher and writer; deny-before-allow ordering belongs to the gate, and the store answers both queries independently.

Bash access declarations

A Bash call may carry a structured access declaration of the filesystem and network deltas the command needs. The declaration requests authority — it never grants it. Each declared delta becomes one more requirement in the same typed request, so a gated command and its deltas share a single combined approval; an omitted gated delta stays OS-blocked by the sandbox at run time, and the model retries with a new call that declares the needed capability. Grants are minted only after the gate's decision, and command issuance is always exact-command even when a wildcard or family rule satisfied the decision.

tools.Bash(
	bash.WithRunner(confinedRunner),
	bash.WithFamilyCatalog(familyEligible),
)

Shared network capability

Bash network deltas, Fetch, and WebSearch all emit the same network capability kind with the same canonical target match encoding, so one saved workspace rule for a host and port serves all three tools. Fetch derives its single endpoint from the validated URL; WebSearch emits one requirement per endpoint its injected SearchProvider declares, and the provider fails closed on any secondary target outside that declaration.

Fail-closed properties

  • Invalid or unparseable arguments fail during preparation; nothing reaches the gate or the filesystem.
  • A prepared tool invoked without its typed artifact refuses to run.
  • Permission-file load failures (missing when required, wrong mode, oversized, malformed, unsupported schema or normalization version) are errors, never empty-rule successes, in interactive mode.
  • Unsegmentable or unprovably simple shell input never matches a family rule.
  • Definition builders reject nil (including typed-nil) dependencies at build time with a DefinitionBuildError.

See the access-profile specification (carbon/docs/specs/access-profiles.md) for the cross-module design, and the historical module specification for the original extraction plan.

Run the full local security suite with:

make secure
go test -race ./...

Documentation

Overview

Package tools provides independent definition builders for Looprig's standard tools. Concrete constructors and options live in focused subpackages.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AskUserDefinition

func AskUserDefinition() tool.Definition

func Bash

func Bash(options ...bash.BashOption) tool.Definition

func BashDefinition

func BashDefinition(resolver AsyncProcessRunnerResolver, options ...bash.BashOption) tool.Definition

BashDefinition builds the session-supervised Bash tool: unlike Bash, a SUPERVISED call (background, or a present yield_time_ms — bash/prepare.go's normalizeSupervision) routes through the shared, runner-free process.Supervisor (bash/supervised.go). resolver supplies the concrete tool.AsyncProcessRunner: Build calls it exactly once, only after Harness has validated bindings, with the validated bindings.LoopID, and rejects a resolver error or a nil/typed-nil returned runner without ever producing a tool. options configure the underlying BashTool exactly like Bash's own — resolved once here, never reapplied per Build.

func EditFileDefinition

func EditFileDefinition(options ...editfile.Option) tool.Definition

EditFileDefinition accepts editfile.Option values such as editfile.WithHostWrites(). Do not pass editfile.WithMutationCoordinator here: this entry point already injects the session-bound coordinator, and a caller-supplied one is applied after it and silently wins, defeating the PathMutation permit and lease-health check with no error.

func FetchDefinition

func FetchDefinition(client *http.Client) tool.Definition

func GlobDefinition

func GlobDefinition(readGuard loop.ReadGuard, options ...glob.GlobOption) tool.Definition

func GrepDefinition

func GrepDefinition(readGuard loop.ReadGuard, options ...grep.GrepOption) tool.Definition

func ProcessInputDefinition

func ProcessInputDefinition() tool.Definition

ProcessInputDefinition builds the mutating ProcessInput tool over the same shared supervisor entry ProcessOutputDefinition resolves (process/ input_tool.go). Argument-free for the identical reason.

func ProcessOutputDefinition

func ProcessOutputDefinition() tool.Definition

ProcessOutputDefinition builds the read-only ProcessOutput tool bound to this session's shared, runner-free process.Supervisor (process/ output_tool.go). Argument-free: unlike BashDefinition it captures no resolver and no options — a caller's owned process is read through the same registry entry Bash and its two sibling definitions share, keyed by process.SupervisorResourceKey alone.

func ProcessStopDefinition

func ProcessStopDefinition() tool.Definition

ProcessStopDefinition builds the mutating ProcessStop tool over the same shared supervisor entry (process/stop_tool.go). Argument-free for the identical reason.

func ReadFileDefinition

func ReadFileDefinition(readGuard loop.ReadGuard, options ...readfile.ReadFileOption) tool.Definition

func TaskDefinitions

func TaskDefinitions() tool.Definition

func WebSearchDefinition

func WebSearchDefinition(provider websearch.SearchProvider) tool.Definition

func WriteFileDefinition

func WriteFileDefinition(options ...writefile.Option) tool.Definition

WriteFileDefinition accepts writefile.Option values such as writefile.WithHostWrites(). Do not pass writefile.WithMutationCoordinator here: this entry point already injects the session-bound coordinator, and a caller-supplied one is applied after it and silently wins, defeating the PathMutation permit and lease-health check with no error.

Types

type AsyncProcessRunnerResolver

type AsyncProcessRunnerResolver func(context.Context, uuid.UUID) (tool.AsyncProcessRunner, error)

AsyncProcessRunnerResolver resolves the concrete tool.AsyncProcessRunner a session-supervised Bash definition binds to, from the Harness-validated bindings.LoopID at Build (design spec "Workspace coordination": "At definition Build, Tools invokes the resolver with the validated bindings.LoopID"). Tools owns this resolver shape; a product composition root supplies the concrete implementation over its own per-role executor set. Runner selection is therefore complete before the concrete Bash tool is ever invoked — it never derives from invocation-time provenance.

type DefinitionBuildError

type DefinitionBuildError = definition.BuildError

Directories

Path Synopsis
Package bash implements the Bash tool: single-command shell execution inside a workspace-contained working directory, with a bounded timeout and a capped combined-output capture.
Package bash implements the Bash tool: single-command shell execution inside a workspace-contained working directory, with a bounded timeout and a capped combined-output capture.
Package editfile exposes the standard workspace file editor.
Package editfile exposes the standard workspace file editor.
Package fetch implements the Fetch tool: one bounded HTTP GET or POST via an injected *http.Client, with no filesystem access.
Package fetch implements the Fetch tool: one bounded HTTP GET or POST via an injected *http.Client, with no filesystem access.
Package glob implements the Glob tool: a workspace-contained, denied-path-excluding filename search over WalkDir-discovered entries.
Package glob implements the Glob tool: a workspace-contained, denied-path-excluding filename search over WalkDir-discovered entries.
Package grep implements the Grep tool: a workspace-contained content search that prefers ripgrep and falls back to a stdlib scan, with two-layer denied-path enforcement.
Package grep implements the Grep tool: a workspace-contained content search that prefers ripgrep and falls back to a stdlib scan, with two-layer denied-path enforcement.
internal
atomicfile
Package atomicfile provides durable, crash-safe atomic replacement of one file's contents (spec "docs/specs/long-running-command-supervision.md", "Manifests and durability": "Manifest updates use write-new, sync, and atomic replace semantics").
Package atomicfile provides durable, crash-safe atomic replacement of one file's contents (spec "docs/specs/long-running-command-supervision.md", "Manifests and durability": "Manifest updates use write-new, sync, and atomic replace semantics").
filemutation
Package filemutation implements the shared mechanics of the two direct mutation tools, WriteFile and EditFile: single-step preparation and canonicalization, workspace containment, atomic publication, optimistic file-freshness concurrency, and permit-scoped cross-loop serialization.
Package filemutation implements the shared mechanics of the two direct mutation tools, WriteFile and EditFile: single-step preparation and canonicalization, workspace containment, atomic publication, optimistic file-freshness concurrency, and permit-scoped cross-loop serialization.
hashcache
Package hashcache memoizes the parse of a byte slice keyed by its SHA-256.
Package hashcache memoizes the parse of a byte slice keyed by its SHA-256.
nofollow
Package nofollow provides one small, platform-portable primitive for opening a file while refusing to traverse a symlink (POSIX) or reparse point (Windows) at the final path component.
Package nofollow provides one small, platform-portable primitive for opening a file while refusing to traverse a symlink (POSIX) or reparse point (Windows) at the final path component.
prepared
Package prepared holds the small helpers the direct file/context tools share at the preparation boundary: reading a call's typed prepared artifact back from the context and building the direct filesystem requirements (empty grant pair — the tool enforces the approved resolved resource itself).
Package prepared holds the small helpers the direct file/context tools share at the preparation boundary: reading a call's typed prepared artifact back from the context and building the direct filesystem requirements (empty grant pair — the tool enforces the approved resolved resource itself).
safetext
Package safetext converts raw process output bytes into model-safe text (spec "docs/specs/long-running-command-supervision.md", "Output capture and storage": "Model-visible text passes through safe-text normalization: invalid UTF-8 is replaced deterministically; disallowed terminal control sequences are escaped or removed; binary detection is reported; normalization is reported").
Package safetext converts raw process output bytes into model-safe text (spec "docs/specs/long-running-command-supervision.md", "Output capture and storage": "Model-visible text passes through safe-text normalization: invalid UTF-8 is replaced deterministically; disallowed terminal control sequences are escaped or removed; binary detection is reported; normalization is reported").
workspace
Package workspace holds the shared, security-sensitive primitives the standard tools depend on: `**`-aware glob matching, workspace path containment, and typed-nil detection for injected dependencies.
Package workspace holds the shared, security-sensitive primitives the standard tools depend on: `**`-aware glob matching, workspace path containment, and typed-nil detection for injected dependencies.
Package permission implements the single hardened workspace permission store defined by the access-profile specification.
Package permission implements the single hardened workspace permission store defined by the access-profile specification.
Package process defines the Tools-owned long-running-command supervision domain: process identity, lifecycle state, the stable error taxonomy, and quota configuration (spec "docs/specs/long-running-command-supervision.md", sections "Identity and authorization", "State machine", "Stable errors", and "Quotas and retention").
Package process defines the Tools-owned long-running-command supervision domain: process identity, lifecycle state, the stable error taxonomy, and quota configuration (spec "docs/specs/long-running-command-supervision.md", sections "Identity and authorization", "State machine", "Stable errors", and "Quotas and retention").
Package readfile implements the ReadFile tool: a workspace-contained, denied-path-aware, symlink-rejecting file reader returning line-numbered text capped by the injected ReadGuard.
Package readfile implements the ReadFile tool: a workspace-contained, denied-path-aware, symlink-rejecting file reader returning line-numbered text capped by the injected ReadGuard.
Package skill implements the Skill tool: an on-demand reader of curated embedded (and optionally untrusted workspace) SKILL.md bodies, scoped to the one agent the tool is bound to.
Package skill implements the Skill tool: an on-demand reader of curated embedded (and optionally untrusted workspace) SKILL.md bodies, scoped to the one agent the tool is bound to.
Package websearch implements the WebSearch tool and its SearchProvider seam, with no filesystem access.
Package websearch implements the WebSearch tool and its SearchProvider seam, with no filesystem access.
Package writefile exposes the standard workspace file writer.
Package writefile exposes the standard workspace file writer.

Jump to

Keyboard shortcuts

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