engine

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Oct 9, 2026 License: MIT Imports: 31 Imported by: 0

README

Native Go agent engine

This package ports Pi's agent loop, Agent, proxy and stream contracts from commit eeac84ca92498ac18b6832754d01aef1d3c5f654. The server uses it by default. It directly consumes the native ai package and telemetry; it does not launch a subprocess or import application policy.

Upstream recheck

On 2026-10-05, upstream main was 200387122ca450d6387f033949423114a270b96c. Compared with the pinned reference, packages/agent and packages/telemetry changed only changelogs and package metadata (version 1.0.2); their source and tests are unchanged. The AI dependency delta was audited: thinking-level sampling defaults and summary-only reasoning selection are ported for the existing compatible adapters, with a new 14-case upstream oracle. The remaining provider delta is Bedrock thinking-block binding; Bedrock is outside the configured provider scope and has no Go adapter here. This check does not establish complete parity of the entire application with upstream main.

Boundaries

Module Responsibility
agentcore/engine Agent state, turns, streaming events, tool scheduling and validation
ai Lossless transcript, provider requests, events, OAuth account attempts
telemetry Request spans, records, attributes and trace snapshots
agentcore/plugins Composable tools and host policies
Consuming application Model ladders, credentials, durable sessions, admission, children and summaries

The engine uses concrete structs and callbacks. Retry, pricing, permissions, compaction, persistence and application tools belong to the host. Tests enforce these dependency boundaries.

Public behavior

Run and Continue execute the low-level loop. AgentLoop and AgentLoopContinue expose event streams. Agent adds persistent state, steering, follow-up, queue modes, hooks and explicit continuation. RunToolCall applies argument preparation, validation and before/after hooks to programmatic and nested calls as well as model-generated calls.

Transcripts preserve block ordering, thinking and tool signatures, tool IDs, system/tool declaration changes, usage, extension fields and explicit nulls. MessageList and concrete value containers retain the live identity semantics needed by the port. Provider messages pass through ai.TranscriptContext; the legacy ChatRequest projection is not used by the native engine.

Provider message and block references remain live across loop events. Start and update events shallow-copy the message envelope; the final message is the provider's original result. Event sinks run under the provider payload lock. Use event.Await(work) to release that lock while waiting for provider work; access live fields only before or after that call. Outside the sink, use the retained event's Synchronize method to inspect its payload. A result wait does not consume FIFO events. Producer settlement is observed separately from terminal-message publication; reader cancellation does not implicitly cancel a provider.

The host commits a selected model before admitting its tool effects. Durable message/effect records, leases and model-selection records support resume. Retries stop after visible output; each child owns its ladder, tools, session and credential binding. Summaries use their own tier and no executable tools.

Provider scope

  • Codex: native Responses SSE/WebSocket and account-pool lifecycle.
  • Claude Code: native Anthropic events, CLI OAuth identity and account rotation.
  • Antigravity: native Cloud Code request envelope, account/project pairing, thinking signatures, tool calls, usage and terminal validation.
  • Gemini: native OpenAI-compatible API using the configured Google endpoint.

Other existing Go adapters remain where callers still use them; expanding the Pi provider catalog is outside this completion scope. The server always uses the native engine; the runtime-selection environment flag and legacy execution branch have been removed. Native history is revision-checked; legacy history requires an explicit migration or a fresh conversation.

Verification and provenance

go test -race ./...
go vet ./...
go run ./examples/scripted

The ordinary Go suite includes the former pi_native integration contracts. Recorded JSON fixtures cover agent/loop behavior, transcript and reference semantics, validation, provider events, telemetry and host callbacks. HTTP and runner tests cover credentials, tools, children, summaries, errors, cancellation, accounting and durable recovery.

