persist

package
v0.1.0-beta.10 Latest Latest
Warning

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

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

Documentation

Overview

Package persist is the JSONL run-state ledger for the gridctl agent runtime. Every run owns a single append-only file at ~/.gridctl/runs/<run_id>.jsonl; each line is a typed Event encoding one observable boundary in the run's lifecycle (node entry/exit, a tool or LLM invocation, a structured-output validation, an approval gate exchange, or a terminal error).

The format is the source of truth for time-travel resume: replaying events in order reconstructs the run's state, which the runtime then hands to the eino adapter's checkpoint/resume surface (see pkg/agent/internal/eino) to continue execution from a chosen step.

Persistence is intentionally additive — events are append-only so a crash mid-write can never corrupt earlier history; consumers ignore trailing partial lines on read. The JSONL shape also gives operators a tail-and-grep affordance without bringing up the daemon.

Index

Constants

View Source
const MaxPayloadBytes = 64 * 1024

MaxPayloadBytes caps the verbatim slice of `Arguments` / `Output` an event carries. The cap protects the ledger from authors feeding a multi-megabyte file read or LLM response straight into a tool call — the JSONL file would balloon, the SSE stream would stall, and the IDE would gag on a single 50 MB line. 64 KB is large enough to keep the common path lossless (typical tool args and prose outputs) and small enough that a truncated payload still renders quickly.

Variables

This section is empty.

Functions

func CapRawJSON

func CapRawJSON(raw []byte) (json.RawMessage, bool)

