core

package
v0.1.57 Latest Latest
Warning

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

Go to latest
Published: Jun 22, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package core defines protocol-level types shared across Whale subsystems.

Keep this package limited to stable data structures and pure helpers with no filesystem, network, UI, or agent runtime side effects. Business flow belongs in agent/app/tools/tui packages instead of here.

Index

Constants

View Source
const (
	// ToolInputEventsSuffix is the filename suffix for tool input event logs.
	ToolInputEventsSuffix = ".tool_input_events.jsonl"
	// ApprovalEventsSuffix is the filename suffix for approval event logs.
	ApprovalEventsSuffix = ".approval_events.jsonl"
)
View Source
const (
	ToolInputRepairNullOptionalOmitted  = "null_optional_omitted"
	ToolInputRepairStringifiedArray     = "stringified_array"
	ToolInputRepairBareStringToArray    = "bare_string_to_array"
	ToolInputRepairEmptyObjectToArray   = "empty_object_to_array"
	ToolInputRepairMarkdownAutolinkPath = "markdown_autolink_path"
	ToolInputRepairSemanticBoolean      = "semantic_boolean_string"
	ToolInputRepairSemanticInteger      = "semantic_integer_string"
)
View Source
const DefaultMaxToolResultChars = 32 * 1024

Variables

This section is empty.

Functions

func ApplyDisplayToolNames added in v0.1.50

func ApplyDisplayToolNames(text string) string

ApplyDisplayToolNames rewrites internal tool names to their model-facing display names within free-form text (tool descriptions, guidance prose) so authored copy stays consistent with the schema without every string having to call DisplayToolName itself. Only snake_case names are rewritten, with word boundaries, so ordinary prose (and capability literals such as "workspace.write") is never corrupted. This is cosmetic: even if a name slips through, the model-supplied name still resolves because the tool remains registered under its internal name.

func AsAnySlice added in v0.1.24

func AsAnySlice(v any) []any

AsAnySlice extracts a []any value, returning nil on mismatch or nil input.

func AsString added in v0.1.24

func AsString(v any) string

AsString extracts a string value from any type, returning empty string on mismatch.

func BoundedTruncationPayload added in v0.1.48

func BoundedTruncationPayload(text string, originalChars int, code, archivePath string) map[string]any

BoundedTruncationPayload replaces the full canonical payload when the rendered text was truncated, so large results are not persisted twice.

func CanonicalToolName added in v0.1.50

func CanonicalToolName(modelName string) string

CanonicalToolName normalizes a model-supplied tool name to whale's internal name, accepting the conventional display name and known aliases case-insensitively. Unrecognized names pass through unchanged (trimmed), so genuinely unknown tools still surface as "tool not found" downstream.

func CanonicalizeToolPayload added in v0.1.48

func CanonicalizeToolPayload(env ToolEnvelope) map[string]any

CanonicalizeToolPayload converts envelope data into the canonical payload form: a JSON-typed map (strings, float64, bool, nested maps/slices) that is byte- and type-identical whether read before persistence or after a save/load cycle. Envelope fields without a struct home travel as reserved keys ("message", "summary", "truncated").

func ContainsArg added in v0.1.24

func ContainsArg(argv []string, want string) bool

ContainsArg reports whether want is present in argv.

func DisplayToolName added in v0.1.50

func DisplayToolName(internal string) string

DisplayToolName returns the model-facing name for an internal tool name. Names without a mapping pass through unchanged.

func FirstLine added in v0.1.24

func FirstLine(s string) string

FirstLine returns the first line of s (up to the first newline), trimmed. Returns the full trimmed input if there is no newline.

func FirstNonEmpty added in v0.1.24

func FirstNonEmpty(values ...string) string

FirstNonEmpty returns the first non-empty (after trimming whitespace) variadic string argument. Returns "" if all values are empty.

func FlattenSchemaForModel

func FlattenSchemaForModel(parameters map[string]any) map[string]any

FlattenSchemaForModel flattens nested object properties into dotted keys (for example: payload.path) to reduce nested JSON generation errors.

func IsReadOnlyToolCall

func IsReadOnlyToolCall(spec ToolSpec, call ToolCall) bool

func IsSessionJSONLName added in v0.1.24

func IsSessionJSONLName(name string) bool

IsSessionJSONLName reports whether name is a session JSONL file (not tool input or approval events).

func LastShellCommandSegment added in v0.1.47

func LastShellCommandSegment(command string) string

LastShellCommandSegment returns the final command in a pipe/&&/; chain, skipping separators inside quotes. The last segment determines the chain's exit code.

