builtin

package
v1.2.0 Latest Latest
Warning

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

Go to latest
Published: May 30, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package builtin ships the small set of opt-in tools that travel with the Harbor binary. Built-ins exist to give a freshly-scaffolded agent a zero-dependency way to prove the planner → executor → trajectory loop without forcing an operator to author Go code or attach an MCP server first.

Phase 83n / D-153. V1.1 ships two built-ins:

  • `clock.now` — returns the current UTC time as RFC 3339 + epoch milliseconds. Useful as a heartbeat / sanity-check tool.
  • `text.echo` — returns its `text` input verbatim. Useful as a smoke-test action the planner can call without side effects.

Built-ins are NEVER registered implicitly. The operator opts in via the `tools.built_in` yaml field, which the dev binary's `bootDevStack` passes to `builtin.Register(cat, names)`. An empty list registers nothing — the registry is purely additive and opt-in by design.

The §4.4 seam pattern applies in the same shape as OAuth drivers (`internal/tools/auth/drivers/oauth2`) and planner drivers (`internal/planner/react`): the `internal/config` validator mirrors `KnownNames()` so a typo in the yaml fails at validation time rather than at boot. A drift test (`builtin_test.go`) asserts the two surfaces stay in lockstep.

Concurrent reuse (D-025). Built-in tools are registered through `inproc.RegisterFunc`, which captures the closure into a fresh `ToolDescriptor` per call. The functions themselves (`clock.Now`, `text.Echo`) hold no per-invocation state and are safe for concurrent use; D-025 is trivially satisfied through the existing inproc driver's contract.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrUnknownBuiltIn is returned when a name in `tools.built_in`
	// is not in the registered set. The wrapped message lists every
	// known name so an operator sees the typo immediately.
	ErrUnknownBuiltIn = errors.New("builtin: unknown built-in tool")
	// ErrRegisterFailed wraps an underlying `inproc.RegisterFunc`
	// failure. Should be impossible at runtime (the inproc deriver
	// has unit tests against all built-in payload types) but is
	// surfaced loudly per §13 fail-loud posture.
	ErrRegisterFailed = errors.New("builtin: failed to register built-in tool")
)

Sentinel errors. Callers (`cmd/harbor/cmd_dev.go::bootDevStack`, the devstack mirror, the config validator) compare via errors.Is.

View Source
var ErrDeclarativeActionMissingTool = errors.New("declarative_action: envelope missing `tool` discriminator")

ErrDeclarativeActionMissingTool is the sentinel a declarative_action invocation returns when the operator-supplied envelope omits the `tool` discriminator. The LLM sees the structured error observation and corrects on the next turn.

View Source
var ErrDeclarativeActionReservedName = errors.New("declarative_action: planner-reserved tool name (use the native call shape instead)")

ErrDeclarativeActionReservedName is the sentinel a declarative_action invocation returns when the LLM tried to use the escape-hatch tool to invoke a planner-reserved name (`_finish` / `_spawn_task` / `_await_task`). These names are NOT in the catalog — they are planner-level primitives the projector handles natively. Emitting them through `declarative_action` is a model-side mistake; the planner surfaces the error observation + escalates `FinishRepair` (for `_finish`) so the next turn's prompt nudges the LLM toward the right channel.

View Source
var ErrIdentityRequired = errors.New("builtin: identity (tenant/user/session) is mandatory")

ErrIdentityRequired is the sentinel meta-tools surface when ctx arrives without a complete (tenant, user, session) triple. Returned instead of silently zero-quadruple-falling-through to a SkillStore call that would either error or — worse — leak across tenants (CLAUDE.md §6 rule 9 + §13).

Functions

func KnownNames

func KnownNames() []string

KnownNames returns the sorted list of built-in tool names the binary ships with. The `internal/config` validator's `allowedBuiltInTools` allowlist mirrors this slice; the `TestKnownNames_MirrorsConfigAllowlist` test enforces no drift.

func Register deprecated

func Register(cat tools.ToolCatalog, names []string) error

Register attaches each named built-in to the catalog. Equivalent to RegisterWith(ctx, names) with a zero RegistryContext — use when no builtins need the skill store.

Deprecated: prefer RegisterWith for new call sites. Kept for backward compatibility with existing tests + devstack wiring.

func RegisterWith added in v1.2.0

func RegisterWith(rc RegistryContext, names []string) error

RegisterWith attaches each named built-in to the catalog, passing the full RegistryContext so builtins that need the SkillStore (skill_search, skill_get) can reach it. Builtins that don't use the store ignore it.

Types

type ArtifactFetchArgs added in v1.2.0

type ArtifactFetchArgs struct {
	// Ref is the artifact identifier surfaced by a prior tool result's
	// fetch footer.
	Ref string `json:"ref"`
	// MaxBytes bounds the returned content. Zero / negative defaults to
	// 64 KiB; values above 1 MiB are clamped to 1 MiB.
	MaxBytes int `json:"max_bytes,omitempty"`
}