Twelve pinned-source ownership cases cover retained provider messages and edits to timestamp, text and usage from message_start, message_update, message_end and finishTurn. They supplement the stream-alias fixtures with cross-event sharing and result-identity checks. Gated native tests cover yielding to a provider from an event sink, reads of the terminal response under its payload lock, and provider work continuing after the terminal event. Direct reads or edits from other goroutines still require caller synchronization.

Another 48 pinned-source cases cover content-array mutation and replacement across assistant events, finish hooks and tool-result message events. ai.MessageContent.Blocks and ToolResult.Content use the same concrete *ai.BlockList, so edits to membership or length reach retained references. Replacing the whole content list leaves earlier references on the old list. Provider and snapshot tests also check growth, sparse holes and shared-list identity; callers use list methods instead of slice indexing or append.

Forty-five pinned-source prompt cases cover Unicode in string content and system-section names/values, before and after mutation, through prompt replay and state JSON. Lone UTF-16 units retain their JSON escapes instead of becoming replacement characters. Native host tests cover prompt and initial-system-prompt decoding through the stream callback boundary and state export.

Forty-eight pinned-source failure cases cover thrown errors/strings, provider error messages and listener edits. They compare events, state at every event, and final state, including empty messages and lone UTF-16 units. Failure-message construction and state export retain the same code units as the transcript.

Twelve pinned-source tool runs cover Unicode names, call IDs and labels through declaration removal, execution updates, pending-call sets and final state. Removal references retain their extension metadata and decoded names, so replay deletes the named tool without changing its identity during JSON import/export.

Eight pinned-source metadata runs cover distinct UTF-16 property names across messages, blocks, tool declarations/results and usage/cost extensions. They compare provider inputs, events and final state after listener edits. Typed envelopes use the shared raw-object codec so opaque keys survive without decoding their values; duplicate keys retain the last value.

The TypeScript source, worker, fixture generators and Bun configuration have been removed. third_party/pi/UPSTREAM.json preserves hashes of the 412 original files verified before removal. LICENSE.pi retains attribution. Fixture agreement does not establish universal byte parity: arbitrary JavaScript objects/prototypes, platform stacks and every malformed transport input are not equivalent to Go values or diagnostics. The port preserves the recorded contracts and supported native execution paths.

Documentation

Overview

Code generated by testdata/generate-format-patterns.ts; DO NOT EDIT. Expressions from TypeBox 1.3.27; see LICENSE.typebox.

Code generated by testdata/generate-unicode-properties.ts; DO NOT EDIT. Pi eeac84ca92498ac18b6832754d01aef1d3c5f654; Bun 1.3.14; Unicode 15.1. Property aliases: Unicode 15.1 UCD; see testdata/unicode-15.1/LICENSE.txt.

Package engine is the native Go port of Pi's agent state machine. It works with the lossless ai transcript rather than the legacy agentcore messages.

Index

Constants

View Source
const Null = jsonjs.Null
View Source
const Undefined = jsonjs.Undefined

Variables

This section is empty.

Functions

func MessagePointers

func MessagePointers(messages []ai.Message) []*ai.Message

MessagePointers exposes the entries of a value slice to the native loop. The loop and its callbacks retain these objects; callers must synchronize any concurrent writes. Replacing a list entry does not replace that object.

func MessageValues

func MessageValues(messages []*ai.Message) []ai.Message

MessageValues projects shared message objects at a snapshot or provider boundary. Nested payloads remain shared and must be treated as immutable.

func SetDefaultStreamFn

func SetDefaultStreamFn(stream StreamFn)

SetDefaultStreamFn supplies Pi's process-wide fallback. Passing nil clears it. Per-run stream arguments take precedence and avoid global configuration.

func StreamProxy

StreamProxy reconstructs assistant messages in Go, without a JS worker. It returns immediately; request/protocol failures settle an error event. Partial pointers stay live. Use the stream's snapshot methods or Synchronize when inspecting payloads while the producer is still running.

Types

type AdmitRequestFn