func MarshalToolEnvelope

func MarshalToolEnvelope(env ToolEnvelope) (string, error)

MarshalToolEnvelope serializes the model-facing tool result. HTML escaping must stay off: the model reads this text and copies payload fragments (file content, shell commands) back into edit/write inputs, so & < > have to survive byte-for-byte. Session 019ead56 documents the failure mode.

func MarshalToolJSON added in v0.1.47

func MarshalToolJSON(v any) ([]byte, error)

MarshalToolJSON serializes any model-visible tool JSON without HTML escaping. Use this instead of json.Marshal whenever the output becomes ToolResult.ModelText: the model reads that text raw, so json.Marshal's HTML escaping would corrupt every payload containing & < >.

func MessagePartsPlainText added in v0.1.39

func MessagePartsPlainText(parts []MessagePart) string

func MessagePlainText added in v0.1.39

func MessagePlainText(msg Message) string

func PathInside added in v0.1.24

func PathInside(path, parent string) (bool, error)

PathInside reports whether path is inside parent (or equal to parent), resolving symlinks through the nearest existing ancestor for accurate boundary checking even when the final path does not exist yet.

func ProviderToolPayload added in v0.1.38

func ProviderToolPayload(tool Tool) map[string]any

func RenderToolResultText added in v0.1.48

func RenderToolResultText(name string, outcome ToolOutcome, code string, payload map[string]any) string

RenderToolResultText renders the model-visible text of a tool result from its canonical payload. This is the phase-2 replacement for serializing the JSON envelope: a compact status header, the content verbatim (never re-escaped), and prefixed trailing lines (note:/warning:/diagnosis:/ recovery:/data:). Deterministic: map rendering goes through MarshalToolJSON (sorted keys), no timestamps.

func RenderTruncatedToolText added in v0.1.48

func RenderTruncatedToolText(text string, maxChars int, archivePath string) string

RenderTruncatedToolText bounds rendered text to maxChars with a head/tail split and an omission marker; archivePath (optional) points at the full text archived on disk.

func RenestFlatInputForSpec

func RenestFlatInputForSpec(spec ToolSpec, raw string) (string, bool)

RenestFlatInputForSpec turns dotted keys back into nested objects when the tool schema is flattened for model interaction.

func SanitizeSessionID added in v0.1.24

func SanitizeSessionID(v string) string

SanitizeSessionID sanitizes a session ID for use in filenames. Non-alphanumeric characters (except - and _) are replaced with _.

func ShellExitMeansNoMatches added in v0.1.47

func ShellExitMeansNoMatches(command string, exitCode int, stdout, stderr string) bool

ShellExitMeansNoMatches reports whether a finished command's exit status conveys "no matches / no results" rather than failure. It keys on the last command segment because that is what determines the exit code. Commands frequently merge streams with 2>&1, so the rules must not assume error text stays on stderr — each command gets the strongest signal its actual behavior allows.

func ShellExitMessage added in v0.1.55

func ShellExitMessage(command string, exitCode int, stdout, stderr string) string

ShellExitMessage returns a human-readable interpretation of a non-zero exit code for the final command in a pipe/&&/; chain. It keys on the last command segment because that is what determines the exit code.

Commands already classified by ShellExitMeansNoMatches (grep, rg, git grep, where, findstr, dir) are excluded here — their exit-1 semantics are handled by the no-match rendering path. This function covers commands whose non-zero exit codes have well-documented meanings beyond "no matches".

Returns "" when no specific interpretation exists.

func ShellSegmentBaseCommand added in v0.1.47

func ShellSegmentBaseCommand(segment string) string

ShellSegmentBaseCommand extracts the command word a segment's exit-code semantics are keyed on ("git grep" is kept as a unit).

func ShouldFlattenSchema

func ShouldFlattenSchema(parameters map[string]any) bool

ShouldFlattenSchema reports whether a tool schema is deep/wide enough to benefit from flattened argument keys for model-side tool calling.

func SkillNameDisabled added in v0.1.24

func SkillNameDisabled(name string, disabled []string) bool

SkillNameDisabled reports whether name is in the disabled list. Comparison is case-insensitive with whitespace trimmed.

func ToolInputRecoveryHint added in v0.1.35

func ToolInputRecoveryHint(toolName, msg string) (string, bool)

func ToolResultModelText added in v0.1.48

func ToolResultModelText(r ToolResult) string

ToolResultModelText returns the model-visible text of a result.