ArtifactFetchArgs is the typed input shape (the inproc deriver generates a JSON Schema the LLM sees from these tags).

type ArtifactFetchOut added in v1.2.0

type ArtifactFetchOut struct {
	Ref       string `json:"ref"`
	MIME      string `json:"mime,omitempty"`
	SizeBytes int64  `json:"size_bytes"`
	Content   string `json:"content,omitempty"`
	// Truncated is set when the artifact's full size exceeds the
	// caller's `max_bytes` request — `Content` carries the head bytes
	// only. The LLM can re-call with a larger `max_bytes` if it
	// needs more (subject to the 1 MiB hard cap).
	Truncated bool `json:"truncated,omitempty"`
	// Error carries soft failures. When set, `Content` is empty and
	// the LLM sees the error message as the observation text.
	Error string `json:"error,omitempty"`
}

ArtifactFetchOut is the typed return shape. `Error` is a soft-error channel (e.g. unknown ref, cross-tenant rejection) the LLM reads as observation text without the runtime aborting the run — same shape as the discovery meta-tools.

type ClockNowArgs

type ClockNowArgs struct{}

ClockNowArgs is the input shape for `clock.now`. The tool takes no arguments — the empty struct keeps the schema deriver (`inproc.DeriveSchema`) happy without forcing the planner to fabricate a payload.

type ClockNowOut

type ClockNowOut struct {
	RFC3339  string `json:"rfc3339"`
	EpochMS  int64  `json:"epoch_ms"`
	Timezone string `json:"timezone"`
}

ClockNowOut is the result shape for `clock.now`. Both representations are returned so callers that need a string format (logging, prompt injection) and callers that need integer math (deduplication windows, freshness checks) get the value in the shape they want without re-parsing.

func ClockNow

func ClockNow(_ context.Context, _ ClockNowArgs) (ClockNowOut, error)

ClockNow returns the current UTC time. Pure / read-only — no dependency on identity, no side effect, safe for concurrent invocation.

type DeclarativeActionArgs added in v1.2.0

type DeclarativeActionArgs struct {
	// Tool is the catalog name to dispatch. Reserved names (`_finish`,
	// `_spawn_task`, `_await_task`) return ErrDeclarativeActionReservedName
	// — they are planner-level, not catalog entries.
	Tool string `json:"tool,omitempty"`
	// Args is the JSON arguments to pass to Tool. Validated against
	// the tool's args schema before dispatch; an invalid shape returns
	// a structured `repair_outcome.args_repaired=true` observation so
	// the next planner step's prompt escalates ArgsRepair guidance.
	Args json.RawMessage `json:"args,omitempty"`
	// Body is the alternate salvage input: a raw JSON envelope (or
	// array of envelopes) that the `repair.ActionParser` parses. A
	// multi-action array trips MultiAction; a parse failure trips
	// ArgsRepair.
	Body json.RawMessage `json:"body,omitempty"`
}

DeclarativeActionArgs is the meta-tool's input envelope. Two canonical shapes the LLM can emit:

  • **Typed**: `{tool: "<catalog name>", args: {...}}`. Direct dispatch — the most common case.
  • **Salvage**: `{body: "<raw text>"}`. The body is fed through `repair.ActionParser`, which tolerates fenced JSON / prose- wrapped JSON / multi-action arrays. Used by LLMs whose instruction-following produces messier output shapes.

When both are supplied, `Tool`/`Args` win. When neither is supplied, the meta-tool returns `ErrDeclarativeActionMissingTool`.

type DeclarativeActionOut added in v1.2.0

type DeclarativeActionOut struct {
	// Dispatched is true when the inner tool's Invoke returned without
	// error. The planner / LLM treat this as a successful dispatch.
	Dispatched bool `json:"dispatched"`
	// Tool is the resolved inner-tool name (echoed for observability —
	// the trajectory step's Action carries declarative_action, not the
	// inner name, so this field surfaces the actual call target).
	Tool string `json:"tool,omitempty"`
	// Observation is the inner tool's typed result, JSON-encoded. The
	// LLM consumes this as the next turn's tool-result content.
	Observation json.RawMessage `json:"observation,omitempty"`
	// Error is the human-readable error message when Dispatched=false.
	Error string `json:"error,omitempty"`
	// RepairOutcome carries the across-step repair classification the
	// React planner reads on the next step (Phase 107c step 10 — D-167).
	// Nil means "no repair signal" (a clean dispatch resets counters
	// the same way a clean native step does).
	RepairOutcome *DeclarativeRepairOutcome `json:"repair_outcome,omitempty"`
}