type AdmitRequestFn func(context.Context, Request, map[string]any) (*RequestAdmission, error)

type AfterToolCall

type AfterToolCall struct {
	BeforeToolCall
	// Result refers to the executed result object. Mutations survive even when
	// After returns nil; a returned override is applied to that mutated value.
	// Replacing this pointer only changes the hook's context, as in Pi.
	Result  *ToolResult
	IsError bool
}

type AfterToolResult

type AfterToolResult = ToolResult

type Agent

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

Agent owns transcript state, queues and awaited subscriptions. It invokes the native Go loop directly; no worker process or legacy Agent is involved.

func NewAgent

func NewAgent(options AgentOptions) (*Agent, error)

func (*Agent) Abort

func (a *Agent) Abort()

func (*Agent) ClearAllQueues

func (a *Agent) ClearAllQueues()

func (*Agent) ClearFollowUpQueue

func (a *Agent) ClearFollowUpQueue()

func (*Agent) ClearSteeringQueue

func (a *Agent) ClearSteeringQueue()

func (*Agent) Configure

func (a *Agent) Configure(options AgentConfig) error

func (*Agent) Continue

func (a *Agent) Continue(ctx context.Context) error

func (*Agent) FollowUp

func (a *Agent) FollowUp(message *ai.Message)

func (*Agent) FollowUpMode

func (a *Agent) FollowUpMode() string

func (*Agent) HasQueuedMessages

func (a *Agent) HasQueuedMessages() bool

func (*Agent) PeekQueuedMessages

func (a *Agent) PeekQueuedMessages() []*ai.Message

func (*Agent) Prompt

func (a *Agent) Prompt(ctx context.Context, input any, images ...*ai.ContentBlock) error

Prompt accepts text, message values/references, native slices or a MessageList. A MessageList preserves live prompt membership during initial event delivery; native slice inputs copy membership. Both retain their message objects. Images accompany text input. Their list membership is copied, while the supplied block objects remain shared with events, history and requests. A nil image argument is an explicitly present null entry. It blocks until the run and its listeners settle. Admission errors are returned directly; run failures become Pi's assistant failure lifecycle.

func (*Agent) Reset

func (a *Agent) Reset() error

func (*Agent) SetFollowUpMode

func (a *Agent) SetFollowUpMode(mode string)

func (*Agent) SetMessageList

func (a *Agent) SetMessageList(messages *MessageList)

SetMessageList mirrors assigning a state array, including its sparse slots. Assignment detaches the outer collection and retains message objects.

func (*Agent) SetMessages

func (a *Agent) SetMessages(messages []*ai.Message)

func (*Agent) SetModel

func (a *Agent) SetModel(model json.RawMessage)

func (*Agent) SetSteeringMode

func (a *Agent) SetSteeringMode(mode string)

func (*Agent) SetThinkingLevel

func (a *Agent) SetThinkingLevel(level string)

func (*Agent) SetToolList

func (a *Agent) SetToolList(tools *ToolList)

func (*Agent) SetTools

func (a *Agent) SetTools(tools []*Tool)

func (*Agent) Signal

func (a *Agent) Signal() context.Context

func (*Agent) State

func (a *Agent) State() State

func (*Agent) Steer

func (a *Agent) Steer(message *ai.Message)

func (*Agent) SteeringMode

func (a *Agent) SteeringMode() string

func (*Agent) Subscribe

func (a *Agent) Subscribe(listener *Listener) func()

func (*Agent) WaitForIdle

func (a *Agent) WaitForIdle(ctx context.Context) error

WaitForIdle observes the current run, including awaited agent_end listeners. Cancelling the wait does not cancel the agent itself.

type AgentConfig

type AgentConfig struct {
	Config
	StreamFn                   StreamFn
	PrepareNextTurn            func(context.Context) (*TurnUpdate, error)
	PrepareNextTurnWithContext func(context.Context, *Turn) (*TurnUpdate, error)
	SteeringMode               string
	FollowUpMode               string
	SessionID                  *string
	ThinkingBudgets            json.RawMessage
	Transport                  string
	MaxRetryDelayMS            *int
}