func WithToolResultArchive added in v0.1.21

func WithToolResultArchive(ctx context.Context, dir, sessionID string) context.Context

Types

type AttachmentKind added in v0.1.39

type AttachmentKind string
const (
	AttachmentKindImage AttachmentKind = "image"
	AttachmentKindPDF   AttachmentKind = "pdf"
	AttachmentKindAudio AttachmentKind = "audio"
	AttachmentKindFile  AttachmentKind = "file"
)

type AttachmentRef added in v0.1.39

type AttachmentRef struct {
	Kind         AttachmentKind `json:"kind"`
	Path         string         `json:"path,omitempty"`
	OriginalPath string         `json:"original_path,omitempty"`
	MIME         string         `json:"mime,omitempty"`
	Filename     string         `json:"filename,omitempty"`
	SizeBytes    int64          `json:"size_bytes,omitempty"`
	SHA256       string         `json:"sha256,omitempty"`
	DisplayName  string         `json:"display_name,omitempty"`
}

type FinishReason

type FinishReason string
const (
	FinishReasonEndTurn  FinishReason = "end_turn"
	FinishReasonToolUse  FinishReason = "tool_use"
	FinishReasonCanceled FinishReason = "canceled"
	FinishReasonError    FinishReason = "error"
)

type Message

type Message struct {
	ID           string
	SessionID    string
	Role         Role
	Text         string
	Parts        []MessagePart `json:"parts,omitempty"`
	Hidden       bool
	Reasoning    string
	ToolCalls    []ToolCall
	ToolResults  []ToolResult
	FinishReason FinishReason
	CreatedAt    time.Time
	UpdatedAt    time.Time
	// Usage carries the provider-reported token accounting for assistant
	// turns; ErrorDetail preserves the failure reason for turns that end
	// with FinishReasonError (session analysis was previously impossible:
	// error turns persisted with empty text and no cause).
	Usage       *MessageUsage `json:"Usage,omitempty"`
	ErrorDetail string        `json:"ErrorDetail,omitempty"`
}

func NormalizeMessageContent added in v0.1.39

func NormalizeMessageContent(msg Message) Message

func TextMessage added in v0.1.39

func TextMessage(sessionID string, role Role, text string, hidden bool) Message

func UserMessageFromParts added in v0.1.39

func UserMessageFromParts(sessionID string, parts []MessagePart, hidden bool) Message

type MessagePart added in v0.1.39

type MessagePart struct {
	Type       MessagePartType `json:"type"`
	Text       string          `json:"text,omitempty"`
	Attachment *AttachmentRef  `json:"attachment,omitempty"`
}

type MessagePartType added in v0.1.39

type MessagePartType string
const (
	MessagePartText       MessagePartType = "text"
	MessagePartAttachment MessagePartType = "attachment"
	MessagePartPlan       MessagePartType = "plan"
)

type MessageUsage added in v0.1.48

type MessageUsage struct {
	PromptTokens          int `json:"prompt_tokens,omitempty"`
	CompletionTokens      int `json:"completion_tokens,omitempty"`
	PromptCacheHitTokens  int `json:"prompt_cache_hit_tokens,omitempty"`
	PromptCacheMissTokens int `json:"prompt_cache_miss_tokens,omitempty"`
}

MessageUsage is the per-turn token accounting persisted with assistant messages, sourced from the provider's usage response.

type ProviderToolPayloadProvider added in v0.1.38

type ProviderToolPayloadProvider interface {
	ProviderToolPayload() map[string]any
}

type ProviderToolSchemaCache added in v0.1.38

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

func NewProviderToolSchemaCache added in v0.1.38

func NewProviderToolSchemaCache() *ProviderToolSchemaCache

type Role

type Role string
const (
	RoleSystem    Role = "system"
	RoleUser      Role = "user"
	RoleAssistant Role = "assistant"
	RoleTool      Role = "tool"
)

type SubagentStep added in v0.1.24

type SubagentStep struct {
	ToolName string `json:"tool_name,omitempty"`
	Status   string `json:"status,omitempty"`
	Summary  string `json:"summary,omitempty"`
}

type Tool

type Tool interface {
	Name() string
	Run(ctx context.Context, call ToolCall) (ToolResult, error)
}

type ToolApprovalHint

type ToolApprovalHint interface {
	ApprovalHint() string
}

type ToolCall

type ToolCall struct {
	ID    string
	Name  string
	Input string
}

type ToolCapabilities

type ToolCapabilities interface {
	Capabilities() []string
}

type ToolDescriber