DeclarativeActionOut is the meta-tool's structured observation shape. The planner walks the trajectory at the start of its next step (see `internal/planner/react/declarative_outcomes.go`) and reads `RepairOutcome` to drive the per-run RepairCounters — closing the across-step repair-escalation loop that the native main path no longer touches.

type DeclarativeRepairOutcome added in v1.2.0

type DeclarativeRepairOutcome struct {
	// ArgsRepaired is true when the inner tool's args failed schema
	// validation OR when the salvage parser could not extract an
	// envelope. Drives planner.RepairCounters.ArgsRepair.
	ArgsRepaired bool `json:"args_repaired,omitempty"`
	// MultiAction is true when the salvage parser returned more than
	// one envelope in a single body. Drives
	// planner.RepairCounters.MultiAction.
	MultiAction bool `json:"multi_action,omitempty"`
	// FinishRepair is true when the LLM tried to invoke a planner-
	// reserved finish marker (`_finish`) through declarative_action.
	// Drives planner.RepairCounters.FinishRepair so the next turn's
	// prompt nudges the LLM toward issuing a content-only terminal.
	FinishRepair bool `json:"finish_repair,omitempty"`
}

DeclarativeRepairOutcome maps onto the per-run `planner.RepairCounters` (Phase 83c — D-145). The React planner reads it at the start of the step that follows a declarative_action dispatch and bumps the matching counter; on a clean step it stays nil so the planner resets all three counters per the existing semantics.

type RegistryContext added in v1.2.0

type RegistryContext struct {
	Catalog       tools.ToolCatalog
	SkillStore    skills.SkillStore
	ArtifactStore artifacts.ArtifactStore
}

RegistryContext carries the dependencies builtins may need at registration time. All fields are optional — a builtin that doesn't use a field ignores it. Builtins that REQUIRE a field (e.g. `skill_search` needs `SkillStore`; `artifact_fetch` needs `ArtifactStore`) fail loud at invoke time with an operator-readable message when the dependency is nil.

type SkillGetArgs added in v1.2.0

type SkillGetArgs struct {
	Name string `json:"name"`
}

type SkillGetOut added in v1.2.0

type SkillGetOut struct {
	Name  string `json:"name"`
	Title string `json:"title"`
	Body  string `json:"body,omitempty"`
	Found bool   `json:"found"`
	Error string `json:"error,omitempty"`
}

type SkillSearchArgs added in v1.2.0

type SkillSearchArgs struct {
	Query string   `json:"query"`
	Tags  []string `json:"tags,omitempty"`
	Limit int      `json:"limit,omitempty"`
}

type SkillSearchOut added in v1.2.0

type SkillSearchOut struct {
	Skills []SkillSearchResult `json:"skills"`
	Count  int                 `json:"count"`
}

type SkillSearchResult added in v1.2.0

type SkillSearchResult struct {
	Name        string  `json:"name"`
	Title       string  `json:"title"`
	Description string  `json:"description"`
	Score       float64 `json:"score,omitempty"`
}

type TextEchoArgs

type TextEchoArgs struct {
	Text string `json:"text"`
	Tag  string `json:"tag,omitempty"`
}

TextEchoArgs is the input shape for `text.echo`. Both fields are emitted in the schema; `text` is the payload, `tag` is an optional caller-supplied label that round-trips alongside the echo so a planner that fans out several echo calls in parallel can tell them apart on the return.

type TextEchoOut

type TextEchoOut struct {
	Echoed string `json:"echoed"`
	Tag    string `json:"tag,omitempty"`
}

TextEchoOut is the result shape for `text.echo`.

func TextEcho

func TextEcho(_ context.Context, in TextEchoArgs) (TextEchoOut, error)

TextEcho returns the input text verbatim. Useful for smoke-testing the planner → executor → trajectory loop without an external dependency, and as a deterministic stand-in when authoring an agent before its real tools are wired.

type ToolGetArgs added in v1.2.0

type ToolGetArgs struct {
	Name string `json:"name"`
}

type ToolGetOut added in v1.2.0

type ToolGetOut struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	ArgsSchema  string `json:"args_schema,omitempty"`
	Found       bool   `json:"found"`
	Error       string `json:"error,omitempty"`
}

type ToolSearchArgs added in v1.2.0

type ToolSearchArgs struct {
	Query string   `json:"query"`
	Tags  []string `json:"tags,omitempty"`
	Limit int      `json:"limit,omitempty"`
}

type ToolSearchOut added in v1.2.0

type ToolSearchOut struct {
	Tools []ToolSearchResult `json:"tools"`
	Count int                `json:"count"`
}

type ToolSearchResult added in v1.2.0

type ToolSearchResult struct {
	Name        string   `json:"name"`
	Description string   `json:"description"`
	Tags        []string `json:"tags"`
}

Jump to

Keyboard shortcuts

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