AgentConfig configures the stateful wrapper. Config supplies the shared loop hooks and stream options; state and owned queues override its model/queue fields. Configure replaces these options atomically for future runs. An already-installed preparation hook reads the latest wrapper callbacks.

type AgentEventStream

type AgentEventStream struct {
	*ai.EventStream[Event, *MessageList]
	// contains filtered or unexported fields
}

AgentEventStream is Pi's unbounded event queue with an independent final message result. Reading events is optional; Result does not consume them. A terminal agent_end settles Result before the producer itself returns. Result and the terminal event retain the same live *MessageList.

Wait observes producer completion, including Go errors and panics. Like Pi's wrapper, a rejected low-level loop does not invent agent_end, close the event queue, or settle Result. Use Wait to observe such contract failures and a cancellable context when waiting for events/results that may never arrive. Provider error/aborted messages are normal terminal results, not failures.

func AgentLoop

func AgentLoop(ctx context.Context, prompts *MessageList, initial Context, config Config, provider StreamFn) *AgentEventStream

AgentLoop starts the low-level loop asynchronously. It queues agent_start before returning, as the original wrapper does before its first await. Cancelling a reader does not abort the run; ctx controls the provider/tools.

func AgentLoopContinue

func AgentLoopContinue(ctx context.Context, initial Context, config Config, provider StreamFn) (*AgentEventStream, error)

AgentLoopContinue validates the existing tail synchronously, then continues without re-emitting old messages. Custom roles are validated by ConvertToLLM at the request boundary, exactly as in Continue.

func (*AgentEventStream) Wait

func (s *AgentEventStream) Wait(ctx context.Context) error

type AgentOptions

type AgentOptions struct {
	InitialState InitialState
	AgentConfig
}

type Array

type Array = jsonjs.Array

func NewArray

func NewArray(values ...any) *Array

type BeforeToolCall

type BeforeToolCall struct {
	AssistantMessage *ai.Message
	// ToolCall is the original block. Field edits survive into execution and
	// lifecycle events; replacing this hook's pointer does not replace it.
	ToolCall *ai.ContentBlock
	// Args contains the validated *Object, *Array or primitive value. Nested
	// edits are shared with execution and After without revalidation. Assigning
	// a different value only replaces this hook context's field, as in Pi.
	// ToolCall.Arguments remains the separate raw input.
	Args    any
	Context *Context
}

type BeforeToolResult

type BeforeToolResult struct {
	Block     bool
	Reason    string
	Terminate bool
}

type Config

type Config struct {
	Model         json.RawMessage
	Reasoning     string
	Options       map[string]any
	ToolExecution string
	ToolHooks
	ConvertToLLM     func(*MessageList) (*MessageList, error)
	TransformContext func(context.Context, *MessageList) (*MessageList, error)
	GetAPIKey        func(string) (string, error)
	FinishTurn       func(context.Context, *Turn) (string, error)
	PrepareRequest   func(context.Context, Request) (*TurnUpdate, error)
	// AdmitRequest, when supplied by a Go host, owns prepare/transform/convert,
	// credential acquisition and streaming. The nil path follows Pi unchanged.
	// It must return the selected request before any of its events are consumed.
	AdmitRequest        AdmitRequestFn
	PrepareNextTurn     func(*Turn) (*TurnUpdate, error)
	GetSteeringMessages func() (*MessageList, error)
	GetFollowUpMessages func() (*MessageList, error)
	// Now supplies Date.now for deterministic replay and differential testing.
	Now func() int64
	// contains filtered or unexported fields
}

type Context

type Context struct {
	Messages *MessageList `json:"messages"`
	Tools    *ToolList    `json:"-"`
	// contains filtered or unexported fields
}

