Help us shape the future of Go! Take the 2026 Go Developer Survey

tools

package module
v0.15.2 Latest Latest
Warning

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

Go to latest
Published: Oct 16, 2026 License: Apache-2.0 Imports: 19 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.

go get github.com/looprig/tools@latest

Pin it together with harness v0.40.0 or later: the retained-output support below depends on it.

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 agent-tool bundle (StartAgent, MessageAgent, ListAgents, StopAgent) for delegation; consumers must not add delegation tools 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 (ReadFileDefinition, WriteFileDefinition, EditFileDefinition, GlobDefinition, GrepDefinition, Bash, BashDefinition, ProcessOutputDefinition, ProcessInputDefinition, ProcessStopDefinition, FetchDefinition, WebSearchDefinition, AskUserDefinition, TaskDefinitions, ReadToolResultDefinition). Each concrete tool has a focused package:

Package Purpose
readfile, writefile, editfile Workspace-contained file read, write and edit.
glob, grep Workspace-contained filename and content search (Grep prefers ripgrep, with a stdlib fallback).
bash Single-command shell execution with a bounded timeout and capped combined output.
process Long-running command supervision (BashDefinition plus the Process* tools); see docs/specs/long-running-command-supervision.md.
fetch, websearch One bounded HTTP request via an injected *http.Client; web search over an injected SearchProvider.
askuser The AskUser tool.
skill On-demand reader of curated embedded (and optionally workspace) SKILL.md bodies.
task The four related task operations.
readtoolresult read_tool_result, which pages a retained tool result.
permission The shared workspace permission rule store.

Shared containment and mutation mechanics remain private under internal. Runnable examples live under examples/ (definitions, permissions, preparation, processes, skills, tasks).

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.

Retained tool output

Bash implements Harness's streaming capture (tool.CapturingInvokableTool). While a command runs, its complete combined output streams to the capture sink, and only then does Bash apply its own 32 KiB head/tail preview. A supervised command streams its retained process spool the same way. When nothing was elided, the captured bytes equal the returned result byte for byte, so Harness retains no object for an ordinary small command.

Every standard definition declares its capture safety (tool.CaptureSafetyDeclarer):

  • Bash and BashDefinition stream only under direct sh -c execution. With an injected runner (bash.WithRunner, including a granted sandbox runner), tool.CommandRunner returns the whole output at once, so the result is resident before capture and the definition declares itself materialized and high-output.
  • ReadFile, Grep, ProcessOutput and ProcessInput are materialized and high-output.
  • Every other tool is small by construction.

Direct sh -c execution runs the command in its own process group:

  • A timed-out or cancelled command kills the whole group, including detached or nohup'd jobs. Only setsid escapes it.
  • Once sh has exited, a descendant still holding the output pipe keeps the call open for at most two seconds. A background job that writes after that loses its output.
  • A terminal Ctrl-C does not reach a running Bash command in a non-raw CLI; context cancellation still kills it.

ReadToolResultDefinition() provides read_tool_result, which the model uses to page through a result that Harness retained in full:

loop.WithTools(
	tools.Bash(),
	tools.ReadToolResultDefinition(),
)
  • The tool declares tool.RequiresToolResultReader, so Harness binds it to a reader scoped to the calling loop.
  • It accepts exactly {capture_id, offset?, max_bytes?}. The model can name a capture only by the id that a retention marker prints.
  • Unknown or foreign ids fail closed, and so do malformed arguments.
  • A rig must wire rig.WithToolResultObjects to register the tool. Without it, loop definition fails.

Consumer obligations for retained output: wire rig.WithToolResultObjects, register read_tool_result only where capture is wired, and set a finite ToolLimits.ResultBytes (with it zero nothing is elided, so nothing is retained). Keep the capture ceiling (ToolLimits.CaptureBytes) at or below what the serving Factory will verify when reading objects back (64 MiB by default).

ProcessOutput caps one call's serialized result at 32 MiB (also the schema maximum for limit_bytes) and clamps limit_bytes to the raw read that is guaranteed to fit, given the encoding's worst-case expansion and a fixed per-process metadata allowance shared across process_ids: about 5.3 MiB for one safe_text process (a byte can serialize as a six-byte JSON escape) and 24 MiB for one base64 process (4/3). Page with next_cursor for more. ProcessInput has no limit_bytes argument and returns a fixed 32 KiB output snapshot.

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 (docs/plans/access-profiles.md in the carbon repository) for the cross-module design, and the historical module specification for the original extraction plan.

Where it sits

Tier 4 in the Looprig graph. Direct Looprig dependencies: core and harness, plus test-only inference and storage. Consumed by carbon and tests.

Development

Go 1.26.9 baseline. Verify standalone against the pinned dependencies, then run the checks:

GOWORK=off go test ./...
make test     # go test -race ./...
make secure   # fmt-check, vet, staticcheck, gosec, go mod verify, govulncheck
make check    # the CI surface: fmt-check, vet, staticcheck, gosec, govulncheck, test, build

License

Apache License 2.0. See LICENSE.

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 ReadToolResultDefinition added in v0.14.0

func ReadToolResultDefinition() tool.Definition

ReadToolResultDefinition builds read_tool_result (package readtoolresult), the tool a model calls to page through a tool result Harness retained in full when the model was shown only a preview. It declares tool.RequiresToolResultReader, so Harness binds it the loop-scoped reader; a rig without readable tool-result objects (rig.WithToolResultObjects) refuses to define a loop that registers it.

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 readtoolresult implements read_tool_result: the tool a model calls to page back through a tool result the loop retained in full when it was shown only a shaped preview.
Package readtoolresult implements read_tool_result: the tool a model calls to page back through a tool result the loop retained in full when it was shown only a shaped preview.
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