tools

package
v0.8.3 Latest Latest
Warning

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

Go to latest
Published: Oct 14, 2026 License: Apache-2.0, MIT Imports: 59 Imported by: 0

Documentation

Overview

Package tools — capability requirements for built-in agent tools.

This file is the Go mirror of `frontend/src/utils/tool-capabilities.ts`. They MUST be kept in sync: whenever you add a tool to the registry with specific KB requirements, update both maps.

Why duplicate it? The frontend uses it to gray out tools and filter KBs in the agent editor and `@` mention menu; the backend uses it as the authoritative last line of defense in the retrieval pipeline — a client that skips the frontend filter (old tab, curl, rogue plugin) shouldn't be able to hand incompatible KBs/files to a tool that would just silently skip them.

Package tools — edit_sandbox_file.

Surgical text replacement for a file already in the session sandbox. write_sandbox_file is the right first step for a new script; this tool exists so a one-line path fix (or similar) does not force the model to regenerate the whole file — that burns tokens and often truncates.

Design notes:

  • Same writable scope as write_sandbox_file: session sandbox except /workspace/input.
  • One input shape: every change is an entry in edits[]. Each entry resolves against the original content, so a batch is order-independent and overlaps are rejected rather than silently corrupting the file.
  • Default is a unique match (Cursor-style). Per-entry replace_all replaces every occurrence. 0 matches or an ambiguous match without replace_all fail without writing.
  • Result is path + replacement count + size; contents are not echoed.

Package tools — list_sandbox_files.

Read-only tool that lets the LLM enumerate files under a session's inspectable sandbox directories. Without this tool, the LLM cannot see files produced by prior skill invocations or staged chat attachments and has to guess paths when chaining work together.

Design notes:

  • Session-scoped: the sandbox path is resolved from the tool exec context (`ToolExecContext.SessionID`). The LLM cannot pass an arbitrary session ID.
  • Paths are inside the current session's sandbox. Relative paths resolve from the workspace root. Omitting `path` lists the layout default (remote: `$WEKNORA_SKILL_OUTPUT_DIR` / `/workspace/output`; host: the workspace root).
  • Read-only: this tool never creates, modifies or deletes anything inside the sandbox. Model-authored files go through write_sandbox_file.
  • Graceful "no sandbox": if the session has never spawned a sandbox yet (chat-only turn, or sandbox was reaped), the tool returns an empty listing with a helpful message rather than an error, so the LLM can decide to invoke a skill first.

Package tools — write_sandbox_file.

Lets the LLM write a text file into the current session's sandbox without stuffing the bytes through a shell_exec heredoc. shell_exec keeps an 8 KiB command cap; generated scripts (PPT builders, reports) routinely exceed it.

Design notes:

  • Session-scoped: the sandbox is resolved from ToolExecContext.SessionID.
  • Path guardrail: writes stay under the session layout's WriteRoots and never under InputDir (staged attachments are the user's, not ours). Remote sessions collect OutputDir for download; host sessions with an empty OutputDir edit in place. Only the InputDir half is enforced here; "finished deliverable" is guidance the tool cannot judge.
  • Content stays out of ToolResult.Data/Output: the model already has the bytes it just sent. The result is path + size so the next call can shell_exec the file.

Package tools — shell_exec.

General shell command execution primitive for the agent. The LLM can freely explore and operate inside its session-scoped Cube MicroVM: inspect files, transform data, run programs, install dependencies, and prepare or verify skill outputs.

Design notes:

  • Session-sandbox capability: registration is feature-gated on the sandbox backend exposing SandboxCommandExecutor (Cube, E2B, Docker). Remote sessions never run on the WeKnora host. Host layouts clamp work_dir to the layout's writable roots; the OS sandbox is the process perimeter.
  • Session-scoped: the sandbox is resolved from ToolExecContext.SessionID so the LLM cannot execute against a foreign session, and installed dependencies persist across subsequent tool calls in the same session.
  • Non-zero exit is a normal signal, not a tool failure: pip install failures, missing binaries, etc. are all valid results the LLM must inspect. Wire-level errors (sandbox unreachable) and commands that were killed or timed out surface as ToolResult.Success = false.
  • Output truncation: shell installers produce thousands of lines that would blow up the LLM context. We keep the head (leading messages) and the tail (final errors) with an ellipsis marker so the tail — usually the most informative segment — is preserved.
  • Command shape blacklist: the sandbox is throwaway, but we still refuse obviously destructive patterns (rm -rf /, fork bombs, mkfs...) to protect the LLM from its own hallucinations.
  • No backgrounding: trailing '&' and 'nohup' are rejected up-front to avoid orphaned processes inside the sandbox. Optional stdin is allowed as data; when the command is an interpreter that would run stdin as a program, the same blacklist and command-size cap apply to that payload.

Package tools — write_skill_file / edit_skill_file.

The installer agent's only writer used to be `shell_exec` with a heredoc. That caps every file at the shell's command-length limit and puts the content through two levels of quoting, so a requirements.json or a small patch script routinely arrived truncated or mangled. These two tools write the same bytes over the file API instead.

They are separate from write_sandbox_file / edit_sandbox_file rather than a loosening of them. Those write /workspace, which is wiped before the snapshot; the skill tree lives under /opt/weknora/tenant/skills and is what the snapshot keeps. Sharing one tool would mean one path guard covering both a per-session scratch area and the shared image.

Scope: one install writes one skill. The tool is constructed with that skill's directory and refuses everything outside it, so an installer cannot reach a neighbouring skill in the shared image even though its shell runs as root. The prompt asks for the same thing; this enforces it.

Package tools — read_file.

Read-only tool that lets the LLM read a file inside the session's sandbox. Known paths can be read directly; directory discovery is optional.

Design notes:

  • Session-scoped: path must belong to the current session's sandbox, enforced by delegating to SandboxFileSource.ReadSessionFile which itself takes the session ID from context.
  • Absolute paths are sandbox-local; relative paths resolve from /workspace. Reading a file does not make it a downloadable artifact.
  • Paginated reads: a page is bounded by a line count, a byte budget, and the registry's rune budget, and a partial page ends with the offset to continue from. Files over 8 MiB are never downloaded; the model gets metadata and uses shell_exec with sed/head/tail/grep/awk instead.
  • Binary handling: an image file is returned as a prepared vision image in ToolResult.Images, which is never persisted; other binary bytes are never returned or base64-encoded. ArtifactCollector remains the download path.

Index

Constants

View Source
const (
	// Capability-scoped MCP discovery and invocation; not tenant-selectable builtins.
	ToolDiscoverMCPTools = "discover_mcp_tools"
	ToolCallMCPTool      = "call_mcp_tool"
	ToolThinking         = "thinking"
	ToolTodoWrite        = "todo_write"
	// Knowledge retrieval surface. search_knowledge covers semantic, keyword
	// and hybrid retrieval over chunk-indexed knowledge bases; read_document
	// reads a document's metadata and chunks (by page, by chunk handle, or by
	// an in-document text search); list_documents browses one knowledge base.
	ToolSearchKnowledge     = "search_knowledge"
	ToolReadDocument        = "read_document"
	ToolListDocuments       = "list_documents"
	ToolQueryKnowledgeGraph = "query_knowledge_graph"
	ToolSearchConversations = "search_conversations"
	ToolSearchMemory        = "search_memory"
	ToolDatabaseQuery       = "database_query"
	ToolDataAnalysis        = "data_analysis"
	ToolDataSchema          = "data_schema"
	ToolWebSearch           = "web_search"
	ToolWebFetch            = "web_fetch"
	// Unified reading: workspace access follows sandbox file capability;
	// skill resources follow SkillsEnabled and do not require a sandbox.
	// attachment:// is added whenever the session has stored attachments,
	// because a file the user just sent is not something an allowlist should
	// be able to hide from the agent.
	ToolReadFile = "read_file"
	// Sandbox filesystem tools (only available when the sandbox backend
	// supports per-session files — Cube, E2B, Docker). list/read inspect
	// the session workspace; write creates text files so generated
	// scripts do not have to travel through a shell_exec heredoc; edit
	// patches an existing file without regenerating it.
	//
	// Deliberately absent from AvailableToolDefinitions and
	// DefaultAllowedTools, like search_memory and web_search: the sandbox
	// switch already decides whether a run has a workspace at all, and
	// registerSandboxFileTools registers these from that capability rather
	// than from the allowlist. A checkbox would have been a lie — clearing
	// it changed nothing.
	ToolListSandboxFiles = "list_sandbox_files"
	ToolWriteSandboxFile = "write_sandbox_file"
	ToolEditSandboxFile  = "edit_sandbox_file"
	// ToolWriteSkillFile / ToolEditSkillFile write the skill tree under
	// /opt/weknora/tenant/skills rather than /workspace, and exist only for
	// the built-in skill installer. They are scoped to the one skill being
	// installed; see internal/agent/tools/skill_file.go.
	//
	// Deliberately absent from AvailableToolDefinitions: these write the
	// shared snapshot image, so they are granted by install mode alone and
	// must not become selectable on a tenant-editable agent config.
	ToolWriteSkillFile = "write_skill_file"
	ToolEditSkillFile  = "edit_skill_file"
	// ToolShellExec lets the LLM execute ad-hoc shell commands inside the
	// current session's sandbox (dependency installs, environment probing).
	// Registered only when the resolved backend advertises the session shell
	// capability (Cube, E2B, Docker). The command never runs on the WeKnora host.
	//
	// Also absent from AvailableToolDefinitions: registerSandboxShellIfAllowed
	// keys it on SkillsEnabled (or install mode), so the shell follows the
	// skills switch and not a per-agent tool checkbox.
	ToolShellExec = "shell_exec"
	// Wiki-related tools (only available when wiki KBs are in scope)
	ToolWikiReadPage    = "wiki_read_page"
	ToolWikiWritePage   = "wiki_write_page"
	ToolWikiReplaceText = "wiki_replace_text"
	ToolWikiRenamePage  = "wiki_rename_page"
	ToolWikiDeletePage  = "wiki_delete_page"
	ToolWikiSearch      = "wiki_search"
	ToolWikiFlagIssue   = "wiki_flag_issue"
	ToolWikiReadIssue   = "wiki_read_issue"
	ToolWikiUpdateIssue = "wiki_update_issue"
)

Tool names constants

View Source
const (
	ToolGroupBase      = "base"
	ToolGroupRAG       = "rag"
	ToolGroupWikiRead  = "wiki_read"
	ToolGroupWikiEdit  = "wiki_edit"
	ToolGroupWikiIssue = "wiki_issue"
	ToolGroupData      = "data"
)

Tool groups the agent editor sections its checkboxes by.

View Source
const (
	LegacyToolExecuteSkillScript = "execute_skill_script"
	LegacyToolReadSkill          = "read_skill"
	LegacyToolReadSandboxFile    = "read_sandbox_file"
	// The pre-consolidation knowledge retrieval surface. knowledge_search and
	// grep_chunks were folded into search_knowledge (mode=semantic|keyword|
	// hybrid); list_knowledge_chunks, get_document_info and
	// wiki_read_source_doc were folded into read_document.
	LegacyToolKnowledgeSearch     = "knowledge_search"
	LegacyToolGrepChunks          = "grep_chunks"
	LegacyToolListKnowledgeChunks = "list_knowledge_chunks"
	LegacyToolGetDocumentInfo     = "get_document_info"
	LegacyToolWikiReadSourceDoc   = "wiki_read_source_doc"
)