type ToolDescriber interface {
	Description() string
}

type ToolEnvelope

type ToolEnvelope struct {
	OK        bool           `json:"ok"`
	Success   bool           `json:"success"`
	Error     string         `json:"error,omitempty"`
	Message   string         `json:"message,omitempty"`
	Code      string         `json:"code,omitempty"`
	Summary   string         `json:"summary,omitempty"`
	Data      map[string]any `json:"data,omitempty"`
	Truncated bool           `json:"truncated,omitempty"`
	Meta      map[string]any `json:"meta,omitempty"`
	Metadata  map[string]any `json:"metadata,omitempty"`
}

func NewToolErrorEnvelope

func NewToolErrorEnvelope(code, message string) ToolEnvelope

func NewToolSuccessEnvelope

func NewToolSuccessEnvelope(data map[string]any) ToolEnvelope

func ParseToolEnvelope

func ParseToolEnvelope(raw string) (ToolEnvelope, bool)

func ToolEnvelopeView added in v0.1.48

func ToolEnvelopeView(res ToolResult) (ToolEnvelope, bool)

ToolEnvelopeView rebuilds the legacy envelope view from the structured channel so existing envelope-shaped consumers keep working; text parsing remains only for results that predate the channel separation.

type ToolInputRepair added in v0.1.10

type ToolInputRepair struct {
	Kind       string
	Path       string
	BeforeType string
	AfterType  string
}

func RepairToolInputForSpec added in v0.1.10

func RepairToolInputForSpec(spec ToolSpec, raw string) (string, []ToolInputRepair)

RepairToolInputForSpec applies narrowly scoped, schema-guided repairs to common tool-call argument shape mistakes. Valid inputs are returned unchanged.

type ToolOutcome added in v0.1.48

type ToolOutcome string

ToolOutcome is the protocol-level disposition of a tool result. It is the single source for "is this an error" across the agent loop, the TUI, and telemetry; OutcomeNoResult exists so "searched fine, found nothing" stops being conflated with failure.

const (
	OutcomeSuccess   ToolOutcome = "success"
	OutcomeNoResult  ToolOutcome = "no_result"
	OutcomeFailure   ToolOutcome = "failure"
	OutcomeTimeout   ToolOutcome = "timeout"
	OutcomeCancelled ToolOutcome = "cancelled"
	OutcomeBlocked   ToolOutcome = "blocked"
)

func OutcomeForErrorCode added in v0.1.48

func OutcomeForErrorCode(code string) ToolOutcome

OutcomeForErrorCode maps a machine-readable error code to its outcome so every error producer classifies uniformly.

func ToolResultOutcome added in v0.1.48

func ToolResultOutcome(r ToolResult) ToolOutcome

ToolResultOutcome returns the protocol outcome, deriving it for results whose producer has not classified them yet (raw envelope in ModelText).

type ToolParamSpec

type ToolParamSpec interface {
	Parameters() map[string]any
}

type ToolPreviewer added in v0.1.2

type ToolPreviewer interface {
	Preview(ctx context.Context, call ToolCall) (map[string]any, error)
}

type ToolProgress added in v0.1.5

type ToolProgress struct {
	ToolCallID       string
	ToolName         string
	Status           string
	Summary          string
	Role             string
	Model            string
	Count            int
	DurationMS       int64
	Metadata         map[string]any
	ProgressMessages []SubagentStep
}

type ToolProgressRunner added in v0.1.5

type ToolProgressRunner interface {
	RunWithProgress(ctx context.Context, call ToolCall, progress func(ToolProgress)) (ToolResult, error)
}

type ToolReadOnly

type ToolReadOnly interface {
	ReadOnly() bool
}

type ToolReadOnlyCheck

type ToolReadOnlyCheck interface {
	ReadOnlyCheck(args map[string]any) bool
}

type ToolRegistry

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

func NewToolRegistry

func NewToolRegistry(tools []Tool) *ToolRegistry

func NewToolRegistryChecked

func NewToolRegistryChecked(tools []Tool) (*ToolRegistry, error)

func (*ToolRegistry) Dispatch

func (r *ToolRegistry) Dispatch(ctx context.Context, call ToolCall) (ToolResult, error)

func (*ToolRegistry) DispatchWithProgress added in v0.1.5

func (r *ToolRegistry) DispatchWithProgress(ctx context.Context, call ToolCall, progress func(ToolProgress)) (ToolResult, error)

func (*ToolRegistry) Get

func (r *ToolRegistry) Get(name string) Tool

