tool

package
v0.27.1 Latest Latest
Warning

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

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

README

pkg/tool

pkg/tool defines the dependency-free contract surface for the tools subsystem: the BaseTool/InvokableTool interfaces every tool implements, the ToolResult value tools return, the tool-owned preparation boundary (CallPreparer, Request, Requirement, RuleCandidate), and the optional capability interfaces the runner probes for via type assertion.

It imports only github.com/looprig/core (plus stdlib). It must never import pkg/loop, the runtime internals, or a concrete tools module, so all of them can depend on it without a cycle. The standard tool implementations (bash, web, …) live in the sibling looprig/tools module.

Task tracking and delegation ownership

Optional task tracking is a standard-tool concern. Consumers that want it may select the TaskDefinitions() bundle from github.com/looprig/tools, which exposes the four task model-facing names from one selected definition. Each bound Loop gets its own task graph, while modes within that Loop share the graph.

Agent collaboration is intentionally different: ListAgents, MessageAgent, StartAgent, and StopAgent are Harness-owned model-facing control tools. Harness derives their schemas, and the StartAgent capability catalog, from the frozen delegate topology and binds them to the parent-scoped delegate controller, so their authority cannot be supplied by optional task tracking. Harness therefore does not import github.com/looprig/tools. The four tools are automatically injected as one bundle into each applicable Loop; consumers must not add them manually. Agents coordinate task work through agent messages rather than shared task memory.

What is tool?

  • BaseToolInfo(ctx) (*ToolInfo, error). The minimal contract; never widened (new behavior is added via separate optional interfaces, never folded into BaseTool).
  • InvokableToolBaseTool plus InvokableRun(ctx, argsJSON) (*ToolResult, error). The runner injects an "error: empty result" block when the result is nil or empty.
  • Definition — a design-time binding of a tool name to a factory that builds live InvokableTool values from tool.Bindings. Loops hold Definition values; the runtime Binds them at loop start.
  • NewEvidenceDefinition — a sealed, read-only definition with frozen ToolInfo. Its EvidenceFactory receives only invocation SessionID, LoopID, and an optional root-only ReadWorkspaceBinding; the API does not expose mutation, observations, delegation, gates, grants, control, or extra tools.
  • ToolMiddleware — wraps each invocation (observability, retry, redaction, …) with a func(next InvokableTool) InvokableTool shape.
  • CallPreparer — the tool-owned preparation boundary. Decodes and validates the untrusted argsJSON, normalizes commands/URLs/paths, resolves canonical resource identities, and produces a typed Request plus an optional opaque PreparedArtifact the tool reads back at execution time.
  • Request / Requirement / RuleCandidate — the typed prepared access request. ValidateRequest re-checks every invariant at the start of evaluation and at both durable codec boundaries.
  • Optional capability interfacesSequential (must not run concurrently with other calls in the batch), ReadGuard consumer, DelegateController requirement, generic asynchronous process contracts, etc. The runner probes for each via type assertion; a tool implementing none is still a valid InvokableTool.

How to use

As a tool author
type MyTool struct{}

func (MyTool) Info(ctx context.Context) (*tool.ToolInfo, error) {
    return &tool.ToolInfo{
        Name:   "my_tool",
        Desc:   "Does a thing",
        Schema: json.RawMessage(`{"type":"object","properties":{...}}`),
    }, nil
}

func (t MyTool) InvokableRun(ctx context.Context, argsJSON string) (*tool.ToolResult, error) {
    // parse + validate args, execute, return a result
    return tool.TextResult("done"), nil
}

Evidence collectors use the narrower factory boundary:

definition := tool.NewEvidenceDefinition(
    "workspace-status",
    tool.RequiresWorkspaceRead,
    []tool.ToolInfo{statusInfo},
    func(ctx context.Context, bindings tool.EvidenceFactoryBindings) ([]tool.InvokableTool, error) {
        return []tool.InvokableTool{
            newStatusTool(bindings.ReadWorkspace.Root),
        }, nil
    },
)

The read-workspace pointer is a per-build copy. Evidence factories must still treat the binding as invocation-scoped and must not retain it after returning.

If your tool is effectful, implement CallPreparer so the gate gets a typed request to decide on:

func (t MyTool) PrepareCall(ctx context.Context, executionID string, argsJSON string) (tool.Request, tool.PreparedArtifact, error) {
    var args myArgs
    if err := json.Unmarshal([]byte(argsJSON), &args); err != nil {
        return tool.Request{}, nil, /* typed error */
    }
    // normalize paths/commands/URLs, resolve canonical identity, ...
    return tool.Request{
        ToolName: "my_tool",
        Summary:  "my_tool(" + args.Path + ")",
        Requirements: []tool.Requirement{{
            Kind:        "fs.write",
            Match:       "Write(" + absPath + ")",
            Description: "Write " + absPath,
            GrantClass:  "fs.write.v1",
            GrantTarget: absPath,
        }},
    }, tool.TokenArtifact{Token: absPath}, nil
}
As a rig composer
operator, _ := loop.Define(
    loop.WithTools(
        tool.NewDefinition("read", tool.RequiresNothing, /* factory */),
        tool.NewDefinition("bash", tool.RequiresSandbox, /* factory */),
    ),
    /* ... */
)

The standard tool implementations live in looprig/tools; a consumer wires those factories and binds a gate.Evaluator (and a sandbox) at the composition root.

Asynchronous processes

AsyncProcessRunner is the shell-agnostic, enforcement-capable seam for supervised processes. PrepareProcess validates grants and reserves enforcement resources without spawning. The caller reads the returned preparation's authoritative WorkspaceAccess, acquires a matching lifetime workspace lease, and then calls its single-use Start. WorkspaceAccess keeps its path slices private: construct it with NewWorkspaceAccess, and read defensive copies through WritePaths and WriteTrees. Closing an unstarted preparation releases its reservation.

The Start context governs setup through handoff only. The returned Process lives independently until wait, close, its deadline, or runner shutdown. Process deadlines are opt-in: a zero ProcessRequest.Deadline means no process deadline and is never replaced by a runner default. Session or runner shutdown still terminates the process.

Process.StreamMode distinguishes stream topology without fallback ambiguity. Pipe mode exposes distinct stdout and stderr readers. PTY mode exposes combined terminal bytes on stdout and a non-nil, closed-empty stderr; unavailable PTY support fails preparation or start instead of falling back to pipes. Process methods other than Wait are concurrency-safe; the supervisor is the sole caller of Wait and calls it exactly once. Returned stdin supports concurrent writes and close: close is idempotent, delivers EOF at most once, and makes later writes fail.

ProcessResult carries a typed terminal reason and timestamps but never an OS PID; model-facing handles belong to the supervising tools module. A process may optionally implement ProcessActivitySource. Every activity invalidates the complete bound observation cache, malformed activity is conservatively broad, and the activity channel closes before Wait returns.

Sibling packages

  • pkg/gate — the three-state evaluator that decides Deny/Gated/Allow on the typed Request produced here.
  • pkg/looploop.WithTools takes tool.Definition values; loop.WithMiddlewares takes tool.ToolMiddleware values.
  • pkg/identityidentity.AgentName used in delegation tooling.
  • github.com/looprig/tools — the standard tool implementations.
  • github.com/looprig/sandbox — satisfies the gate.AccessSource/GrantIssuer seams the gate needs to decide the requirements tools produce.