Retired tool identifiers are retained only for decoding existing histories and ignoring obsolete allowlist entries. They have no implementation or registration path and are never offered in model tool schemas.

View Source
const (
	SearchModeHybrid   = "hybrid"
	SearchModeSemantic = "semantic"
	SearchModeKeyword  = "keyword"
)

Retrieval modes accepted by search_knowledge.

View Source
const DataAnalysisTableName = "dataset"

DataAnalysisTableName is the only table name the model needs to know when querying a CSV/Excel document through data_analysis. Each document is loaded into a physical DuckDB table named after its knowledge ID; that name is never shown to the model. Every query runs on a private connection where "dataset" is a temporary view over the selected document, so the model references the same fixed identifier for every document and the knowledge ID never has to appear inside SQL text.

View Source
const (
	// DefaultMaxToolOutput is the default maximum character (rune) count for tool output.
	// Outputs exceeding this limit are truncated with head + tail preservation.
	// This counts Unicode characters (runes), not bytes, so CJK text is treated fairly.
	DefaultMaxToolOutput = 24000
)

Variables

View Source
var ToolCapabilityRequirements = map[string]ToolRequirement{

	"thinking":   {},
	"todo_write": {},

	"search_knowledge":      {AnyOf: []KBCapability{CapVector, CapKeyword}, ConsumesFiles: true},
	"read_document":         documentReaderRequirement,
	"list_documents":        documentReaderRequirement,
	"query_knowledge_graph": {AllOf: []KBCapability{CapGraph}, ConsumesFiles: true},
	"database_query":        {AnyOf: []KBCapability{CapVector, CapKeyword}, ConsumesFiles: true},

	"knowledge_search":      {AnyOf: []KBCapability{CapVector, CapKeyword}, ConsumesFiles: true},
	"grep_chunks":           {AnyOf: []KBCapability{CapVector, CapKeyword}, ConsumesFiles: true},
	"list_knowledge_chunks": {AnyOf: []KBCapability{CapVector, CapKeyword}, ConsumesFiles: true},
	"get_document_info":     {AnyOf: []KBCapability{CapVector, CapKeyword}, ConsumesFiles: true},

	"wiki_search":          {AllOf: []KBCapability{CapWiki}},
	"wiki_read_page":       {AllOf: []KBCapability{CapWiki}},
	"wiki_read_source_doc": documentReaderRequirement,
	"wiki_flag_issue":      {AllOf: []KBCapability{CapWiki}},
	"wiki_write_page":      {AllOf: []KBCapability{CapWiki}},
	"wiki_replace_text":    {AllOf: []KBCapability{CapWiki}},
	"wiki_rename_page":     {AllOf: []KBCapability{CapWiki}},
	"wiki_delete_page":     {AllOf: []KBCapability{CapWiki}},
	"wiki_read_issue":      {AllOf: []KBCapability{CapWiki}},
	"wiki_update_issue":    {AllOf: []KBCapability{CapWiki}},

	"data_analysis": {AnyOf: []KBCapability{CapVector, CapKeyword}, ConsumesFiles: true},
	"data_schema":   {AnyOf: []KBCapability{CapVector, CapKeyword}, ConsumesFiles: true},
}

ToolCapabilityRequirements maps tool names to their capability needs. Keep this aligned with `frontend/src/utils/tool-capabilities.ts`.