Context retains live message/tool collections shared with hooks. Replacing a field detaches that collection; edits through a retained list remain visible.

type Event

type Event struct {
	Type                  string
	Message               *ai.Message
	Messages              *MessageList
	ToolResults           *MessageList
	AssistantMessageEvent *ai.AssistantMessageEvent
	ToolCallID            string
	ToolName              string
	Args                  json.RawMessage
	Result                *ToolResult
	PartialResult         *ToolResult
	IsError               bool
	// contains filtered or unexported fields
}

func (Event) Await

func (e Event) Await(work func() error) error

Await yields a provider payload lock while a synchronous event sink waits, then reacquires it before returning. Do not inspect live message fields in work. Non-provider events call work directly. A provider event's Await is scoped to its sink invocation and must not be retained for later use.

func (Event) MarshalJSON

func (e Event) MarshalJSON() ([]byte, error)

MarshalJSON emits exactly the fields belonging to each Pi event variant.

func (Event) Synchronize

func (e Event) Synchronize(read func())

Synchronize protects access to a retained provider event's live payload. Sinks already hold this lock; use this method only outside the sink callback. Like Pi, retaining an event does not detach its nested messages or blocks.

type EventSink

type EventSink func(Event) error

EventSink is awaited. Parallel tools may enter it concurrently; a sink that keeps mutable state must synchronize its own writes.

type InitialState

type InitialState struct {
	SystemPrompt  string
	Model         json.RawMessage
	ThinkingLevel string
	Tools         []*Tool
	Messages      []*ai.Message
}

type JSONMethod

type JSONMethod = jsonjs.JSONMethod

type List

type List[T any] struct {
	// contains filtered or unexported fields
}

List preserves collection identity when entries or length change. Container operations are synchronized; callers still synchronize edits to shared items. Values returns a detached, dense slice; Has distinguishes holes from zero T.

func NewList

func NewList[T any](values ...T) *List[T]

func (*List[T]) Append

func (l *List[T]) Append(values ...T) int

func (*List[T]) Clone

func (l *List[T]) Clone() *List[T]

Clone copies indexed entries, retaining item references and sparse slots. Like Array.slice, it drops ordinary named properties.

func (*List[T]) Delete

func (l *List[T]) Delete(index int)

func (*List[T]) DeleteProperty

func (l *List[T]) DeleteProperty(name string)

func (*List[T]) Get

func (l *List[T]) Get(index int) T

func (*List[T]) GetProperty

func (l *List[T]) GetProperty(name string) (T, bool)

GetProperty reads indexed or ordinary enumerable data properties, excluding inherited properties and the reserved length property.

func (*List[T]) Has

func (l *List[T]) Has(index int) bool

func (*List[T]) Keys

func (l *List[T]) Keys() []int

func (*List[T]) Len

func (l *List[T]) Len() int

func (*List[T]) MarshalJSON

func (l *List[T]) MarshalJSON() ([]byte, error)

func (*List[T]) PropertyKeys

func (l *List[T]) PropertyKeys() []string

PropertyKeys includes enumerable own data properties in JavaScript key order. Keys returns only array indices. Neither method includes the length property.

func (*List[T]) Set

func (l *List[T]) Set(index int, value T)

func (*List[T]) SetLength

func (l *List[T]) SetLength(length int)

func (*List[T]) SetProperty

func (l *List[T]) SetProperty(name string, value T)

SetProperty defines an enumerable own data property, including names such as __proto__; it does not invoke JavaScript prototype setters or accessors.

func (*List[T]) UnmarshalJSON

func (l *List[T]) UnmarshalJSON(raw []byte) error

func (*List[T]) Values

func (l *List[T]) Values() []T

type Listener

type Listener struct {
	Handle func(context.Context, Event) error
}

Listener has identity independently of its Go callback. Subscribing the same pointer twice is idempotent, as with Pi's Set of listener functions.