How it is designed

       tool.Definition (design-time)
                │
                │  loop.Bind (at loop start)
                ▼
       tool.InvokableTool (live)
                │
   ┌────────────┴────────────┐
   │  Optional: CallPreparer  │
   │  PrepareCall(argsJSON)   │
   │   → tool.Request          │
   │   → PreparedArtifact       │
   └────────────┬────────────┘
                │
                ▼
       pkg/gate.Evaluator
                │
                │  Deny / Gated (one combined approval) / Allow
                ▼
       InvokableRun(ctx, argsJSON)
                │
                ▼
       *tool.ToolResult  (content blocks)
Ownership: tools prepare, the gate decides, the sandbox enforces

This split is invariant in the codebase (see CLAUDE.md):

  • Tools own preparation. Each tool decodes and validates its own untrusted arguments, normalizes commands/URLs/paths, and produces the typed Request. Invalid input fails during preparation and never reaches the gate.
  • pkg/gate owns the three-state decision. It never parses tool arguments; it consumes the typed Request and applies deny-before-allow, one combined approval, response routing.
  • The sandbox owns enforcement. It satisfies gate.AccessSource/GrantIssuer without importing harness; harness never imports a sandbox package.
Sealed PreparedArtifact

PreparedArtifact is interface{ preparedArtifact() } — the method is unexported, so only types in this package can satisfy it. A type in another package cannot masquerade as a PreparedArtifact; concrete artifacts therefore live in this package alongside the seal. TokenArtifact is the minimal single-string case; richer preparers declare their own concrete artifact type here.

Strict typing, no any

ToolResult.Content is []content.Block (a sealed interface); the runner injects an "error: empty result" block when the result is nil or empty. There is intentionally no Terminate field in v1 — a turn ends only via the model emitting no more tool calls or an abort.

Documentation

Overview

Package tool defines the dependency-free contract surface for the tools subsystem: the BaseTool/InvokableTool interfaces every tool implements, the ToolResult value tools return, the tool-owned preparation boundary (CallPreparer, Request, Requirement, RuleCandidate — see preparation.go), and the optional capability interfaces the runner probes for via type assertion. It imports only core packages (plus stdlib); it must never import pkg/loop, the runtime internals, or a concrete tools module, so all of them can depend on it without a cycle.

Ownership: tools own preparation — decoding untrusted arguments, normalizing commands/URLs/paths, resolving canonical resource identities — and produce the typed prepared Request. The three-state Deny/Gated/Allow decision, the single combined approval, and response routing belong to pkg/gate; sandbox profiles and OS enforcement belong to the enforcing consumer behind structural seams; durable rule persistence is consumer-provided.

Index

Constants

View Source
const (
	// MaxProcessHandleBytes bounds the opaque URL-safe process identifier.
	MaxProcessHandleBytes = 128
	// MaxProcessDiagnosticBytes bounds the only free-form process lifecycle
	// detail admitted to durable metadata.
	MaxProcessDiagnosticBytes = 512
)
View Source
const CapabilityCommandExecute = "command.execute"

CapabilityCommandExecute is the normalized capability kind for starting a command. Every prepared command.execute requirement is command-backed: it carries GrantClassCommandStart and the exact normalized command as its grant target.

View Source
const GrantClassCommandStart = "command.start.v1"

GrantClassCommandStart is the enforcement class of a single-spawn, exact-command start grant. A saved wildcard or family rule may satisfy the gate decision, but issuance under this class remains exact-command.

View Source
const MaxModelFacingErrorBytes = 256 << 10

MaxModelFacingErrorBytes bounds an explicitly model-facing error detail at every boundary where it can be persisted or rendered. It is deliberately shared by the event codec, session runtime, and agent-tool presentation layer.

Variables

This section is empty.

Functions

func BoundModelFacingErrorDetail

func BoundModelFacingErrorDetail(value string) string

BoundModelFacingErrorDetail normalizes invalid UTF-8 and returns a complete rune prefix no larger than MaxModelFacingErrorBytes.

func ModelFacingErrorDetail

func ModelFacingErrorDetail(err error) (detail string, marked bool)

ModelFacingErrorDetail finds an explicitly marked error through ordinary Unwrap chains and errors.Join trees. It deliberately does not call errors.As: an error's custom As method is executable code and can fabricate a marker. Traversal is bounded and cycle-aware because errors are external boundary values in the session/runtime path.

func SchemaDigest

func SchemaDigest(schema json.RawMessage) (string, error)

SchemaDigest returns the lowercase hex SHA-256 of a tool's argument JSON Schema, canonicalized with encoding/json's Compact so that insignificant whitespace cannot change a tool's recorded identity. It is the identity projection used for durable external-toolset records: the schema of an externally supplied tool may embed third-party text (descriptions, defaults, examples) that must never reach the journal, so only this digest crosses the boundary.

An empty or nil schema digests as the empty byte string rather than erroring: a schema-less tool is legal (a tool taking no arguments) and has a stable identity. Invalid JSON is a typed error — an unparseable schema has no canonical form, so fabricating a digest over raw bytes would let two different schemas that differ only in whitespace claim different identities while a caller believes the digest is canonical.

func ValidateRequest

func ValidateRequest(request Request) error

ValidateRequest validates all normalized request, requirement, candidate, and exact-command grant invariants.

Types

type AgentState

type AgentState string

AgentState is the bounded mechanical lifecycle state of a persistent child agent.

const (
	AgentStateStarting    AgentState = "starting"
	AgentStateWorking     AgentState = "working"
	AgentStateIdle        AgentState = "idle"
	AgentStateUnavailable AgentState = "unavailable"
)

type ArgvRunner

type ArgvRunner interface {
	RunArgv(ctx context.Context, dir string, argv []string) (output []byte, exitCode int, err error)
}

ArgvRunner runs a direct argv (no shell interpretation) — used by Grep, whose rg invocation is already a safe argv and must not gain a shell.

type AsyncProcessRunner

type AsyncProcessRunner interface {
	PrepareProcess(context.Context, ProcessRequest) (PreparedProcess, error)
}

AsyncProcessRunner prepares enforcement for an asynchronous process without spawning it. A successful preparation reserves any enforcement resources needed for the process lifetime.

type Auditable

type Auditable interface {
	AuditSummary(argsJSON string) string
}

Auditable is implemented by tools that can emit a redacted, length-capped one-line summary of a call for the ToolCallStarted audit event. The summary must never contain secrets, full file contents, headers, or request bodies.

type BaseTool

type BaseTool interface {
	Info(ctx context.Context) (*ToolInfo, error)
}

BaseTool is the minimal contract: every tool can describe itself. It is never widened — new behavior is added via separate optional capability interfaces, never folded into BaseTool (design Rule 1 / Open-Closed).

type Bindings

type Bindings struct {
	SessionID     uuid.UUID
	LoopID        uuid.UUID
	Workspace     *WorkspaceBinding
	ReadWorkspace *ReadWorkspaceBinding
	Delegate      DelegateController
	Process       *ProcessBinding
	// ExtraTools are additional tool definitions the LOOP appends to every mode's
	// toolset at Bind, beyond the definition's own WithTools. The composition root uses
	// it to inject the derived, definition-scoped atomic agent-tool bundle (StartAgent,
	// MessageAgent, ListAgents, and StopAgent) into a loop WITHOUT mutating the immutable
	// loop definition. Per-tool factories never see it (attenuateBindings drops it); only
	// loop.Bind consumes it.
	ExtraTools []Definition
}

Bindings contains session-specific runtime capabilities supplied to a Definition. SessionID and LoopID must be non-zero. Definitions retain no Bindings between Build calls.