CapRawJSON returns a value safe to store as a payload field of type json.RawMessage, along with a flag indicating whether the original exceeded the cap. Over-cap values are replaced with a JSON-encoded string of the first MaxPayloadBytes — that keeps the field syntactically valid JSON (so readers don't choke) while flagging loss. Callers should also set their payload's Truncated field when the returned flag is true.

func CapString

func CapString(s string) (string, bool)

CapString returns the first MaxPayloadBytes of s and a flag noting whether the original was longer. Used for plain-text payload fields (e.g. ToolResultPayload.Output) where the cap is purely a length trim — no JSON-quoting is needed.

func DefaultRunsDir

func DefaultRunsDir() string

DefaultRunsDir resolves the default runs directory under the user's home. Mirrors pkg/state.BaseDir(); duplicated here to keep pkg/agent/persist independent of pkg/state (the agent runtime must remain importable from packages that pkg/state itself depends on).

func NewRunID

func NewRunID() string

NewRunID produces a fresh run identifier in the shape "run_<rfc3339-compact>_<8-byte-hex>". The compact RFC 3339 prefix keeps `runs list` lexicographically sortable by start time without reading the file body.

Types

type ApprovalRequestPayload

type ApprovalRequestPayload struct {
	// ApprovalID is the handle the CLI / web-UI / MCP consumer uses
	// to respond. Format is run-id-scoped: ULID-shaped, deterministic
	// across replay so a resumed run sees the same handle.
	ApprovalID string `json:"approval_id"`

	// Prompt is the human-readable description of what's being
	// approved. Authors emit this from the skill via the approval()
	// binding (TS) or the Approver interface (Go).
	Prompt string `json:"prompt"`

	// TimeoutSeconds is the server-side timeout after which the
	// runtime auto-rejects the request. Zero means "no timeout";
	// callers should default to 86_400 (24h) per the design.
	TimeoutSeconds int64 `json:"timeout_seconds,omitempty"`

	// WarnAtSeconds is the elapsed time after which the runtime
	// emits a "still pending" warning to slog. Defaults to 80% of
	// TimeoutSeconds when zero on read.
	WarnAtSeconds int64 `json:"warn_at_seconds,omitempty"`
}

ApprovalRequestPayload is the body of an EventApprovalRequest event. The runtime suspends after writing this event and waits for a matching EventApprovalResponse.

type ApprovalResponsePayload

type ApprovalResponsePayload struct {
	// ApprovalID matches the EventApprovalRequest it answers.
	ApprovalID string `json:"approval_id"`

	// Approved reports the human's decision.
	Approved bool `json:"approved"`

	// Reason is the optional free-text justification.
	Reason string `json:"reason,omitempty"`

	// Source records who supplied the response: "cli", "web",
	// "mcp", or "timeout" for an auto-rejection.
	Source string `json:"source,omitempty"`
}

ApprovalResponsePayload is the body of an EventApprovalResponse event.

type Bus

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

Bus is a process-wide pub/sub fan-out for run lifecycle events. The per-run JSONL ledger remains the durable source of truth; the bus is a best-effort live-tail surface for cross-run observers (the /runs workspace, the bottom-panel waterfall, anything else that needs to see events as they happen without subscribing per-run).

Sends are non-blocking — a slow subscriber loses events rather than stalling the recorder. Dropped subscribers see a one-shot `stream_restarted` notification on the next successful delivery so clients can decide whether to refetch the affected run.

func NewBus

func NewBus() *Bus

NewBus constructs an empty bus.

func (*Bus) Publish

func (b *Bus) Publish(ev Event)

Publish fans out an event to every subscriber. Subscribers whose channel is full have their dropped flag set instead of receiving the event; the SSE handler converts that into a `stream_restarted` frame on the next successful send.

func (*Bus) Subscribe

func (b *Bus) Subscribe() (*Subscriber, func())

Subscribe registers a new subscriber and returns its channel along with an unsubscribe function. Callers must Unsubscribe on shutdown or the bus will hold the channel alive past its owning goroutine.

type ErrorPayload

type ErrorPayload struct {
	// Message is the propagated error text.
	Message string `json:"message"`

	// NodeID names the node that produced the error, when known.
	// Empty for runtime-level errors that don't belong to a single
	// node.
	NodeID string `json:"node_id,omitempty"`
}

ErrorPayload is the body of an EventError event.

type Event

type Event struct {
	// RunID is the run this event belongs to. Repeated on every line
	// so a reader can sanity-check the file's contents.
	RunID string `json:"run_id"`

	// Seq is the event's position in the run. Monotonic from 1; gaps
	// indicate a corrupted ledger.
	Seq uint64 `json:"seq"`

	// Time is the wall-clock timestamp the event was recorded. UTC
	// always; the recorder normalises before writing.
	Time time.Time `json:"time"`

	// Type discriminates Payload. Always one of the EventType
	// constants above.
	Type EventType `json:"type"`

	// Payload is the type-specific body. Stored as RawMessage on
	// read to avoid forcing the reader into a sealed-union shape.
	Payload json.RawMessage `json:"payload,omitempty"`
}

Event is a single line in a run's JSONL ledger. Payload is shaped per Type — readers switch on Type and unmarshal Payload into the matching struct. The raw JSON is preserved verbatim so a future reader running against an older file doesn't drop fields it doesn't recognise.

func MarshalEvent

func MarshalEvent(runID string, seq uint64, eventType EventType, payload any) (Event, error)

MarshalEvent renders a typed payload into an Event ready for write. The payload is JSON-encoded once here so the recorder doesn't need type-switching at the file-I/O layer.

type EventType

type EventType string

EventType discriminates Event records on disk. The vocabulary is closed: callers MUST use one of the constants below so the reader can route each event to a typed payload without falling back to a generic map.

const (
	// EventRunStarted is emitted exactly once per run, before any
	// node activity. It anchors the run's metadata (skill, input
	// digest, parent) so a reader can render the run header without
	// scanning the whole file.
	EventRunStarted EventType = "run_started"

	// EventRunCompleted is the terminal event for a run that ran to
	// completion. Carries the final status (ok / error / cancelled)
	// and the OTel root span ID so traces can be cross-referenced.
	EventRunCompleted EventType = "run_completed"

	// EventNodeEnter is emitted when execution enters a graph node.
	// NodeID identifies the call site; NodeName is the author-facing
	// label.
	EventNodeEnter EventType = "node_enter"

	// EventNodeExit is emitted when a node returns. Carries the
	// duration in microseconds and the success flag; a node that
	// errored emits both EventNodeExit (with success=false) and
	// EventError (with the propagated message).
	EventNodeExit EventType = "node_exit"

	// EventToolCall captures a tool invocation request. The arguments
	// are stored verbatim (json.RawMessage) so the model's exact
	// emission survives a round-trip without provider-specific
	// reformatting.
	EventToolCall EventType = "tool_call"

	// EventToolResult captures the runtime's reply to a tool call.
	// IsError mirrors the gateway's error flag so a reader can render
	// failures distinctly without parsing the output.
	EventToolResult EventType = "tool_result"

	// EventLLMCall captures a Provider.Generate or Provider.Stream
	// invocation. Records the model, prompt-token count, and any
	// stop sequences so cost can be reconstructed offline.
	EventLLMCall EventType = "llm_call"

	// EventLLMChunk captures a single streaming chunk. Chunks are
	// optional in the persisted ledger — recorders may skip them for
	// large streams and emit only the final response — but when
	// present they are ordered per-call.
	EventLLMChunk EventType = "llm_chunk"

	// EventStructuredOutput captures a validated structured-output
	// result. Carries the JSON schema fingerprint and the validated
	// payload; the schema delta UI in the IDE diff-renders against
	// this event.
	EventStructuredOutput EventType = "structured_output"

	// EventApprovalRequest is emitted when the runtime hits an
	// approval gate. The accompanying ApprovalID is the handle a CLI
	// or web-UI consumer uses to respond.
	EventApprovalRequest EventType = "approval_request"

	// EventApprovalResponse is the matching reply: approve or reject,
	// with optional reason text. The runtime resumes (or aborts) the
	// run after writing this event.
	EventApprovalResponse EventType = "approval_response"

	// EventError is emitted whenever the runtime catches an error it
	// cannot handle locally. May appear mid-run (e.g. a transient
	// provider failure that the orchestrator retries) or as the
	// terminal event for a failed run.
	EventError EventType = "error"
)

type LLMCallPayload

type LLMCallPayload struct {
	// Model is the canonical model ID the runtime asked for.
	Model string `json:"model"`

	// Provider is the gridctl provider package responsible for the
	// call ("anthropic", "openai", "google", "gateway"). Populated
	// by the adapter, not the model.
	Provider string `json:"provider,omitempty"`

	// PromptTokens is the input-token count reported by the
	// provider.
	PromptTokens int `json:"prompt_tokens,omitempty"`

	// OutputTokens is the output-token count reported by the
	// provider on completion. Zero for events recorded before the
	// final chunk.
	OutputTokens int `json:"output_tokens,omitempty"`

	// CacheReadTokens captures cache-hit savings; cache-write tokens
	// land alongside (Anthropic-only on the providers we cover).
	CacheReadTokens  int `json:"cache_read_tokens,omitempty"`
	CacheWriteTokens int `json:"cache_write_tokens,omitempty"`

	// CostUSD is the dollar cost computed by pkg/pricing, in
	// fractional US dollars. Stored alongside the call so the
	// inspect view doesn't need the pricing table to render the
	// totals.
	CostUSD float64 `json:"cost_usd,omitempty"`
}

LLMCallPayload is the body of an EventLLMCall event.

type LLMChunkPayload

type LLMChunkPayload struct {
	// Index is the chunk's order within the streamed response.
	// Monotonic from 0.
	Index int `json:"index"`

	// Delta is the text appended in this chunk.
	Delta string `json:"delta,omitempty"`
}

LLMChunkPayload is the body of an EventLLMChunk event.

type ListFilter

type ListFilter struct {
	Status   string    // exact match against RunSummary.Status; empty = any
	Skill    string    // exact match against RunSummary.Skill; empty = any
	Parent   string    // exact match against ParentRunID; empty = any
	Since    time.Time // include runs with StartedAt >= Since; zero = no lower bound
	BeforeID string    // cursor: skip runs newer-or-equal to this ID (run ID is time-ordered)
}

ListFilter constrains a List call without forcing every caller through an in-memory post-filter. Empty fields match everything.

Note: scanning the runs directory and reading each summary stays linear in total runs on disk. TODO(runs-index): once installs grow past ~10k runs the API handler should consult a sidecar index (sqlite or an mtime-bucketed manifest) instead of re-scanning.

type NodeEnterPayload

type NodeEnterPayload struct {
	// NodeID is a stable identifier for the node within the run
	// graph. Used by `runs resume --from-step <node_id>`.
	NodeID string `json:"node_id"`

	// NodeName is the author-facing label. May differ from NodeID
	// (e.g. autogenerated IDs for anonymous lambda nodes).
	NodeName string `json:"node_name,omitempty"`

	// SpanID is the OTel span the node executes under. Empty when
	// tracing is not configured.
	SpanID string `json:"span_id,omitempty"`
}

NodeEnterPayload is the body of an EventNodeEnter event.

type NodeExitPayload

type NodeExitPayload struct {
	// NodeID matches the EventNodeEnter that opened the node.
	NodeID string `json:"node_id"`

	// DurationMicros is the wall-clock duration the node held
	// execution, in microseconds. The microsecond resolution keeps
	// the JSONL parseable as integers across languages while still
	// resolving fast Go skills.
	DurationMicros int64 `json:"duration_micros"`

	// Success reports whether the node returned without error.
	Success bool `json:"success"`
}

NodeExitPayload is the body of an EventNodeExit event.

type Recorder

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

Recorder is an append-only writer for a single run's JSONL ledger. All write paths funnel through Record, which serialises encoding and write so two goroutines emitting events on the same run don't interleave bytes mid-line.

func (*Recorder) Close

func (r *Recorder) Close() error

Close flushes and closes the underlying file. Calling Close more than once is safe; subsequent calls are no-ops.

func (*Recorder) NextSeq

func (r *Recorder) NextSeq() uint64

NextSeq advances and returns the next sequence number. Exposed so callers that compose their own Event values keep the sequence monotonic; Record handles this automatically when the caller passes a payload through MarshalEvent.

func (*Recorder) Path

func (r *Recorder) Path() string

Path returns the on-disk path the Recorder writes to.

func (*Recorder) Record

func (r *Recorder) Record(eventType EventType, payload any) (Event, error)

Record appends a typed payload as an Event. The sequence number is allocated from the recorder's monotonic counter; Time is set to time.Now().UTC(). After the durable write succeeds the event fans out to the store's global bus so cross-run subscribers (the /runs live tail) see it immediately. Bus publish is non-blocking — a slow subscriber drops the event rather than stalling the recorder.

func (*Recorder) RunID

func (r *Recorder) RunID() string

RunID returns the run identifier this Recorder writes to.

type ResumePlan

type ResumePlan struct {
	// RunID is the run being resumed.
	RunID string `json:"run_id"`

	// FromNodeID is the resume point. Empty means "resume from the
	// last completed step" — the runtime defaults to the highest
	// EventNodeExit it sees in the ledger.
	FromNodeID string `json:"from_node_id,omitempty"`

	// Replay is the event slice the runtime replays to rebuild
	// state. Always starts with the EventRunStarted record so
	// metadata (skill, input, parent) is available; ends just
	// before the resume point.
	Replay []Event `json:"replay"`

	// LastSeq is the seq of the last event in Replay. New events
	// emitted on resume must use Seq > LastSeq.
	LastSeq uint64 `json:"last_seq"`

	// PendingApproval is set when the resume target is an in-flight
	// approval gate. Carries the ApprovalID so a CLI consumer can
	// route the user toward `runs approve` instead of resume.
	PendingApproval string `json:"pending_approval,omitempty"`

	// Started captures the EventRunStarted payload as a convenience
	// for runtime initialisation; saves the runtime a second
	// ledger walk.
	Started *RunStartedPayload `json:"started,omitempty"`
}

ResumePlan is the projection a runtime consumes to restart a run from a chosen step. Phase E delivers the persistence half: reading the JSONL ledger and producing a plan whose Replay slice carries every event the runtime needs to rebuild state up to FromNode.

The runtime-side half (handing the plan to eino's compose/checkpoint resume surface through the adapter) lands when graph execution drivers do; until then the plan is consumed by the CLI's `runs resume` to mark the run as resumed in the ledger and surface the rehydrated context to operators.

type RunCompletedPayload

type RunCompletedPayload struct {
	// Status is one of "ok", "error", "cancelled", "suspended" — the
	// last meaning the run is awaiting an approval response.
	Status string `json:"status"`

	// Output is the run's final output payload, when Status is
	// "ok". JSON-encoded; readers decode against the skill's output
	// schema. Truncated to MaxPayloadBytes (and wrapped as a JSON
	// string) when Truncated is set.
	Output json.RawMessage `json:"output,omitempty"`

	// Truncated is true when Output was capped at MaxPayloadBytes.
	Truncated bool `json:"truncated,omitempty"`

	// Error is the terminal error message when Status is "error" or
	// "cancelled". Empty otherwise.
	Error string `json:"error,omitempty"`
}

RunCompletedPayload is the body of an EventRunCompleted event.

type RunStartedPayload

type RunStartedPayload struct {
	// Skill is the unprefixed skill name the run was launched
	// against. Empty when the run is an ad-hoc graph composition with
	// no registered skill.
	Skill string `json:"skill,omitempty"`

	// InputDigest is the SHA-256 of the canonical-JSON-encoded
	// input. Recorded so the inspect view can collapse identical
	// inputs across re-runs without surfacing the full payload.
	InputDigest string `json:"input_digest,omitempty"`

	// Input is the raw input as the caller supplied it. Stored
	// verbatim so resume can re-issue the run without recomputing.
	Input json.RawMessage `json:"input,omitempty"`

	// ParentRunID links a child run (e.g. a Handoff) to its parent.
	// Empty for a top-level run.
	ParentRunID string `json:"parent_run_id,omitempty"`

	// TraceID is the OTel root span ID for the run, hex-encoded.
	// Empty when tracing is not configured.
	TraceID string `json:"trace_id,omitempty"`

	// Flavor describes whether the run originated from a typed Go
	// skill ("go") or a TypeScript skill in the sandbox ("ts").
	// Markdown-only prompt skills surface as "prompt".
	Flavor string `json:"flavor,omitempty"`
}

RunStartedPayload is the body of an EventRunStarted event.

type RunSummary

type RunSummary struct {
	RunID           string    `json:"run_id"`
	Path            string    `json:"path,omitempty"`
	Skill           string    `json:"skill,omitempty"`
	Flavor          string    `json:"flavor,omitempty"`
	Status          string    `json:"status"`
	StartedAt       time.Time `json:"started_at,omitempty"`
	CompletedAt     time.Time `json:"completed_at,omitempty"`
	ModTime         time.Time `json:"mtime,omitempty"`
	EventCount      int       `json:"event_count"`
	ParentRunID     string    `json:"parent_run_id,omitempty"`
	TraceID         string    `json:"trace_id,omitempty"`
	PendingApproval string    `json:"pending_approval,omitempty"`
	Error           string    `json:"error,omitempty"`
}

RunSummary is the header view of a run, computed from the JSONL ledger by Store.List and Store.Summary. The shape is intentionally flat for `gridctl runs list --format json` consumers.

type Store

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

Store is the file-backed run-state ledger. One Store instance serves the whole process; concurrent OpenWriter calls for the same run ID return the same underlying writer (single-writer per run).

func NewStore

func NewStore(dir string) *Store

NewStore constructs a Store rooted at the given directory. The directory is created on first write; passing an empty string falls back to the default ~/.gridctl/runs/ location via DefaultRunsDir.

func (*Store) BuildResumePlan

func (s *Store) BuildResumePlan(runID, fromNodeID string) (ResumePlan, error)

BuildResumePlan reads a run's ledger and projects a ResumePlan up to the requested resume point. Passing an empty fromNodeID resumes from the last completed step (the highest EventNodeExit observed).

func (*Store) Bus

func (s *Store) Bus() *Bus

Bus exposes the global event bus so cross-run observers (the /runs workspace SSE stream, future cross-run metrics) can subscribe to every event without polling the disk. The bus is best-effort: events arrive after the durable JSONL write and slow consumers see drops surfaced as `stream_restarted` sentinels rather than backpressure against the recorder.

func (*Store) CloseAll

func (s *Store) CloseAll() error

CloseAll flushes and closes every active recorder. Returns the first close error, if any; the rest are skipped.

func (*Store) Dir

func (s *Store) Dir() string

Dir returns the configured runs directory.

func (*Store) List

func (s *Store) List(limit int) ([]RunSummary, error)

List enumerates every run on disk, newest first by mtime. Each summary is derived from the head of the file (RunStarted) and the tail (last RunCompleted, if any) — neither is required to be present on a partially-written ledger.

func (*Store) ListFiltered

func (s *Store) ListFiltered(filter ListFilter, limit int) ([]RunSummary, error)

ListFiltered is List with optional filters and an ID-based cursor. Runs are returned newest first; pass the last RunID from the prior page in `BeforeID` to fetch the next page.

func (*Store) OpenWriter

func (s *Store) OpenWriter(runID string) (*Recorder, error)

OpenWriter returns a Recorder for the given run ID, creating the file (and directory) on first call. Subsequent calls for the same run ID return the cached Recorder so a run is guaranteed to have exactly one writer; callers must Close when done with the run.

func (*Store) PathFor

func (s *Store) PathFor(runID string) string

PathFor returns the on-disk path for a given run ID.

func (*Store) Read

func (s *Store) Read(runID string) ([]Event, error)

Read returns all events recorded for a run, in file order. Trailing partial lines (e.g. from a crash mid-write) are silently skipped — the on-disk shape is append-only, so a partial line can only ever be the last record.

func (*Store) Stream

func (s *Store) Stream(ctx context.Context, runID string, fn func(Event) error) error

Stream invokes fn for each event in the run, in file order. Stream stops early if fn returns an error or if ctx is cancelled. Useful for SSE event streaming and for resume's replay loop, both of which want to avoid materialising the full ledger.

func (*Store) Summary

func (s *Store) Summary(runID string) (RunSummary, error)

Summary derives a run header from its ledger. Returns os.ErrNotExist when the run file is absent.

type StructuredOutputPayload

type StructuredOutputPayload struct {
	// SchemaDigest is the SHA-256 of the canonical JSON Schema the
	// payload was validated against. Used by the IDE's schema-delta
	// view to highlight cross-run drift.
	SchemaDigest string `json:"schema_digest,omitempty"`

	// Output is the validated payload, JSON-encoded.
	Output json.RawMessage `json:"output"`
}

StructuredOutputPayload is the body of an EventStructuredOutput event.

type Subscriber

type Subscriber struct {
	C chan Event
	// contains filtered or unexported fields
}

Subscriber is one live tail of the global event stream. The SSE handler reads from C, checks Dropped before each send, and emits a sentinel when the flag flips from true → false.

func (*Subscriber) TakeDropped

func (s *Subscriber) TakeDropped() bool

TakeDropped clears the dropped flag and returns whether the subscriber had missed events since the last call. SSE handlers call this before each send so the sentinel rides ahead of the next event.

type ToolCallPayload

type ToolCallPayload struct {
	// CallID is the provider-issued tool-call identifier; matches
	// agent.ToolCall.ID. Used to pair tool_call with tool_result.
	CallID string `json:"call_id"`

	// NodeID, when set, links the tool call to the wrapping
	// node_enter/node_exit pair so the inspector can collapse
	// per-tool detail under its node row.
	NodeID string `json:"node_id,omitempty"`

	// Name is the prefixed tool name (server__tool) the gateway
	// dispatches against. Recording the prefixed form keeps the
	// ledger unambiguous when the same tool name is exposed by
	// multiple servers.
	Name string `json:"name"`

	// Arguments is the JSON-encoded argument object as the model
	// emitted it. Verbatim — provider key ordering and escaping is
	// preserved, unless the encoded form exceeded MaxPayloadBytes,
	// in which case it is replaced with the first MaxPayloadBytes
	// as a JSON-quoted string and Truncated is set. Consumers
	// treating a truncated payload as opaque get a syntactically
	// valid value either way.
	Arguments json.RawMessage `json:"arguments,omitempty"`

	// Truncated is true when Arguments was capped at MaxPayloadBytes.
	// Inspectors render the prefix and a "truncated" marker.
	Truncated bool `json:"truncated,omitempty"`
}

ToolCallPayload is the body of an EventToolCall event.

type ToolResultPayload

type ToolResultPayload struct {
	// CallID matches the EventToolCall it answers.
	CallID string `json:"call_id"`

	// NodeID, when set, mirrors the matching EventToolCall.NodeID so
	// the inspector can pair the two without an arguments lookup.
	NodeID string `json:"node_id,omitempty"`

	// Output is the textual content the runtime surfaced to the
	// model. Multi-block content collapses to a single string here;
	// structured-content support follows when the gateway exposes it
	// uniformly. Truncated to MaxPayloadBytes when Truncated is set.
	Output string `json:"output,omitempty"`

	// Truncated is true when Output was capped at MaxPayloadBytes.
	Truncated bool `json:"truncated,omitempty"`

	// IsError mirrors the gateway's error flag.
	IsError bool `json:"is_error,omitempty"`
}

ToolResultPayload is the body of an EventToolResult event.

Jump to

Keyboard shortcuts

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