func (*ToolRegistry) ReplaceTools added in v0.1.6

func (r *ToolRegistry) ReplaceTools(tools []Tool) error

func (*ToolRegistry) SetMaxResultChars

func (r *ToolRegistry) SetMaxResultChars(limit int)

func (*ToolRegistry) Snapshot added in v0.1.23

func (r *ToolRegistry) Snapshot() *ToolRegistry

func (*ToolRegistry) Spec

func (r *ToolRegistry) Spec(name string) (ToolSpec, bool)

func (*ToolRegistry) Specs

func (r *ToolRegistry) Specs() []ToolSpec

func (*ToolRegistry) Tools

func (r *ToolRegistry) Tools() []Tool

type ToolResult

type ToolResult struct {
	ToolCallID string
	Name       string
	Metadata   map[string]any `json:"metadata,omitempty"`
	Outcome    ToolOutcome    `json:"Outcome,omitempty"`
	Code       string         `json:"Code,omitempty"`
	Payload    any            `json:"Payload,omitempty"`
	ModelText  string         `json:"ModelText,omitempty"`
}

ToolResult is channel-separated: Outcome/Code/Payload are the structured channel for the TUI, recovery, and evals; ModelText is the only text the model sees, rendered once at creation and never re-rendered. Tools that still produce a legacy envelope put it in ModelText; the dispatch funnel re-derives and re-renders it. Error-ness is derived from Outcome via IsError().

func FinalizeToolResultChannels added in v0.1.48

func FinalizeToolResultChannels(res ToolResult) ToolResult

FinalizeToolResultChannels backfills the channel-separated fields on a result produced outside the dispatch funnel (agent special tools, blocked markers, recovery wrappers, abort-skip placeholders). ModelText takes the Content bytes verbatim; Outcome/Code/Payload are derived exactly the way the legacy session decoder derives them, so a result finalized live and the same result reloaded from an old session file classify identically. Idempotent: results that already carry ModelText pass through unchanged.

func NewToolResultError added in v0.1.48

func NewToolResultError(call ToolCall, code, msg string, data map[string]any) ToolResult

NewToolResultError wraps an error code/message (with optional data) in the standard error envelope.

func NewToolResultFromEnvelope added in v0.1.48

func NewToolResultFromEnvelope(call ToolCall, env ToolEnvelope, metadata map[string]any) ToolResult

NewToolResultFromEnvelope is the sanctioned producer for tool results. ModelText is rendered exactly once here; phase 1 renders it with the legacy envelope serializer so model-visible bytes are unchanged, phase 2 swaps the renderer for plain text.

func NewToolResultSuccess added in v0.1.48

func NewToolResultSuccess(call ToolCall, data map[string]any, metadata map[string]any) ToolResult

NewToolResultSuccess wraps map data in the standard success envelope.

func (ToolResult) IsError

func (r ToolResult) IsError() bool

IsError reports whether the result is a failure-class outcome. OutcomeNoResult ("searched fine, found nothing") is not an error. Unclassified results (raw envelope in ModelText, pre-funnel) derive their outcome on the fly so the answer never depends on which side of the dispatch funnel the caller sits.

func (ToolResult) MarshalJSON added in v0.1.48

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

func (*ToolResult) UnmarshalJSON added in v0.1.48

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

type ToolSpec

type ToolSpec struct {
	Name             string
	Description      string
	Parameters       map[string]any
	ReadOnly         bool
	ReadOnlyCheck    func(args map[string]any) bool
	Capabilities     []string
	ApprovalHint     string
	SupportsParallel bool
}

func DescribeTool

func DescribeTool(t Tool) ToolSpec

type ToolSupportsParallel

type ToolSupportsParallel interface {
	SupportsParallel() bool
}

type UserInputAnswer

type UserInputAnswer struct {
	ID      string `json:"id"`
	Label   string `json:"label"`
	Value   string `json:"value"`
	IsOther bool   `json:"is_other,omitempty"`
}

type UserInputOption

type UserInputOption struct {
	Label       string `json:"label"`
	Description string `json:"description"`
}

type UserInputQuestion

type UserInputQuestion struct {
	Header   string            `json:"header"`
	ID       string            `json:"id"`
	Question string            `json:"question"`
	Options  []UserInputOption `json:"options"`
}

type UserInputRequest

type UserInputRequest struct {
	Questions []UserInputQuestion `json:"questions"`
}

type UserInputResponse

type UserInputResponse struct {
	Answers []UserInputAnswer `json:"answers"`
}

Jump to

Keyboard shortcuts

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