Documentation
¶
Overview ¶
Package agentloop implements a general-purpose agentic loop: send prompts to an LLM through llmhub, let the model invoke tools, execute them, feed the results back, and repeat until the model produces a final answer. Budgets bound every run; hooks observe every step.
Index ¶
- Variables
- func ExtractJSONObject(text string) (string, error)
- func LoadSkills(dirs ...string) ([]Skill, []SkillIssue, error)
- func Settings(opts ...Option) map[string]any
- type Agent
- func (a *Agent) Ask(ctx context.Context, query string, opts ...Option) (*Answer, error)
- func (a *Agent) AskStream(ctx context.Context, query string, opts ...Option) (<-chan Event, error)
- func (a *Agent) Conversation(opts ...Option) *Conversation
- func (a *Agent) SkillIssues() []SkillIssue
- func (a *Agent) Tools() []Tool
- type Answer
- type AnswerStatus
- type Compactor
- type Conversation
- func (c *Conversation) Ask(ctx context.Context, query string, opts ...Option) (*Answer, error)
- func (c *Conversation) AskStream(ctx context.Context, query string, opts ...Option) (<-chan Event, error)
- func (c *Conversation) Messages() []*llmhub.Message
- func (c *Conversation) Reset()
- func (c *Conversation) TotalUsage() llmhub.UsageMetadata
- type Event
- type EventKind
- type Handler
- type Hooks
- type Option
- func WithCompactor(c Compactor) Option
- func WithDupCallLimit(n int) Option
- func WithGenerateOptions(opts ...llmhub.Option) Option
- func WithHooks(h Hooks) Option
- func WithMaxSteps(n int) Option
- func WithMaxToolCalls(n int) Option
- func WithParallelTools(n int) Option
- func WithPinnedSkills(names ...string) Option
- func WithSetting(key string, value any) Option
- func WithSkills(dirs ...string) Option
- func WithStreamWatchdog(d time.Duration) Option
- func WithSystemPrompt(p string) Option
- func WithToolChoice(choice llmhub.ToolChoice) Option
- func WithTools(t ...Tool) Option
- type Skill
- type SkillIssue
- type TokenCompactor
- type Tool
- type ToolResult
Constants ¶
This section is empty.
Variables ¶
var ( // ErrInvalidInput reports invalid arguments, such as an empty query. ErrInvalidInput = errors.New("agentloop: invalid input") // ErrBudgetExhausted reports that the MaxSteps ceiling was reached // before the model produced a final answer. ErrBudgetExhausted = errors.New("agentloop: budget exhausted") // ErrSkipToolCall is returned from a BeforeToolCall hook to veto the // execution of a single tool call without aborting the run; the call's // result message says "skipped". ErrSkipToolCall = errors.New("agentloop: skip tool call") // ErrNoLLMClient reports that New was called with a nil llmhub client. ErrNoLLMClient = errors.New("agentloop: no LLM client") // ErrConversationBusy reports an Ask or AskStream started on a // Conversation while another turn is still in flight. ErrConversationBusy = errors.New("agentloop: conversation busy") // ErrStreamStale reports a streaming run aborted because no chunk // arrived within the WithStreamWatchdog window. ErrStreamStale = errors.New("agentloop: stream stale") // ErrTooManyPinnedSkills reports a WithPinnedSkills call naming more // than two skills; at most two bodies may be inlined into the system // prompt. ErrTooManyPinnedSkills = errors.New("agentloop: too many pinned skills") )
Sentinel errors reported by the loop. Every one is reachable from the error returned by Agent.Ask via errors.Is.
var ErrNoJSON = errors.New("agentloop: no JSON object found")
ErrNoJSON reports that no balanced JSON object could be extracted from model text. It is returned (possibly wrapped) by ExtractJSONObject.
Functions ¶
func ExtractJSONObject ¶
ExtractJSONObject returns the first JSON object embedded in text, tolerating the ways models wrap structured output: markdown fences (```json … ```), prose before or after the object, and balanced braces inside string literals. When several objects are present the first valid one wins. The returned substring is valid JSON; the caller still unmarshals it into its target type. When nothing extractable is present the returned error wraps ErrNoJSON.
func LoadSkills ¶
func LoadSkills(dirs ...string) ([]Skill, []SkillIssue, error)
LoadSkills scans the given skills roots — directories whose immediate children are skill folders — in precedence order and returns the valid skills plus the issues encountered on the way. Within and across roots the first occurrence of a name wins; later duplicates are reported as issues and never merged. A skill folder itself (a directory containing SKILL.md) is not a root: passed as an argument it loads nothing and is reported as exactly one SkillIssue with the reason "skill folder, not a skills root (pass its parent directory)" — pass the parent directory instead.
Loading is fail-closed: a skill folder that fails any check (frontmatter structure, name regex and directory-name equality, description length, non-empty body, NUL-byte check) is reported as a SkillIssue and omitted. The error return is reserved for systemic failures such as a permission-denied scan root; a root that is merely missing or not a directory yields an issue, not an error. Unicode Tags (U+E0001-U+E007F) and bidi overrides (U+202A-U+202E, U+2066-U+2069) are stripped from name, description, and body; when stripping changed anything an advisory SkillIssue ("unicode steganography stripped") is recorded and the sanitized skill proceeds through validation.
Types ¶
type Agent ¶
type Agent struct {
// contains filtered or unexported fields
}
Agent runs the agentic loop over an llmhub client. An Agent is immutable after construction and safe for concurrent use; per-run state lives in locals.
func New ¶
New creates a general-purpose agent over the given llmhub client with no tools and no system prompt; configure it with options. It returns an error wrapping ErrNoLLMClient when llm is nil.
func (*Agent) Ask ¶
Ask runs the loop to completion for one user query and returns the full trace. opts override the Agent's construction options for this call only; they are applied after the constructor options, so later options win. A run that aborts mid-loop (transport, hook, or context error) returns the error alongside a partial StatusFailed Answer carrying the transcript so far — check err first, but never assume the Answer is nil on error.
func (*Agent) AskStream ¶
AskStream is Ask with streaming, mirroring llmhub's streaming semantics: underlying llmhub Stream chunks are forwarded as they arrive — reasoning deltas (EventReasoningDelta) and content deltas (EventAnswerDelta) from EVERY step, each tagged with Step. Deltas from steps that end in tool calls are interim model commentary — streamed for live UIs but excluded from the final Answer.Text, which is the final step's text. Every delta event carries an llmhub Timing snapshot; Done carries the complete set. Intermediate tool/step events are also delivered so CLI/UI consumers see progress. The channel closes after Done or Error; consumers must drain it until close. A run that receives no stream chunk for the WithStreamWatchdog window aborts with ErrStreamStale.
func (*Agent) Conversation ¶
func (a *Agent) Conversation(opts ...Option) *Conversation
Conversation starts a multi-turn session. The opts set the system message (prepended exactly once and frozen for the session's lifetime) and the default per-turn options; opts passed to Ask or AskStream apply only to that turn, and a per-turn WithSystemPrompt is ignored in favor of the frozen one.
func (*Agent) SkillIssues ¶
func (a *Agent) SkillIssues() []SkillIssue
SkillIssues returns the issues skill loading recorded at construction: every SkillIssue LoadSkills produced while resolving WithSkills, in scan order. Loading is fail-closed — issues never fail construction (only an unreadable scan root does) and never change the catalog — so this accessor is how a headless consumer learns why a skill is missing without re-running the scan. The result is a snapshot copy; mutating it affects no Agent. It is nil when no skills were configured. Both presets forward WithSkills to New unchanged, so the same issues are reachable through their wrapped core Agent.
func (*Agent) Tools ¶
Tools returns the Agent's registered tools in registration order — the construction-time set, with skills resolved. The result is a copy; mutating it affects no Agent. Presets use it to re-issue one merged WithTools for an Ask (the core's WithTools replaces the set, it never merges).
type Answer ¶
type Answer struct {
// Text is the final step's assistant text (empty on graceful stops
// before the model answered).
Text string
// ReasoningText concatenates the reasoning blocks across steps.
ReasoningText string
// Steps counts model inferences (one step = one inference plus its tool
// executions).
Steps int
// ToolCalls counts executed tool invocations (skips not counted).
ToolCalls int
// Status reports how the run finished.
Status AnswerStatus
// Usage sums token and cost accounting across steps.
Usage llmhub.UsageMetadata
// Timing aggregates llmhub latency telemetry: Total and Chunks are
// summed across steps; Connected, FirstByte, and FirstToken come from
// the first step. It is nil only when the provider bypassed llmhub's
// Client wrapper.
Timing *llmhub.TimingMetrics
// Tools records every execution in order — the trace.
Tools []ToolResult
// Messages is the full transcript of the run: system (if configured),
// user, then assistant/tool messages as they were appended.
Messages []*llmhub.Message
}
Answer is the full result of one Ask: the final text plus the complete trace that produced it, making every run inspectable.
type AnswerStatus ¶
type AnswerStatus int
AnswerStatus reports how an Ask finished.
const ( // StatusCompleted means the model produced a final answer — including // the forced answer delivered under llmhub.NoToolChoice after the tool // budget ran out. StatusCompleted AnswerStatus = iota // StatusBudgetExhausted means the MaxSteps ceiling was reached first; // Ask also returns an error wrapping ErrBudgetExhausted. StatusBudgetExhausted // StatusStopped means the run stopped gracefully: a BeforeGenerate hook // set Skip, or the duplicate-call limit detected a loop. StatusStopped // StatusFailed means the run aborted before completing: a transport, // hook, compactor, or context error ended it mid-loop. Ask also returns // the error, and the Answer carries the partial transcript with its // tail repaired for the next provider request — synthesized results // for unanswered tool calls, then an abort notice. StatusFailed )
type Compactor ¶
Compactor bounds context growth. Compact runs before each model call, after BeforeGenerate appends and after the RebuildContext hook; ev.Context is pre-seeded to the current LLM view and ev.Usage carries the previous step's provider usage (zero on the first step). A compactor may replace ev.Context. Implementations must be deterministic (same input view and state → same output, or prompt caching busts), must preserve the trailing assistant tool-call message and all of its tool results, and must treat the replacement as ephemeral (the transcript, Answer.Messages, and Conversation history stay complete).
type Conversation ¶
type Conversation struct {
// contains filtered or unexported fields
}
Conversation runs a sequence of Asks over a shared message history: follow-up turns see prior turns' user messages, tool calls, results, and answers. The history starts with the system message (when configured) and each turn's own messages append to it, so the transcript is append-only — the loop never edits, reorders, or rewrites prior messages — which preserves provider prompt caching across turns. Answer.Messages carries only that run's messages (the conversation prefix excluded). A Conversation is not safe for concurrent use: an Ask or AskStream started while another turn is in flight fails fast with ErrConversationBusy.
func (*Conversation) Ask ¶
Ask runs one turn of the conversation to completion. Budgets (tool calls, steps, duplicate detection) apply per turn. Every turn joins the history once it ends: completed answers, budget-exhausted partial runs, and — the default — failed turns too. An aborted run (transport, hook, or context error) returns the error alongside a StatusFailed partial Answer whose messages join the history, closed by synthesized results for unanswered tool calls and an abort notice, so the next turn sees what was attempted.
func (*Conversation) AskStream ¶
func (c *Conversation) AskStream(ctx context.Context, query string, opts ...Option) (<-chan Event, error)
AskStream is Conversation.Ask with streaming (see Agent.AskStream): all loop events are forwarded on the returned channel, which closes after Done or Error. The turn's messages join the history when the run ends — failed turns included — just like Ask. The channel must be drained until close.
func (*Conversation) Messages ¶
func (c *Conversation) Messages() []*llmhub.Message
Messages returns the full transcript so far: the system message (when configured) plus one entry per run message across turns. The caller must not mutate the returned messages.
func (*Conversation) Reset ¶
func (c *Conversation) Reset()
Reset drops the turn history and usage totals. The system message is kept and still opens the next turn.
func (*Conversation) TotalUsage ¶
func (c *Conversation) TotalUsage() llmhub.UsageMetadata
TotalUsage returns the usage summed across all turns since construction or the last Reset.
type Event ¶
type Event struct {
Kind EventKind
// Step is the 1-based model inference the event belongs to.
Step int
// Query is the user query (Start).
Query string
// Messages is the transcript so far. For BeforeGenerate and
// AfterGenerate the list is appendable; appends become permanent
// transcript entries.
Messages []*llmhub.Message
// Context is the LLM-visible view for this step: pre-seeded to the
// transcript and replaceable by the RebuildContext hook and the
// Compactor.
Context []*llmhub.Message
// Usage is the previous step's provider usage (RebuildContext and
// Compact); zero on a run's first step.
Usage llmhub.UsageMetadata
// Skip requests a graceful stop (BeforeGenerate).
Skip bool
// Finish reports — and may be flipped to change — whether the response
// ends the run: true when it carried no tool calls (AfterGenerate).
Finish bool
// Response is the model response (AfterGenerate).
Response *llmhub.Response
// Call is the model-requested tool call (tool events).
Call *llmhub.ToolCallContent
// Args holds the raw call arguments; BeforeToolCall may rewrite them.
Args json.RawMessage
// Result is the payload delivered to the model (AfterToolCall).
Result string
// Err is the error associated with the event (AfterToolCall handler
// errors, Error).
Err error
// Duration is the tool execution's wall time (AfterToolCall).
Duration time.Duration
// Delta is the incremental streamed text (reserved for streaming).
Delta string
// Timing carries llmhub latency telemetry snapshots (reserved for
// streaming and Done).
Timing *llmhub.TimingMetrics
// Answer is the fully populated result (Done).
Answer *Answer
}
Event is the single event type passed to every hook (plain struct, not interface). Which fields are populated depends on Kind.
type Handler ¶
Handler executes a tool invocation. args is the raw JSON arguments string from the model (llmhub ToolCallContent.Arguments). The return value is inserted verbatim as the tool result message content; a returned error is delivered to the model as an {"error": ...} observation and the loop continues, except for context errors (cancellation or deadline), which abort the run.
type Hooks ¶
type Hooks struct {
// OnStart fires when a run begins.
OnStart func(ctx context.Context, ev *Event) error
// BeforeGenerate fires before each LLM call; it may APPEND to
// ev.Messages (the appends are permanent transcript entries) and may
// set ev.Skip to stop gracefully before querying.
BeforeGenerate func(ctx context.Context, ev *Event) error
// RebuildContext fires after BeforeGenerate, before each LLM call:
// ev.Messages is the full transcript (read-only) and ev.Context is
// pre-seeded to it — the hook may observe or replace ev.Context
// wholesale. Replacements are ephemeral: they affect only this call,
// while the transcript, Answer.Messages, and Conversation history stay
// untouched. Hooks must preserve the trailing assistant tool-call
// message and its tool results so the context stays answerable.
RebuildContext func(ctx context.Context, ev *Event) error
// AfterGenerate fires after each LLM response (the assistant message is
// already appended); it may APPEND to ev.Messages. Finish arrives
// pre-set to "no tool calls"; the hook may flip it either way — false
// continues the loop with the appended messages, true stops without
// executing the tools. The forced-final step after the tool budget is
// spent always finishes, veto or not.
AfterGenerate func(ctx context.Context, ev *Event) error
// BeforeToolCall fires before each tool call; it may rewrite ev.Args,
// and returning ErrSkipToolCall vetoes the execution.
BeforeToolCall func(ctx context.Context, ev *Event) error
// AfterToolCall fires when a tool execution completes; ev.Result,
// ev.Err, and ev.Duration describe the execution.
AfterToolCall func(ctx context.Context, ev *Event) error
// OnDelta receives streaming answer/reasoning deltas (EventAnswerDelta
// / EventReasoningDelta distinguishes them) in stream order, in
// addition to the AskStream channel.
OnDelta func(ctx context.Context, ev *Event) error // OnDone fires once when the run finishes; ev.Answer is fully
// populated.
OnDone func(ctx context.Context, ev *Event) error
// OnError fires once on terminal failure; ev.Err carries the error,
// which is still returned from Ask.
OnError func(ctx context.Context, ev *Event) error
}
Hooks is the extension surface of the loop: optional callbacks fired synchronously at each step. Unset fields are no-ops. A hook returning an error aborts the run and Ask returns a wrapped error naming the hook, except ErrSkipToolCall from BeforeToolCall, which merely skips that tool call. Hooks must be fast and non-blocking (BeforeToolCall may block for approval gates). The *Event is valid only during the hook call; copy anything retained.
type Option ¶
type Option func(*config)
Option configures an Agent at construction time or a single Ask at call time. Options apply in order, so per-call options (applied after the constructor's) override the Agent defaults.
func WithCompactor ¶
WithCompactor installs a Compactor that bounds context growth. It runs after the RebuildContext hook before every LLM call and may replace the view wholesale; replacements are ephemeral, exactly like the hook's (see the Compactor interface for the contract, and NewTokenCompactor for the shipped reference implementation). Unset means no compaction.
func WithDupCallLimit ¶
WithDupCallLimit sets how many consecutive identical tool calls with identical output (same name and arguments, unchanged result) are tolerated; that many repeats deliver the tool's real output with a warning appended, and one more identical call stops the run gracefully with StatusStopped before it executes. A differing call or a changed output resets the counter. 0 disables detection entirely; the default is 3.
func WithGenerateOptions ¶
WithGenerateOptions forwards llmhub options to every Generate call (for example temperature or a prompt-cache key). Repeated uses append; per-call options are appended after the Agent's, so later llmhub options win.
func WithHooks ¶
WithHooks installs hook callbacks, the library's observability and extension surface. Unset hook fields are no-ops.
func WithMaxSteps ¶
WithMaxSteps caps model inferences per Ask; values below 1 are ignored. When unset the ceiling defaults to 2*MaxToolCalls+2. Exceeding it fails the run with ErrBudgetExhausted.
func WithMaxToolCalls ¶
WithMaxToolCalls caps tool executions per Ask; the default is 5 and the minimum is 1. Values below 1 are ignored, keeping the previous setting. Once the budget is spent, the loop forces a final answer with llmhub.NoToolChoice.
func WithParallelTools ¶
WithParallelTools bounds concurrent execution of parallel-eligible tool calls (Sequential == false) per step; the default is 3 and 1 forces strictly sequential execution. Values below 1 are ignored, keeping the previous setting.
func WithPinnedSkills ¶
WithPinnedSkills inlines the full bodies of the named skills into the system prompt at construction — the sanctioned always-on exception to the load-on-demand discipline, for the couple of procedures every run needs. At most two names are allowed: New fails with an error wrapping ErrTooManyPinnedSkills when more are given, and with an error wrapping ErrInvalidInput when a name is not among the loaded skills (load them with WithSkills in the same construction). Like WithSkills this is a construction-time option; per-call use has no effect.
func WithSetting ¶
WithSetting attaches a named value to the option list on behalf of the preset that defines it. The core itself ignores settings; a preset reads them back with Settings to decode its own options before forwarding the option list to New. Later options with the same key win.
func WithSkills ¶
WithSkills enables skills: it loads the skill folders found in the given skills roots — directories whose immediate children are skill folders — in precedence order (the first occurrence of a name wins). Passing a skill folder itself instead of a root is fail-closed like any other invalid input: it is reported as a SkillIssue by LoadSkills, and construction proceeds without it. The recorded issues stay observable afterwards through Agent.SkillIssues (see the README's Skills section, "Diagnosing dropped skills"). This is a construction-time option: New consumes the directories once, appends the byte-stable "## Skills" catalog section to the system prompt, and registers the skill_read tool; passing it to Ask or AskStream has no effect. A scan root that cannot be read at all (for example permission-denied) fails construction.
func WithStreamWatchdog ¶
WithStreamWatchdog aborts AskStream when no stream chunk arrives for d — the Hermes stale-stream pattern; the default is 90s and 0 disables the watchdog. Negative durations are ignored.
func WithSystemPrompt ¶
WithSystemPrompt prepends a system message to every Ask. The prompt is byte-stable for the lifetime of an Agent; dynamic facts belong in the query text.
func WithToolChoice ¶
func WithToolChoice(choice llmhub.ToolChoice) Option
WithToolChoice sets the default tool-choice policy sent with every Generate call; the default is llmhub.AutoToolChoice. The loop forces llmhub.NoToolChoice itself when the tool budget is exhausted.
type Skill ¶
type Skill struct {
// Name is the skill identifier. It must equal the directory name and
// match ^[a-z0-9]+(-[a-z0-9]+)*$ with 1-64 characters.
Name string
// Description is the catalog line — the router. Write it
// imperatively ("Use when…"); the spec caps it at 1024 characters.
Description string
// Body is the SKILL.md markdown below the frontmatter, with
// invisible characters stripped (steganography defense).
Body string
// Dir is the resolved absolute path of the skill folder.
Dir string
// LinkedFiles lists the skill's resource files: relative slash paths
// under references/ and assets/, precomputed (symlinks resolved,
// escapes dropped, sorted) so the model never needs to list the
// directory. Scripts under scripts/ are deliberately not listed:
// they run via an execution tool and only their output enters
// context (L3).
LinkedFiles []string
// ScriptFiles lists the skill's executable helpers: relative slash
// paths under scripts/, precomputed like LinkedFiles (symlinks
// resolved, escapes dropped, sorted) so the body can reference them.
// They are advertised for execution, never for reading: run them
// through the agent's execution tool from Dir and only their output
// enters context (L3).
ScriptFiles []string
}
Skill is one validated skill folder: a repeatable procedure the model discovers through the catalog (L0) and loads on demand (L1/L2).
type SkillIssue ¶
type SkillIssue struct {
// Dir is the skill directory the issue refers to (the scan root
// itself when the directory could not be scanned).
Dir string
// Name is the skill folder name, populated for every rejection of a
// specific skill folder so a dropped skill stays identifiable. It is
// empty only for issues about the scan root itself.
Name string
// Reason is a short stable string, e.g. "bad frontmatter",
// "name mismatch", "empty body", "duplicate skill", or
// "unicode steganography stripped".
Reason string
}
SkillIssue reports a fail-closed outcome of skill loading: either a skipped skill directory and why it was rejected, or an advisory about a loaded skill whose content required sanitization. Invalid skills are omitted from the result — never an error, never a crash.
type TokenCompactor ¶
type TokenCompactor struct {
// contains filtered or unexported fields
}
TokenCompactor is the shipped reference Compactor: a deterministic, token-triggered projection that keeps long runs inside the model's context window without any LLM calls. Construct it with NewTokenCompactor and install it with WithCompactor. Its memoized checkpoint state is per run — the loop clones it at the start of each run — so a single TokenCompactor may be shared by any number of concurrent runs and Agents. (A custom Compactor that delegates to an embedded TokenCompactor bypasses that cloning: its runs would share one memo.)
func NewTokenCompactor ¶
func NewTokenCompactor(windowTokens, tailTokens int64, keepFirst int) *TokenCompactor
NewTokenCompactor returns the reference Compactor configured for a model. windowTokens is the model's usable context window; the compactor trips when the previous step's measured PromptTokens reach window − max(15% of window, 16000) and never before (a zero usage — a run's first step — is a no-op). tailTokens is the verbatim-recent-tail budget in estimated tokens (characters/4; 0 or less selects 15000). keepFirst is how many messages after the system message stay verbatim at the head of every projection (0 or less selects 1 — the task message). A non-positive windowTokens disables the compactor: it never trips.
The character/4 estimate is deliberately crude and is used only to size the tail and to decide between pruning and a checkpoint stub; the trigger itself is the exact provider-reported number.
func (*TokenCompactor) Compact ¶
func (t *TokenCompactor) Compact(ctx context.Context, ev *Event) error
Compact implements Compactor. Until the trigger trips it leaves ev.Context untouched — the no-op is the common case and costs nothing. Once tripped it replaces ev.Context with the projection and keeps projecting, with the same frozen checkpoint and tail boundary: the transcript only appends, so the projection grows at its end and the prefix stays byte-stable. When the measured size of the view trips again, the state is recomputed — the elided range grows, the tail budget stays, and the checkpoint is refrozen (one cache bust per trip).
type Tool ¶
type Tool struct {
// Def is the schema shown to the model: name, description, and JSON
// Schema parameters.
Def llmhub.Tool
// Run executes the tool.
Run Handler
// Sequential forces strict call-order execution that never overlaps
// with anything (the inverse of dsh's isConcurrencySafe, chosen so the
// Go zero value means the common default). Tools with the zero value
// are eligible for parallel batching bounded by WithParallelTools; set
// Sequential to true for stateful tools (shell sessions,
// transactions).
Sequential bool
// Repetitive exempts the tool from duplicate-call loop detection. Set
// it for tools whose job is polling or status: identical calls are
// expected, and changing output is the progress signal. The tool still
// counts against MaxToolCalls and MaxSteps like any other.
Repetitive bool
}
Tool is one model-callable tool: its llmhub definition plus the Go handler that executes it.
type ToolResult ¶
type ToolResult struct {
// Name is the tool name the model requested.
Name string
// ID is the provider call ID.
ID string
// Args are the arguments as executed (post-rewrite by BeforeToolCall).
Args json.RawMessage
// Output is the payload delivered to the model: the handler's return
// value, or the {"error": ...} / warning observation that replaced it.
Output string
// Err is the handler error, if any.
Err error
// Duration is the handler's wall time.
Duration time.Duration
}
ToolResult records one tool execution in the trace.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package bash implements the general-purpose shell preset: an agent that completes tasks by executing shell commands in an Environment until it reports completion.
|
Package bash implements the general-purpose shell preset: an agent that completes tasks by executing shell commands in an Environment until it reports completion. |
|
examples
|
|
|
ask
command
Command ask is a general-purpose runner for the three agentloop agents:
|
Command ask is a general-purpose runner for the three agentloop agents: |
|
Package search provides the search preset: an agentloop.Agent wired with web_search and fetch_page tools backed by the Serper API, plus source extraction from the tool trace.
|
Package search provides the search preset: an agentloop.Agent wired with web_search and fetch_page tools backed by the Serper API, plus source extraction from the tool trace. |