type MessageList

type MessageList = List[*ai.Message]

func Continue

func Continue(ctx context.Context, initial Context, config Config, emit EventSink, stream StreamFn) (*MessageList, error)

Continue reuses the transcript without emitting the existing prompt again.

func Run

func Run(ctx context.Context, prompts *MessageList, initial Context, config Config, emit EventSink, stream StreamFn) (*MessageList, error)

Run starts a prompt invocation. Its returned list is shared with Turn.NewMessages and the agent_end event, and excludes pre-existing history. The prompt remains live during initial event iteration unless declaring tools creates a replacement list. Context/result membership is copied before events. Hooks and sink failures propagate without inventing agent_end.

type Object

type Object = jsonjs.Object

Object and Array retain nested identity across argument hooks and execution. Callers must synchronize concurrent access to shared values.

func NewObject

func NewObject(properties ...Property) *Object

type Property

type Property = jsonjs.Property

type ProxyStreamOptions

type ProxyStreamOptions struct {
	ProxyURL  string
	AuthToken string
	Options   map[string]any
	Client    *http.Client
	Now       func() int64
}

ProxyStreamOptions configures Pi's compact HTTP streaming protocol. Options holds provider controls; only the protocol's explicit allowlist is sent. Client and Now are optional Go transport/clock dependencies.

type Request

type Request struct {
	Context       *Context
	Model         json.RawMessage
	ThinkingLevel string
}

type RequestAdmission

type RequestAdmission struct {
	Request Request
	Stream  *ai.AssistantMessageEventStream
}

RequestAdmission is a Go host extension for orchestration which can select a different prepared request before releasing provider events. Context contains native messages/tools, not the transformed provider-only transcript.

type State

type State struct {
	Model            json.RawMessage `json:"model,omitempty"`
	ThinkingLevel    string          `json:"thinkingLevel"`
	Tools            *ToolList       `json:"tools"`
	Messages         *MessageList    `json:"messages"`
	IsStreaming      bool            `json:"isStreaming"`
	StreamingMessage *ai.Message     `json:"streamingMessage,omitempty"`
	PendingToolCalls *ToolCallSet    `json:"pendingToolCalls"`
	ErrorMessage     *string         `json:"errorMessage,omitempty"`
}

State snapshots scalar fields while retaining live message/tool collections and message objects. List operations are synchronized; callers synchronize concurrent item edits. Use SetMessages/SetTools to replace a collection.

func (State) MarshalJSON

func (s State) MarshalJSON() ([]byte, error)

func (State) SystemPrompt

func (s State) SystemPrompt() string

SystemPrompt replays the current collection lazily, like Pi's state getter. Holding State retains that collection even when the agent later replaces it.

type StreamFn

type StreamFn = ai.StreamFn

StreamFn must encode provider failures as error/aborted stream messages. A returned Go error is a contract failure and interrupts the low-level loop. Models and option fields remain raw to retain provider-specific metadata.

func GetDefaultStreamFn

func GetDefaultStreamFn() (StreamFn, error)

type Tool

type Tool struct {
	ai.Tool
	Label            string
	OutputSchema     json.RawMessage
	Replay           string
	ExecutionMode    string
	PrepareArguments func(json.RawMessage) (json.RawMessage, error)
	// Execute shares validated *Object/*Array values with hooks; primitives
	// are passed by value. Nested edits are shared, local reassignment is not.
	// Updates share result objects with hooks and events.
	// Tools must synchronize access when retaining or sharing these pointers.
	Execute func(context.Context, string, any, func(*ToolResult)) (*ToolResult, error)
}

Tool is shared by pointer. Preparation selects the object before invoking argument and lifecycle hooks; later field edits affect that selected tool, while replacing its entry in Context.Tools only affects future selection. Callers must synchronize concurrent mutation of tool definitions/callbacks.