type CallPreparer

type CallPreparer interface {
	PrepareCall(ctx context.Context, executionID uuid.UUID, argsJSON string) (Request, PreparedArtifact, error)
}

CallPreparer is the tool-owned preparation boundary: decode and validate the untrusted argsJSON, normalize commands/URLs/paths, resolve canonical resource identities, and produce the typed access Request for this call plus an optional opaque per-call artifact the tool reads back at execution time.

The runner mints executionID once per call and invokes PrepareCall exactly once, before any permission evaluation; invalid input fails here and never reaches the gate. A pure tool returns an empty Request (no requirements). A tool that does NOT implement CallPreparer is treated as an unprepared effectful tool and fails closed: the call is never evaluated or executed.

type CommandRunner

type CommandRunner interface {
	RunCommand(ctx context.Context, dir, command string) (output []byte, exitCode int, err error)
}

CommandRunner runs a shell command in a confined environment. A nil runner means direct execution (bare-harness default). Implemented by the sandbox Executor; harness never imports sandbox — the coupling is structural (§10.1).

type Definition

type Definition interface {
	Name() string
	ProducedToolNames() []string
	ToolInfos() []ToolInfo
	Requirements() Requirements
	Build(context.Context, Bindings) ([]InvokableTool, error)
	// contains filtered or unexported methods
}

Definition is immutable tool metadata plus a factory that builds concrete, session-bound tool instances.

func NewBundleDefinition

func NewBundleDefinition(name string, producedToolNames []string, requirements Requirements, factory Factory) Definition

NewBundleDefinition returns an immutable factory-backed definition whose one build produces the declared concrete model-facing tool names. The metadata lets composition fingerprint a bundle without invoking its runtime-bound factory.

func NewDefinition

func NewDefinition(name string, requirements Requirements, factory Factory) Definition

NewDefinition returns an immutable, factory-backed definition. Validation is performed by Build so the constructor remains composable in declarative rig configuration while still returning typed failures at the runtime boundary.

func NewEvidenceDefinition

func NewEvidenceDefinition(name string, requirements Requirements, infos []ToolInfo, factory EvidenceFactory) Definition

NewEvidenceDefinition returns a sealed definition with immutable, model-facing metadata and a capability set limited to RequiresWorkspaceRead. Produced tool names are derived from infos so the two declarations cannot drift.

Validation is performed by Build, matching NewDefinition and NewBundleDefinition's declarative construction behavior.

type DelegateAgent

type DelegateAgent struct {
	AgentID        uuid.UUID
	Name           string
	AgentType      string
	State          AgentState
	QueuedMessages int
	Runtime        DelegateRuntime
	AgentMode      string
}

DelegateAgent is the bounded immutable identity and live mechanical state of one directly owned child agent.

type DelegateArtifact

type DelegateArtifact struct {
	Request DelegateRequest
	Runtime *DelegateRuntime
}

DelegateArtifact is a prepared, fully validated agent-tool call. It is created once in PrepareCall and consumed once in execution by StartAgent, MessageAgent, ListAgents, or StopAgent.

type DelegateController

type DelegateController interface {
	Execute(ctx context.Context, request DelegateRequest) (DelegateResult, error)
}

DelegateController is the only delegation capability exposed to a built tool. The runtime binds it to the tool's parent; tools never receive a session controller.

type DelegateDeliveryStatus

type DelegateDeliveryStatus string

DelegateDeliveryStatus identifies how a message reached (or failed to reach) its target. It is intentionally separate from the target response's terminal status.

const (
	DelegateDeliveryAcceptedPending DelegateDeliveryStatus = "accepted_pending"
	DelegateDeliveryInjected        DelegateDeliveryStatus = "injected"
	DelegateDeliveryQueued          DelegateDeliveryStatus = "queued"
	DelegateDeliveryRejected        DelegateDeliveryStatus = "rejected"
	DelegateDeliveryUnknown         DelegateDeliveryStatus = "delivery_unknown"
	DelegateDeliveryUntrackable     DelegateDeliveryStatus = "delivered_untrackable"
)

type DelegateOperation

type DelegateOperation uint8

DelegateOperation identifies an operation on a parent-scoped delegate.

const (
	DelegateStart DelegateOperation = iota + 1
	DelegateSend
	DelegateInterrupt
	DelegateStatus
)

type DelegateRequest

type DelegateRequest struct {
	Operation       DelegateOperation
	AgentID         uuid.UUID
	AgentType       string
	Name            string
	AgentMode       string
	Message         string
	WaitForResponse bool
	TimeoutSeconds  *int
	ParentToolUseID string
	// Runtime is the prepared, catalog-resolved agent-harness/model/effort tuple
	// for a DelegateStart; nil for every other operation and for a start with no
	// runtime choice. The controller re-resolves it against its OWN parent-scoped
	// RuntimeCatalog before applying it (defense in depth) rather than trusting
	// this value as final.
	Runtime *DelegateRuntime
}

DelegateRequest is the typed command passed to a parent-scoped delegate controller. Fields not used by the selected Operation remain zero-valued.

AgentMode is the requested initial mode for a DelegateStart; the empty string means "use the target definition's initial mode". AgentType and AgentMode carry the untrusted model selection as plain strings (this package does not import the loop/identity domain types); the controller resolves and validates them.

