Documentation
¶
Overview ¶
Copyright 2026 Teradata
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright 2026 Teradata ¶
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright 2026 Teradata ¶
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright 2026 Teradata ¶
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright 2026 Teradata ¶
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright 2026 Teradata ¶
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright 2026 Teradata ¶
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright 2026 Teradata ¶
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright 2026 Teradata ¶
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright 2026 Teradata ¶
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright 2026 Teradata ¶
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright 2026 Teradata ¶
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright 2026 Teradata ¶
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright 2026 Teradata ¶
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright 2026 Teradata ¶
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright 2026 Teradata ¶
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright 2026 Teradata ¶
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright 2026 Teradata ¶
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright 2026 Teradata ¶
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Index ¶
- func Count() int
- func List() []string
- func Register(tool Tool)
- func RegisterCustomHook(name string, ctor func(b HookBinding) (Hook, error))
- func Unregister(name string)
- func ValidateHooksConfig(cfg HooksConfig) error
- func ValidateHooksConfigWithRegistry(cfg HooksConfig, reg CustomHookRegistry) error
- type AdmissionRequest
- type AdmissionResult
- type ApprovedSetAccessor
- type AskResolver
- type AuditHook
- type BuiltinToolProvider
- type CallIdentity
- type Chain
- type ChainDeps
- type ContactHumanConfig
- type ContactHumanTool
- type CustomHookRegistry
- type Decision
- type DecisionKind
- type Error
- type Executor
- func (e *Executor) ApprovedSet() ApprovedSetAccessor
- func (e *Executor) Execute(ctx context.Context, toolName string, params map[string]interface{}) (result *Result, err error)
- func (e *Executor) ExecuteWithTool(ctx context.Context, tool Tool, params map[string]interface{}) (result *Result, err error)
- func (e *Executor) ListAvailableTools() []Tool
- func (e *Executor) ListToolsByBackend(backend string) []Tool
- func (e *Executor) SetAdmissionChain(chain *Chain)
- func (e *Executor) SetApprovedSet(accessor ApprovedSetAccessor)
- func (e *Executor) SetBuiltinToolProvider(provider BuiltinToolProvider)
- func (e *Executor) SetIdentityResolver(resolver func(context.Context) string)
- func (e *Executor) SetMCPManager(manager MCPManager)
- func (e *Executor) SetPermissionChecker(checker *PermissionChecker)
- func (e *Executor) SetSharedMemory(sharedMemory *storage.SharedMemoryStore, threshold int64)
- func (e *Executor) SetToolRegistry(registry ToolRegistry)
- func (e *Executor) SharedMemoryThreshold() int64
- func (e *Executor) Stats() ExecutorStats
- type ExecutorStats
- type Hook
- type HookBinding
- type HooksConfig
- type HumanRequest
- type HumanRequestStore
- type InMemoryHumanRequestStore
- func (s *InMemoryHumanRequestStore) Close() error
- func (s *InMemoryHumanRequestStore) ExpireRequest(ctx context.Context, requestID, respondedBy string) error
- func (s *InMemoryHumanRequestStore) Get(ctx context.Context, id string) (*HumanRequest, error)
- func (s *InMemoryHumanRequestStore) ListBySession(ctx context.Context, sessionID string) ([]*HumanRequest, error)
- func (s *InMemoryHumanRequestStore) ListPending(ctx context.Context) ([]*HumanRequest, error)
- func (s *InMemoryHumanRequestStore) RespondToRequest(ctx context.Context, requestID, status, response, respondedBy string, ...) error
- func (s *InMemoryHumanRequestStore) Store(ctx context.Context, req *HumanRequest) error
- func (s *InMemoryHumanRequestStore) Update(ctx context.Context, req *HumanRequest) error
- type InstrumentedExecutor
- func (e *InstrumentedExecutor) Execute(ctx context.Context, toolName string, params map[string]interface{}) (*Result, error)
- func (e *InstrumentedExecutor) ExecuteWithTool(ctx context.Context, tool Tool, params map[string]interface{}) (*Result, error)
- func (e *InstrumentedExecutor) ListAvailableTools() []Tool
- func (e *InstrumentedExecutor) ListToolsByBackend(backend string) []Tool
- type JSONNotifier
- type JSONSchema
- func FromJSON(data []byte) (*JSONSchema, error)
- func NewArraySchema(description string, items *JSONSchema) *JSONSchema
- func NewBooleanSchema(description string) *JSONSchema
- func NewNumberSchema(description string) *JSONSchema
- func NewObjectSchema(description string, properties map[string]*JSONSchema, required []string) *JSONSchema
- func NewStringSchema(description string) *JSONSchema
- func NormalizeSchema(schema *JSONSchema) *JSONSchema
- func (s *JSONSchema) MarshalJSON() ([]byte, error)
- func (s *JSONSchema) ToJSON() ([]byte, error)
- func (s *JSONSchema) WithDefault(value interface{}) *JSONSchema
- func (s *JSONSchema) WithEnum(values ...interface{}) *JSONSchema
- func (s *JSONSchema) WithFormat(format string) *JSONSchema
- func (s *JSONSchema) WithLength(minLen, maxLen *int) *JSONSchema
- func (s *JSONSchema) WithPattern(pattern string) *JSONSchema
- func (s *JSONSchema) WithRange(min, max *float64) *JSONSchema
- type MCPManager
- type Matcher
- type MatcherSpec
- type MockTool
- type NoOpNotifier
- type Notifier
- type PermissionChecker
- func (pc *PermissionChecker) Advertisable(toolName string) bool
- func (pc *PermissionChecker) CheckPermission(ctx context.Context, toolName string, params map[string]interface{}) error
- func (pc *PermissionChecker) IsToolAllowed(toolName string) bool
- func (pc *PermissionChecker) IsToolDisabled(toolName string) bool
- func (pc *PermissionChecker) IsYOLOMode() bool
- func (pc *PermissionChecker) RequiresApproval() bool
- type PermissionConfig
- type PostToolHook
- type PromptAwareTool
- type Registry
- func (r *Registry) Count() int
- func (r *Registry) Get(name string) (Tool, bool)
- func (r *Registry) IsRegistered(name string) bool
- func (r *Registry) List() []string
- func (r *Registry) ListByBackend(backend string) []Tool
- func (r *Registry) ListTools() []Tool
- func (r *Registry) Register(tool Tool)
- func (r *Registry) Unregister(name string)
- type Result
- type SQLiteConfig
- type SQLiteHumanRequestStore
- func (s *SQLiteHumanRequestStore) Close() error
- func (s *SQLiteHumanRequestStore) ExpireRequest(ctx context.Context, requestID, respondedBy string) error
- func (s *SQLiteHumanRequestStore) Get(ctx context.Context, id string) (*HumanRequest, error)
- func (s *SQLiteHumanRequestStore) ListBySession(ctx context.Context, sessionID string) ([]*HumanRequest, error)
- func (s *SQLiteHumanRequestStore) ListPending(ctx context.Context) ([]*HumanRequest, error)
- func (s *SQLiteHumanRequestStore) RespondToRequest(ctx context.Context, requestID, status, response, respondedBy string, ...) error
- func (s *SQLiteHumanRequestStore) Store(ctx context.Context, req *HumanRequest) error
- func (s *SQLiteHumanRequestStore) Update(ctx context.Context, req *HumanRequest) error
- type Tool
- type ToolRegistry
- type ToolScope
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RegisterCustomHook ¶ added in v1.4.0
func RegisterCustomHook(name string, ctor func(b HookBinding) (Hook, error))
RegisterCustomHook records a compiled-in custom hook constructor under name, the entrypoint a deployment calls from its init/startup to contribute a clearance/DLP hook. It takes a Go function value, not a script, path, or plugin file — nothing is interpreted at runtime and no sandbox is used. A later registration under the same name replaces the earlier constructor.
func ValidateHooksConfig ¶ added in v1.4.0
func ValidateHooksConfig(cfg HooksConfig) error
ValidateHooksConfig reports the first structurally invalid binding in a tools.hooks config: an unknown kind, an empty scope, an uncompilable matcher/read_pattern, or a kind missing a required field. It shares validateBinding with BuildChainFromConfig, so a config that validates clean here builds without a structural error, and it wraps each fault with the offending binding's index and kind in BuildChainFromConfig's style. It is deps-free — callable at save time without a live ChainDeps — so a custom binding's name-registry membership is left to build (an unregistered name fails closed there).
func ValidateHooksConfigWithRegistry ¶ added in v1.4.0
func ValidateHooksConfigWithRegistry(cfg HooksConfig, reg CustomHookRegistry) error
ValidateHooksConfigWithRegistry is ValidateHooksConfig plus custom-name resolution: when a registry is supplied, a custom binding whose Name has no registered constructor is a validation error — so a host's save door can reject an unknown custom hook at save time (C-010) instead of the operator discovering a deny-all agent at the next session build. Pass ProcessCustomHookRegistry() to validate against the process registry.
Types ¶
type AdmissionRequest ¶ added in v1.4.0
type AdmissionRequest struct {
Ctx context.Context
ToolName string
Params map[string]interface{} // the tool's own params (MCP nested/carried op lives here)
UserID string // caller identity resolved from Ctx
SessionID string // session identity resolved from Ctx
State ApprovedSetAccessor // may be nil until an approved-set store is wired
}
AdmissionRequest is the immutable view of a tool call handed to every hook. State is the approved-set accessor and may be nil until one is wired.
type AdmissionResult ¶ added in v1.4.0
AdmissionResult is the outcome of running the chain for one tool call. AuditDecision is set when a matched hook is an AuditHook and its AuditDecisionFor returns a non-empty string; it is carried to the persist path.
func (AdmissionResult) PersistedDecision ¶ added in v1.4.0
func (r AdmissionResult) PersistedDecision() string
PersistedDecision is the value the executor stamps into the tool-execution record: a matched audit hook's decision when one fired, and otherwise "deny" for a denied call — a denial is intrinsically classifiable and the analytics view keys policy_denied_count on this column, so it must ride every deny, audited or not. A non-denied, non-audited call stamps nothing.
type ApprovedSetAccessor ¶ added in v1.4.0
type ApprovedSetAccessor interface {
Record(ctx context.Context, stateKey string, ids []CallIdentity) error
Contains(ctx context.Context, stateKey string, id CallIdentity) (bool, error)
ForgetSession(sessionID string)
}
ApprovedSetAccessor records and queries prior approvals keyed by a caller state key. ForgetSession is the reclamation half of the contract: a host that retires a session drops its approvals through it (the agent's DeleteSession does), so the set's growth is bounded by LIVE sessions, not by every session the process ever served.
func NewApprovedSet ¶ added in v1.4.0
func NewApprovedSet() ApprovedSetAccessor
NewApprovedSet builds an ApprovedSetAccessor. The composition layer binds it to the executor so a gated-allowlist reads it as AdmissionRequest.State.
type AskResolver ¶ added in v1.4.0
type AskResolver interface {
Resolve(req AdmissionRequest, d Decision) Decision
}
AskResolver turns an Ask verdict into a terminal Allow/Deny. A nil resolver means Ask fails closed to Deny.
func NewHITLAskResolver ¶ added in v1.4.0
func NewHITLAskResolver(store HumanRequestStore, timeout, poll time.Duration, notifier Notifier) AskResolver
NewHITLAskResolver builds the Ask resolver wired into ChainDeps.Ask. timeout bounds the turn-blocking wait (default 300s when non-positive); poll is the store poll interval (default 1s, mirroring ContactHumanConfig). notifier is fired once the pending request is stored so the hold surfaces on the progress stream; a nil notifier disables the emit (the hold is unaffected).
type AuditHook ¶ added in v1.4.0
AuditHook is a Hook that additionally reports how a final decision should be recorded. The chain checks matched hooks for this interface and carries the non-empty result on AdmissionResult.AuditDecision. A "" result is not recorded.
type BuiltinToolProvider ¶ added in v1.1.0
BuiltinToolProvider is an interface for getting builtin tools. This avoids import cycles with pkg/shuttle/builtin.
type CallIdentity ¶ added in v1.4.0
type CallIdentity string
CallIdentity is the shared call-equality key for approved-set membership. Canonicalize is the normalization that derives it.
func Canonicalize ¶ added in v1.4.0
func Canonicalize(toolName string, params map[string]interface{}, stmtParam string) CallIdentity
Canonicalize derives the call-equality key an approved-set membership check compares on. Normalization is conservative and fail-closed — it prefers a false-deny over a false-allow and never widens the equivalence class: it takes the SQL statement param named by stmtParam, trims it, collapses internal whitespace runs to a single space, and strips a trailing ';'. No keyword or case folding, no semantic rewrite. The write side that records approvals and this read side normalize through this one function, so a rendered statement matches its re-emitted whitespace-different form while a textually-outside statement does not. toolName selects the normalization; only the SQL statement normalization exists today.
type Chain ¶ added in v1.4.0
type Chain struct {
// contains filtered or unexported fields
}
Chain evaluates admission hooks for every tool call and combines their verdicts into a single decision. It also fans out post-tool observation to its PostToolHooks after a tool body runs.
func BuildChainFromConfig ¶ added in v1.4.0
func BuildChainFromConfig(cfg HooksConfig, deps ChainDeps) (*Chain, error)
BuildChainFromConfig assembles the admission chain from a tools.hooks config. The name-level permission hook is placed first when a checker is supplied, then one hook per binding in config order. Each gated-allowlist binding also contributes a recorder post-tool hook — the sole writer of its approved set — so the render tool's completions populate the set the gated read side checks. A malformed binding — unknown kind, empty scope, bad matcher/read pattern, a gated-allowlist missing state_key/source_tool, or a custom name with no registered hook — is an error, so serve aborts rather than run silently ungoverned (fail-closed).
func NewChain ¶ added in v1.4.0
func NewChain(hooks []Hook, postHooks []PostToolHook, askResolver AskResolver) *Chain
NewChain builds a chain from ordered admission hooks, post-tool hooks, and an optional Ask resolver. Hook order is the caller's responsibility (the name-level permission hook is placed first by the builder); combination is order-independent.
func (*Chain) Admit ¶ added in v1.4.0
func (c *Chain) Admit(req AdmissionRequest) AdmissionResult
Admit evaluates every hook whose Matches reports true, combines their verdicts as Deny > Ask > Allow (most restrictive wins), and resolves an Ask outcome via the AskResolver — with no resolver, Ask fails closed to Deny. A hook that panics is treated as a matched Deny (fail-closed). With no matching hook the decision is NoDecision, which the executor treats as today's pass-through. For each matched AuditHook, AuditDecisionFor(final) sets AdmissionResult.AuditDecision.
func (*Chain) Observe ¶ added in v1.4.0
func (c *Chain) Observe(req AdmissionRequest, result *Result)
Observe fans a completed tool call out to every post-tool hook. A nil chain or nil hook is a no-op; a panicking hook is contained so observation cannot break execution that already completed.
type ChainDeps ¶ added in v1.4.0
type ChainDeps struct {
Perm *PermissionChecker
ApprovedSet ApprovedSetAccessor
Ask AskResolver
Custom CustomHookRegistry
}
ChainDeps carries the collaborators BuildChainFromConfig needs to turn bindings into live hooks: the name-level permission checker (folded in as the first hook), the approved-set accessor a gated-allowlist reads, the resolver an Ask verdict defers to, and the registry a custom binding resolves against. The pending-emit collaborator is NOT injected here — it is a constructor argument of NewHITLAskResolver, the one way in.
type ContactHumanConfig ¶
type ContactHumanConfig struct {
Store HumanRequestStore
Notifier Notifier
Timeout time.Duration // Default timeout for requests (default: 5 minutes)
PollInterval time.Duration // How often to check for responses (default: 1 second)
Tracer observability.Tracer // Tracer for observability (default: NoOpTracer)
Logger *zap.Logger // Logger for structured logging (default: NoOp logger)
}
ContactHumanConfig configures the ContactHumanTool.
type ContactHumanTool ¶
type ContactHumanTool struct {
// contains filtered or unexported fields
}
ContactHumanTool provides human-in-the-loop capabilities for agents. Implements 12-Factor Agent Compliance (Factor 7: Human Oversight & Approval).
Use cases: - Approval workflows (e.g., "Should I delete this data?") - High-stakes decisions (e.g., "Confirm this financial transaction") - Ambiguity resolution (e.g., "Which interpretation is correct?") - Quality gates (e.g., "Review this generated code before deployment")
func NewContactHumanTool ¶
func NewContactHumanTool(config ContactHumanConfig) *ContactHumanTool
NewContactHumanTool creates a new human-in-the-loop tool.
func (*ContactHumanTool) Backend ¶
func (t *ContactHumanTool) Backend() string
func (*ContactHumanTool) Description ¶
func (t *ContactHumanTool) Description() string
Description returns the tool description. Deprecated: Description loaded from PromptRegistry (prompts/tools/human.yaml). This fallback is used only when prompts are not configured.
func (*ContactHumanTool) InputSchema ¶
func (t *ContactHumanTool) InputSchema() *JSONSchema
func (*ContactHumanTool) Name ¶
func (t *ContactHumanTool) Name() string
type CustomHookRegistry ¶ added in v1.4.0
type CustomHookRegistry interface {
// contains filtered or unexported methods
}
CustomHookRegistry resolves a custom binding's Name to the constructor that builds its hook. The constructor receives the full binding, so a custom hook is placed in the chain with the binding's scope and matcher like any library policy. A name with no registered constructor is a build error.
func ProcessCustomHookRegistry ¶ added in v1.4.0
func ProcessCustomHookRegistry() CustomHookRegistry
ProcessCustomHookRegistry returns the process-wide custom hook registry to pass as ChainDeps.Custom at serve, so a `kind:"custom"` binding resolves against the constructors RegisterCustomHook compiled in.
type Decision ¶ added in v1.4.0
type Decision struct {
Kind DecisionKind
Reason string
}
Decision is a hook's verdict plus a human-readable reason carried on Deny/Ask.
type DecisionKind ¶ added in v1.4.0
type DecisionKind int
DecisionKind is the verdict a hook (or the combined chain) reaches for a tool call. Ordering by restrictiveness is Deny > Ask > Allow; NoDecision is the sentinel returned when no hook matched a request and is never produced by a hook's Evaluate.
const ( // Allow lets the tool body run. Allow DecisionKind = iota // Deny blocks the tool body; the executor returns a permission_denied Result. Deny // Ask defers to an AskResolver; with no resolver wired it fails closed to Deny. Ask // NoDecision means no hook matched. The executor treats it as a pass-through: // the tool runs exactly as if no chain were attached. NoDecision )
type Error ¶
type Error struct {
// Code is a machine-readable error code
Code string
// Message is a human-readable error message
Message string
// Details provides additional error context
Details map[string]interface{}
// Retryable indicates if the operation can be retried
Retryable bool
// Suggestion provides a suggestion for fixing the error
Suggestion string
}
Error represents a tool execution error with structured information.
type Executor ¶
type Executor struct {
// contains filtered or unexported fields
}
Executor executes tools with tracking and error handling.
func NewExecutor ¶
NewExecutor creates a new tool executor.
func (*Executor) ApprovedSet ¶ added in v1.4.0
func (e *Executor) ApprovedSet() ApprovedSetAccessor
ApprovedSet returns the wired approved-set accessor (nil when none). The set is per-accessor state, so introspection must go through the same instance the executor threads to hooks.
func (*Executor) Execute ¶
func (e *Executor) Execute(ctx context.Context, toolName string, params map[string]interface{}) (result *Result, err error)
Execute executes a tool by name with the given parameters.
func (*Executor) ExecuteWithTool ¶
func (e *Executor) ExecuteWithTool(ctx context.Context, tool Tool, params map[string]interface{}) (result *Result, err error)
ExecuteWithTool executes a specific tool instance (not from registry).
func (*Executor) ListAvailableTools ¶
ListAvailableTools returns all tools available in the executor's registry.
func (*Executor) ListToolsByBackend ¶
ListToolsByBackend returns all tools for a specific backend.
func (*Executor) SetAdmissionChain ¶ added in v1.4.0
SetAdmissionChain configures the admission hook chain consulted before every tool body runs. A nil chain leaves execution as a pure pass-through.
func (*Executor) SetApprovedSet ¶ added in v1.4.0
func (e *Executor) SetApprovedSet(accessor ApprovedSetAccessor)
SetApprovedSet configures the approved-set accessor threaded to admission hooks as AdmissionRequest.State. A nil accessor leaves a gated-allowlist with no store to read, so it fails closed.
func (*Executor) SetBuiltinToolProvider ¶ added in v1.1.0
func (e *Executor) SetBuiltinToolProvider(provider BuiltinToolProvider)
SetBuiltinToolProvider configures the builtin tool provider for dynamic builtin tool registration.
func (*Executor) SetIdentityResolver ¶ added in v1.4.0
SetIdentityResolver configures how AdmissionRequest.UserID is read from the call context. pkg/shuttle cannot import the storage layer that owns the user-id context key without an import cycle, so the composition layer injects the resolver here. A nil resolver yields an empty UserID.
func (*Executor) SetMCPManager ¶
func (e *Executor) SetMCPManager(manager MCPManager)
SetMCPManager configures the MCP manager for dynamic MCP tool registration.
func (*Executor) SetPermissionChecker ¶
func (e *Executor) SetPermissionChecker(checker *PermissionChecker)
SetPermissionChecker configures permission checking for tool execution.
func (*Executor) SetSharedMemory ¶
func (e *Executor) SetSharedMemory(sharedMemory *storage.SharedMemoryStore, threshold int64)
SetSharedMemory configures shared memory for large result handling.
func (*Executor) SetToolRegistry ¶
func (e *Executor) SetToolRegistry(registry ToolRegistry)
SetToolRegistry configures the tool registry for dynamic tool discovery. When a tool is not found in the local registry, the executor will check the tool registry and dynamically register MCP tools if found.
func (*Executor) SharedMemoryThreshold ¶ added in v1.4.0
SharedMemoryThreshold reports the byte threshold at or above which a result is stored by reference. Exposed so callers can assert both offload sites agree.
func (*Executor) Stats ¶ added in v1.1.0
func (e *Executor) Stats() ExecutorStats
Stats returns metrics about executor operations. Includes large parameter optimization statistics.
type ExecutorStats ¶ added in v1.1.0
type ExecutorStats struct {
LargeParamStores int64 // Count of parameters stored in shared memory
LargeParamDerefs int64 // Count of parameters dereferenced
LargeParamBytesStored int64 // Total bytes stored for parameters
LargeParamDerefErrors int64 // Count of dereference failures
}
ExecutorStats holds metrics about executor operations.
type Hook ¶ added in v1.4.0
type Hook interface {
Matches(req AdmissionRequest) bool
Evaluate(req AdmissionRequest) Decision
}
Hook is a single admission policy: it decides whether it applies to a request (Matches) and, when it does, returns its verdict (Evaluate).
type HookBinding ¶ added in v1.4.0
type HookBinding struct {
// Kind selects the policy: "gated-allowlist" | "denylist" | "audit" | "ask" | "custom".
Kind string `mapstructure:"kind" json:"kind"`
// Scope is the tool selector: an exact tool name or a "<prefix>*" pattern,
// with the same match semantics as tools.permissions.
Scope string `mapstructure:"scope" json:"scope"`
// Matcher selects calls by their params, including a dispatch tool's
// carried/nested op addressed by path. An absent matcher selects every call
// to the scoped tools (a scope-only binding).
Matcher MatcherSpec `mapstructure:"matcher" json:"matcher"`
// StateKey names the approved-set partition a gated-allowlist reads.
StateKey string `mapstructure:"state_key" json:"state_key"`
// ReadPattern classifies a read-only call (gated-allowlist): anchored to
// each statement's head, and every statement of the payload must match.
ReadPattern string `mapstructure:"read_pattern" json:"read_pattern"`
// StmtParam names the param holding the statement whose identity is checked
// (gated-allowlist, required); shared with the render/record side's
// canonicalization.
StmtParam string `mapstructure:"stmt_param" json:"stmt_param"`
// Pattern is not a valid field: a denylist selects calls with Matcher.
// Declared only so a config that sets it fails validation loudly instead of
// being dropped by the decoder.
Pattern string `mapstructure:"pattern" json:"pattern"`
// SourceTool names the render/record tool a gated-allowlist trusts as the
// source of approved-set entries.
SourceTool string `mapstructure:"source_tool" json:"source_tool"`
// ResultPath locates the rendered statements within a render Result.
ResultPath string `mapstructure:"result_path" json:"result_path"`
// Name is the registry key of a custom hook.
Name string `mapstructure:"name" json:"name"`
}
HookBinding declares one library policy purely from config: which policy (Kind), the tools it governs (Scope), the params it selects on (Matcher), and the policy-specific parameters. A binding is turned into a live Hook by BuildChainFromConfig; a malformed binding fails serve startup (fail-closed).
func (*HookBinding) UnmarshalJSON ¶ added in v1.4.0
func (b *HookBinding) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes a binding with key normalization: any case/underscore variant of a known field lands on its canonical field, unknown keys fail.
type HooksConfig ¶ added in v1.4.0
type HooksConfig struct {
Bindings []HookBinding `mapstructure:"hooks" json:"bindings"`
}
HooksConfig is the `tools.hooks` block Viper unmarshals: an ordered list of library-policy bindings. It lives in package shuttle (not cmd/looms) so a library builder can construct an admission chain without importing main. The json tags define the wire shape an out-of-process host (e.g. an agent profile's hooks_config) transports. The documented JSON key set is the snake_case one on the tags; the historical Go-field-name casing keeps decoding through the normalizing UnmarshalJSON below, which folds any case/underscore variant of a known key onto its canonical spelling before a strict decode — so "StateKey" and "state_key" both land, and a key that is no spelling of any field still fails loudly.
func ParseHooksConfig ¶ added in v1.4.0
func ParseHooksConfig(raw []byte) (HooksConfig, error)
ParseHooksConfig decodes a JSON hooks config and refuses the silent-empty trap: a document whose keys miss the schema fails loudly (unknown fields are rejected), so a mis-keyed config errors at the save door instead of building a chain that governs nothing. A genuinely empty document (empty input, "{}", "null", or an explicitly empty bindings list) parses to an empty config.
func (*HooksConfig) UnmarshalJSON ¶ added in v1.4.0
func (c *HooksConfig) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes the config with key normalization at every level, so even a decode NOT routed through ParseHooksConfig is strict: a top-level key that is no spelling of the list key fails loudly instead of yielding a zero-binding config that validates clean and governs nothing.
type HumanRequest ¶
type HumanRequest struct {
ID string `json:"id"`
AgentID string `json:"agent_id"`
SessionID string `json:"session_id"`
Question string `json:"question"`
Context map[string]interface{} `json:"context"`
RequestType string `json:"request_type"` // "approval", "decision", "input", "review"
Priority string `json:"priority"` // "low", "normal", "high", "critical"
Kind string `json:"kind"` // "approval" (hook-held) | "question" (contact_human); absent → question
Summary string `json:"summary"` // display digest: tool+args (approval) | question text (question)
Timeout time.Duration `json:"timeout"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at"`
// Params carries the held call's full parameter map for an approval; a
// question has no held call and leaves it empty. Stamped at origin, never
// re-derived downstream.
Params map[string]interface{} `json:"params"`
// ParamsTruncated reports that the paramsMaxBytes bound cut whole pairs out
// of Params.
ParamsTruncated bool `json:"params_truncated"`
// Response fields (populated when human responds)
Status string `json:"status"` // "pending", "approved", "rejected", "timeout", "responded"
Response string `json:"response"`
ResponseData map[string]interface{} `json:"response_data"`
RespondedAt *time.Time `json:"responded_at"`
RespondedBy string `json:"responded_by"`
}
HumanRequest represents a request for human input.
type HumanRequestStore ¶
type HumanRequestStore interface {
// Store saves a new human request
Store(ctx context.Context, req *HumanRequest) error
// Get retrieves a human request by ID. Absence contract: the postgres
// store returns (nil, nil) for a missing row so callers can distinguish
// absence from a store failure; the sqlite and in-memory stores return an
// error. Callers must treat BOTH a nil request and an error as
// possibly-absent (the ask waiter does), never dereference unchecked.
Get(ctx context.Context, id string) (*HumanRequest, error)
// Update updates an existing human request
Update(ctx context.Context, req *HumanRequest) error
// List returns all pending requests (for human review interface)
ListPending(ctx context.Context) ([]*HumanRequest, error)
// ListBySession returns all requests for a session
ListBySession(ctx context.Context, sessionID string) ([]*HumanRequest, error)
// RespondToRequest resolves a pending, non-expired request exactly once.
// The expiry guard is the store's own — no caller payload can lift it. On
// an already-decided or expired request it is a no-op returning nil (the
// caller reads current state via Get). Errors only on a missing row / store failure.
RespondToRequest(ctx context.Context, requestID, status, response, respondedBy string, responseData map[string]interface{}) error
// ExpireRequest terminally closes a pending request as "timeout" on behalf
// of the harness — the waiter's give-up path, a canceled turn's abandon
// write, an expiry sweep. It is the ONLY path that may close a row past its
// expiry; a row already resolved is left untouched (closing is not
// resolving). A missing row is a no-op. respondedBy records the closing
// actor (e.g. "system:expiry", "system:cancel").
ExpireRequest(ctx context.Context, requestID, respondedBy string) error
// Close releases any resources held by the store.
Close() error
}
HumanRequestStore manages storage and retrieval of human requests.
type InMemoryHumanRequestStore ¶
type InMemoryHumanRequestStore struct {
// contains filtered or unexported fields
}
InMemoryHumanRequestStore provides an in-memory implementation of HumanRequestStore. Suitable for testing and single-instance deployments.
func NewInMemoryHumanRequestStore ¶
func NewInMemoryHumanRequestStore() *InMemoryHumanRequestStore
NewInMemoryHumanRequestStore creates a new in-memory store.
func (*InMemoryHumanRequestStore) Close ¶ added in v1.2.0
func (s *InMemoryHumanRequestStore) Close() error
Close is a no-op; in-memory store has no resources to release.
func (*InMemoryHumanRequestStore) ExpireRequest ¶ added in v1.4.0
func (s *InMemoryHumanRequestStore) ExpireRequest(ctx context.Context, requestID, respondedBy string) error
ExpireRequest terminally closes a pending request as "timeout" regardless of its expiry — the harness's close for abandoned or swept rows. A resolved row is left untouched (closing is not resolving); a missing row is a no-op.
func (*InMemoryHumanRequestStore) Get ¶
func (s *InMemoryHumanRequestStore) Get(ctx context.Context, id string) (*HumanRequest, error)
func (*InMemoryHumanRequestStore) ListBySession ¶
func (s *InMemoryHumanRequestStore) ListBySession(ctx context.Context, sessionID string) ([]*HumanRequest, error)
func (*InMemoryHumanRequestStore) ListPending ¶
func (s *InMemoryHumanRequestStore) ListPending(ctx context.Context) ([]*HumanRequest, error)
func (*InMemoryHumanRequestStore) RespondToRequest ¶
func (s *InMemoryHumanRequestStore) RespondToRequest(ctx context.Context, requestID, status, response, respondedBy string, responseData map[string]interface{}) error
RespondToRequest resolves a pending, non-expired request exactly once. On an already-decided or expired request it is a no-op returning nil, so the caller reads current state via Get. Errors only on a missing request. The expiry guard is the store's own: no status value in the caller's payload can lift it — terminal closes past expiry go through ExpireRequest.
func (*InMemoryHumanRequestStore) Store ¶
func (s *InMemoryHumanRequestStore) Store(ctx context.Context, req *HumanRequest) error
func (*InMemoryHumanRequestStore) Update ¶
func (s *InMemoryHumanRequestStore) Update(ctx context.Context, req *HumanRequest) error
type InstrumentedExecutor ¶
type InstrumentedExecutor struct {
// contains filtered or unexported fields
}
InstrumentedExecutor wraps an Executor with observability instrumentation. It captures detailed traces and metrics for every tool execution, including: - Tool name and parameters - Execution duration and success/failure - Result data and errors - Backend information
This wrapper is transparent and can wrap any Executor.
func NewInstrumentedExecutor ¶
func NewInstrumentedExecutor(executor *Executor, tracer observability.Tracer) *InstrumentedExecutor
NewInstrumentedExecutor creates a new instrumented tool executor.
func (*InstrumentedExecutor) Execute ¶
func (e *InstrumentedExecutor) Execute(ctx context.Context, toolName string, params map[string]interface{}) (*Result, error)
Execute executes a tool by name with observability instrumentation.
func (*InstrumentedExecutor) ExecuteWithTool ¶
func (e *InstrumentedExecutor) ExecuteWithTool(ctx context.Context, tool Tool, params map[string]interface{}) (*Result, error)
ExecuteWithTool executes a specific tool instance with observability.
func (*InstrumentedExecutor) ListAvailableTools ¶
func (e *InstrumentedExecutor) ListAvailableTools() []Tool
ListAvailableTools delegates to the underlying executor.
func (*InstrumentedExecutor) ListToolsByBackend ¶
func (e *InstrumentedExecutor) ListToolsByBackend(backend string) []Tool
ListToolsByBackend delegates to the underlying executor.
type JSONNotifier ¶
type JSONNotifier struct {
// contains filtered or unexported fields
}
JSONNotifier sends notifications as JSON to a configured endpoint (webhook).
func NewJSONNotifier ¶
func NewJSONNotifier(webhookURL string) *JSONNotifier
NewJSONNotifier creates a new JSON webhook notifier.
func (*JSONNotifier) Notify ¶
func (n *JSONNotifier) Notify(ctx context.Context, req *HumanRequest) error
type JSONSchema ¶
type JSONSchema struct {
Type string `json:"type"`
Description string `json:"description,omitempty"`
Properties map[string]*JSONSchema `json:"properties,omitempty"`
Required []string `json:"required,omitempty"`
Items *JSONSchema `json:"items,omitempty"`
Enum []interface{} `json:"enum,omitempty"`
Default interface{} `json:"default,omitempty"`
Format string `json:"format,omitempty"`
Pattern string `json:"pattern,omitempty"`
Minimum *float64 `json:"minimum,omitempty"`
Maximum *float64 `json:"maximum,omitempty"`
MinLength *int `json:"minLength,omitempty"`
MaxLength *int `json:"maxLength,omitempty"`
// Composite schema keywords (JSON Schema §10.2.1)
// Required for MCP tools that use nullable types like anyOf: [{type: "string"}, {type: "null"}]
AnyOf []*JSONSchema `json:"anyOf,omitempty"`
OneOf []*JSONSchema `json:"oneOf,omitempty"`
AllOf []*JSONSchema `json:"allOf,omitempty"`
Not *JSONSchema `json:"not,omitempty"`
}
JSONSchema represents a JSON Schema for tool parameters. This follows the JSON Schema spec for type definitions.
func FromJSON ¶
func FromJSON(data []byte) (*JSONSchema, error)
FromJSON creates a JSONSchema from JSON bytes.
func NewArraySchema ¶
func NewArraySchema(description string, items *JSONSchema) *JSONSchema
NewArraySchema creates a new array schema.
func NewBooleanSchema ¶
func NewBooleanSchema(description string) *JSONSchema
NewBooleanSchema creates a new boolean schema.
func NewNumberSchema ¶
func NewNumberSchema(description string) *JSONSchema
NewNumberSchema creates a new number schema.
func NewObjectSchema ¶
func NewObjectSchema(description string, properties map[string]*JSONSchema, required []string) *JSONSchema
NewObjectSchema creates a new object schema with the given properties.
func NewStringSchema ¶
func NewStringSchema(description string) *JSONSchema
NewStringSchema creates a new string schema.
func NormalizeSchema ¶
func NormalizeSchema(schema *JSONSchema) *JSONSchema
NormalizeSchema ensures a JSON Schema complies with JSON Schema draft 2020-12. This is critical for Bedrock Claude models which strictly validate schemas.
Common issues fixed: - Object types with nil properties -> empty map {} - Missing type fields -> inferred from structure - Nested objects with nil properties -> recursively normalized
func (*JSONSchema) MarshalJSON ¶
func (s *JSONSchema) MarshalJSON() ([]byte, error)
MarshalJSON implements custom JSON marshaling to ensure Bedrock compliance. Object types must have "properties": {} (not omitted) per JSON Schema 2020-12.
func (*JSONSchema) ToJSON ¶
func (s *JSONSchema) ToJSON() ([]byte, error)
ToJSON converts the schema to JSON bytes.
func (*JSONSchema) WithDefault ¶
func (s *JSONSchema) WithDefault(value interface{}) *JSONSchema
WithDefault adds a default value to the schema.
func (*JSONSchema) WithEnum ¶
func (s *JSONSchema) WithEnum(values ...interface{}) *JSONSchema
WithEnum adds enum values to the schema.
func (*JSONSchema) WithFormat ¶
func (s *JSONSchema) WithFormat(format string) *JSONSchema
WithFormat adds a format constraint to the schema.
func (*JSONSchema) WithLength ¶
func (s *JSONSchema) WithLength(minLen, maxLen *int) *JSONSchema
WithLength adds length constraints to the schema.
func (*JSONSchema) WithPattern ¶
func (s *JSONSchema) WithPattern(pattern string) *JSONSchema
WithPattern adds a pattern constraint to the schema.
func (*JSONSchema) WithRange ¶
func (s *JSONSchema) WithRange(min, max *float64) *JSONSchema
WithRange adds min/max constraints to the schema.
type MCPManager ¶
MCPManager is an interface for getting MCP clients. This avoids import cycles with pkg/mcp/manager.
type Matcher ¶ added in v1.4.0
type Matcher struct {
// contains filtered or unexported fields
}
Matcher is a compiled param selector: a resolved param path plus a comparison op over Value. A Matcher with no path matches every call (an unfiltered binding governs every call to its scoped tools).
func (Matcher) MatchesParams ¶ added in v1.4.0
MatchesParams reports whether the call's params satisfy the matcher. A match-all matcher always matches. Otherwise the path is resolved through nested maps to a value that is compared, as a string, under the op; an unresolved path does not match.
type MatcherSpec ¶ added in v1.4.0
type MatcherSpec struct {
ParamPath string `mapstructure:"param_path" json:"param_path"`
Op string `mapstructure:"op" json:"op"`
Value string `mapstructure:"value" json:"value"`
}
MatcherSpec is the raw matcher a binding carries (the HookBinding.Matcher field). ParamPath addresses the param to test and may traverse nested maps (a dispatch tool's carried op) using dot-separated segments. Op is the comparison against Value: "equals", "regex", or "contains".
func (MatcherSpec) Compile ¶ added in v1.4.0
func (s MatcherSpec) Compile() (Matcher, error)
Compile turns a MatcherSpec into a Matcher, validating at serve. An empty spec compiles to a match-all Matcher. A non-empty spec requires a ParamPath and a known Op; a "regex" op compiles Value and a bad pattern is an error (fail-closed, L-Cfg-1).
func (*MatcherSpec) UnmarshalJSON ¶ added in v1.4.0
func (m *MatcherSpec) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes a matcher spec with the same key normalization as HookBinding.
type MockTool ¶
type MockTool struct {
MockName string
MockDescription string
MockSchema *JSONSchema
MockBackend string
MockExecute func(ctx context.Context, params map[string]interface{}) (*Result, error)
ExecuteCount int
LastParams map[string]interface{}
// contains filtered or unexported fields
}
MockTool is a mock implementation of the Tool interface for testing. It allows tests to control all tool behavior and verify interactions. Thread-safe for concurrent testing.
func (*MockTool) Description ¶
Description returns the mock tool description.
func (*MockTool) InputSchema ¶
func (m *MockTool) InputSchema() *JSONSchema
InputSchema returns the mock input schema.
type NoOpNotifier ¶
type NoOpNotifier struct{}
NoOpNotifier is a no-op implementation of Notifier for testing.
func (*NoOpNotifier) Notify ¶
func (n *NoOpNotifier) Notify(ctx context.Context, req *HumanRequest) error
type Notifier ¶
type Notifier interface {
// Notify sends a notification about a human request
Notify(ctx context.Context, req *HumanRequest) error
}
Notifier sends notifications to humans when their input is requested.
type PermissionChecker ¶
type PermissionChecker struct {
// contains filtered or unexported fields
}
PermissionChecker checks if a tool can be executed based on configuration. Entries in the allow/deny lists match a tool name either exactly or, when the entry ends with "*", by prefix. This lets a whole MCP server's tools be trusted with a single pattern (e.g. "opendata:*" matches "opendata:query"), rather than enumerating every tool name. A bare "*" matches everything.
func NewPermissionChecker ¶
func NewPermissionChecker(config PermissionConfig) *PermissionChecker
NewPermissionChecker creates a new permission checker.
func (*PermissionChecker) Advertisable ¶ added in v1.4.0
func (pc *PermissionChecker) Advertisable(toolName string) bool
Advertisable reports whether a tool should be offered to the LLM at all.
A tool the checker would unconditionally refuse — one that is hard-disabled, or one that requires approval while no approval mechanism is wired up — is hidden from the model. Otherwise the model only discovers it can't use the tool by calling it and eating a denial: that wastes a turn and records an intentional policy decision as a tool failure in the analytics. Hiding such tools costs no capability (they could never have run) and keeps the model's tool surface honest.
This mirrors CheckPermission's decision tree but never blocks and has no side effects, so it is safe to call once per tool per turn. It is the single place to revisit when an interactive approval callback is implemented (at which point approval-required tools become advertisable again).
func (*PermissionChecker) CheckPermission ¶
func (pc *PermissionChecker) CheckPermission(ctx context.Context, toolName string, params map[string]interface{}) error
CheckPermission checks if a tool can be executed. Returns nil if allowed, error if denied.
func (*PermissionChecker) IsToolAllowed ¶
func (pc *PermissionChecker) IsToolAllowed(toolName string) bool
IsToolAllowed returns true if a tool is explicitly allowed (whitelist), matching exact names and "<prefix>*" patterns.
func (*PermissionChecker) IsToolDisabled ¶
func (pc *PermissionChecker) IsToolDisabled(toolName string) bool
IsToolDisabled returns true if a tool is explicitly disabled (blacklist), matching exact names and "<prefix>*" patterns.
func (*PermissionChecker) IsYOLOMode ¶
func (pc *PermissionChecker) IsYOLOMode() bool
IsYOLOMode returns true if YOLO mode is enabled.
func (*PermissionChecker) RequiresApproval ¶
func (pc *PermissionChecker) RequiresApproval() bool
RequiresApproval returns true if user approval is required for tools.
type PermissionConfig ¶
type PermissionConfig struct {
RequireApproval bool
YOLO bool
AllowedTools []string
DisabledTools []string
DefaultAction string // "allow" or "deny"
TimeoutSeconds int
}
PermissionConfig holds permission configuration.
type PostToolHook ¶ added in v1.4.0
type PostToolHook interface {
Observe(req AdmissionRequest, result *Result)
}
PostToolHook observes a tool call after the body has run. It never gates execution; it exists for recording and audit side effects.
type PromptAwareTool ¶
type PromptAwareTool struct {
// contains filtered or unexported fields
}
PromptAwareTool wraps a tool and loads descriptions from PromptRegistry. Falls back to tool's native Description() if prompt not found. This enables externalized tool descriptions while maintaining backward compatibility.
func (*PromptAwareTool) Backend ¶
func (p *PromptAwareTool) Backend() string
Backend returns the backend type this tool requires (delegates to wrapped tool).
func (*PromptAwareTool) Description ¶
func (p *PromptAwareTool) Description() string
Description loads the tool description from PromptRegistry. Falls back to the wrapped tool's native Description() if: - PromptRegistry lookup fails - Prompt is not found - Prompt is empty
This ensures tools always have a valid description, even if prompts are not configured.
func (*PromptAwareTool) Execute ¶
func (p *PromptAwareTool) Execute(ctx context.Context, params map[string]interface{}) (*Result, error)
Execute runs the tool with given parameters (delegates to wrapped tool).
func (*PromptAwareTool) InputSchema ¶
func (p *PromptAwareTool) InputSchema() *JSONSchema
InputSchema returns the JSON Schema for tool parameters (delegates to wrapped tool).
func (*PromptAwareTool) Name ¶
func (p *PromptAwareTool) Name() string
Name returns the tool's unique identifier (delegates to wrapped tool).
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry manages tool registration and lookup.
func (*Registry) IsRegistered ¶ added in v1.1.0
IsRegistered checks if a tool is already registered. This is useful for progressive tool disclosure (registering tools only when needed).
func (*Registry) ListByBackend ¶
ListByBackend returns all tools for a specific backend. Pass empty string to get backend-agnostic tools.
func (*Registry) Register ¶
Register registers a tool with the registry. If a tool with the same name already exists, it will be replaced.
func (*Registry) Unregister ¶
Unregister removes a tool from the registry.
type Result ¶
type Result struct {
// Success indicates if the tool executed successfully
Success bool
// Data contains the result data (format varies by tool)
// For small results, data is stored here directly
// For large results, use DataReference instead
Data interface{}
// Error contains error information if execution failed
Error *Error
// Metadata contains tool-specific metadata
Metadata map[string]interface{}
// ExecutionTime in milliseconds
ExecutionTimeMs int64
// CacheHit indicates if this result came from cache
CacheHit bool
// DataReference points to large result data in shared memory
// When set, Data field should contain only a brief summary
DataReference *loomv1.DataReference
}
Result represents the outcome of tool execution.
type SQLiteConfig ¶
type SQLiteConfig struct {
Path string // Database file path (default: ":memory:")
Tracer observability.Tracer // Tracer for observability (default: NoOpTracer)
}
SQLiteConfig configures the SQLite store.
type SQLiteHumanRequestStore ¶
type SQLiteHumanRequestStore struct {
// contains filtered or unexported fields
}
SQLiteHumanRequestStore provides persistent SQLite storage for human requests. Suitable for production deployments with request history and audit trail.
func NewSQLiteHumanRequestStore ¶
func NewSQLiteHumanRequestStore(config SQLiteConfig) (*SQLiteHumanRequestStore, error)
NewSQLiteHumanRequestStore creates a new SQLite-backed human request store.
func (*SQLiteHumanRequestStore) Close ¶
func (s *SQLiteHumanRequestStore) Close() error
Close closes the database connection.
func (*SQLiteHumanRequestStore) ExpireRequest ¶ added in v1.4.0
func (s *SQLiteHumanRequestStore) ExpireRequest(ctx context.Context, requestID, respondedBy string) error
ExpireRequest terminally closes a pending row as "timeout" regardless of its expiry — the harness's close for abandoned or swept rows. A resolved row matches zero rows and is left untouched (closing is not resolving); a missing row is a no-op.
func (*SQLiteHumanRequestStore) Get ¶
func (s *SQLiteHumanRequestStore) Get(ctx context.Context, id string) (*HumanRequest, error)
Get retrieves a human request by ID.
func (*SQLiteHumanRequestStore) ListBySession ¶
func (s *SQLiteHumanRequestStore) ListBySession(ctx context.Context, sessionID string) ([]*HumanRequest, error)
ListBySession returns all requests for a session.
func (*SQLiteHumanRequestStore) ListPending ¶
func (s *SQLiteHumanRequestStore) ListPending(ctx context.Context) ([]*HumanRequest, error)
ListPending returns all pending requests.
func (*SQLiteHumanRequestStore) RespondToRequest ¶
func (s *SQLiteHumanRequestStore) RespondToRequest(ctx context.Context, requestID, status, response, respondedBy string, responseData map[string]interface{}) error
RespondToRequest resolves a pending, non-expired request exactly once. On an already-decided or expired request it is a no-op returning nil, so the caller reads current state via Get. Errors only on a missing request.
func (*SQLiteHumanRequestStore) Store ¶
func (s *SQLiteHumanRequestStore) Store(ctx context.Context, req *HumanRequest) error
Store saves a new human request to the database.
func (*SQLiteHumanRequestStore) Update ¶
func (s *SQLiteHumanRequestStore) Update(ctx context.Context, req *HumanRequest) error
Update updates an existing human request.
type Tool ¶
type Tool interface {
// Name returns the tool's unique identifier
Name() string
// Description returns a human-readable description for LLM context
Description() string
// InputSchema returns the JSON Schema for tool parameters
InputSchema() *JSONSchema
// Execute runs the tool with given parameters
Execute(ctx context.Context, params map[string]interface{}) (*Result, error)
// Backend returns the backend type this tool requires (e.g., "teradata", "postgres", "api")
// Empty string means the tool is backend-agnostic
Backend() string
}
Tool defines the interface for executable tools (shuttles) in the agent framework. Tools are the primary mechanism for agents to interact with backends and perform domain-specific operations. Each tool encapsulates a single capability.
Why "shuttle"? Tools "shuttle" data and execution between the LLM and the backend, like a shuttle in weaving carries thread back and forth across the loom.
func ListByBackend ¶
ListByBackend returns all tools for a specific backend from the global registry.
func ListTools ¶
func ListTools() []Tool
ListTools returns all registered tools from the global registry.
func MustGet ¶
MustGet retrieves a tool from the global registry and panics if not found. This is useful for testing and initialization code.
func NewPromptAwareTool ¶
func NewPromptAwareTool(tool Tool, registry prompts.PromptRegistry, promptKey string) Tool
NewPromptAwareTool wraps a tool with PromptRegistry-based descriptions. If registry is nil, returns the original tool unchanged (no wrapping overhead).
Example:
registry := prompts.NewFileRegistry("./prompts")
tool := builtin.NewHTTPClientTool()
wrapped := shuttle.NewPromptAwareTool(tool, registry, "tools.http_request")
description := wrapped.Description() // Loads from prompts/tools/http_request.yaml
type ToolRegistry ¶
type ToolRegistry interface {
Search(ctx context.Context, req *loomv1.SearchToolsRequest) (*loomv1.SearchToolsResponse, error)
}
ToolRegistry is an interface for dynamic tool discovery. This avoids import cycles with pkg/tools/registry.
type ToolScope ¶ added in v1.4.0
type ToolScope struct {
// contains filtered or unexported fields
}
ToolScope is the set of tools a binding governs, derived from the binding's Scope selector. It reuses the exact/prefix semantics of tools.permissions: a bare tool name matches exactly, a trailing "*" matches by prefix (e.g. "opendata:*" governs every "opendata:" tool), and a bare "*" governs all.
func NewToolScope ¶ added in v1.4.0
NewToolScope compiles a single Scope selector into a ToolScope.
func (ToolScope) MatchesTool ¶ added in v1.4.0
MatchesTool reports whether the named tool is governed by this scope.
Source Files
¶
- admission_approvedset.go
- admission_ask.go
- admission_ask_hook.go
- admission_build.go
- admission_chain.go
- admission_denylist.go
- admission_hook.go
- admission_matcher.go
- admission_recorder.go
- admission_registry.go
- admission_scope.go
- executor.go
- hook_config.go
- human_store_sqlite.go
- human_tool.go
- instrumented_executor.go
- mock_tool.go
- permission_checker.go
- prompt_aware_tool.go
- registry.go
- schema_validator.go
- tool.go