Tools absent from this map default to "no requirement" and are treated as always available / file-consuming (permissive fallback: unknown MCP tools shouldn't silently break).

Functions

func BrowserSkillScope

func BrowserSkillScope(ctx context.Context) browserskill.Scope

BrowserSkillScope names the caller's browser. A shared agent runs in its owner's workspace, but the browser stays paired to the member who asked.

func CanRunConcurrently

func CanRunConcurrently(name string) bool

CanRunConcurrently is an explicit allowlist of built-in reads. Mutations, arbitrary code, and unknown/MCP tools form execution barriers. In particular, a file write must finish before the next shell or skill call starts.

func CastParams

func CastParams(args json.RawMessage, schema json.RawMessage) json.RawMessage

CastParams performs schema-driven type casting on tool arguments. LLMs sometimes return incorrect types (e.g., "true" instead of true, "123" instead of 123). This function attempts safe conversions based on the JSON Schema definition of the tool's parameters.

If the schema is nil or cannot be parsed, the original args are returned unchanged.

func CompactToolOutputForHistory

func CompactToolOutputForHistory(toolName string, result *types.ToolResult) string

CompactToolOutputForHistory rebuilds a short tool message when replaying history.

func CountContentLines

func CountContentLines(s string) int

CountContentLines is the +N / -M unit for sandbox file mutations: empty is 0, a trailing newline does not add an extra line.

func DefaultAllowedTools

func DefaultAllowedTools() []string

DefaultAllowedTools returns the default allowed tools list.

func EffectiveAllowedTools

func EffectiveAllowedTools(allowed []string) []string

EffectiveAllowedTools returns the built-in tools an agent's allowed_tools value asks for. A nil list means the value was never set, as on agents saved before allowed_tools existed, and selects the default tools. Any other list, including an empty one, is taken as given: an empty list means no built-in tools. Retired names are rewritten to their successors.

Tool registration, the session config and the rerank requirement all go through this function, so they cannot disagree about what an empty list means.

func FormatMatchType

func FormatMatchType(mt types.MatchType) string

FormatMatchType converts MatchType to a human-readable string

func FormatValidationErrors

func FormatValidationErrors(errs []ValidationError) string

FormatValidationErrors formats a list of validation errors into a human-readable string.

func GetMCPToolsInfo

func GetMCPToolsInfo(
	ctx context.Context,
	services []*types.MCPService,
	mcpManager *mcp.MCPManager,
) (map[string][]string, error)

GetMCPToolsInfo returns information about available MCP tools

func GetRelevanceLevel

func GetRelevanceLevel(score float64) string

GetRelevanceLevel converts a score to a human-readable relevance level

func GoRecovered

func GoRecovered(ctx context.Context, fn func())

GoRecovered runs fn on a new goroutine and contains a panic raised there.

recover only covers the goroutine that calls it, so the barrier in executeRecovered (registry.go) protects the call to tool.Execute alone: a goroutine a tool starts for itself — an MCP client, a sandbox, a third-party SDK, a retrieval adapter — must install its own barrier, or one panic escapes every recover site and takes the whole server (and every conversation, upload and sync in flight) down with it.

Deferred calls inside fn still run while a panic unwinds, so wait groups and cleanup are released. A caller that must also degrade the failed work into an error result uses RecoverGoroutine directly and records it in onPanic.

func IsLegacyRetrievalTool

func IsLegacyRetrievalTool(name string) bool

IsLegacyRetrievalTool reports whether name is a retired knowledge retrieval tool that NormalizeAllowedTools rewrites to its successor.

func KBSatisfiesAgentRequirements

func KBSatisfiesAgentRequirements(caps types.KBCapabilities, agentMode string, allowedTools []string) bool

KBSatisfiesAgentRequirements is the agent-aware variant of KBSatisfiesToolRequirements: it also enforces the implicit capability constraints of `agentMode` (currently: quick-answer requires vector or keyword indexing on the KB).

func KBSatisfiesToolRequirements

func KBSatisfiesToolRequirements(caps types.KBCapabilities, allowedTools []string) bool

KBSatisfiesToolRequirements reports whether a single KB is compatible with the agent's tool set. "Compatible" means: the KB exposes at least one capability that some tool in `allowedTools` requires. When the tool set imposes no KB requirement at all, every KB is considered compatible.

func MCPToolNamesByServiceID

func MCPToolNamesByServiceID(registry *ToolRegistry) map[string][]string

MCPToolNamesByServiceID returns registered MCP tool names grouped by service ID.

func NewWikiDeletePageTool

func NewWikiDeletePageTool(
	wikiPageService interfaces.WikiPageService,
	kbIDs []string,
	routes ...*WikiRouteResolver,
) types.Tool

NewWikiDeletePageTool creates a new wiki_delete_page tool

func NewWikiFlagIssueTool

func NewWikiFlagIssueTool(
	wikiService interfaces.WikiPageService,
	kbIDs []string,
	routes ...*WikiRouteResolver,
) *wikiFlagIssueTool

func NewWikiReadIssueTool

func NewWikiReadIssueTool(wikiService interfaces.WikiPageService, kbIDs []string) types.Tool

func NewWikiReadPageTool

func NewWikiReadPageTool(
	wikiService interfaces.WikiPageService,
	knowledgeService interfaces.KnowledgeService,
	scopes []WikiScope,
	routes *WikiRouteResolver,
) types.Tool

func NewWikiRenamePageTool

func NewWikiRenamePageTool(
	wikiPageService interfaces.WikiPageService,
	kbIDs []string,
	routes ...*WikiRouteResolver,
) types.Tool

NewWikiRenamePageTool creates a new wiki_rename_page tool

func NewWikiReplaceTextTool

func NewWikiReplaceTextTool(
	wikiPageService interfaces.WikiPageService,
	kbIDs []string,
	knowledgeService interfaces.KnowledgeService,
	routes ...*WikiRouteResolver,
) *wikiReplaceTextTool

NewWikiReplaceTextTool creates a new wiki_replace_text tool

func NewWikiSearchTool

func NewWikiSearchTool(
	wikiService interfaces.WikiPageService,
	knowledgeService interfaces.KnowledgeService,
	scopes []WikiScope,
	routes *WikiRouteResolver,
) types.Tool

func NewWikiUpdateIssueTool

func NewWikiUpdateIssueTool(wikiService interfaces.WikiPageService, kbIDs []string) types.Tool

func NewWikiWritePageTool

func NewWikiWritePageTool(
	wikiPageService interfaces.WikiPageService,
	kbIDs []string,
	knowledgeService interfaces.KnowledgeService,
	routes ...*WikiRouteResolver,
) *wikiWritePageTool

NewWikiWritePageTool creates a new wiki_write_page tool

func NormalizeAllowedTools

func NormalizeAllowedTools(allowed []string) []string

NormalizeAllowedTools rewrites retired tool names in an allowlist to their successors and drops duplicates while preserving first-seen order.

func NormalizeToolCallID

func NormalizeToolCallID(id string, toolName string, index int) string

NormalizeToolCallID normalizes a tool call ID for cross-provider compatibility.

Different LLM providers return IDs in different formats:

  • OpenAI: "call_abc123" (well-formed)
  • Some providers: "" (empty)
  • Some providers: very long UUIDs or random strings
  • Some providers: IDs with special characters

This function ensures IDs are:

  • Non-empty (generates deterministic ID from tool name + index if empty)
  • Within length limits (truncated with hash suffix if too long)
  • Alphanumeric-safe (special characters replaced)

func OutputBudget

func OutputBudget(ctx context.Context) int

OutputBudget returns the ceiling published by the registry, in runes, falling back to DefaultMaxToolOutput for callers that execute a tool directly.

func RecoverGoroutine

func RecoverGoroutine(ctx context.Context, label string, onPanic func())

RecoverGoroutine is deferred by a goroutine that must not let a panic escape (either directly or through GoRecovered). It logs the panic with its stack, then calls onPanic — when set — on that same goroutine so the caller can mark the failed unit of work: one MCP service, one knowledge base, one retrieval call. label names that work in the log; an empty one leaves it unnamed.

onPanic deliberately receives no panic value: a recovered value carries internals (paths, addresses) that must not reach the model or the user.

func RegisterMCPTools

func RegisterMCPTools(
	ctx context.Context,
	registry *ToolRegistry,
	services []*types.MCPService,
	mcpManager *mcp.MCPManager,
	gate approval.MCPApproval,
	authWaitTimeoutSeconds int,
	lookup MCPServiceLookup,
	metadata *MCPMetadataIO,
) (int, error)

RegisterMCPTools installs a scoped directory and call proxy without connecting to MCP servers or advertising their full schemas. The count is services, not tools: discovery occurs on demand during tool execution.

func RepairJSON

func RepairJSON(s string) string

RepairJSON attempts to fix common JSON malformations from LLM outputs. LLMs sometimes produce:

  • Truncated JSON (missing closing brackets/braces)
  • Trailing commas before closing brackets
  • Single quotes instead of double quotes
  • Unquoted keys
  • Invalid backslash escapes inside strings (e.g. regex patterns like "C\+\+", "\d+", "\.log$") where the LLM forgot to double-escape.
  • Extra tokens after the top-level value (e.g. `{}""`, `{"a":1}{"a":1}`)

Returns the repaired JSON string. If repair is not possible, returns the original string unchanged (caller should handle parse errors).

func RepairJSONDetail

func RepairJSONDetail(s string) (string, bool)

RepairJSONDetail is RepairJSON that also reports whether the payload had to be closed off to parse — an unterminated string or an unbalanced bracket. That only happens when the provider stopped emitting mid-argument, so the values now parse but are partial: `content` holds half a file, `query` holds half a sentence. Callers must refuse such a call rather than run it. Escape and trailing-comma fixes are ordinary malformations, not truncation, and do not set the flag.

func RetiredToolReplacement

func RetiredToolReplacement(name string) string

RetiredToolReplacement tells the model how to replace a removed tool. Empty when name was never a WeKnora tool.

func SandboxFileCallProgress

func SandboxFileCallProgress(toolName string, args map[string]any) map[string]any

SandboxFileCallProgress is the UI payload for a write/edit call: path, running +/− line counts, and a short preview. File bodies stay off the wire.

func SanitizeAgentStepsForStorage

func SanitizeAgentStepsForStorage(steps []types.AgentStep) []types.AgentStep

SanitizeAgentStepsForStorage strips LLM-only payloads from persisted steps.

func SanitizeMessages

func SanitizeMessages(messages []chat.Message) []chat.Message

SanitizeMessages validates and fixes a message array for LLM compatibility. It handles common issues that cause provider API errors:

  • Ensures no consecutive same-role messages (some providers reject these)
  • Verifies tool result messages have matching tool_call in the preceding assistant message
  • Removes empty content messages that can cause API errors

Returns the sanitized message slice (may be shorter than input).

func SanitizeSandboxFileCallArgs

func SanitizeSandboxFileCallArgs(toolName string, args map[string]any) map[string]any

SanitizeSandboxFileCallArgs strips write/edit file bodies from arguments sent to the client. Live progress events already carry stats only; this covers the later "tool hint" emit that still has the full JSON.

func SanitizeToolDataForPersist

func SanitizeToolDataForPersist(toolName string, data map[string]interface{}) map[string]interface{}

SanitizeToolDataForPersist returns a copy of tool Data safe for DB / SSE replay.

func SanitizeToolResultForClient

func SanitizeToolResultForClient(toolName string, result *types.ToolResult) map[string]interface{}

SanitizeToolResultForClient builds stream / persistence metadata for the UI.

func SerializeMCPToolResult

func SerializeMCPToolResult(result *types.ToolResult) (string, error)

SerializeMCPToolResult serializes an MCP tool result for display

func ShouldOmitRawToolOutput

func ShouldOmitRawToolOutput(_ string, data map[string]interface{}) bool

ShouldOmitRawToolOutput reports whether the raw XML/text Output should be excluded from SSE replay and persisted agent_steps. The full Output remains available in-memory for the current agent turn.

func StreamContentForToolResult

func StreamContentForToolResult(toolName string, success bool, errMsg string, data map[string]interface{}) string

StreamContentForToolResult is the short SSE Content field for tool results.

func StripThinkBlocks

func StripThinkBlocks(content string) string

StripThinkBlocks removes <think>…</think> blocks from LLM output content. Some models (DeepSeek, Qwen, etc.) embed chain-of-thought reasoning inside <think> tags in the content field. These should be stripped before:

  • Displaying content to users
  • Storing content in agent state / context manager
  • Emitting content via EventBus

Returns the cleaned string, or empty string if input is empty or becomes empty.

func SuccessorToolName

func SuccessorToolName(name string) string

SuccessorToolName returns the current tool that replaces a retired allowlist entry, or name itself when it is not retired.

func SwitchManagedTools

func SwitchManagedTools() []string

SwitchManagedTools returns the tools an allowed_tools entry does not register: other agent settings register or withhold them, such as the web search, memory, skills, sandbox and MCP settings, or the built-in skill installer. registerTools either strips them from the list and re-adds them from their switch, or skips them and leaves them to the code that owns them. The agent editor uses this list to explain such stored entries instead of offering them as checkboxes.

func ToolsConsumeFiles

func ToolsConsumeFiles(allowedTools []string) bool

ToolsConsumeFiles reports whether any tool in the allowed-tools list can use user-provided file references. Used to gate the `@file` listing in the chat input (and potentially SearchKnowledge defensively on the backend). An empty allowed-tools list is treated as "unknown → permissive".

func TruncateToolOutput

func TruncateToolOutput(output string, maxChars int) string

TruncateToolOutput truncates tool output that exceeds maxChars runes, preserving the head (70%) and tail (30%) sections with a truncation marker in between. This prevents large tool results from consuming the entire LLM context window while keeping the most useful parts (headers/summaries at the start, conclusions at the end).

maxChars is counted in Unicode characters (runes), not bytes, ensuring consistent behavior across English, Chinese, and other multi-byte character sets.

If maxChars <= 0 or the output is within the limit, it is returned unchanged.

func WithOutputBudget

func WithOutputBudget(ctx context.Context, maxChars int) context.Context

WithOutputBudget publishes the registry's output ceiling to the tool being executed. Tools that render several independent records in one result use it to shape their own output: the registry's fallback truncation keeps the head and tail of the whole blob, which for a batched result deletes the records in the middle and leaves the surviving ones cut mid-tag.

func WithToolExecContext

func WithToolExecContext(ctx context.Context, meta *ToolExecContext) context.Context

WithToolExecContext returns ctx that carries ToolExecContext for MCP approval and similar features.

Types

type AgentToolCatalog

type AgentToolCatalog struct {
	// Tools are the selectable tools, in display order.
	Tools []AgentToolInfo `json:"tools"`
	// DefaultTools apply when an agent's allowed_tools was never set (null).
	DefaultTools []string `json:"default_tools"`
	// SwitchManagedTools follow other agent settings; naming them in the list
	// does not register them.
	SwitchManagedTools []string `json:"switch_managed_tools"`
}

AgentToolCatalog is everything the agent editor needs to show an allowed_tools list the way the runtime reads it.

func BuildAgentToolCatalog

func BuildAgentToolCatalog() AgentToolCatalog

BuildAgentToolCatalog assembles the catalog from the tool definitions and the capability map, so the editor never keeps its own copy of either.

type AgentToolInfo

type AgentToolInfo struct {
	AvailableTool
	Requirement AgentToolRequirement `json:"requirement"`
}

AgentToolInfo is a selectable tool together with its capability requirement.

type AgentToolRequirement

type AgentToolRequirement struct {
	AnyOf []KBCapability `json:"any_of,omitempty"`
	AllOf []KBCapability `json:"all_of,omitempty"`
}

AgentToolRequirement is the JSON form of a tool's ToolRequirement: the knowledge base capabilities the agent's scope must expose for the tool to be registered.

type AllowedToolsCheck

type AllowedToolsCheck struct {
	// Tools is the list to store: retired names rewritten, duplicates and
	// switch-managed tools removed. Nil when the request left the list unset.
	Tools []string
	// Rejected are names no built-in tool answers to that the request adds.
	Rejected []string
	// KeptUnknown are such names that were already stored. They are kept so
	// that an agent saved before an upgrade can still be saved.
	KeptUnknown []string
	// Dropped are names removed from the list because naming them there has
	// no effect: switch-managed tools, which other agent settings decide on,
	// and retired skill tools without a successor.
	Dropped []string
}

AllowedToolsCheck is the outcome of checking an allowed_tools list on save.

func CheckAllowedTools

func CheckAllowedTools(stored, requested []string) AllowedToolsCheck

CheckAllowedTools validates a requested allowed_tools list against the selectable tools. stored is the list currently saved for the agent, nil for a new agent.

type AvailableTool

type AvailableTool struct {
	Name        string `json:"name"`
	Label       string `json:"label"`
	Description string `json:"description"`
	Group       string `json:"group"`
	// Danger marks tools that modify content, which the editor highlights.
	Danger bool `json:"danger,omitempty"`
}

AvailableTool describes a tool an agent's allowed_tools list may select. The agent editor renders its tool checkboxes from these definitions.

func AvailableToolDefinitions

func AvailableToolDefinitions() []AvailableTool

AvailableToolDefinitions returns the tools an agent's allowed_tools list may select, in the order the agent editor shows them. Keep this in sync with registered tools in this package.

type BaseTool

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

BaseTool provides common functionality for tools

func NewBaseTool

func NewBaseTool(name, description string, schema json.RawMessage) BaseTool

NewBaseTool creates a new base tool

func (*BaseTool) Description

func (t *BaseTool) Description() string

Description returns the tool description

func (*BaseTool) Name

func (t *BaseTool) Name() string

Name returns the tool name

func (*BaseTool) Parameters

func (t *BaseTool) Parameters() json.RawMessage

Parameters returns the tool parameters schema

type BrowserSkillTool

type BrowserSkillTool struct {
	BaseTool
	// contains filtered or unexported fields
}

BrowserSkillTool binds native browser commands to one member and conversation.

func NewBrowserSkillTool

func NewBrowserSkillTool(
	manager *browserskill.Manager,
	scope browserskill.Scope,
	session string,
	searchInstructions ...string,
) *BrowserSkillTool

NewBrowserSkillTool creates a session-bound adapter to upstream RPC.

func (*BrowserSkillTool) Cleanup

func (t *BrowserSkillTool) Cleanup(ctx context.Context)

Cleanup reclaims successful temporary tasks; unfinished work retains its pages. Execute can exit with a cancelled context; cleanup must still reach the extension.

func (*BrowserSkillTool) Execute

Execute validates tool arguments and dispatches through the authorized task.

func (*BrowserSkillTool) ValidateArguments

func (t *BrowserSkillTool) ValidateArguments(args json.RawMessage) error

ValidateArguments enforces the flat schema and the selected operation's required fields before opening a browser or executing any page action.

type ColumnInfo

type ColumnInfo struct {
	Name     string `json:"name"`
	Type     string `json:"type"`
	Nullable string `json:"nullable"`
}

ColumnInfo represents information about a single column

type DataAnalysisInput

type DataAnalysisInput struct {
	KnowledgeID string `json:"knowledge_id" jsonschema:"short dN document ID to query"`
	SQL         string `` //nolint:lll // jsonschema tag
	/* 189-byte string literal not displayed */
}

type DataAnalysisTool

type DataAnalysisTool struct {
	BaseTool
	// contains filtered or unexported fields
}

func NewDataAnalysisTool

func NewDataAnalysisTool(
	knowledgeBaseService interfaces.KnowledgeBaseService,
	knowledgeService interfaces.KnowledgeService,
	tenantService interfaces.TenantService,
	fileService interfaces.FileService,
	db *sql.DB,
	sessionID string,
	storageResolvers ...interfaces.StorageBackendResolver,
) *DataAnalysisTool

func (*DataAnalysisTool) Cleanup

func (t *DataAnalysisTool) Cleanup(ctx context.Context)

Cleanup cleans up the session-specific schema

func (*DataAnalysisTool) Execute

Execute executes the SQL query on DuckDB (only read-only queries are allowed)

func (*DataAnalysisTool) LoadFromCSV

func (t *DataAnalysisTool) LoadFromCSV(ctx context.Context, filename string, tableName string) (*TableSchema, error)

LoadFromCSV loads data from a CSV file into a DuckDB table and returns the table schema Parameters:

  • ctx: context for cancellation and timeout
  • filename: path to the CSV file
  • tableName: name of the table to create

Returns:

  • *TableSchema: schema information of the created table
  • error: any error that occurred during the operation

func (*DataAnalysisTool) LoadFromExcel

func (t *DataAnalysisTool) LoadFromExcel(ctx context.Context, filename string, tableName string) (*TableSchema, error)

LoadFromExcel loads data from an Excel file into a DuckDB table and returns the table schema.

Multi-sheet workbooks are supported: XLSX sheets containing only formatting are skipped; rows from the remaining sheets are unioned (UNION ALL BY NAME) into a single table. A synthetic '__sheet_name' column is added so downstream SQL can filter / aggregate per sheet. If sheet enumeration fails for any reason, we fall back to reading just the first sheet (original behavior).

Parameters:

  • ctx: context for cancellation and timeout
  • filename: path to the Excel file
  • tableName: name of the table to create

Returns:

  • *TableSchema: schema information of the created table
  • error: any error that occurred during the operation

Note: requires the DuckDB 'excel' extension (for read_xlsx) and the 'spatial' extension (for st_read_meta used to enumerate sheets).

func (*DataAnalysisTool) LoadFromKnowledge

func (t *DataAnalysisTool) LoadFromKnowledge(ctx context.Context, knowledge *types.Knowledge) (*TableSchema, error)

LoadFromKnowledge loads data from a Knowledge entity into a DuckDB table and returns the table schema. It automatically determines the file type and calls the appropriate loading method.

The source file is first materialized to a local temp file via FileService.GetFile so DuckDB's st_read / read_xlsx / read_csv_auto can open it directly. This side-steps provider-specific URL schemes (e.g. the local:// URL returned by the local file service) that DuckDB's extensions cannot resolve on their own.

Parameters:

  • ctx: context for cancellation and timeout
  • knowledge: the Knowledge entity containing file information

Returns:

  • *TableSchema: schema information of the created table
  • error: any error that occurred during the operation

func (*DataAnalysisTool) LoadFromKnowledgeID

func (t *DataAnalysisTool) LoadFromKnowledgeID(ctx context.Context, knowledgeID string) (*TableSchema, error)

LoadFromKnowledgeID loads data from a Knowledge ID into a DuckDB table and returns the table schema Parameters:

  • ctx: context for cancellation and timeout
  • knowledgeID: the ID of the Knowledge entity

Returns:

  • string: the name of the created table
  • *TableSchema: schema information of the created table
  • error: any error that occurred during the operation

func (*DataAnalysisTool) LoadFromTable

func (t *DataAnalysisTool) LoadFromTable(ctx context.Context, tableName string) (*TableSchema, error)

LoadFromTable retrieves the schema information of an existing table Parameters:

  • ctx: context for cancellation and timeout
  • tableName: name of the table to query

Returns:

  • *TableSchema: schema information of the table
  • error: any error that occurred during the operation

Note: This function does NOT create the table, it only retrieves schema information

func (*DataAnalysisTool) TableName

func (t *DataAnalysisTool) TableName(knowledge *types.Knowledge) string

func (*DataAnalysisTool) WithSearchTargets

func (t *DataAnalysisTool) WithSearchTargets(searchTargets types.SearchTargets) *DataAnalysisTool

WithSearchTargets enables the Agent-only authorization boundary. Other internal data-analysis callers retain their existing service-owned scope. The flag is set independently of the slice length: an Agent turn that ended up with no search target must reject every document, not fall back to unrestricted access.

type DataSchemaInput

type DataSchemaInput struct {
	KnowledgeID string `json:"knowledge_id" jsonschema:"short dN document ID to query"`
}

type DataSchemaTool

type DataSchemaTool struct {
	BaseTool
	// contains filtered or unexported fields
}

func NewDataSchemaTool

func NewDataSchemaTool(knowledgeService interfaces.KnowledgeService, chunkRepo interfaces.ChunkRepository, targetChunkTypes ...types.ChunkType) *DataSchemaTool

func (*DataSchemaTool) Execute

func (t *DataSchemaTool) Execute(ctx context.Context, args json.RawMessage) (*types.ToolResult, error)

Execute executes the tool logic

func (*DataSchemaTool) WithSearchTargets

func (t *DataSchemaTool) WithSearchTargets(searchTargets types.SearchTargets) *DataSchemaTool

WithSearchTargets enables Agent request-scope authorization. An Agent turn with no search target must reject every document rather than fall back to the unrestricted service-owned lookup.

type DatabaseQueryInput

type DatabaseQueryInput struct {
	SQL string `` /* 138-byte string literal not displayed */
}

type DatabaseQueryTool

type DatabaseQueryTool struct {
	BaseTool
	// contains filtered or unexported fields
}

DatabaseQueryTool allows AI to query the database with auto-injected tenant_id for security

func NewDatabaseQueryTool

func NewDatabaseQueryTool(db *gorm.DB, searchTargets types.SearchTargets) *DatabaseQueryTool

NewDatabaseQueryTool creates a new database query tool

func (*DatabaseQueryTool) Execute

Execute executes the database query tool

type EditSandboxFileInput

type EditSandboxFileInput struct {
	Path  string          `json:"path" jsonschema:"Absolute or /workspace-relative sandbox path of an existing text file (not /workspace/input)."` //nolint:lll // one-line struct tag
	Edits sandboxEditList ``                                                                                                                       //nolint:lll // one-line struct tag
	/* 290-byte string literal not displayed */
}

EditSandboxFileInput defines the input parameters for edit_sandbox_file.

There is deliberately one shape. A flat old_string/new_string pair alongside edits[] would save a model a dozen bytes on a one-line fix and cost it a choice it can get wrong, plus a rule about which of the two replace_all attaches to. Every replacement goes in edits[], and a single-element array is the ordinary way to change one thing.

type EditSandboxFileTool

type EditSandboxFileTool struct {
	BaseTool
	// contains filtered or unexported fields
}

EditSandboxFileTool applies an exact string replacement to a sandbox file.

func NewEditSandboxFileTool

func NewEditSandboxFileTool(editor SandboxFileEditor) *EditSandboxFileTool

NewEditSandboxFileTool constructs the tool. `editor` MUST NOT be nil.

func (*EditSandboxFileTool) BindSession

func (s *EditSandboxFileTool) BindSession(id string)

func (*EditSandboxFileTool) Cleanup

func (t *EditSandboxFileTool) Cleanup(ctx context.Context) error

Cleanup releases any resources.

func (*EditSandboxFileTool) Description

func (t *EditSandboxFileTool) Description() string

Description is built from the session sandbox layout so host paths never hard-code /workspace.

func (*EditSandboxFileTool) Execute

Execute reads the file, applies the replacement, and writes it back.

func (*EditSandboxFileTool) Parameters

func (t *EditSandboxFileTool) Parameters() json.RawMessage

Parameters rewrites workspace paths in the schema to match the session layout.

type EditSkillFileInput

type EditSkillFileInput struct {
	Path       string `` /* 139-byte string literal not displayed */
	OldString  string `` /* 134-byte string literal not displayed */
	NewString  string `json:"new_string" jsonschema:"Replacement text. Use an empty string to delete the matched text."`
	ReplaceAll bool   `` /* 132-byte string literal not displayed */
}

EditSkillFileInput defines the input parameters for edit_skill_file.

type EditSkillFileTool

type EditSkillFileTool struct {
	BaseTool
	// contains filtered or unexported fields
}

EditSkillFileTool applies an exact string replacement inside the skill tree.

func NewEditSkillFileTool

func NewEditSkillFileTool(store SkillFileStore, skillDir string) *EditSkillFileTool

NewEditSkillFileTool constructs the tool. `store` MUST NOT be nil and `skillDir` MUST be the directory of the skill this install owns.

func (*EditSkillFileTool) Cleanup

func (t *EditSkillFileTool) Cleanup(ctx context.Context) error

Cleanup releases any resources.

func (*EditSkillFileTool) Execute

Execute reads the file, applies the replacement, and writes it back.

type KBCapability

type KBCapability string

KBCapability names the capability flags a knowledge base can expose. Values mirror the keys of `types.KBCapabilities`.

const (
	CapVector  KBCapability = "vector"
	CapKeyword KBCapability = "keyword"
	CapWiki    KBCapability = "wiki"
	CapGraph   KBCapability = "graph"
	CapFAQ     KBCapability = "faq"
)

type KBFilter

type KBFilter struct {
	AnyOf []KBCapability
}

KBFilter is the derived "KB must expose at least one of these capabilities" predicate for a set of allowed tools (see DeriveKBFilterFromTools).

func DeriveKBFilterForAgent

func DeriveKBFilterForAgent(agentMode string, allowedTools []string) KBFilter

DeriveKBFilterForAgent derives the effective KB capability filter for a given agent configuration. It combines the implicit constraint from `agentMode` (quick-answer forces vector|keyword) with the tool-derived filter (any_of capabilities required by some allowed tool).

The returned filter is the UNION of both contributions, which matches the existing any_of semantics: a KB passes iff it exposes at least one of the listed capabilities.

func DeriveKBFilterFromTools

func DeriveKBFilterFromTools(allowedTools []string) KBFilter

DeriveKBFilterFromTools derives a capability filter such that a KB passes iff at least ONE of the allowed tools has a requirement the KB satisfies. Tools without any KB requirement don't contribute — if the allowed-tools list contains only such tools, the returned filter is empty (accept all).

func (KBFilter) IsEmpty

func (f KBFilter) IsEmpty() bool

IsEmpty reports whether the filter imposes no constraint (either no capability accumulated, or explicit zero value).

type ListDocumentsInput

type ListDocumentsInput struct {
	KnowledgeBaseID string `json:"knowledge_base_id"`
	Keyword         string `json:"keyword,omitempty"`
	Page            int    `json:"page,omitempty"`
	PageSize        int    `json:"page_size,omitempty"`
}

ListDocumentsInput defines the input parameters for list_documents.

type ListDocumentsTool

type ListDocumentsTool struct {
	BaseTool
	// contains filtered or unexported fields
}

ListDocumentsTool pages through the documents of one knowledge base.

func NewListDocumentsTool

func NewListDocumentsTool(
	knowledgeService interfaces.KnowledgeService, searchTargets types.SearchTargets,
) *ListDocumentsTool

NewListDocumentsTool creates a new list_documents tool.

func (*ListDocumentsTool) Execute

Execute lists documents, honouring any document/tag scope pinned on the KB.

type ListSandboxFilesInput

type ListSandboxFilesInput struct {
	// Path is the absolute path inside the sandbox to list. When empty
	// the tool falls back to layoutDefaultListDir. Relative paths resolve
	// from the workspace root.
	Path string `` //nolint:lll // one-line struct tag
	/* 182-byte string literal not displayed */
	// MaxEntries caps the listing size to protect the LLM context.
	// Zero uses defaultListSandboxMaxEntries.
	MaxEntries int `` /* 206-byte string literal not displayed */
}

ListSandboxFilesInput defines the input parameters for list_sandbox_files.

type ListSandboxFilesTool

type ListSandboxFilesTool struct {
	BaseTool
	// contains filtered or unexported fields
}

ListSandboxFilesTool exposes SandboxFileSource.ListSessionFiles to the agent as a read-only enumeration primitive.

func NewListSandboxFilesTool

func NewListSandboxFilesTool(source SandboxFileSource) *ListSandboxFilesTool

NewListSandboxFilesTool constructs the tool. `source` MUST NOT be nil: callers should feature-gate registration in the agent bootstrap when the sandbox backend does not support per-session file inspection.

func (*ListSandboxFilesTool) BindSession

func (s *ListSandboxFilesTool) BindSession(id string)

func (*ListSandboxFilesTool) Cleanup

func (t *ListSandboxFilesTool) Cleanup(ctx context.Context) error

Cleanup releases any resources.

func (*ListSandboxFilesTool) Description

func (t *ListSandboxFilesTool) Description() string

Description is built from the session sandbox layout so host paths never hard-code /workspace.

func (*ListSandboxFilesTool) Execute

Execute enumerates files under the requested path inside the current session's sandbox.

func (*ListSandboxFilesTool) Parameters

func (t *ListSandboxFilesTool) Parameters() json.RawMessage

Parameters rewrites workspace paths in the schema to match the session layout.

type MCPCallTool

type MCPCallTool struct {
	BaseTool
	// contains filtered or unexported fields
}

MCPCallTool resolves catalog references and invokes their MCP targets.

func (*MCPCallTool) Execute

func (t *MCPCallTool) Execute(ctx context.Context, raw json.RawMessage) (*types.ToolResult, error)

Execute revalidates the catalog target and runs the shared execution pipeline.

func (*MCPCallTool) Parameters

func (t *MCPCallTool) Parameters() json.RawMessage

Parameters advertises only references whose complete definitions are in the current function list. Execution still revalidates scope, schema and policy.

type MCPCatalog

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

MCPCatalog is owned by one Agent engine and one authorization principal. Only its server snapshots mutate; the executable registry stays fixed. No credential-bearing service object is serialized to the model.

type MCPDiscoverTool

type MCPDiscoverTool struct {
	BaseTool
	// contains filtered or unexported fields
}

MCPDiscoverTool exposes the authorized directory and exact tool definitions.

func (*MCPDiscoverTool) Description

func (t *MCPDiscoverTool) Description() string

Description advertises source-level guidance alongside direct tools, just as Codex's namespace/source descriptions accompany model-visible definitions. Specific tool descriptions and schemas live in their own function entries.

func (*MCPDiscoverTool) Execute

Execute lists authorized services or tools, or describes an exact tool.

func (*MCPDiscoverTool) OutputLimitChars

func (t *MCPDiscoverTool) OutputLimitChars(raw json.RawMessage) int

OutputLimitChars permits a single schema to exceed the ordinary text budget. Preserve complete JSON up to an explicit hard ceiling rather than silently truncate parameter rules.

type MCPInput

type MCPInput = map[string]any

type MCPMetadataIO

type MCPMetadataIO struct {
	Get func(context.Context, uint64, string) (*types.MCPMetadata, error)
	Put func(context.Context, uint64, string, []*types.MCPTool, string) error
}

MCPMetadataIO reads persisted directories and optionally writes a snapshot listed from an already-authorized live connection. Put must not be used to publish a partial tools/list.

type MCPOAuthSession

type MCPOAuthSession struct {
	EventBus           *event.EventBus
	SessionID          string
	AssistantMessageID string
	UserID             string
	RequestID          string
	// ApprovalCtx is the parent ctx without per-operation timeouts; when nil,
	// the caller ctx is used for the OAuth wait.
	ApprovalCtx context.Context
	// ExecTimeout, when >0, caps the retry ctx after a successful authorization.
	ExecTimeout time.Duration
	// AuthWaitTimeoutSeconds is the agent-level, user-configured number of
	// seconds to wait for in-conversation OAuth authorization. See oauthWaitTimeout.
	AuthWaitTimeoutSeconds int
}

MCPOAuthSession carries chat/session metadata so MCP connect and tool registration can pause for in-conversation OAuth. Nil disables the prompt.

type MCPRegisteredTool

type MCPRegisteredTool struct {
	*MCPTool
	// contains filtered or unexported fields
}

MCPRegisteredTool is a model-visible definition bound to one authorized catalog and schema. It must not bypass the directory's live permission and configuration checks just because its definition was advertised up front.

func (*MCPRegisteredTool) Description

func (t *MCPRegisteredTool) Description() string

Description identifies the external service and original tool.

func (*MCPRegisteredTool) Execute

Execute revalidates the catalog before invoking the bound tool.

type MCPServiceLookup

type MCPServiceLookup func(context.Context, uint64, string) (*types.MCPService, error)

MCPServiceLookup revalidates a service from the already-authorized ID set. It must preserve the application's tenant / builtin-service access rules.

type MCPTool

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

MCPTool wraps an MCP service tool to implement the Tool interface

func NewMCPTool

func NewMCPTool(
	service *types.MCPService, mcpTool *types.MCPTool,
	mcpManager *mcp.MCPManager, gate approval.MCPApproval, authWaitTimeoutSeconds int,
) *MCPTool

NewMCPTool creates a new MCP tool wrapper. authWaitTimeoutSeconds carries the agent-level OAuth wait timeout applied when a tool call triggers in-conversation auth.

func (*MCPTool) Description

func (t *MCPTool) Description() string

Description returns the tool description. Prefix indicates external/untrusted source to reduce indirect prompt injection impact.

func (*MCPTool) Execute

func (t *MCPTool) Execute(ctx context.Context, args json.RawMessage) (*types.ToolResult, error)

Execute executes the MCP tool

func (*MCPTool) Name

func (t *MCPTool) Name() string

Name returns the unique name for this tool. Format: mcp_{service_name}_{tool_name} — uses the human-readable service name so that tool names remain stable across MCP server reconnections (fixes #715).

Security: service names must be unique per tenant (enforced by DB unique index on (tenant_id, name)). The ToolRegistry uses first-wins semantics to prevent a later service from overwriting an already-registered tool (GHSA-67q9-58vj-32qx).

Note: OpenAI API requires tool names to match ^[a-zA-Z0-9_-]+$ and max 64 chars.

func (*MCPTool) Parameters

func (t *MCPTool) Parameters() json.RawMessage

Parameters returns the JSON Schema for tool parameters

func (*MCPTool) ValidateArguments

func (t *MCPTool) ValidateArguments(args json.RawMessage) error

ValidateArguments checks the complete, immutable MCP schema. Compilation is cached per tool snapshot; refreshing a definition creates a new MCPTool. Built-in tools keep their established validation and conversion semantics.

type PlanStep

type PlanStep struct {
	ID          string `json:"id" jsonschema:"Unique identifier for this step (e.g., 'step1', 'step2')"`
	Description string `json:"description" jsonschema:"Clear description of what to investigate or accomplish in this step"`
	Status      string `json:"status" jsonschema:"Current status: pending (not started), in_progress (executing), completed (finished)"`
}

PlanStep represents a single step in the research plan

type QueryKnowledgeGraphInput

type QueryKnowledgeGraphInput struct {
	KnowledgeBaseIDs []string `json:"knowledge_base_ids" jsonschema:"Array of short bN knowledge base IDs to query"`
	Query            string   `json:"query" jsonschema:"Query content (entity name or query text)"`
}

QueryKnowledgeGraphInput defines the input parameters for query knowledge graph tool

type QueryKnowledgeGraphTool

type QueryKnowledgeGraphTool struct {
	BaseTool
	// contains filtered or unexported fields
}

QueryKnowledgeGraphTool queries the knowledge graph for entities and relationships

func NewQueryKnowledgeGraphTool

func NewQueryKnowledgeGraphTool(
	knowledgeService interfaces.KnowledgeBaseService,
	searchTargets ...types.SearchTargets,
) *QueryKnowledgeGraphTool

NewQueryKnowledgeGraphTool creates a new query knowledge graph tool

func (*QueryKnowledgeGraphTool) Execute

Execute performs the knowledge graph query with concurrent KB processing

func (*QueryKnowledgeGraphTool) WithGraph

WithGraph lets the tool query the graph store for entities and relations. Without it the tool only has text search, which is what it used to do in every case: the tool never touched the graph, so a graph-only knowledge base always answered "no relevant graph information".

func (*QueryKnowledgeGraphTool) WithKnowledgeScope

func (t *QueryKnowledgeGraphTool) WithKnowledgeScope(
	knowledgeService interfaces.KnowledgeService,
) *QueryKnowledgeGraphTool

WithKnowledgeScope enables document/tag-level result filtering for Agent calls. The graph backend queries by KB, so the tool must enforce narrower SearchTargets before returning any result to the model.

type ReadDocumentInput

type ReadDocumentInput struct {
	ID      string `json:"id"`
	Offset  int    `json:"offset,omitempty"`
	Limit   int    `json:"limit,omitempty"`
	Query   string `json:"query,omitempty"`
	Regex   bool   `json:"regex,omitempty"`
	Context int    `json:"context,omitempty"`
}

ReadDocumentInput defines the input parameters for read_document.

type ReadDocumentTool

type ReadDocumentTool struct {
	BaseTool
	// contains filtered or unexported fields
}

ReadDocumentTool reads document metadata and chunks by page, by chunk handle, or by an in-document text search.

func NewReadDocumentTool

func NewReadDocumentTool(
	knowledgeService interfaces.KnowledgeService,
	chunkService interfaces.ChunkService,
	searchTargets types.SearchTargets,
) *ReadDocumentTool

NewReadDocumentTool creates a new read_document tool.

func (*ReadDocumentTool) Execute

Execute resolves the id, then dispatches to page, chunk or query reading.

type ReadFileInput

type ReadFileInput struct {
	Path string `` //nolint:lll // JSON schema tags must remain on one line.
	/* 224-byte string literal not displayed */
	LineOffset int `` //nolint:lll // JSON schema tags must remain on one line.
	/* 128-byte string literal not displayed */
	Offset   int    `json:"offset,omitempty" jsonschema:"1-based line number; continue at next_offset"`
	Limit    int    `json:"limit,omitempty" jsonschema:"Text lines (default 2000); attachment images (default 1, max 4; full always 1)"` //nolint:lll // JSON schema tags must remain on one line.
	MaxBytes int64  `json:"max_bytes,omitempty" jsonschema:"Text byte budget; at most 65536 (web: 51200)"`
	Query    string `json:"query,omitempty" jsonschema:"Find lines containing all words, case-insensitively"`
	Regex    bool   `json:"regex,omitempty" jsonschema:"Match query as one regular expression"`
	Detail   string `` //nolint:lll // JSON schema tags must remain on one line.
	/* 182-byte string literal not displayed */
	Focus string `` //nolint:lll // JSON schema tags must remain on one line.
	/* 126-byte string literal not displayed */
}

type ReadFileTool

type ReadFileTool struct {
	BaseTool
	// contains filtered or unexported fields
}

ReadFileTool exposes one read operation over capability-scoped sources. Skill resources use the manager's allowlist and bundle loader, never an arbitrary host path. Filesystem paths are resolved only inside the current session's sandbox and retain its file type, size, and output limits.

func NewReadFileTool

func NewReadFileTool(source SandboxFileSource) *ReadFileTool

func (*ReadFileTool) BindSession

func (t *ReadFileTool) BindSession(id string)

BindSession records the session so Description() and Parameters() advertise that session's workspace instead of the remote /workspace copy.

func (*ReadFileTool) Execute

func (t *ReadFileTool) Execute(ctx context.Context, args json.RawMessage) (*types.ToolResult, error)

func (*ReadFileTool) ExecutionTimeout

func (t *ReadFileTool) ExecutionTimeout() time.Duration

ExecutionTimeout gives the attachment parse wait room to return a structured result before the engine's outer tool deadline cancels the lookup.

func (*ReadFileTool) Parameters

func (t *ReadFileTool) Parameters() json.RawMessage

Parameters rewrites workspace paths in the schema to match the session layout.

func (*ReadFileTool) WithAttachmentCopies

func (t *ReadFileTool) WithAttachmentCopies(copies map[string]string) *ReadFileTool

WithAttachmentCopies makes reading a staged sandbox copy of an attachment return the attachment's parsed text. The original bytes of a document are binary to read_file, and the parse may not have finished when the copy was staged, so the copy and the attachment must read the same.

func (*ReadFileTool) WithAttachments

func (t *ReadFileTool) WithAttachments(source interfaces.AttachmentReader) *ReadFileTool

WithAttachments adds the files the user attached to this conversation. The application reader owns authorization, parse waits and optional projections.

func (*ReadFileTool) WithSkills

func (t *ReadFileTool) WithSkills(manager *skills.Manager, shell bool) *ReadFileTool

func (*ReadFileTool) WithWebPages

func (t *ReadFileTool) WithWebPages(source WebPageSource) *ReadFileTool

WithWebPages adds session-scoped access to complete fetched page snapshots.

type SandboxCommandExecutor

type SandboxCommandExecutor interface {
	ExecShellCommand(
		ctx context.Context,
		sessionID string,
		command string,
		workDir string,
		timeout time.Duration,
		env map[string]string,
	) (*sandbox.ExecuteResult, error)
}

SandboxCommandExecutor is the narrow, tool-facing subset of a session-aware sandbox manager that supports executing arbitrary shell commands. In production it is satisfied by *sandbox.SessionBoundManager; tests can stub it with an in-memory fake. Kept local to the tools package for the same reason as SandboxFileSource — no dependency leak into higher layers.

type SandboxEdit

type SandboxEdit struct {
	OldString  string `json:"old_string" jsonschema:"Exact text to find, unique in the file and not overlapping any other edit in this call."` //nolint:lll // one-line struct tag
	NewString  string `json:"new_string" jsonschema:"Replacement text. Use an empty string to delete the matched text."`
	ReplaceAll bool   `` //nolint:lll // one-line struct tag
	/* 132-byte string literal not displayed */
}

SandboxEdit is one targeted replacement.

type SandboxFileEditor

type SandboxFileEditor interface {
	StatSessionFile(ctx context.Context, sessionID, filePath string) (*sandbox.RemoteStatEntry, error)
	ReadSessionFile(ctx context.Context, sessionID, filePath string) ([]byte, error)
	WriteSessionWorkspaceFile(ctx context.Context, sessionID, filePath string, content []byte) error
}

SandboxFileEditor reads then writes a session workspace file. Production uses *sandbox.SessionBoundManager via SessionFileStore.

type SandboxFileSink

type SandboxFileSink interface {
	StatSessionFile(ctx context.Context, sessionID, filePath string) (*sandbox.RemoteStatEntry, error)
	ReadSessionFile(ctx context.Context, sessionID, filePath string) ([]byte, error)
	WriteSessionWorkspaceFile(ctx context.Context, sessionID, filePath string, content []byte) error
}

SandboxFileSink is the file-store slice write_sandbox_file needs. Production uses *sandbox.SessionBoundManager via SessionFileStore.

It is not write-only: append mode has to see the file that is already there. The remote backends expose no atomic append, so this reads and rewrites the whole file, which is what edit_sandbox_file already does.

type SandboxFileSource

type SandboxFileSource interface {
	ListSessionFiles(ctx context.Context, sessionID, dir string) ([]sandbox.RemoteDirEntry, error)
	StatSessionFile(ctx context.Context, sessionID, path string) (*sandbox.RemoteStatEntry, error)
	ReadSessionFile(ctx context.Context, sessionID, path string) ([]byte, error)
}

SandboxFileSource is the narrow, tool-facing subset of a session-aware sandbox manager. In production it is satisfied by *sandbox.SessionBoundManager; tests can stub it with an in-memory fake.

Keeping the interface local to the tools package avoids leaking a dependency on internal/application/service (which is a higher layer) and mirrors the pattern used by ArtifactCollector.SandboxArtifactSource.

type SandboxInstallCommandExecutor

type SandboxInstallCommandExecutor interface {
	ExecShellCommandWithOptions(
		ctx context.Context,
		sessionID string,
		command string,
		opts sandbox.ShellExecOptions,
	) (*sandbox.ExecuteResult, error)
}

SandboxInstallCommandExecutor is the privileged counterpart of SandboxCommandExecutor, satisfied by *sandbox.SessionBoundManager via sandbox.SessionInstallShellExecutor. It exists as its own named type so the install privilege can only be handed over deliberately: nothing that merely implements ExecShellCommand can be mistaken for it.

type SearchConversationsInput

type SearchConversationsInput struct {
	Query string `json:"query"`
	Limit int    `json:"limit,omitempty"`
}

SearchConversationsInput defines the input parameters for the tool.

type SearchConversationsTool

type SearchConversationsTool struct {
	BaseTool
	// contains filtered or unexported fields
}

SearchConversationsTool lets the agent look things up in the user's own chat history.

A long-term memory feature that distils conversations into a few dozen sentences will always lose detail — the exact config someone was given three weeks ago is not a durable fact about them, and storing it would be the wrong shape. Keeping the transcripts searchable and letting the agent go back for them is the same division of labour MemGPT uses: a small always-present summary plus retrieval into the full history on demand.

func NewSearchConversationsTool

func NewSearchConversationsTool(
	messageService interfaces.MessageService, ownerID, currentSessionID string,
) *SearchConversationsTool

NewSearchConversationsTool creates the conversation history search tool.

func (*SearchConversationsTool) Execute

Execute searches the user's own past conversations.

type SearchKnowledgeInput

type SearchKnowledgeInput struct {
	Query            string   `json:"query"`
	Mode             string   `json:"mode,omitempty"`
	KnowledgeBaseIDs []string `json:"knowledge_base_ids,omitempty"`
	Limit            int      `json:"limit,omitempty"`
}

SearchKnowledgeInput defines the input parameters for search_knowledge.

type SearchKnowledgeTool

type SearchKnowledgeTool struct {
	BaseTool
	// contains filtered or unexported fields
}

SearchKnowledgeTool searches chunk-indexed knowledge bases in semantic, keyword or hybrid mode and returns reranked, de-duplicated chunks.

func NewSearchKnowledgeTool

func NewSearchKnowledgeTool(
	knowledgeBaseService interfaces.KnowledgeBaseService,
	knowledgeService interfaces.KnowledgeService,
	chunkService interfaces.ChunkService,
	searchTargets types.SearchTargets,
	rerankModel rerank.Reranker,
	cfg *config.Config,
) *SearchKnowledgeTool

NewSearchKnowledgeTool creates a new search_knowledge tool.

func (*SearchKnowledgeTool) Execute

Execute executes the search.

func (*SearchKnowledgeTool) WithContextImages

func (t *SearchKnowledgeTool) WithContextImages(enabled bool) *SearchKnowledgeTool

WithContextImages attaches to the result the images of the results that rest on an image matched by its own vector, at most searchutil.MaxContextImages. Enable it only for a vision model: the agent describes tool images to a model that cannot see them, and these already carry captions.

func (*SearchKnowledgeTool) WithPatternFilter

func (t *SearchKnowledgeTool) WithPatternFilter(re *regexp.Regexp) *SearchKnowledgeTool

WithPatternFilter restricts results to chunks whose text matches re. The candidate pool is widened to the maximum limit before filtering.

type SearchMemoryInput

type SearchMemoryInput struct {
	Query string `json:"query"`
	Limit int    `json:"limit,omitempty"`
}

SearchMemoryInput defines the input parameters for the tool.

type SearchMemoryTool

type SearchMemoryTool struct {
	BaseTool
	// contains filtered or unexported fields
}

SearchMemoryTool lets the agent reach into the user's long-term memory store beyond what this turn's recall injected.

Recall is computed once, before the loop starts, against the question the user opened with, and it admits five situational items inside a 600-rune budget. Both of those are the right call for something that rides in every single turn's system prompt, and both stop being the right call once an agent has spent ten iterations working its way to a sub-problem the opening question never mentioned. This is the same division of labour SearchConversationsTool describes — a small always-present summary plus retrieval on demand — applied to the memory store rather than to transcripts.

The tool takes no owner argument. Which memory space is read is derived entirely from the request context inside the service, which is what keeps "read someone else's memories" from being reachable by writing a different id into a tool call.

func NewSearchMemoryTool

func NewSearchMemoryTool(memoryService interfaces.MemoryService) *SearchMemoryTool

NewSearchMemoryTool creates the long-term memory search tool.

func (*SearchMemoryTool) Execute

func (t *SearchMemoryTool) Execute(
	ctx context.Context, args json.RawMessage,
) (*types.ToolResult, error)

Execute searches the user's own long-term memory.

type SequentialThinkingInput

type SequentialThinkingInput struct {
	Thought           string `json:"thought"`
	NextThoughtNeeded bool   `json:"next_thought_needed"`
	ThoughtNumber     int    `json:"thought_number"`
	TotalThoughts     int    `json:"total_thoughts"`
	IsRevision        bool   `json:"is_revision,omitempty"`
	RevisesThought    *int   `json:"revises_thought,omitempty"`
	BranchFromThought *int   `json:"branch_from_thought,omitempty"`
	BranchID          string `json:"branch_id,omitempty"`
	NeedsMoreThoughts bool   `json:"needs_more_thoughts,omitempty"`
}

SequentialThinkingInput defines the input parameters for sequential thinking tool

type SequentialThinkingTool

type SequentialThinkingTool struct {
	BaseTool
	// contains filtered or unexported fields
}

SequentialThinkingTool is a dynamic and reflective problem-solving tool This tool helps analyze problems through a flexible thinking process that can adapt and evolve

func NewSequentialThinkingTool

func NewSequentialThinkingTool() *SequentialThinkingTool

NewSequentialThinkingTool creates a new sequential thinking tool instance

func (*SequentialThinkingTool) Execute

Execute executes the sequential thinking tool

type ShellExecInput

type ShellExecInput struct {
	Stdin string `` /* 200-byte string literal not displayed */
	// Command is the shell command to execute. Runs under Bash.
	Command string `json:"command" jsonschema:"Shell command to execute (single line, supports pipes and && chaining). Runs under Bash."`
	// WorkDir is the working directory for the command; defaults to /workspace.
	WorkDir string `` //nolint:lll // one-line struct tag
	/* 151-byte string literal not displayed */
	// TimeoutSec caps execution time. Zero uses the default (120s); the
	// value is hard-capped at 600s regardless of what the LLM requests.
	TimeoutSec int `json:"timeout_sec,omitempty" jsonschema:"Per-call timeout in seconds. Defaults to 120, hard-capped at 600."`
	// MaxOutputBytes caps returned stdout. Stderr has an independent smaller
	// fixed budget, and the complete model-visible output is capped at 64 KiB.
	MaxOutputBytes int `` /* 222-byte string literal not displayed */
	// MaxStderrBytes caps returned stderr independently from stdout.
	MaxStderrBytes int `json:"max_stderr_bytes,omitempty" jsonschema:"Maximum bytes returned from stderr. Defaults to 8192, hard-capped at 16384."`
	// Env carries extra environment variables merged into the shell's env.
	Env map[string]string `` /* 141-byte string literal not displayed */
	// SkillName, when set, pulls that skill's scoped environment variables
	// (API keys) into this one command's process only. Resolution
	// uses the caller-scoped SkillEnvResolver, so values
	// are per-caller (taken from ctx) and never persist. Omitting it leaves
	// shell_exec's behaviour unchanged.
	SkillName string `` //nolint:lll // JSON schema tags must remain on one line.
	/* 206-byte string literal not displayed */
}

ShellExecInput defines the input parameters for shell_exec.

type ShellExecTool

type ShellExecTool struct {
	BaseTool
	// contains filtered or unexported fields
}

ShellExecTool executes shell commands inside the session's sandbox.

func NewHostInstallShellExecTool

func NewHostInstallShellExecTool(
	executor SandboxInstallCommandExecutor, versionDir string,
) *ShellExecTool

NewHostInstallShellExecTool is the Lite installer's shell: commands run as the desktop user under an install-only OS sandbox, and versionDir is the only directory they may write.

func NewInstallShellExecTool

func NewInstallShellExecTool(
	executor SandboxInstallCommandExecutor, skillDir string,
) *ShellExecTool

NewInstallShellExecTool constructs the install-mode variant: commands run as root and may work inside the skills image root. It is registered only for the built-in skill installer agent (see AgentConfig.SkillInstallMode).

skillDir becomes the default working directory, because every command an install runs belongs there. A validated directory is required for that: an empty or unrecognised one falls back to /workspace rather than guessing.

func NewShellExecTool

func NewShellExecTool(executor SandboxCommandExecutor, envResolver skills.SkillEnvResolver) *ShellExecTool

NewShellExecTool constructs the tool. `executor` MUST NOT be nil: callers should feature-gate registration when the sandbox backend does not support ad-hoc shell execution (i.e. is not Cube).

func (*ShellExecTool) BindSession

func (s *ShellExecTool) BindSession(id string)

func (*ShellExecTool) Cleanup

func (t *ShellExecTool) Cleanup(ctx context.Context) error

Cleanup releases any resources.

func (*ShellExecTool) Description

func (t *ShellExecTool) Description() string

Description is generated from the session sandbox layout. Install mode keeps the constructor-built text.

func (*ShellExecTool) Execute

func (t *ShellExecTool) Execute(ctx context.Context, args json.RawMessage) (*types.ToolResult, error)

Execute runs the requested command inside the current session's sandbox.

func (*ShellExecTool) OutputLimitChars

func (t *ShellExecTool) OutputLimitChars(args json.RawMessage) int

OutputLimitChars lets ToolRegistry preserve shell_exec's explicitly bounded, caller-configurable output instead of applying its lower generic limit again.

func (*ShellExecTool) Parameters

func (t *ShellExecTool) Parameters() json.RawMessage

Parameters rewrites workspace paths in the schema to match the session layout.

func (*ShellExecTool) WithEnvCapture

func (t *ShellExecTool) WithEnvCapture(capture SkillEnvCapture) *ShellExecTool

WithEnvCapture attaches an optional capture hook. A nil hook is a no-op so callers can pass the wiring result through without a nil check.

func (*ShellExecTool) WithSkillEnvironment

func (t *ShellExecTool) WithSkillEnvironment(manager *skills.Manager) *ShellExecTool

type SkillEnvCapture

type SkillEnvCapture func(ctx context.Context, skillName string, pairs map[string]string)

SkillEnvCapture records NAME=value pairs a successful shell_exec already used for one skill. The tools package does not persist them; the agent service supplies the write. Values must not be logged by the caller.

type SkillFileStore

type SkillFileStore interface {
	StatSessionFile(ctx context.Context, sessionID, filePath string) (*sandbox.RemoteStatEntry, error)
	ReadSessionFile(ctx context.Context, sessionID, filePath string) ([]byte, error)
	WriteSessionFile(ctx context.Context, sessionID, filePath string, content []byte) error
}

SkillFileStore is the write surface these tools need. Production uses *sandbox.SessionBoundManager, whose WriteSessionFile already refuses any path outside the skills image root; the per-skill scope below narrows that to the one directory this install owns.

type TableSchema

type TableSchema struct {
	TableName string                 `json:"table_name"`
	Columns   []ColumnInfo           `json:"columns"`
	RowCount  int64                  `json:"row_count"`
	Metadata  map[string]interface{} `json:"metadata,omitempty"`
}

TableSchema represents the schema information of a table

func (*TableSchema) Description

func (t *TableSchema) Description() string

Description builds the model-facing schema description. It names the table as DataAnalysisTableName, the identifier the model must use in SQL; the physical TableName stays internal.

type ThinkStreamSplitter

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

ThinkStreamSplitter incrementally separates inline <think>…</think> reasoning from user-facing answer text as content arrives chunk-by-chunk. It is the streaming counterpart to StripThinkBlocks: where StripThinkBlocks operates on a fully-accumulated string, this splitter routes each chunk live so the thinking portion can stream into the "thought" UI area and the answer portion into the "final answer" area without waiting for the whole response.

Tag boundaries that straddle two chunks (e.g. "<thi" at the end of one chunk and "nk>" at the start of the next) are handled by buffering the trailing bytes that could still become a tag prefix until the next Feed call. Call Flush at end-of-stream to drain any buffered remainder.

The splitter is NOT safe for concurrent use; create one per stream.

func NewThinkStreamSplitter

func NewThinkStreamSplitter() *ThinkStreamSplitter

NewThinkStreamSplitter returns a splitter ready to receive content chunks.

func (*ThinkStreamSplitter) Feed

func (sp *ThinkStreamSplitter) Feed(s string) (thinkOut, answerOut string)

Feed consumes one content chunk and returns the portions that are now unambiguously thinking text and answer text respectively. Either return value may be empty. Bytes that could still be part of a tag spanning into the next chunk are buffered internally and surface on a later Feed or on Flush.

func (*ThinkStreamSplitter) Flush

func (sp *ThinkStreamSplitter) Flush() (thinkOut, answerOut string)

Flush drains any buffered remainder at end-of-stream. An unterminated <think> block is treated as thinking text; anything else is answer text.

type TodoWriteInput

type TodoWriteInput struct {
	Task  string     `json:"task,omitempty" jsonschema:"The complex task or question you need to create a plan for"`
	Steps []PlanStep `json:"steps" jsonschema:"Array of research plan steps with status tracking"`
}

TodoWriteInput defines the input parameters for todo_write tool

type TodoWriteTool

type TodoWriteTool struct {
	BaseTool
}

TodoWriteTool implements a planning tool for complex tasks This is an optional tool that helps organize multi-step research

func NewTodoWriteTool

func NewTodoWriteTool() *TodoWriteTool

NewTodoWriteTool creates a new todo_write tool instance

func (*TodoWriteTool) Execute

func (t *TodoWriteTool) Execute(ctx context.Context, args json.RawMessage) (*types.ToolResult, error)

Execute executes the todo_write tool

type ToolExecContext

type ToolExecContext struct {
	SessionID          string
	AssistantMessageID string
	RequestID          string
	ToolCallID         string
	UserID             string // principal storage ID of the originating session; used by HITL gates for authorization (issue #1173)
	EventBus           *event.EventBus
	// ApprovalCtx is the parent ctx WITHOUT defaultToolExecTimeout; used when the tool
	// must wait for human approval that may exceed normal tool exec timeout (issue #1173).
	// Falls back to the per-tool execCtx when nil.
	ApprovalCtx context.Context
	// ExecTimeout mirrors the per-tool exec timeout the engine applied to the
	// outer ctx. Tools that legitimately consume that ctx (e.g. MCP human
	// approval) can re-derive a fresh timeout from ApprovalCtx using this
	// value instead of hard-coding a duration. Zero means "fallback to 60s".
	ExecTimeout time.Duration
}

ToolExecContext is attached to context during agent tool execution (per tool call).

func ToolExecFromContext

func ToolExecFromContext(ctx context.Context) (*ToolExecContext, bool)

ToolExecFromContext returns metadata attached by the agent engine, if any.

type ToolExecutor

type ToolExecutor interface {
	types.Tool

	// GetContext returns any context-specific data needed for tool execution
	GetContext() map[string]interface{}
}

ToolExecutor is a helper interface for executing tools

type ToolRegistry

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

ToolRegistry manages the registration and retrieval of tools

func NewToolRegistry

func NewToolRegistry() *ToolRegistry

NewToolRegistry creates a new tool registry

func (*ToolRegistry) BindSession

func (r *ToolRegistry) BindSession(id string)

BindSession tells sandbox tools which session's layout to advertise in Description() and Parameters(). Host adapters refuse an empty session ID.

func (*ToolRegistry) Cleanup

func (r *ToolRegistry) Cleanup(ctx context.Context)

Cleanup cleans up all registered tools that implement the types.Cleanable interface. This is called at the end of agent sessions to release tool-specific resources.

func (*ToolRegistry) ExecuteTool

func (r *ToolRegistry) ExecuteTool(
	ctx context.Context,
	name string,
	args json.RawMessage,
) (*types.ToolResult, error)

ExecuteTool executes a tool by name with the given arguments

func (*ToolRegistry) GetFunctionDefinitions

func (r *ToolRegistry) GetFunctionDefinitions() []types.FunctionDefinition

GetFunctionDefinitions returns function definitions for all registered tools. The slice is sorted by tool name so the serialized payload sent to the LLM is byte-identical across requests. Providers that key prompt caching on a byte-level prefix match (e.g. Qwen explicit caching) require this — map iteration order would otherwise reshuffle the tools block and break cache hits.

func (*ToolRegistry) GetModelFunctionDefinitions

func (r *ToolRegistry) GetModelFunctionDefinitions() []types.FunctionDefinition

GetModelFunctionDefinitions is the stable model-facing projection of the registry.

func (*ToolRegistry) GetTool

func (r *ToolRegistry) GetTool(name string) (types.Tool, error)

GetTool retrieves a tool by name

func (*ToolRegistry) HasMCPServer

func (r *ToolRegistry) HasMCPServer(id string) bool

HasMCPServer lets the prompt bind an explicit service mention without requiring eager tool discovery merely to generate a tool-name prefix.

func (*ToolRegistry) ListTools

func (r *ToolRegistry) ListTools() []string

ListTools returns all registered tool names sorted alphabetically. Sorting keeps the order stable across calls — Go map iteration is intentionally randomized.

func (*ToolRegistry) MCPCallTarget

func (r *ToolRegistry) MCPCallTarget(ctx context.Context, name string, raw json.RawMessage) *types.ToolCallTarget

MCPCallTarget separates UI/audit identity from the model's proxy call. The original call name, arguments, ID and provider metadata remain replayable.

func (*ToolRegistry) PrepareMCPTools

func (r *ToolRegistry) PrepareMCPTools(ctx context.Context)

PrepareMCPTools advertises sources up front, loading full functions only after describe. This is application-level deferred loading via ordinary functions. Production readers use persisted metadata and perform no upstream discovery.

func (*ToolRegistry) PrepareMCPToolsDirect

func (r *ToolRegistry) PrepareMCPToolsDirect(ctx context.Context)

PrepareMCPToolsDirect is the full-exposure compatibility path.

func (*ToolRegistry) RefreshMCPTools

func (r *ToolRegistry) RefreshMCPTools(ctx context.Context)

RefreshMCPTools publishes ready definitions between model requests, including catalogs loaded by discovery/OAuth or refreshed after the initial request. No network discovery occurs here. Policy checks remain fresh even when the metadata snapshot is cached. Call only when parallel tool execution is idle.

func (*ToolRegistry) RegisterDeferredTool

func (r *ToolRegistry) RegisterDeferredTool(tool types.Tool)

RegisterDeferredTool retains execution capability without advertising the full definition to the model. Registration is completed before execution.

func (*ToolRegistry) RegisterTool

func (r *ToolRegistry) RegisterTool(tool types.Tool)

RegisterTool adds a tool to the registry. If a tool with the same name is already registered, the existing one is kept (first-wins) to prevent tool execution hijacking via name collision (GHSA-67q9-58vj-32qx).

func (*ToolRegistry) RememberMCPHistory

func (r *ToolRegistry) RememberMCPHistory(messages []chat.Message)

RememberMCPHistory republishes tools this session already described or called so a new engine does not require another describe round.

func (*ToolRegistry) SetMaxToolOutputSize

func (r *ToolRegistry) SetMaxToolOutputSize(maxChars int)

SetMaxToolOutputSize sets the maximum character length for tool output. Values <= 0 will use DefaultMaxToolOutput.

type ToolRequirement

type ToolRequirement struct {
	AnyOf         []KBCapability
	AllOf         []KBCapability
	ConsumesFiles bool
	// Auxiliary tools work on any KB listed in AnyOf/AllOf but do not by
	// themselves make a KB worth selecting: a document reader can open a
	// wiki-only KB's sources, yet it should not pull wiki-only KBs into a
	// RAG agent's "all knowledge bases" scope. They contribute to the derived
	// filter only when no primary tool has a requirement.
	Auxiliary bool
}

ToolRequirement declares what a tool needs from the KB scope.

  • AnyOf: scope must expose at least ONE listed capability.
  • AllOf: scope must expose ALL listed capabilities.
  • ConsumesFiles: the tool reads user-provided file refs from `knowledge_ids`; the chat input uses this to decide whether to even offer the `@file` list to the user.

A nil/zero requirement means "no KB dependency" — the tool is always available and doesn't care about files.

type ValidationError

type ValidationError struct {
	Param   string // parameter name (or "" for top-level errors)
	Message string // human-readable error description
}

ValidationError describes a specific parameter validation failure.

func ValidateParams

func ValidateParams(args json.RawMessage, schema json.RawMessage) []ValidationError

ValidateParams checks args against the JSON Schema declared by tool.Parameters(). Returns nil if valid, or a list of validation errors.

Supported checks:

  • required: ensures all required fields are present and non-null
  • type: verifies the JSON type matches (string, number, integer, boolean, array, object)
  • enum: checks the value is in the allowed set
  • minimum / maximum: numeric bounds
  • minLength / maxLength: string length bounds

type WebFetchInput

type WebFetchInput struct {
	Items []WebFetchItem `json:"items" jsonschema:"One to eight reads with url, optional offset and limit"`
}

WebFetchInput defines the input parameters for web fetch tool.

type WebFetchItem

type WebFetchItem struct {
	URL    string `json:"url" jsonschema:"wN page ID or absolute HTTP(S) URL"`
	Offset int    `json:"offset,omitempty" jsonschema:"Zero-based character offset; use next_offset to continue"`
	Limit  int    `json:"limit,omitempty" jsonschema:"Maximum characters to return, from 1 to 8000; default 8000"`
}

WebFetchItem represents a single web fetch task.

type WebFetchTool

type WebFetchTool struct {
	BaseTool
	// contains filtered or unexported fields
}

WebFetchTool reads page snapshots for the current Agent run.

func NewWebFetchTool

func NewWebFetchTool() *WebFetchTool

NewWebFetchTool creates a new web_fetch tool instance.

func (*WebFetchTool) Execute

func (t *WebFetchTool) Execute(ctx context.Context, args json.RawMessage) (*types.ToolResult, error)

Execute runs web_fetch and preserves successful items when a batch partially fails.

func (*WebFetchTool) WithPageSource

func (t *WebFetchTool) WithPageSource(source WebPageSource) *WebFetchTool

WithPageSource adds complete page storage independently of the in-memory cache.

type WebPageSource

type WebPageSource interface {
	Save(context.Context, string) (string, error)
	Read(context.Context, string) ([]byte, error)
}

WebPageSource stores complete pages and authorizes reads against the current session.

type WebSearchInput

type WebSearchInput struct {
	Query     string `json:"query" jsonschema:"Search query string"`
	Count     *int   `json:"count,omitempty" jsonschema:"Number of results to return (1 to the configured maximum)"`
	Country   string `json:"country,omitempty" jsonschema:"Two-letter code or ALL (Brave, Serply); omit for default"`
	Freshness string `json:"freshness,omitempty" jsonschema:"pd/pw/pm/py; Brave also accepts YYYY-MM-DDtoYYYY-MM-DD"`
	Content   bool   `json:"content,omitempty" jsonschema:"Fetch page excerpts; default false"`
}

WebSearchInput defines the input parameters for web search tool

type WebSearchTool

type WebSearchTool struct {
	BaseTool
	// contains filtered or unexported fields
}

WebSearchTool performs web searches and returns results

func NewWebSearchTool

func NewWebSearchTool(
	webSearchService interfaces.WebSearchService,
	maxResults int,
	providerID string,
) *WebSearchTool

NewWebSearchTool creates a new web search tool

func (*WebSearchTool) Execute

func (t *WebSearchTool) Execute(ctx context.Context, args json.RawMessage) (*types.ToolResult, error)

Execute executes the web search tool

func (*WebSearchTool) WithPageReader

func (t *WebSearchTool) WithPageReader(reader *WebFetchTool) *WebSearchTool

WithPageReader shares page snapshots and full-output storage with web_fetch.

type WikiRouteResolver

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

WikiRouteResolver stores request-local provenance for wiki slugs. It is server-side routing state, not a model-handle registry: only KBs already in the current WikiScope can ever be returned.

func NewWikiRouteResolver

func NewWikiRouteResolver() *WikiRouteResolver

type WikiScope

type WikiScope struct {
	KnowledgeBaseID string
	KnowledgeIDs    []string
	TagIDs          []string
}

WikiScope describes the effective retrieval scope for a single wiki knowledge base within an agent session.

  • KnowledgeBaseID: the wiki KB to search.
  • KnowledgeIDs: OPTIONAL whitelist of source knowledge (document) IDs. When non-empty, a wiki page is only surfaced if its SourceRefs intersect this set. Used to honour user-level @mentions that pin the search to specific documents inside a KB.
  • TagIDs: OPTIONAL whitelist of document tags. When non-empty, a wiki page is only surfaced if at least one of its SourceRefs belongs to any tag.

func NewWikiScopesFromKBIDs

func NewWikiScopesFromKBIDs(kbIDs []string) []WikiScope

NewWikiScopesFromKBIDs is a convenience constructor for callers that only carry plain KB IDs and don't need per-document filtering (e.g. legacy tests).

func NewWikiScopesFromSearchTargets

func NewWikiScopesFromSearchTargets(searchTargets types.SearchTargets, wikiKBIDs []string) []WikiScope

NewWikiScopesFromSearchTargets merges all targets for a Wiki KB into one scope. SearchTargets are alternatives selected by the user, so document and tag constraints are combined as a union; an unrestricted whole-KB target supersedes narrower targets for that KB.

type WriteSandboxFileInput

type WriteSandboxFileInput struct {
	Path string `` //nolint:lll // JSON schema tags must remain on one line.
	/* 210-byte string literal not displayed */
	Content string `` //nolint:lll // one-line struct tag
	/* 242-byte string literal not displayed */
	Mode string `` //nolint:lll // one-line struct tag
	/* 198-byte string literal not displayed */
}

WriteSandboxFileInput defines the input parameters for write_sandbox_file.

The byte limit is deliberately absent from the `content` description: it depends on the agent's per-round token budget and is stated in the tool description, which is built per session.

type WriteSandboxFileTool

type WriteSandboxFileTool struct {
	BaseTool
	// contains filtered or unexported fields
}

WriteSandboxFileTool writes a text file into the session sandbox.

func NewWriteSandboxFileTool

func NewWriteSandboxFileTool(sink SandboxFileSink, completionTokens int) *WriteSandboxFileTool

NewWriteSandboxFileTool constructs the tool. `sink` MUST NOT be nil.

completionTokens is the agent's per-round completion-token budget, which is what actually bounds how much the model can emit in one call. It shapes the size guidance in the description; it is not enforced, for the reasons in Execute. Pass 0 when it is not known.

func (*WriteSandboxFileTool) BindSession

func (s *WriteSandboxFileTool) BindSession(id string)

func (*WriteSandboxFileTool) Cleanup

func (t *WriteSandboxFileTool) Cleanup(ctx context.Context) error

Cleanup releases any resources.

func (*WriteSandboxFileTool) Description

func (t *WriteSandboxFileTool) Description() string

Description is built from the session sandbox layout so host paths never hard-code /workspace.

func (*WriteSandboxFileTool) Execute

Execute writes the requested file into the current session's sandbox.

func (*WriteSandboxFileTool) Parameters

func (t *WriteSandboxFileTool) Parameters() json.RawMessage

Parameters rewrites workspace paths in the schema to match the session layout.

type WriteSkillFileInput

type WriteSkillFileInput struct {
	Path    string `` /* 136-byte string literal not displayed */
	Content string `` /* 145-byte string literal not displayed */
}

WriteSkillFileInput defines the input parameters for write_skill_file.

type WriteSkillFileTool

type WriteSkillFileTool struct {
	BaseTool
	// contains filtered or unexported fields
}

WriteSkillFileTool writes a text file into the skill directory under install.

func NewWriteSkillFileTool

func NewWriteSkillFileTool(store SkillFileStore, skillDir string) *WriteSkillFileTool

NewWriteSkillFileTool constructs the tool. `store` MUST NOT be nil and `skillDir` MUST be the directory of the skill this install owns.

func (*WriteSkillFileTool) Cleanup

func (t *WriteSkillFileTool) Cleanup(ctx context.Context) error

Cleanup releases any resources.

func (*WriteSkillFileTool) Execute

Execute writes the file after confirming it lands inside the skill directory.

Jump to

Keyboard shortcuts

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