TimeoutSeconds bounds one response. nil means an interruptible, unbounded response (only the parent turn's own cancellation can end it); a non-nil value is a non-negative second count after which the controller returns a typed timed-out result. A negative value is invalid and rejected by the envelope boundary.

type DelegateResponseStatus

type DelegateResponseStatus uint8

DelegateResponseStatus is the terminal status of one agent response. It is intentionally separate from the persistent agent's lifecycle state.

const (
	DelegateResponseUnknown DelegateResponseStatus = iota
	DelegateResponseCompleted
	DelegateResponseInterrupted
	DelegateResponseFailed
	DelegateResponseTimedOut
)

type DelegateResult

type DelegateResult struct {
	AgentID        uuid.UUID
	Name           string
	State          AgentState
	DeliveryStatus DelegateDeliveryStatus
	Response       string
	ResponseStatus DelegateResponseStatus
	// CorrelationID is an internal command/response identity used by session
	// orchestration. Agent tools never encode or accept it.
	CorrelationID uuid.UUID
	PreviousState AgentState
	Agents        []DelegateAgent
	Truncated     bool
}

DelegateResult is the typed result of a delegate-controller operation.

type DelegateRuntime

type DelegateRuntime struct {
	Harness       string
	Profile       string
	Source        string
	SelectionKind string
	Model         string
	SmallModel    string
	Effort        string
	Explicit      DelegateRuntimeExplicit
}

DelegateRuntime is the dependency-safe representation of a resolved child runtime. The delegation package converts the loop catalog's named types into these stable aliases at the preparation boundary; pkg/tool deliberately does not import pkg/loop.

type DelegateRuntimeExplicit

type DelegateRuntimeExplicit struct {
	Harness bool
	Source  bool
	Model   bool
	Effort  bool
}

DelegateRuntimeExplicit records which selectors were supplied by the caller. Defaults are resolved into the same concrete runtime fields, but remain distinguishable for downstream pinning and audit decisions.

type DelegateStatusValue

type DelegateStatusValue uint8

DelegateStatusValue is the pre-agent mechanical/terminal vocabulary retained temporarily inside session orchestration. It is not part of DelegateResult and cannot reach model-facing agent JSON. Phase 3 removes its wait collector.

const (
	DelegateStatusUnknown DelegateStatusValue = iota
	DelegateStatusRunning
	DelegateStatusCompleted
	DelegateStatusInterrupted
	DelegateStatusFailed
	DelegateStatusTimedOut
	DelegateStatusQueued
	DelegateStatusIdle
)

type EvidenceFactory

type EvidenceFactory func(context.Context, EvidenceFactoryBindings) ([]InvokableTool, error)

EvidenceFactory builds concrete, read-only evidence tools for one invocation origin. It may be called concurrently and must not retain or mutate bindings after returning.

type EvidenceFactoryBindings

type EvidenceFactoryBindings struct {
	SessionID     uuid.UUID
	LoopID        uuid.UUID
	ReadWorkspace *ReadWorkspaceBinding
}

EvidenceFactoryBindings contains the complete capability set supplied to an evidence factory for one invocation origin. It deliberately has no generic workspace, mutation coordinator, observations, delegation, session, gate, grant, control, or extra-tool capability.

type EvidenceKindDeclarer

type EvidenceKindDeclarer interface {
	EvidenceRequirementKinds() []string
}

EvidenceKindDeclarer is an optional capability (probed by type assertion, mirroring Sequential/Auditable/WriteTarget) implemented by an evidence Definition that can enumerate — independent of any session, loop, or specific call's arguments — every Requirement.Kind value its built tools may ever produce via CallPreparer.PrepareCall. Evidence-policy construction (internal/hustleruntime) uses it to fail fast when a declared kind is outside the consumer's AllowedKinds allowlist, rather than discovering the mismatch lazily as an EvidenceFailureForbiddenCapability on the tool's first real evidence call. A Definition implementing none of this is still a fully valid evidence definition; its produced kinds are still checked per-call (unchanged).

type EvidenceObservation

type EvidenceObservation interface {
	ObservedRequirement(request Request, result *ToolResult) (target string, token string, ok bool)
}

EvidenceObservation is an optional capability (probed by type assertion, mirroring Sequential/Auditable/WriteTarget) implemented by a target-sensitive evidence tool that can derive the canonical-identity/token pair it observed for one completed call from that call's own prepared Request and produced ToolResult (design §13.4, TOCTOU). It is probed only AFTER a successful InvokableRun, from the evidence runtime's per-call loop (internal/hustleruntime/evidence_runner.go) — never before, and never for a call that failed.

Harness never computes, interprets, or canonicalizes target or token itself: deriving canonical identity and minting a stable token from observed metadata is entirely tool/consumer-owned, the same reasoning gate.EvidenceContainmentVerifier's own doc comment gives for why Harness does not own path canonicalization. This package cannot name that gate type directly (pkg/gate imports pkg/tool, so the reverse would cycle), which is why the method returns plain strings rather than a gate.ObservationRequirement — the evidence runtime, which already imports both packages, does that conversion (and validation) at the call site.

ok=false means this particular call made no target-sensitive observation (for example a glob/grep-style tool that matched many candidates rather than resolving one canonical target) — nothing is recorded, and evidence execution and authorization are completely unaffected either way. A tool implementing none of this capability simply never contributes an observation for any of its calls; it is still a fully valid evidence tool.

type Factory

type Factory func(context.Context, Bindings) ([]InvokableTool, error)

Factory builds concrete tools for one runtime binding. It may be invoked concurrently by separate Build calls and must synchronize any shared captured state. Per-build mutable state belongs inside each invocation. A Factory must not return nil tools.

type FileObservation

type FileObservation struct {
	Observed bool
	Present  bool
	Hash     [32]byte
}

FileObservation is the private concurrency token standard file tools keep for one canonical path. It never appears in model output, events, or audit summaries.

type GrantedRunner

type GrantedRunner interface {
	RunCommandWithGrants(ctx context.Context, dir, command string, grants []string) (output []byte, exitCode int, err error)
}

GrantedRunner is an optional capability (probed by type assertion) for running a command with escalation grant tokens. Wiring the grant flow is a later task; the interface lives here with the others.

type InvalidBindingsError

type InvalidBindingsError struct {
	Field string
	Cause error
}

InvalidBindingsError reports a present but invalid runtime binding.

func (*InvalidBindingsError) Error

func (e *InvalidBindingsError) Error() string

func (*InvalidBindingsError) Unwrap

func (e *InvalidBindingsError) Unwrap() error

type InvalidDefinitionError

type InvalidDefinitionError struct{ Field string }

InvalidDefinitionError reports invalid immutable definition metadata.

func (*InvalidDefinitionError) Error

func (e *InvalidDefinitionError) Error() string

type InvalidRequirementsError

type InvalidRequirementsError struct{ Unknown Requirements }

InvalidRequirementsError reports requirement bits unknown to this package.

func (*InvalidRequirementsError) Error

func (e *InvalidRequirementsError) Error() string

type InvalidSchemaError

type InvalidSchemaError struct{ Cause error }

InvalidSchemaError reports that a tool's advertised argument schema is not valid JSON, so it has no canonical form to digest. Callers errors.As it to distinguish a malformed tool self-description from a transport or build failure.

func (*InvalidSchemaError) Error

func (e *InvalidSchemaError) Error() string

func (*InvalidSchemaError) Unwrap

func (e *InvalidSchemaError) Unwrap() error

type InvokableTool

type InvokableTool interface {
	BaseTool
	InvokableRun(ctx context.Context, argsJSON string) (*ToolResult, error)
}

InvokableTool is a BaseTool that can be executed with JSON-encoded arguments. argsJSON is the untrusted, model-supplied argument object; the implementation is responsible for parsing and validating it.

type MissingBindingError

type MissingBindingError struct{ Requirement Requirements }

MissingBindingError reports a required runtime capability that was absent.

func (*MissingBindingError) Error

func (e *MissingBindingError) Error() string

type ModelFacingError

type ModelFacingError interface {
	ModelFacingError() string
}

ModelFacingError is the narrow opt-in marker for an error detail that is safe to expose to the model. The marker is intentionally not inferred from Error, a stable error kind, or any other ordinary error text.

type NilBuiltToolError

type NilBuiltToolError struct{ Index int }

NilBuiltToolError reports a nil factory result. Index is -1 when the returned slice itself is nil, otherwise it identifies the nil element.

func (*NilBuiltToolError) Error

func (e *NilBuiltToolError) Error() string

type PreparedArtifact

type PreparedArtifact interface {
	// contains filtered or unexported methods
}

PreparedArtifact is the opaque, per-call artifact a CallPreparer produces — read by the producing tool at both PrepareCall and InvokableRun time, opaque to the runner. Sealed via an unexported marker so only deliberate types satisfy it (no bare any): a type in another package cannot supply the unexported method, so it cannot masquerade as a PreparedArtifact. Concrete artifacts therefore live in this package alongside the seal.

type PreparedCall

type PreparedCall struct {
	ExecutionID uuid.UUID
	Request     Request
	Artifact    PreparedArtifact
	Grants      []string
}

PreparedCall is the prepared execution contract for one tool call: the minted execution ID, the validated typed Request, the tool's opaque per-call artifact, and — after the combined gate resolves — the fresh execution-bound grant tokens issued for THIS call. Tokens travel only here, never in an ambient grant context, a prompt, a journal, or an audit record. The Grants slice is owned by the runner; readers must not mutate it.

type PreparedProcess

type PreparedProcess interface {
	EffectiveWorkspaceAccess() WorkspaceAccess
	Start(context.Context) (Process, error)
	Close() error
}

PreparedProcess is a validated, reserved process start. Its effective workspace access is authoritative and immutable. Start consumes the preparation at most once; Close releases an unstarted preparation. EffectiveWorkspaceAccess returns a deep value that shares no mutable backing storage with the preparation or with values returned by earlier calls.

The Start context governs setup through process handoff only. Once Start returns a Process, that process lives until Wait, Close, its deadline, or runner shutdown independently of the Start context.

type Process

type Process interface {
	Stdout() io.ReadCloser
	Stderr() io.ReadCloser
	Stdin() io.WriteCloser
	StreamMode() ProcessStreamMode
	Wait(context.Context) (ProcessResult, error)
	Resize(context.Context, uint16, uint16) error
	Signal(context.Context, ProcessSignal) error
	Close(context.Context) error
}

Process is a running asynchronous process. Streams are available immediately after Start. StreamMode reports a valid, unambiguous pipe or PTY shape and must match the admitted ProcessRequest; implementations must never silently fall back to a different stream mode.

Methods other than Wait are safe to call concurrently with each other and with stream I/O. Signal targets the complete process tree. Close is idempotent. The supervising owner is the sole Wait caller and calls it exactly once; Wait confirms process-tree exit before returning.

The returned stdin supports concurrent Write and Close calls. Closing it is idempotent, delivers EOF to the process at most once, and causes later writes to fail.

Process deliberately exposes no OS process identifier. A model-facing process handle belongs in the supervising tool layer, not this runner layer.

type ProcessActivity

type ProcessActivity struct {
	Kind WorkspaceActivityKind
}

ProcessActivity reports workspace activity from a running process. Every activity invalidates the complete observation cache bound to that process; scoped observation paths are intentionally not represented.

func (ProcessActivity) EffectiveKind

func (a ProcessActivity) EffectiveKind() WorkspaceActivityKind

EffectiveKind returns the conservative activity classification. Invalid activity always maps to broad invalidation and can never narrow the immutable lifetime workspace lease.

type ProcessActivitySource

type ProcessActivitySource interface {
	Activities() <-chan ProcessActivity
}

ProcessActivitySource is an optional capability implemented by a Process that can report workspace activity. The activity channel must close before Process.Wait returns.

type ProcessBinding

type ProcessBinding struct {
	Registry SessionResourceRegistry
}

ProcessBinding contains the session-scoped capabilities supplied to process tool definitions.

type ProcessCompletionNotification

type ProcessCompletionNotification struct {
	CommandID     uuid.UUID             `json:"command_id,omitzero"`
	SessionID     uuid.UUID             `json:"session_id,omitzero"`
	LoopID        uuid.UUID             `json:"loop_id,omitzero"`
	ProcessHandle string                `json:"process_handle"`
	State         ProcessLifecycleState `json:"state"`
	Reason        ProcessTerminalReason `json:"reason"`
}

ProcessCompletionNotification is the bounded terminal notification submitted to the owning loop.

func (ProcessCompletionNotification) Validate

func (n ProcessCompletionNotification) Validate() error

Validate checks stable identity, the bounded process handle, and one of the terminal state/reason pairs accepted by completed or lost lifecycle records.

type ProcessCompletionNotifier

type ProcessCompletionNotifier interface {
	NotifyProcessCompletion(context.Context, ProcessCompletionNotification) error
}

ProcessCompletionNotifier submits a bounded process terminal notification to the process owner's loop.

type ProcessError

type ProcessError struct {
	Code  ProcessErrorCode
	Cause error
}

ProcessError reports a classified asynchronous-runner failure. Cause carries implementation detail for programmatic inspection and trusted logs; callers should expose only Code at untrusted boundaries.

func (*ProcessError) Error

func (e *ProcessError) Error() string

func (*ProcessError) Is

func (e *ProcessError) Is(target error) bool

Is matches ProcessError values by stable code.

func (*ProcessError) Unwrap

func (e *ProcessError) Unwrap() error

Unwrap returns the underlying runner failure, if any.

type ProcessErrorCode

type ProcessErrorCode string

ProcessErrorCode is a stable asynchronous-runner failure classification.

const (
	ProcessErrorLifetimeEnforcementUnavailable ProcessErrorCode = "lifetime_enforcement_unavailable"
	ProcessErrorSpawnFailed                    ProcessErrorCode = "spawn_failed"
	ProcessErrorSetupFailed                    ProcessErrorCode = "process_setup_failed"
	ProcessErrorPTYUnavailable                 ProcessErrorCode = "pty_unavailable"
	ProcessErrorSignalFailed                   ProcessErrorCode = "signal_failed"
	ProcessErrorWaitFailed                     ProcessErrorCode = "wait_failed"
	ProcessErrorTeardownFailed                 ProcessErrorCode = "teardown_failed"
)

func (ProcessErrorCode) Valid

func (c ProcessErrorCode) Valid() bool

Valid reports whether c is a recognized asynchronous-runner error code.

type ProcessLifecycleKind

type ProcessLifecycleKind uint8

ProcessLifecycleKind identifies one of the five durable process lifecycle record shapes.

const (
	ProcessLifecycleStarted ProcessLifecycleKind = iota + 1
	ProcessLifecycleBackgrounded
	ProcessLifecycleCompleted
	ProcessLifecycleStopRequested
	ProcessLifecycleLost
)

func (ProcessLifecycleKind) Valid

func (k ProcessLifecycleKind) Valid() bool

Valid reports whether k belongs to the closed lifecycle kind domain.

type ProcessLifecycleMetadata

type ProcessLifecycleMetadata struct {
	EventID           uuid.UUID             `json:"event_id,omitzero"`
	Kind              ProcessLifecycleKind  `json:"kind"`
	SessionID         uuid.UUID             `json:"session_id,omitzero"`
	LoopID            uuid.UUID             `json:"loop_id,omitzero"`
	ProcessHandle     string                `json:"process_handle"`
	OriginExecutionID uuid.UUID             `json:"origin_execution_id,omitzero"`
	State             ProcessLifecycleState `json:"state"`
	ProcessCreatedAt  time.Time             `json:"process_created_at"`
	ProcessStartedAt  time.Time             `json:"process_started_at,omitzero"`
	ProcessFinishedAt time.Time             `json:"process_finished_at,omitzero"`
	HasExitCode       bool                  `json:"has_exit_code,omitzero"`
	ExitCode          int32                 `json:"exit_code,omitzero"`
	Reason            ProcessTerminalReason `json:"reason,omitzero"`
	Diagnostic        string                `json:"diagnostic,omitzero"`
}

ProcessLifecycleMetadata is the bounded, neutral process lifecycle payload shared with Harness. It intentionally excludes commands, output, stdin, environment, host paths, spool paths, and OS process identifiers.

func (ProcessLifecycleMetadata) Validate

func (m ProcessLifecycleMetadata) Validate() error

Validate checks the closed kind/state/reason matrix, stable identity, bounded strings, process-clock ordering, and exit-code presence contract.

type ProcessLifecyclePublisher

type ProcessLifecyclePublisher interface {
	PublishProcessLifecycle(context.Context, ProcessLifecycleMetadata) error
}

ProcessLifecyclePublisher durably publishes bounded, metadata-only process lifecycle transitions.

type ProcessLifecycleState

type ProcessLifecycleState uint8

ProcessLifecycleState is the portable state carried by lifecycle records and process completion notifications.

const (
	ProcessLifecycleStarting ProcessLifecycleState = iota + 1
	ProcessLifecycleRunning
	ProcessLifecycleExited
	ProcessLifecycleFailed
	ProcessLifecycleTimedOut
	ProcessLifecycleInterrupted
	ProcessLifecycleTerminated
	ProcessLifecycleKilled
	ProcessLifecycleLostOnRestore
)

func (ProcessLifecycleState) Valid

func (s ProcessLifecycleState) Valid() bool

Valid reports whether s belongs to the closed lifecycle state domain.

type ProcessLifecycleValidationError

type ProcessLifecycleValidationError struct {
	Field string
}

ProcessLifecycleValidationError identifies the first invalid lifecycle or completion-notification field.

func (*ProcessLifecycleValidationError) Error

type ProcessRequest

type ProcessRequest struct {
	Command           string
	Directory         string
	Grants            []string
	OriginExecutionID uuid.UUID
	Deadline          time.Time
	PTY               bool
}

ProcessRequest describes one shell-agnostic asynchronous process admission. Grants are opaque, execution-bound tokens. Deadline is the process lifetime deadline. A zero Deadline means no process deadline and must never be replaced by a runner default; session or runner shutdown still terminates the process.

func (ProcessRequest) Clone

func (r ProcessRequest) Clone() ProcessRequest

Clone returns a deep copy sharing no slice backing storage with the receiver.

func (ProcessRequest) HasDeadline

func (r ProcessRequest) HasDeadline() bool

HasDeadline reports whether the request defines a process lifetime deadline.

type ProcessResult

type ProcessResult struct {
	ExitCode   int
	Reason     ProcessTerminalReason
	StartedAt  time.Time
	FinishedAt time.Time
}

ProcessResult is the terminal result of an asynchronous process. ExitCode is the portable executable exit status. StartedAt and FinishedAt are runner timestamps. OS process identifiers are intentionally excluded.

type ProcessSignal

type ProcessSignal uint8

ProcessSignal is a portable process-tree signal request.

const (
	ProcessSignalInterrupt ProcessSignal = iota + 1
	ProcessSignalTerminate
	ProcessSignalKill
)

func (ProcessSignal) Valid

func (s ProcessSignal) Valid() bool

Valid reports whether s is a recognized portable signal.

type ProcessStreamMode

type ProcessStreamMode uint8

ProcessStreamMode describes the running process's stream topology.

const (
	// ProcessStreamModePipes exposes distinct non-nil Stdout and Stderr pipe
	// readers.
	ProcessStreamModePipes ProcessStreamMode = iota + 1
	// ProcessStreamModePTY exposes combined terminal bytes through Stdout.
	// Stderr remains non-nil but is closed and empty.
	ProcessStreamModePTY
)

func (ProcessStreamMode) Valid

func (m ProcessStreamMode) Valid() bool

Valid reports whether m is a recognized process stream mode.

type ProcessTerminalReason

type ProcessTerminalReason uint8

ProcessTerminalReason classifies why a process reached a terminal state.

const (
	ProcessTerminalExited ProcessTerminalReason = iota + 1
	ProcessTerminalTimedOut
	ProcessTerminalInterrupted
	ProcessTerminalTerminated
	ProcessTerminalKilled
	ProcessTerminalRunnerShutdown
	ProcessTerminalFailed
	ProcessTerminalOutputLimit
	ProcessTerminalLostOnRestore
)

func (ProcessTerminalReason) Valid

func (r ProcessTerminalReason) Valid() bool

Valid reports whether r is a recognized terminal reason.

type ProducedToolNamesError

type ProducedToolNamesError struct {
	Kind     ProducedToolNamesErrorKind
	Index    int
	Name     string
	Declared []string
	Actual   []string
	Cause    error
}

ProducedToolNamesError reports that immutable produced-name metadata is invalid or does not exactly describe the concrete tools returned by Build. Declared and Actual contain normalized, sorted names for a set mismatch.

func (*ProducedToolNamesError) Error

func (e *ProducedToolNamesError) Error() string

func (*ProducedToolNamesError) Unwrap

func (e *ProducedToolNamesError) Unwrap() error

type ProducedToolNamesErrorKind

type ProducedToolNamesErrorKind string

ProducedToolNamesErrorKind identifies a fail-closed produced-name metadata violation discovered while binding a definition.

const (
	ProducedToolNameEmpty     ProducedToolNamesErrorKind = "declared_name_empty"
	ProducedToolNameDuplicate ProducedToolNamesErrorKind = "declared_name_duplicate"
	BuiltToolInfoInvalid      ProducedToolNamesErrorKind = "built_tool_info_invalid"
	BuiltToolNameEmpty        ProducedToolNamesErrorKind = "built_tool_name_empty"
	BuiltToolNameDuplicate    ProducedToolNamesErrorKind = "built_tool_name_duplicate"
	ProducedToolNamesMismatch ProducedToolNamesErrorKind = "produced_names_mismatch"
)

type ReadWorkspaceBinding

type ReadWorkspaceBinding struct {
	Root string
}

ReadWorkspaceBinding is the structurally read-only workspace capability supplied to evidence definitions. Root is absolute, clean, and canonicalized by the consumer before Build. This type intentionally exposes no mutation, observation, delegation, gate, grant, or control capability.

type Request

type Request struct {
	ToolName           string        `json:"tool_name,omitempty"`
	Summary            string        `json:"summary,omitempty"`
	ExecutionID        string        `json:"execution_id,omitempty"`
	Command            string        `json:"command,omitempty"`
	WorkingDirectory   string        `json:"working_directory,omitempty"`
	ExpiresAtUnixMilli int64         `json:"expires_at_unix_milli,omitempty"`
	Requirements       []Requirement `json:"requirements,omitempty"`
}

Request is a prepared, validated access request. Execution binding fields are required whenever any requirement requests a grant. They are ordinary grant inputs, never minted token material. Pure tools may prepare an empty request with no requirements.

func (Request) Clone

func (r Request) Clone() Request

Clone returns a deep copy sharing no backing storage with the receiver.

type RequestValidationError

type RequestValidationError struct {
	Kind  RequestValidationErrorKind
	Field string
}

RequestValidationError reports a prepared-request invariant violation.

func (*RequestValidationError) Error

func (e *RequestValidationError) Error() string

type RequestValidationErrorKind

type RequestValidationErrorKind string

RequestValidationErrorKind classifies an invalid prepared request.

const (
	RequestFieldInvalid          RequestValidationErrorKind = "field_invalid"
	RequestRequirementsDuplicate RequestValidationErrorKind = "requirement_duplicate"
	RequestCandidatesDuplicate   RequestValidationErrorKind = "candidate_duplicate"
	RequestGrantPairInvalid      RequestValidationErrorKind = "grant_pair_invalid"
	RequestCommandGrantInvalid   RequestValidationErrorKind = "command_grant_invalid"
	RequestGrantBindingMissing   RequestValidationErrorKind = "grant_binding_missing"
)

type Requirement

type Requirement struct {
	Kind        string          `json:"kind"`
	Scope       string          `json:"scope"`
	Match       string          `json:"match"`
	Description string          `json:"description"`
	GrantClass  string          `json:"grant_class,omitempty"`
	GrantTarget string          `json:"grant_target,omitempty"`
	Candidates  []RuleCandidate `json:"candidates,omitempty"`
}

Requirement describes one normalized capability needed by a prepared tool call. Scope is used only for access routing, Match only for stored-rule matching, and Description only for bounded display. GrantClass and GrantTarget are an optional pair: both empty means the direct tool enforces the approved resource itself, while a populated pair requests one post-decision executor grant.

func (Requirement) Clone

func (r Requirement) Clone() Requirement

Clone returns a deep copy sharing no backing storage with the receiver.

type Requirements

type Requirements uint8

Requirements is the set of runtime capabilities a Definition needs before it can build concrete tools.

const (
	// RequiresWorkspace marks definitions that build workspace-bound tools.
	RequiresWorkspace Requirements = 1 << iota
	// RequiresDelegateController marks definitions that build delegation tools.
	RequiresDelegateController
	// RequiresWorkspaceRead marks definitions that only need the canonical
	// workspace root for read-only evidence collection.
	RequiresWorkspaceRead
	// RequiresProcessServices marks definitions that build session-supervised
	// process tools.
	RequiresProcessServices
)

type RuleCandidate

type RuleCandidate struct {
	Kind        string `json:"kind"`
	Match       string `json:"match"`
	Description string `json:"description"`
	GrantClass  string `json:"grant_class,omitempty"`
	GrantTarget string `json:"grant_target,omitempty"`
}

RuleCandidate is the exact reusable allow rule displayed to the user and offered for durable persistence after a workspace approval. It contains no grant or token material; GrantClass and GrantTarget describe only the structural enforcement contract a future match must preserve.

type Sequential

type Sequential interface {
	Sequential() bool
}

Sequential is implemented by tools that must not run concurrently with other tool calls in the same batch. Sequential() reports whether this call must be serialized.

type SessionResource

type SessionResource interface {
	Activate(context.Context, SessionResourceServices) error
	Shutdown(context.Context) error
}

SessionResource is session-owned state shared by tool definitions. Activate late-binds live session services after construction and restore planning; Shutdown releases the resource during session teardown.

type SessionResourceRegistry

type SessionResourceRegistry interface {
	GetOrCreate(context.Context, string, func(string) (SessionResource, error)) (SessionResource, error)
}

SessionResourceRegistry atomically resolves one session-owned resource by key. The factory receives a private storage directory reserved for that key.

type SessionResourceServices

type SessionResourceServices struct {
	// contains filtered or unexported fields
}

SessionResourceServices is the immutable late-bound service set supplied to every session resource after the live session has been constructed. Its zero value is invalid; use NewSessionResourceServices.

func NewSessionResourceServices

func NewSessionResourceServices(
	publisher ProcessLifecyclePublisher,
	notifier ProcessCompletionNotifier,
	workflowPublisher WorkflowActivityPublisher,
) (SessionResourceServices, error)

NewSessionResourceServices constructs an immutable, fully populated service set and rejects both nil interfaces and interfaces containing typed nils.

func (SessionResourceServices) ProcessCompletionNotifier

func (s SessionResourceServices) ProcessCompletionNotifier() ProcessCompletionNotifier

ProcessCompletionNotifier returns the validated completion notifier.

func (SessionResourceServices) ProcessLifecyclePublisher

func (s SessionResourceServices) ProcessLifecyclePublisher() ProcessLifecyclePublisher

ProcessLifecyclePublisher returns the validated lifecycle publisher.

func (SessionResourceServices) Validate

func (s SessionResourceServices) Validate() error

Validate reports whether the service set is safe to give to a session resource.

func (SessionResourceServices) WorkflowActivityPublisher

func (s SessionResourceServices) WorkflowActivityPublisher() WorkflowActivityPublisher

WorkflowActivityPublisher returns the validated trusted workflow publisher.

type SessionResourceServicesValidationError

type SessionResourceServicesValidationError struct {
	Field string
}

SessionResourceServicesValidationError reports a missing or typed-nil late-bound session service.

func (*SessionResourceServicesValidationError) Error

type SkillArtifact

type SkillArtifact struct {
	Workspace bool   // true for an untrusted workspace snapshot
	RelPath   string // workspace-relative source path, e.g. ".skills/<name>/SKILL.md"
	Size      int64  // snapshot length in bytes
	SHA256    string // full hex SHA-256 of the snapshot bytes
	Body      string // the parsed markdown body to inject (the approved bytes)
}

SkillArtifact is the concrete PreparedArtifact a workspace-skill preparation step produces: a single TOCTOU-safe snapshot of an untrusted `.skills/<name>/SKILL.md` taken ONCE — before the permission prompt — and bound to the call (design §7a). It satisfies the sealed PreparedArtifact interface via the unexported preparedArtifact marker, so it must live in this package alongside the seal; a type in another package cannot supply the marker and so cannot masquerade as a PreparedArtifact.

One artifact carries BOTH halves of the load:

  • the snapshot Body — read at execution time so the bytes that run are EXACTLY the bytes that were approved (never a re-open, which a workspace writer could swap between prompt and execution);
  • the metadata + hash (RelPath, Size, SHA256) — surfaced for the human gate, which renders the metadata but never the Body.

Workspace distinguishes a workspace (untrusted, gated) snapshot from any future trusted source; embedded skills are auto-approved and need no artifact.

type TokenArtifact

type TokenArtifact struct{ Token string }

TokenArtifact is the minimal concrete PreparedArtifact: it carries a single opaque Token (e.g. a content hash or a callID-bound nonce) that the producing tool reads at both PrepareCall and InvokableRun time. It is the simplest artifact a CallPreparer can return when the bound value is a single string; richer CallPreparers declare their own concrete artifact type in this package.

type ToolExecuteFunc

type ToolExecuteFunc func(ctx context.Context, argsJSON string) (*ToolResult, error)

ToolExecuteFunc is the terminal/next step in a middleware chain: it runs the tool against argsJSON and returns its result.

type ToolInfo

type ToolInfo struct {
	Name   string
	Desc   string
	Schema json.RawMessage
}

ToolInfo is a tool's self-description. Schema is a JSON Schema describing the tool's argument object; it maps 1:1 to inference.Tool.Schema.

func (ToolInfo) Clone

func (i ToolInfo) Clone() ToolInfo

Clone returns an independent copy of i, including owned schema bytes.

type ToolMiddleware

type ToolMiddleware func(ctx context.Context, t InvokableTool, argsJSON string, next ToolExecuteFunc) (*ToolResult, error)

ToolMiddleware wraps tool execution. It receives the tool, the (untrusted) argsJSON, and the next step in the chain; it may run logic before/after and must call next to proceed (or short-circuit by not calling it).

type ToolResult

type ToolResult struct {
	Content []content.Block
}

ToolResult is the value an InvokableTool returns. Content must hold at least one block; the runner injects an "error: empty result" block when it is nil or empty. There is intentionally no Terminate field in v1 — a turn ends only via the model emitting no more tool calls or an abort.

func TextResult

func TextResult(s string) *ToolResult

TextResult is the convenience constructor for the common single-text-block result. It always returns a non-nil *ToolResult holding exactly one *content.TextBlock, even for the empty string.

type WorkflowActivityMetadata

type WorkflowActivityMetadata struct {
	EventID           uuid.UUID `json:"event_id,omitzero"`
	SessionID         uuid.UUID `json:"session_id,omitzero"`
	RunID             uuid.UUID `json:"run_id"`
	WorkflowName      string    `json:"workflow_name"`
	WorkflowVersion   string    `json:"workflow_version"`
	Kind              string    `json:"kind"`
	Status            string    `json:"status"`
	VertexID          uuid.UUID `json:"vertex_id,omitzero"`
	VertexLabel       string    `json:"vertex_label,omitempty"`
	CompletedVertices uint32    `json:"completed_vertices,omitzero"`
	TotalVertices     uint32    `json:"total_vertices,omitzero"`
	Message           string    `json:"message,omitempty"`
	OccurredAt        time.Time `json:"occurred_at"`
}

WorkflowActivityMetadata is the transport-neutral, bounded input to the trusted workflow publication seam. EventID is a stable source activity ID and must be preserved across retries; the session runtime uses the validated OccurredAt as the stable creation envelope when it stamps the sealed event.

type WorkflowActivityPublisher

type WorkflowActivityPublisher interface {
	PublishWorkflowActivity(context.Context, WorkflowActivityMetadata) error
}

WorkflowActivityPublisher durably publishes bounded workflow lifecycle metadata for one owning Harness session. The neutral DTO keeps pkg/tool below the sealed event package in the dependency graph; the session runtime converts it into event.WorkflowActivity and validates it before the Hub/journal path.

type WorkspaceAccess

type WorkspaceAccess struct {
	Kind WorkspaceAccessKind
	// contains filtered or unexported fields
}

WorkspaceAccess is the runner's authoritative, immutable description of a prepared process's workspace access. WritePaths are canonical individual paths and WriteTrees are canonical directory trees. They are meaningful only for WorkspaceAccessScopedWrite.

func NewWorkspaceAccess

func NewWorkspaceAccess(
	kind WorkspaceAccessKind,
	writePaths []string,
	writeTrees []string,
) WorkspaceAccess

NewWorkspaceAccess constructs an access description without retaining the caller's path slices.

func (WorkspaceAccess) Clone

func (a WorkspaceAccess) Clone() WorkspaceAccess

Clone returns a deep copy sharing no slice backing storage with the receiver.

func (WorkspaceAccess) WritePaths

func (a WorkspaceAccess) WritePaths() []string

WritePaths returns a defensive copy of the canonical individual write paths.

func (WorkspaceAccess) WriteTrees

func (a WorkspaceAccess) WriteTrees() []string

WriteTrees returns a defensive copy of the canonical write directory trees.

type WorkspaceAccessKind

type WorkspaceAccessKind uint8

WorkspaceAccessKind classifies authoritative process workspace access.

const (
	// WorkspaceAccessReadOnly may coexist with readers and structured writes.
	WorkspaceAccessReadOnly WorkspaceAccessKind = iota + 1
	// WorkspaceAccessScopedWrite writes only the canonical paths and trees
	// declared by WorkspaceAccess.
	WorkspaceAccessScopedWrite
	// WorkspaceAccessBroadWrite may write anywhere in the bound workspace.
	WorkspaceAccessBroadWrite
)

func (WorkspaceAccessKind) Valid

func (k WorkspaceAccessKind) Valid() bool

Valid reports whether k is a recognized workspace access classification.

type WorkspaceActivityKind

type WorkspaceActivityKind uint8

WorkspaceActivityKind classifies process-reported filesystem activity.

const (
	// WorkspaceActivityWrite reports filesystem activity within the immutable
	// access reserved by the prepared process.
	WorkspaceActivityWrite WorkspaceActivityKind = iota + 1
	// WorkspaceActivityBroadWrite requests conservative broad invalidation.
	WorkspaceActivityBroadWrite
)

func (WorkspaceActivityKind) Valid

func (k WorkspaceActivityKind) Valid() bool

Valid reports whether k is a recognized workspace activity kind.

type WorkspaceBinding

type WorkspaceBinding struct {
	Root         string
	Coordinator  WorkspaceCoordinator
	Observations WorkspaceObservations
}

WorkspaceBinding contains the workspace capabilities supplied at build time.

Observations is the loop-scoped file-observation set shared by every workspace tool bound to the SAME loop (the file toolset and Bash). It is OPTIONAL: a nil value means "no shared set", in which case the file toolset builds its own private observation map and Bash performs no invalidation (the standalone/bare path). When present it is created once per loop binding so independent file definitions and Bash share exactly the same state.

type WorkspaceCoordinator

type WorkspaceCoordinator interface {
	Acquire(ctx context.Context, operation WorkspaceOperation, canonicalPath string) (WorkspacePermit, error)
	Healthy() error
}

WorkspaceCoordinator is the narrow workspace-mutation coordination seam used by runtime-bound tools. Acquire expects WorkspaceOperationPathMutation with a non-empty canonical workspace-contained path, and WorkspaceOperationWholeMutation or WorkspaceOperationCheckpoint with an empty canonicalPath. Acquire blocks until the permit is granted or ctx is done (a done ctx returns a typed error and removes the waiter). Healthy reports whether the underlying workspace lease is healthy; a structured mutator MUST NOT commit when it returns an error (fail-secure).

type WorkspaceLifetimeCoordinator

type WorkspaceLifetimeCoordinator interface {
	AcquireLifetime(ctx context.Context, access WorkspaceAccess) (WorkspacePermit, error)
}

WorkspaceLifetimeCoordinator is the optional long-lived workspace coordination capability implemented by a WorkspaceCoordinator that can reserve a prepared process's authoritative access for its complete lifetime. AcquireLifetime blocks until the access is compatible with every active mutation and lifetime reservation, or ctx is done.

type WorkspaceObservations

type WorkspaceObservations interface {
	WithPath(canonicalPath string, fn func(*FileObservation) error) error
	InvalidateAll()
}

WorkspaceObservations is the loop-scoped file-observation set shared between one loop's file tools and opaque workspace mutators such as Bash. WithPath holds the path's critical section for the callback, allowing a tool to compare filesystem state and commit atomically relative to other operations in the same Loop.

func NewWorkspaceObservations

func NewWorkspaceObservations() WorkspaceObservations

NewWorkspaceObservations returns an empty concurrency-safe observation set for one Loop binding.

type WorkspaceOperation

type WorkspaceOperation uint8

WorkspaceOperation identifies the scope of a workspace mutation permit.

const (
	// WorkspaceOperationPathMutation permits mutation of canonicalPath. It is
	// SHARED across DIFFERENT canonical paths (many run concurrently) but
	// EXCLUSIVE on the SAME canonical path (per-path serialization), and is
	// wholly excluded by a WholeMutation or a Checkpoint permit.
	WorkspaceOperationPathMutation WorkspaceOperation = iota + 1
	// WorkspaceOperationWholeMutation permits a whole-workspace mutation (Bash and
	// other unknown-path mutators). It is EXCLUSIVE against every PathMutation and
	// against other whole/checkpoint permits.
	WorkspaceOperationWholeMutation
	// WorkspaceOperationCheckpoint is the exclusive snapshot/restore gate. Like a
	// WholeMutation it is EXCLUSIVE against every mutation and every other exclusive
	// permit, and it likewise requires an empty canonicalPath; it is a DISTINCT
	// operation so a checkpoint/restore actor names its intent (the checkpoint
	// boundary Task 15 consumes) rather than masquerading as a Bash mutation.
	WorkspaceOperationCheckpoint
)

type WorkspacePermit

type WorkspacePermit interface {
	Release()
}

WorkspacePermit is an acquired workspace mutation permit. Release is idempotent so callers may safely defer it immediately after acquisition.

type WriteTarget

type WriteTarget interface {
	WriteTarget(argsJSON string) (key string, ok bool, err error)
}

WriteTarget lets the runner group same-path mutations without importing the tools package: a write tool returns its resolved write path as key with ok=true, and the runner serializes calls sharing a key. ok=false means the call is not a write (no serialization). A non-nil err (e.g. unparseable args) is treated like invalid args: tool-result error, not executed, not grouped.

Jump to

Keyboard shortcuts

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