func (Tool) MarshalJSON

func (t Tool) MarshalJSON() ([]byte, error)

type ToolCallSet

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

ToolCallSet is an immutable, insertion-ordered set of executing tool call IDs. Agent retains the same set between tool events and replaces it at every start, end, reset and run settlement. Retained sets can be read concurrently.

func (*ToolCallSet) Has

func (s *ToolCallSet) Has(id string) bool

func (*ToolCallSet) Len

func (s *ToolCallSet) Len() int

func (*ToolCallSet) MarshalJSON

func (s *ToolCallSet) MarshalJSON() ([]byte, error)

MarshalJSON matches JSON.stringify on Pi's Set. Transport adapters that need an array of IDs must explicitly serialize Values instead.

func (*ToolCallSet) Values

func (s *ToolCallSet) Values() []string

Values returns a detached slice in insertion order.

type ToolHooks

type ToolHooks struct {
	Before func(context.Context, *BeforeToolCall) (*BeforeToolResult, error)
	After  func(context.Context, AfterToolCall) (*AfterToolResult, error)
}

type ToolList

type ToolList = List[*Tool]

type ToolOutcome

type ToolOutcome struct {
	ToolCall *ai.ContentBlock `json:"toolCall"`
	Result   *ToolResult      `json:"result"`
	IsError  bool             `json:"isError"`
}

func RunToolCall

func RunToolCall(ctx context.Context, call *ai.ContentBlock, tools *ToolList, assistant *ai.Message, current *Context, hooks ToolHooks, update func(*ToolResult) error) (ToolOutcome, error)

RunToolCall applies the same preparation, validation and hooks as a model call, without emitting lifecycle events or appending transcript messages. The input call, hook call and outcome call retain the same pointer. The selected tool object survives replacement of its entry in tools. A nil tools argument uses current.Tools; an explicit empty list overrides it. Results and updates retain the tool's pointers unless a hook overrides or fails. An update callback's panic propagates synchronously to the executing tool; a returned error represents a rejected update promise and rejects the call after execution settles. Tools that launch goroutines own their panic boundary.

type ToolResult

type ToolResult struct {
	Content           *ai.BlockList `json:"content"`
	Details           any           `json:"-"`
	StructuredContent any           `json:"-"`
	Usage             *ai.Usage     `json:"usage,omitempty"`
	IsError           *bool         `json:"isError,omitempty"`
	Terminate         *bool         `json:"terminate,omitempty"`
	// contains filtered or unexported fields
}

ToolResult shares its content list, Details and StructuredContent with hooks/events. Values use *Object, *Array or primitives. nil/Undefined omit an optional field; Null retains explicit JSON null. Concurrent access needs synchronization.

func (ToolResult) MarshalJSON

func (r ToolResult) MarshalJSON() ([]byte, error)

func (*ToolResult) UnmarshalJSON

func (r *ToolResult) UnmarshalJSON(raw []byte) error

JS tools can return additional result metadata and explicit nulls. Keep only fields the typed projection would omit. Dynamic value fields decode into the shared live graph and do not need another retained raw copy. Native field updates take precedence, and copies of a result share only immutable source metadata.

type Turn

type Turn struct {
	Message     *ai.Message
	ToolResults *MessageList
	Context     *Context
	NewMessages *MessageList
}

Turn is shared by pointer between FinishTurn and PrepareNextTurn. Its initial fields reference the completed message, per-turn tool results, active context and accumulating run result. Replacing fields changes this retained view, not the loop's own references; context replacement takes effect through TurnUpdate. Edits belong to awaited callbacks; concurrent access requires synchronization.

type TurnUpdate

type TurnUpdate struct {
	Context *Context
	// Messages stays live until the loop spreads prepared and pending messages.
	Messages      *MessageList
	Model         json.RawMessage
	ThinkingLevel *string
}

Jump to

Keyboard shortcuts

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