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 ¶
- Variables
- func KnownNames() []string
- func Register(cat tools.ToolCatalog, names []string) errordeprecated
- func RegisterWith(rc RegistryContext, names []string) error
- type ArtifactFetchArgs
- type ArtifactFetchOut
- type ClockNowArgs
- type ClockNowOut
- type DeclarativeActionArgs
- type DeclarativeActionOut
- type DeclarativeRepairOutcome
- type RegistryContext
- type SkillGetArgs
- type SkillGetOut
- type SkillSearchArgs
- type SkillSearchOut
- type SkillSearchResult
- type TextEchoArgs
- type TextEchoOut
- type ToolGetArgs
- type ToolGetOut
- type ToolSearchArgs
- type ToolSearchOut
- type ToolSearchResult
Constants ¶
This section is empty.
Variables ¶
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.
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.
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.
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 SkillSearchArgs ¶ added in v1.2.0
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 TextEchoArgs ¶
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 ¶
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 ToolSearchArgs ¶ added in v1.2.0
type ToolSearchOut ¶ added in v1.2.0
type ToolSearchOut struct {
Tools []ToolSearchResult `json:"tools"`
Count int `json:"count"`
}