effects

package
v0.14.2 Latest Latest
Warning

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

Go to latest
Published: Apr 26, 2026 License: Apache-2.0 Imports: 30 Imported by: 0

Documentation

Overview

Package effects provides BrainStore, a two-tier semantic cache (user + project). Part of M-BRAIN (Persistent Semantic Cache).

Package effects provides the SharedIndex effect for similarity-based semantic retrieval. Part of M-DX16 (Deterministic Semantic Retrieval).

Package effects provides the in-memory implementation of SharedIndex. Part of M-DX16 (Deterministic Semantic Retrieval).

Package effects provides the SharedMem effect for shared memory caching. Part of M-DX15 (Semantic Caching MVP).

Package effects provides InMemorySharedCache, a thread-safe in-memory cache. Part of M-DX15 (Semantic Caching MVP).

Package effects provides SQLiteSharedCache, a persistent SharedCache backed by SQLite. Part of M-BRAIN (Persistent Semantic Cache).

Index

Constants

This section is empty.

Variables

View Source
var ErrNoAIHandler = errors.New("no AI model configured — add --ai <model> flag (e.g. --ai gemini-2-5-flash), or --ai-stub for testing")

ErrNoAIHandler is returned when AI.call is invoked without a configured handler

View Source
var Registry = map[string]map[string]EffOp{
	"IO":      {},
	"FS":      {},
	"Clock":   {},
	"Net":     {},
	"Debug":   {},
	"Stream":  {},
	"Process": {},
}

Registry holds all effect operations organized by effect name

Structure:

Registry["IO"]["print"] = ioPrint
Registry["IO"]["println"] = ioPrintln
Registry["FS"]["readFile"] = fsReadFile
Registry["Clock"]["now"] = clockNow
Registry["Clock"]["sleep"] = clockSleep
Registry["Net"]["httpGet"] = netHttpGet
Registry["Net"]["httpPost"] = netHttpPost

This registry is initialized at package load time with nested maps pre-created, making it safe for concurrent reads and allowing RegisterOp to work in init() functions.

Functions

func Call

func Call(ctx *EffContext, effectName, opName string, args []eval.Value) (eval.Value, error)

Call invokes an effect operation

This is the main entry point for effect execution. It performs:

  1. Capability checking (deny if not granted)
  2. Operation lookup (find the effect implementation)
  3. Execution (call the EffOp function)

Parameters:

  • ctx: The effect context (with capability grants)
  • effectName: The effect name (e.g., "IO", "FS")
  • opName: The operation name (e.g., "print", "readFile")
  • args: The arguments to pass to the operation

Returns:

  • The result value from the operation
  • An error if capability is missing, operation not found, or execution fails

Example:

result, err := effects.Call(ctx, "IO", "println", []eval.Value{
    &eval.StringValue{Value: "Hello!"},
})

func ClearGlobalTraces

func ClearGlobalTraces()

ClearGlobalTraces clears all traces from the global registry. Useful for resetting state between tests.

func ExportFrameRecord

func ExportFrameRecord(f BrainFrame, tier string) map[string]interface{}

ExportFrameRecord creates an export-ready map from a BrainFrame, including base64 embedding.

func FormatEnvError

func FormatEnvError(varName string, err error) string

FormatEnvError formats environment errors with redaction

Creates user-friendly error messages while redacting sensitive values. Provides actionable suggestions for common error cases.

Parameters:

  • varName: Environment variable name
  • err: The error that occurred

Returns:

  • Formatted error message with redacted sensitive data

Example:

FormatEnvError("API_KEY", errors.New("not found"))
// "environment variable 'API_KEY' not found. Set with: export API_KEY=<value>"

func FormatReport

func FormatReport(br *BudgetReport) string

FormatReport formats a budget report as human-readable text

M-DX25: Shows both semantic (charged to caller) and physical (actual calls) counts. When semantic == physical, shows simplified format.

Example output:

Budget Report:
  main:               IO 20        (when semantic == physical)
  getDefaultProject:  FS semantic 5/5 physical 12

Total: IO=20, FS semantic=5 physical=12

func FormatReportForError

func FormatReportForError(br *BudgetReport) string

FormatReportForError formats a budget report summary for inclusion in error messages

func FormatReportJSON

func FormatReportJSON(br *BudgetReport) ([]byte, error)

FormatReportJSON formats a budget report as JSON

func ImportFrameEmbedding

func ImportFrameEmbedding(record map[string]interface{}, f *BrainFrame)

ImportFrameEmbedding decodes base64 embedding from an import record.

func IsSensitiveVarName

func IsSensitiveVarName(name string) bool

IsSensitiveVarName checks if a variable name matches sensitive patterns

Checks against patterns: key, secret, token, password, credential (case-insensitive).

Parameters:

  • name: Environment variable name

Returns:

  • true if name matches any sensitive pattern
  • false otherwise

Example:

IsSensitiveVarName("API_KEY")      // true
IsSensitiveVarName("SECRET")       // true
IsSensitiveVarName("DEBUG")        // false
IsSensitiveVarName("PATH")         // false

func NetHTTPRequest

func NetHTTPRequest(ctx *EffContext, args []eval.Value) (eval.Value, error)

NetHTTPRequest implements Net.httpRequest(method, url, headers, body) -> Result[HttpResponse, NetError]

Advanced HTTP client with custom headers, status codes, and structured error handling.

Parameters:

  • method: HTTP method (GET, POST, PUT, PATCH, DELETE, HEAD)
  • url: Target URL (must pass allowlist/security validation)
  • headers: List of {name, value} records
  • body: Request body (empty string for GET)

Returns:

  • Ok(HttpResponse) on success (includes 4xx/5xx status codes)
  • Err(NetError) for transport/validation failures

Security:

  • Blocks hop-by-hop headers (Connection, Transfer-Encoding, etc.)
  • Blocks Host, Accept-Encoding, Content-Length overrides
  • Strips Authorization on cross-origin redirects
  • Case-insensitive header matching, preserves order
  • Method whitelist (GET, POST, PUT, PATCH, DELETE, HEAD)

Example AILANG code:

let headers = [{name: "Authorization", value: "Bearer token"}];
match httpRequest("POST", url, headers, body) {
  Ok(resp) -> if resp.ok then resp.body else "Error: " ++ show(resp.status)
  Err(err) -> match err {
    Transport(msg) -> "Network error: " ++ msg
    DisallowedHost(host) -> "Blocked: " ++ host
    InvalidHeader(hdr) -> "Bad header: " ++ hdr
    BodyTooLarge(size) -> "Response too large: " ++ show(size)
  }
}

NetHTTPRequest implements the _net_httpRequest builtin Exported for use in new builtin registry (internal/builtins/register.go)

func NewManagedProcess

func NewManagedProcess(parentCtx context.Context, cmdPath string, args []string) (*managedProcess, error)

NewManagedProcess spawns a subprocess with a writable stdin pipe.

The subprocess is started immediately. A background goroutine (writeLoop) drains the write channel and writes to the stdin pipe. Stdout and stderr are discarded — use asyncExecProcess for stdout streaming.

Parameters:

  • parentCtx: parent context (cancelled on program exit)
  • cmdPath: resolved absolute path to the binary
  • args: command arguments (no shell expansion)

func ProcessCloseStdin

func ProcessCloseStdin(ctx *EffContext, args []eval.Value) (eval.Value, error)

ProcessCloseStdin closes the stdin pipe of a managed process.

Args: [handle: ProcessHandle(int)] Returns: () — unit

func ProcessSpawn

func ProcessSpawn(ctx *EffContext, args []eval.Value) (eval.Value, error)

ProcessSpawn spawns a subprocess with a writable stdin pipe.

Args: [cmd: string, args: [string]] Returns: ProcessHandle(int) — opaque handle stored in ProcessContext.managed

func ProcessWriteStdin

func ProcessWriteStdin(ctx *EffContext, args []eval.Value) (eval.Value, error)

ProcessWriteStdin writes bytes to a managed process's stdin.

Args: [handle: ProcessHandle(int), data: bytes] Returns: Result[(), string] — Ok(()) on success, Err(reason) on failure

func RecordTrace

func RecordTrace(name string)

RecordTrace records a span name in the global registry. Call this from instrumented code points (compiler, evaluator, etc.)

func RedactEnvSnapshot

func RedactEnvSnapshot(snapshot map[string]string) map[string]string

RedactEnvSnapshot redacts sensitive values in an environment snapshot

Returns a new map with sensitive values replaced by "[REDACTED]". Used for safe logging/debugging of environment snapshots.

Parameters:

  • snapshot: Original environment snapshot

Returns:

  • New map with sensitive values redacted

Example:

original := map[string]string{
    "API_KEY": "sk-proj-secret",
    "DEBUG": "true",
}
redacted := RedactEnvSnapshot(original)
// {"API_KEY": "[REDACTED]", "DEBUG": "true"}

func RedactEnvValue

func RedactEnvValue(name, value string) string

RedactEnvValue redacts sensitive environment variable values

Checks if the variable name matches sensitive patterns (key, secret, token, password). If redaction is enabled and the name is sensitive, returns "[REDACTED]". Otherwise returns the original value.

Redaction can be disabled with: AILANG_REDACT_ENV=off

Parameters:

  • name: Environment variable name
  • value: Environment variable value

Returns:

  • Original value if not sensitive or redaction disabled
  • "[REDACTED]" if sensitive and redaction enabled

Example:

RedactEnvValue("API_KEY", "sk-proj-abc123")     // "[REDACTED]"
RedactEnvValue("DEBUG", "true")                  // "true"
RedactEnvValue("SECRET_TOKEN", "sensitive-data") // "[REDACTED]"

func RedactErrorMessage

func RedactErrorMessage(message string) string

RedactErrorMessage redacts sensitive values from error messages

Scans error messages for patterns that look like sensitive values (long alphanumeric strings, base64-like patterns) and replaces them with "[REDACTED]".

Patterns redacted:

  • API keys: sk-proj-*, sk-ant-*, etc.
  • Long tokens: strings with 20+ alphanumeric/dash/underscore chars
  • Base64-like strings: 16+ chars of [A-Za-z0-9+/=]

Parameters:

  • message: Error message that may contain sensitive values

Returns:

  • Error message with sensitive values replaced by "[REDACTED]"

Example:

RedactErrorMessage("failed to auth with key sk-proj-abc123def456")
// "failed to auth with key [REDACTED]"

func RegisterOp

func RegisterOp(effectName, opName string, op EffOp)

RegisterOp registers an effect operation

This function is used by effect implementation files (io.go, fs.go) to register their operations at package initialization time.

Parameters:

  • effectName: The effect name (e.g., "IO")
  • opName: The operation name (e.g., "print")
  • op: The operation implementation

Example:

func init() {
    RegisterOp("IO", "print", ioPrint)
    RegisterOp("IO", "println", ioPrintln)
}

func StreamAsyncExecProcess

func StreamAsyncExecProcess(ctx *EffContext, args []eval.Value) (eval.Value, error)

StreamAsyncExecProcess spawns a subprocess and delivers its stdout as SourceBytes events.

Args: [cmd: string, args: [string], name: string, priority: int, chunkSize: int] Returns: StreamSource(int) — opaque handle stored in StreamContext.sources Requires: Process capability (for subprocess spawning) + Stream capability (for source creation)

func StreamAsyncReadStdinLines

func StreamAsyncReadStdinLines(ctx *EffContext, args []eval.Value) (eval.Value, error)

StreamAsyncReadStdinLines creates an EventSource that reads lines from stdin.

Args: [name: string, priority: int] Returns: StreamSource(int) — opaque handle stored in StreamContext.sources

func StreamClose

func StreamClose(ctx *EffContext, args []eval.Value) (eval.Value, error)

streamClose closes a connection.

func StreamConnect

func StreamConnect(ctx *EffContext, args []eval.Value) (eval.Value, error)

StreamConnect establishes a WebSocket connection.

Args: [url: string, config: record{protocol, headers, subprotocols}] Returns: Result[StreamConn(int), StreamError]

func StreamGetStatus

func StreamGetStatus(ctx *EffContext, args []eval.Value) (eval.Value, error)

StreamGetStatus returns connection status as a StreamStatus ADT value.

func StreamOnEvent

func StreamOnEvent(ctx *EffContext, args []eval.Value) (eval.Value, error)

streamOnEvent registers an event handler.

Args: [conn: StreamConn(int), handler: StreamEvent -> bool] Returns: unit

func StreamRunEventLoop

func StreamRunEventLoop(ctx *EffContext, args []eval.Value) (eval.Value, error)

streamRunEventLoop blocks, dispatching events to the registered handler.

M-ASYNC-IO: Now delegates to selectEventsLoop with a single connSource. This makes runEventLoop sugar over the general-purpose multiplexer, preserving backward compatibility while sharing the same implementation.

Args: [conn: StreamConn(int)] Returns: unit

func StreamSSEConnect

func StreamSSEConnect(ctx *EffContext, args []eval.Value) (eval.Value, error)

StreamSSEConnect opens an SSE (Server-Sent Events) connection.

SSE is a unidirectional HTTP streaming protocol used by AI APIs (Anthropic, OpenAI, Gemini) for token-by-token response streaming. Reuses the existing event dispatch infrastructure (eventBuffer, onEvent, runEventLoop).

Args: [url: string, config: record{headers}] Returns: Result[StreamConn(int), StreamError]

func StreamSSEPost

func StreamSSEPost(ctx *EffContext, args []eval.Value) (eval.Value, error)

StreamSSEPost opens an SSE connection via HTTP POST.

This is the standard pattern for AI API streaming: Claude, OpenAI, and Gemini all use POST+SSE where the request body contains the prompt/config and the response is streamed back as Server-Sent Events.

Args: [url: string, body: string, config: record{headers}] Returns: Result[StreamConn(int), StreamError]

func StreamSelectEvents

func StreamSelectEvents(ctx *EffContext, args []eval.Value) (eval.Value, error)

StreamSelectEvents runs the deterministic multi-source event loop.

Args: [sources: list of StreamSource(int), handler: StreamEvent -> bool] Returns: unit

func StreamSend

func StreamSend(ctx *EffContext, args []eval.Value) (eval.Value, error)

streamSend sends a message on a connection.

Args: [conn: StreamConn(int), msg: StreamMessage(Text(string)|Bin(bytes))] Returns: Result[unit, StreamError]

func StreamSourceOfConn

func StreamSourceOfConn(ctx *EffContext, args []eval.Value) (eval.Value, error)

StreamSourceOfConn wraps an existing StreamConnection as an EventSource.

Args: [conn: StreamConn(int), name: string, priority: int] Returns: StreamSource(int) — opaque handle stored in StreamContext.sources

func TraceCheckImpl

func TraceCheckImpl(ctx *EffContext, args []eval.Value) (eval.Value, error)

TraceCheckImpl is the implementation for the _trace_check builtin. It checks if a trace with the given name exists in the global registry.

Type: string -> bool

Types

type AIContext

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

AIContext holds the handler for the current execution

Thread-safety: AIContext is designed for single-threaded use within one evaluation. Create a new context for each step/tick.

func NewAIContext

func NewAIContext(handler AIHandler) *AIContext

NewAIContext creates a context with the given handler

IMPORTANT: Pass nil only in tests to verify error handling. Production code should always have a real handler or explicit stub.

func (*AIContext) Call

func (c *AIContext) Call(input string) (string, error)

Call invokes the AI handler with the given input

Returns ErrNoAIHandler if no handler is configured. This is intentional - no silent fallbacks for AI calls.

func (*AIContext) CallImage

func (c *AIContext) CallImage(prompt, outputPath, options string) (string, error)

CallImage generates an image and writes it to outputPath.

func (*AIContext) CallImageBase64

func (c *AIContext) CallImageBase64(prompt, options string) (string, error)

CallImageBase64 generates an image and returns it as base64 JSON.

func (*AIContext) CallJson

func (c *AIContext) CallJson(input string, schema string) (string, error)

CallJson invokes the AI handler requesting structured JSON output. If schema is non-empty, providers enforce the schema on the response.

type AIHandler

type AIHandler interface {
	Call(input string) (string, error)
	// CallJson sends a request configured for structured JSON output.
	// If schema is non-empty, providers enforce the schema.
	// Returns raw JSON string (caller parses to Json ADT).
	CallJson(input string, schema string) (string, error)
	// CallImage generates an image and writes it to outputPath.
	// Options is a JSON string with optional fields: aspect_ratio, mime_type.
	// Returns the output path on success.
	CallImage(prompt string, outputPath string, options string) (string, error)
	// CallImageBase64 generates an image and returns it as a JSON string
	// containing base64-encoded data: {"base64": "...", "mime_type": "image/png"}.
	CallImageBase64(prompt string, options string) (string, error)
}

AIHandler interface for pluggable AI implementation

The AI effect is AILANG's general-purpose AI oracle - an opaque, host-provided effect for calling external AI/ML systems. Use cases include:

  • Game NPC decision-making (via typed wrappers)
  • CLI tools with AI assistance
  • Agents written in AILANG calling LLMs
  • Data analysis pipelines with AI steps

The interface is intentionally simple: string → string By convention, JSON is used for input/output, but this is not enforced.

type AssertionResult

type AssertionResult struct {
	Passed   bool
	Message  string
	Location string // "file.ail:42" (auto-injected by compiler)
}

AssertionResult represents the result of a debug assertion

type BrainFrame

type BrainFrame struct {
	Key          string
	Namespace    string
	Value        []byte
	SimHash      int64
	Content      string
	Version      int
	CreatedAt    int64
	UpdatedAt    int64
	ExpiresAt    *int64
	Source       string
	Embedding    []float32 // optional embedding vector
	EmbeddingDim int       // dimension of embedding (0 if none)
	EmbedModel   string    // model that produced the embedding
}

BrainFrame represents a frame stored in the brain with its metadata.

type BrainScope

type BrainScope string

BrainScope controls which tier(s) to query or write to.

const (
	ScopeBoth    BrainScope = "both"    // Query both, merge results (default)
	ScopeUser    BrainScope = "user"    // User brain only
	ScopeProject BrainScope = "project" // Project brain only
)

type BrainSearchResult

type BrainSearchResult struct {
	Frame BrainFrame
	Score float64
	Tier  string // "user" or "project"
}

SearchResult represents a search hit with a relevance score.

type BrainStats

type BrainStats struct {
	TotalFrames int
	Namespaces  map[string]int // namespace -> frame count
	OldestFrame int64          // unix millis
	NewestFrame int64          // unix millis
}

BrainStats holds aggregate statistics about the brain.

type BrainStore

type BrainStore struct {
	User    *SQLiteSharedCache // ~/.ailang/state/brain.db (cross-project)
	Project *SQLiteSharedCache // .ailang/state/brain.db (project-specific)
}

BrainStore wraps two SQLiteSharedCache instances: user-level (global) and project-level (repo-scoped). Both are queried by default with project results ranked higher. Follows the same two-tier pattern as Claude Code settings and ailang messages inboxes.

func NewBrainStore

func NewBrainStore(userDBPath, projectDBPath string, opts ...CacheOption) (*BrainStore, error)

NewBrainStore creates a BrainStore from two database paths. Either path may be empty to skip that tier. Optional CacheOption values are applied to both caches (e.g., WithEmbedder).

func (*BrainStore) Close

func (b *BrainStore) Close() error

Close closes both database connections.

func (*BrainStore) EmbeddingStats

func (b *BrainStore) EmbeddingStats() map[string]struct {
	Total, WithEmbedding int
	Models               map[string]int
}

EmbeddingStats returns embedding coverage for both tiers.

func (*BrainStore) Promote

func (b *BrainStore) Promote(key string) bool

Promote copies a frame from the project brain to the user brain. Returns false if the frame doesn't exist in the project brain.

func (*BrainStore) Put

func (b *BrainStore) Put(f BrainFrame, scope BrainScope) error

Put writes a frame to the appropriate tier (project by default).

func (*BrainStore) Search

func (b *BrainStore) Search(namespace string, queryHash int64, limit int, scope BrainScope) []BrainSearchResult

Search queries both brains by SimHash and merges results. Project results get a boost factor to rank higher than user results at equal scores.

func (*BrainStore) SearchByEmbedding

func (b *BrainStore) SearchByEmbedding(query []float32, namespace string, limit int, scope BrainScope) []BrainSearchResult

SearchByEmbedding queries both brains by cosine similarity.

func (*BrainStore) SearchText

func (b *BrainStore) SearchText(query string, namespace string, limit int, scope BrainScope) []BrainSearchResult

SearchText queries both brains by keyword and merges results.

func (*BrainStore) SearchThreeTier

func (b *BrainStore) SearchThreeTier(query string, queryHash int64, queryEmbedding []float32, namespace string, limit int, scope BrainScope) []BrainSearchResult

SearchThreeTier performs a three-tier search: cosine > SimHash > text. Cosine results get a +0.1 boost over SimHash-only results. Results are merged and deduplicated by key.

func (*BrainStore) Stats

func (b *BrainStore) Stats() map[string]BrainStats

Stats returns combined stats from both brains.

type BudgetContext

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

BudgetContext tracks per-effect usage counts against configured limits

Budgets are type-level constraints that limit how many times an effect can be invoked within a function scope. Each function invocation gets a fresh budget (per-invocation semantics).

M-DX25 M4: Now supports minimum usage requirements (@min=N).

Example:

budget := NewBudgetContext(map[string]*int{"IO": intPtr(5)})
for i := 0; i < 6; i++ {
    if err := budget.CheckAndConsume("IO", "file.ail:10:5"); err != nil {
        // Returns BudgetExhaustedError on the 6th call
        return err
    }
}

func NewBudgetContext

func NewBudgetContext(limits map[string]*int) *BudgetContext

NewBudgetContext creates a new budget context with the specified limits

Parameters:

  • limits: Map of effect name to budget limit. nil values mean unlimited. A nil map means no budgets (all effects unlimited).

Returns:

  • A new BudgetContext ready to track usage

Example:

// IO limited to 5 calls, FS unlimited
limits := map[string]*int{"IO": intPtr(5)}
budget := NewBudgetContext(limits)

func NewBudgetContextWithMin

func NewBudgetContextWithMin(limits map[string]*int, minLimits map[string]*int) *BudgetContext

NewBudgetContextWithMin creates a new budget context with limits and minimums

M-DX25 M4: Supports minimum usage requirements to verify effects were exercised.

Parameters:

  • limits: Map of effect name to max budget limit. nil values mean unlimited.
  • minLimits: Map of effect name to minimum required usage. nil values mean no minimum.

Example:

// IO: at least 1 call, at most 5 calls
limits := map[string]*int{"IO": intPtr(5)}
minLimits := map[string]*int{"IO": intPtr(1)}
budget := NewBudgetContextWithMin(limits, minLimits)

func (*BudgetContext) ChargeSemanticOnly

func (bc *BudgetContext) ChargeSemanticOnly(effect string, count int)

ChargeSemanticOnly increments only the semantic usage count

M-DX25: Used by PopScopeAndChargeCaller to charge the caller's semantic budget with the callee's declared amount. This does NOT increment physical usage (which is tracked separately at builtin invocation).

Parameters:

  • effect: The effect name
  • count: The amount to charge (typically callee's declared @limit)

func (*BudgetContext) CheckAndConsume

func (bc *BudgetContext) CheckAndConsume(effect, position string) error

CheckAndConsume attempts to use one unit of budget for an effect

This is the main method called before each effect operation. It checks if budget is available and consumes one unit if so.

Parameters:

  • effect: The effect name
  • position: Source position for error reporting (optional)

Returns:

  • nil if the operation is allowed (budget consumed or unlimited)
  • BudgetExhaustedError if the budget is exhausted

Example:

if err := ctx.Budget.CheckAndConsume("IO", "file.ail:10:5"); err != nil {
    return nil, err // Budget exhausted
}
// Proceed with IO operation

func (*BudgetContext) CheckMinimum

func (bc *BudgetContext) CheckMinimum(position string) error

CheckMinimum verifies that minimum usage requirements are met

M-DX25 M4: Called on scope exit to ensure effects were exercised. Uses physical usage count (actual calls) for verification.

Parameters:

  • position: Source position for error reporting (optional)

Returns:

  • nil if all minimums are met
  • BudgetUnderrunError if any minimum is not satisfied

func (*BudgetContext) Clone

func (bc *BudgetContext) Clone() *BudgetContext

Clone creates a copy of the budget context

Used for nested scopes where a fresh budget is needed. The clone has the same limits but zero usage.

Returns:

  • A new BudgetContext with same limits, fresh usage counters

func (*BudgetContext) HasBudget

func (bc *BudgetContext) HasBudget(effect string) bool

HasBudget checks if an effect has a budget limit configured

Parameters:

  • effect: The effect name to check

Returns:

  • true if a budget limit exists for this effect
  • false if the effect is unlimited

func (*BudgetContext) HasMinimum

func (bc *BudgetContext) HasMinimum(effect string) bool

HasMinimum checks if an effect has a minimum usage requirement

M-DX25 M4: Minimum budgets ensure effects were actually exercised.

Parameters:

  • effect: The effect name to check

Returns:

  • true if a minimum requirement exists for this effect

func (*BudgetContext) Limit

func (bc *BudgetContext) Limit(effect string) (int, bool)

Limit returns the budget limit for an effect

Parameters:

  • effect: The effect name

Returns:

  • The limit value and true if a budget exists
  • 0 and false if the effect is unlimited

func (*BudgetContext) LimitsMap

func (bc *BudgetContext) LimitsMap() map[string]int

LimitsMap returns a copy of the limits per effect

Returns:

  • Map of effect name to limit (-1 means check if it exists in the map)

func (*BudgetContext) Merge

func (bc *BudgetContext) Merge(other *BudgetContext) *BudgetContext

Merge combines two budget contexts

For budget composition (e.g., nested scopes), limits are summed. Usage is taken from the current context.

Parameters:

  • other: Another budget context to merge

Returns:

  • A new BudgetContext with combined limits

func (*BudgetContext) MinLimitsMap

func (bc *BudgetContext) MinLimitsMap() map[string]int

MinLimitsMap returns a copy of the minimum limits per effect

Returns:

  • Map of effect name to minimum requirement

func (*BudgetContext) Minimum

func (bc *BudgetContext) Minimum(effect string) (int, bool)

Minimum returns the minimum usage requirement for an effect

Parameters:

  • effect: The effect name

Returns:

  • The minimum value and true if a minimum exists
  • 0 and false if no minimum is set

func (*BudgetContext) PhysicalUsageMap

func (bc *BudgetContext) PhysicalUsageMap() map[string]int

PhysicalUsageMap returns a copy of the physical usage counts per effect

Returns:

  • Map of effect name to physical usage count

func (*BudgetContext) PhysicalUsed

func (bc *BudgetContext) PhysicalUsed(effect string) int

PhysicalUsed returns the physical usage count for an effect

Physical counts track actual builtin invocations, independent of semantic budget charging. Always incremented, even with --no-budgets.

Parameters:

  • effect: The effect name

Returns:

  • The current physical usage count (0 if never used)

func (*BudgetContext) Remaining

func (bc *BudgetContext) Remaining(effect string) int

Remaining returns how many uses are left for an effect

Parameters:

  • effect: The effect name

Returns:

  • The remaining uses, or -1 if unlimited

func (*BudgetContext) Reset

func (bc *BudgetContext) Reset()

Reset clears all usage counters

Used when entering a new function scope with per-invocation budget semantics.

func (*BudgetContext) UsageMap

func (bc *BudgetContext) UsageMap() map[string]int

UsageMap returns a copy of the semantic usage counts per effect

Returns:

  • Map of effect name to semantic usage count

func (*BudgetContext) Used

func (bc *BudgetContext) Used(effect string) int

Used returns how many times an effect has been used (semantic count)

Parameters:

  • effect: The effect name

Returns:

  • The current semantic usage count (0 if never used)

type BudgetExhaustedError

type BudgetExhaustedError struct {
	Effect   string // The effect name (e.g., "IO", "FS")
	Limit    int    // The budget limit (semantic)
	Used     int    // How many times the effect was used (semantic)
	Physical int    // Physical usage count (actual builtin calls)
	Position string // Source position where budget was exhausted (optional)
}

BudgetExhaustedError represents a budget exhaustion error

This error is returned when an effect operation exceeds its allocated budget. Budgets limit the number of times an effect can be invoked within a scope.

Example error output:

effect 'IO' budget exhausted: semantic limit=5, used=5 (physical: 19)
Hint: Increase the budget with @limit=N or refactor to use fewer IO operations

func NewBudgetExhaustedError

func NewBudgetExhaustedError(effect string, limit, used int, position string, physical int) *BudgetExhaustedError

NewBudgetExhaustedError creates a new budget exhausted error

Parameters:

  • effect: The name of the effect
  • limit: The budget limit (semantic)
  • used: How many times the effect was used (semantic)
  • position: Source position (optional, can be empty)
  • physical: Physical usage count (actual builtin calls)

Returns:

  • A new BudgetExhaustedError

Example:

if budget.Remaining("IO") <= 0 {
    return NewBudgetExhaustedError("IO", 5, 5, "file.ail:10:5", 19)
}

func (*BudgetExhaustedError) Error

func (e *BudgetExhaustedError) Error() string

Error implements the error interface

Returns a formatted error message with budget details including physical count

type BudgetReport

type BudgetReport struct {
	// FunctionUsage maps function name -> effect name -> semantic usage count
	FunctionUsage map[string]map[string]int
	// FunctionPhysicalUsage maps function name -> effect name -> physical usage count
	FunctionPhysicalUsage map[string]map[string]int
	// FunctionLimits maps function name -> effect name -> limit (nil = unlimited)
	FunctionLimits map[string]map[string]*int
	// TotalUsage aggregates all semantic effect usage
	TotalUsage map[string]int
	// TotalPhysicalUsage aggregates all physical effect usage
	TotalPhysicalUsage map[string]int
	// CurrentFunction tracks which function we're recording for
	CurrentFunction string
}

BudgetReport tracks per-function, per-effect budget usage for reporting

M-DX25: Tracks both semantic (declared budget) and physical (actual calls) counts.

Example output:

Budget Report:
  main:               IO semantic 3/5 physical 19
  getDefaultProject:  FS semantic 5/5 physical 12

func NewBudgetReport

func NewBudgetReport() *BudgetReport

NewBudgetReport creates a new budget report

func (*BudgetReport) EnterFunction

func (br *BudgetReport) EnterFunction(name string, limits map[string]int)

EnterFunction marks entry into a function for attribution

func (*BudgetReport) ExitFunction

func (br *BudgetReport) ExitFunction(name string, budget *BudgetContext)

ExitFunction marks exit from a function, recording final usage from budget context

func (*BudgetReport) HasUsage

func (br *BudgetReport) HasUsage() bool

HasUsage returns true if any usage was recorded

func (*BudgetReport) RecordUsage

func (br *BudgetReport) RecordUsage(effect string, count int)

RecordUsage records physical effect usage for the current function This is called for each actual builtin invocation (physical count)

type BudgetReportJSON

type BudgetReportJSON struct {
	Functions     map[string]FunctionBudgetJSON `json:"functions"`
	Total         map[string]int                `json:"total"`
	TotalPhysical map[string]int                `json:"total_physical,omitempty"`
}

BudgetReportJSON is the JSON-serializable format for budget reports

type BudgetUnderrunError

type BudgetUnderrunError struct {
	Effect   string // The effect name (e.g., "Net", "IO")
	Min      int    // The minimum required usage
	Actual   int    // The actual physical usage count
	Position string // Source position where check occurred (optional)
}

BudgetUnderrunError represents a minimum usage requirement not met

M-DX25 M4: This error is returned when a function with @min=N budget constraint returns without having exercised the effect at least N times.

Example error output:

effect 'Net' budget underrun: min=1, actual=0
Hint: Ensure the function actually performs at least 1 Net operation

func NewBudgetUnderrunError

func NewBudgetUnderrunError(effect string, min, actual int, position string) *BudgetUnderrunError

NewBudgetUnderrunError creates a new budget underrun error

Parameters:

  • effect: The name of the effect
  • min: The minimum required usage
  • actual: The actual physical usage count
  • position: Source position (optional, can be empty)

Returns:

  • A new BudgetUnderrunError

func (*BudgetUnderrunError) Error

func (e *BudgetUnderrunError) Error() string

Error implements the error interface

Returns a formatted error message with underrun details

type CacheOption

type CacheOption func(*SQLiteSharedCache)

CacheOption configures a SQLiteSharedCache after creation.

func WithEmbedder

func WithEmbedder(e Embedder) CacheOption

WithEmbedder sets an embedder for auto-embedding on PutFrame. When set, PutFrame will automatically compute and store embeddings for frames that have content but no embedding.

type Capability

type Capability struct {
	Name string // Effect name: "IO", "FS", "Net", etc.

	// Meta holds optional metadata for future use
	// Examples:
	//   - "budget": rate limits or quotas
	//   - "trace": tracing context
	//   - "sandbox": filesystem root restriction
	Meta map[string]any
}

Capability represents a granted runtime capability

Capabilities are tokens that grant permission to execute effects. Each capability has a name (e.g., "IO", "FS", "Net") and optional metadata for future extensions (budgets, tracing, sandboxing rules).

Example:

ioCap := NewCapability("IO")
fsCap := NewCapability("FS")
fsCap.Meta["sandbox"] = "/tmp"

func NewCapability

func NewCapability(name string) Capability

NewCapability creates a basic capability with the given name

The capability is created with an empty metadata map that can be populated for advanced use cases.

Parameters:

  • name: The effect name (e.g., "IO", "FS")

Returns:

  • A new Capability with empty metadata

Example:

cap := NewCapability("IO")
cap.Meta["max_writes"] = 100  // Optional: add budget limit

type CapabilityError

type CapabilityError struct {
	Effect string // The required effect name (e.g., "IO", "FS")
}

CapabilityError represents a missing capability error

This error is returned when an effect operation requires a capability that has not been granted. The error message includes the effect name and helpful hints for the user.

Example error output:

effect 'IO' requires capability, but none provided
Hint: Run with --caps IO

func NewCapabilityError

func NewCapabilityError(effect string) *CapabilityError

NewCapabilityError creates a new capability error

Parameters:

  • effect: The name of the required effect

Returns:

  • A new CapabilityError

Example:

if !ctx.HasCap("FS") {
    return NewCapabilityError("FS")
}

func (*CapabilityError) Error

func (e *CapabilityError) Error() string

Error implements the error interface

Returns a formatted error message with the missing capability name

type ClockContext

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

ClockContext provides monotonic time for Clock effect

The clock context maintains a monotonic time anchor to prevent time travel bugs caused by NTP adjustments, DST changes, or manual clock changes.

For production (AILANG_SEED unset):

  • now() returns: epoch + time.Since(startTime)
  • Guarantees monotonic time (never goes backwards)

For testing (AILANG_SEED set):

  • now() returns: virtual (starts at 0)
  • sleep() advances virtual (no real delay)
  • Fully deterministic and reproducible

func NewClockContext

func NewClockContext() *ClockContext

NewClockContext creates a new clock context with monotonic time anchor

The clock context captures the current time at creation and uses it as a monotonic reference point for all future time operations.

Returns:

  • A new ClockContext with startTime and epoch initialized

type ContractCheck

type ContractCheck struct {
	Kind     ast.ContractKind // requires, ensures, or invariant
	Passed   bool             // Whether the contract held
	Message  string           // User-provided or auto-generated message
	Location string           // "file.ail:42" (auto-injected by compiler)
	Function string           // Function name where contract was checked
}

ContractCheck represents the result of a single contract check

This captures all information needed to report or analyze contract violations, including the contract kind, whether it passed, the function it was in, and source location.

type ContractContext

type ContractContext struct {
	Mode ContractMode // How to handle violations
	// contains filtered or unexported fields
}

ContractContext collects contract check results during execution

The contract context is the host-side handler for contract verification. It accumulates contract check results during AILANG execution, with collection only available from the host (not from AILANG code).

This implements the "write-only from AILANG" design pattern:

  • AILANG code can trigger contract checks via generated code
  • Only the host can call Collect(), Reset(), and configure Mode
  • Prevents AILANG code from branching on its own contract state

Thread-safety: ContractContext is designed for single-threaded use within one evaluation. Create a new context for each run.

func NewContractContext

func NewContractContext() *ContractContext

NewContractContext creates a new contract context with default Panic mode

The context starts empty in Panic mode. Call SetMode() to change how violations are handled before running AILANG code.

func NewContractContextWithMode

func NewContractContextWithMode(mode ContractMode) *ContractContext

NewContractContextWithMode creates a contract context with specified mode

func (*ContractContext) CheckEnsures

func (c *ContractContext) CheckEnsures(cond bool, msg, location string) error

CheckEnsures records a postcondition check result

This is called by generated code before function return. Behavior follows same pattern as CheckRequires.

func (*ContractContext) CheckInvariant

func (c *ContractContext) CheckInvariant(cond bool, msg, location string) error

CheckInvariant records an invariant check result

This is called by generated code at module/type boundaries. Behavior follows same pattern as CheckRequires.

func (*ContractContext) CheckRequires

func (c *ContractContext) CheckRequires(cond bool, msg, location string) error

CheckRequires records a precondition check result

This is called by generated code at function entry. In Panic mode, violations cause an immediate panic. In Report mode, violations are collected for later analysis. In Off mode, this is a no-op.

Parameters:

  • cond: The boolean result of evaluating the precondition
  • msg: User-provided message or auto-generated predicate string
  • location: Source location "file.ail:42" (injected by compiler)

Returns error in Panic mode if check fails, nil otherwise.

func (*ContractContext) Collect

func (c *ContractContext) Collect() ContractOutput

Collect returns all accumulated contract check results (HOST-ONLY)

This method is NOT exposed to AILANG code. Only the host can collect contract output after AILANG execution completes.

func (*ContractContext) CurrentFunction

func (c *ContractContext) CurrentFunction() string

CurrentFunction returns the current function name. Used by trace recording to annotate contract check events.

func (*ContractContext) HasViolations

func (c *ContractContext) HasViolations() bool

HasViolations returns true if any contract checks failed

func (*ContractContext) Reset

func (c *ContractContext) Reset()

Reset clears the contract context for the next run

Call this between runs to start fresh. Mode is preserved.

func (*ContractContext) SetFunction

func (c *ContractContext) SetFunction(name string)

SetFunction sets the current function for context in error messages

Called by generated code when entering a function with contracts.

func (*ContractContext) SetMode

func (c *ContractContext) SetMode(mode ContractMode)

SetMode changes how contract violations are handled

Changing mode during execution is allowed but may lead to inconsistent behavior - prefer setting mode before evaluation.

func (*ContractContext) Violations

func (c *ContractContext) Violations() []ContractCheck

Violations returns only the failed contract checks

func (*ContractContext) ViolationsByKind

func (c *ContractContext) ViolationsByKind(kind ast.ContractKind) []ContractCheck

ViolationsByKind returns failed checks filtered by contract kind

type ContractMode

type ContractMode int

ContractMode controls how contract violations are handled

Three modes support different use cases:

  • Panic: Production safety - fail fast on any violation
  • Report: Testing/analysis - collect all violations for inspection
  • Off: Disabled - no runtime overhead from contract checks
const (
	ContractModePanic  ContractMode = iota // Panic on first violation (default)
	ContractModeReport                     // Collect all violations, continue execution
	ContractModeOff                        // Skip all contract checks (no overhead)
)

func (ContractMode) String

func (m ContractMode) String() string

String returns the string representation of ContractMode

type ContractOutput

type ContractOutput struct {
	Checks     []ContractCheck
	Mode       ContractMode
	TotalCount int
	PassCount  int
	FailCount  int
}

ContractOutput is the structured output from contract collection

type DebugContext

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

DebugContext collects debug data during execution

The debug context is the host-side handler for the Debug effect. It accumulates logs and assertions during AILANG execution, with collection only available from the host (not from AILANG code).

This implements the "write-only from AILANG" design:

  • AILANG code can call Debug.log and Debug.check
  • Only the host can call Collect() and Reset()
  • Prevents AILANG code from branching on its own debug state

Note: We use "check" instead of "assert" because "assert" is a reserved keyword

Thread-safety: DebugContext is designed for single-threaded use within one evaluation. Create a new context for each step/tick.

func NewDebugContext

func NewDebugContext() *DebugContext

NewDebugContext creates a new debug context

The context starts empty with timestamp 0. Call SetTimestamp() before running AILANG code if you want meaningful timestamps.

func (*DebugContext) Check

func (d *DebugContext) Check(cond bool, msg, location string)

Check records an assertion result (formerly Assert)

This is called by the Debug.check effect operation. Location is auto-injected by the compiler. Assertions are collected, not thrown - the program continues. Note: Named "Check" because "assert" is a reserved keyword in AILANG.

func (*DebugContext) Collect

func (d *DebugContext) Collect() DebugOutput

Collect returns all accumulated debug data (HOST-ONLY)

This method is NOT exposed to AILANG code. Only the host can collect debug output after AILANG execution completes.

func (*DebugContext) FailedAssertions

func (d *DebugContext) FailedAssertions() []AssertionResult

FailedAssertions returns only the failed assertions

func (*DebugContext) HasFailedAssertions

func (d *DebugContext) HasFailedAssertions() bool

HasFailedAssertions returns true if any assertions failed

func (*DebugContext) Log

func (d *DebugContext) Log(msg, location string)

Log adds a log entry to the debug context

This is called by the Debug.log effect operation. Location is auto-injected by the compiler.

func (*DebugContext) Reset

func (d *DebugContext) Reset()

Reset clears the debug context for the next step/tick

Call this between ticks/steps to start fresh.

func (*DebugContext) SetTimestamp

func (d *DebugContext) SetTimestamp(t int64)

SetTimestamp sets the logical timestamp for subsequent log entries

The meaning of timestamp is host-defined:

  • Game engine: frame/tick index
  • Test runner: test case index
  • CLI: 0 or monotonic counter

AILANG code MUST NOT rely on timestamp meaning.

type DebugOutput

type DebugOutput struct {
	Logs       []LogEntry
	Assertions []AssertionResult
}

DebugOutput is the structured output from debug collection

type DeterminismMode

type DeterminismMode int

DeterminismMode controls result ordering guarantees for similarity search.

const (
	// DeterminismStrict guarantees identical results for identical queries on fixed index state.
	// Uses deterministic tie-breaking: (score DESC, key ASC).
	// Slightly slower due to sorting overhead.
	DeterminismStrict DeterminismMode = iota

	// DeterminismBestEffort allows non-deterministic ordering when scores are equal.
	// Faster for large result sets but may produce different orderings between runs.
	DeterminismBestEffort
)

func (DeterminismMode) String

func (m DeterminismMode) String() string

String returns the string representation of the determinism mode.

type EffContext

type EffContext struct {
	Caps           map[string]Capability // Effect name → Capability grant
	Env            EffEnv                // Environment configuration
	Clock          *ClockContext         // Clock effect state (monotonic time)
	Net            *NetContext           // Net effect configuration (security settings)
	Debug          *DebugContext         // Debug effect state (logs, assertions)
	AI             *AIContext            // AI effect state (handler for AI.call)
	SharedMem      *SharedMemContext     // SharedMem effect state (v0.5.11 M-DX15)
	SharedIndex    *SharedIndexContext   // SharedIndex effect state (v0.5.11 M-DX16)
	Contracts      *ContractContext      // Contract effect state (M-VERIFY)
	Stream         *StreamContext        // Stream effect state (M-STREAM-BIDI)
	Process        *ProcessContext       // Process effect state (M-PROCESS)
	Budget         *BudgetContext        // Budget tracking for effect limits (v0.7.0 M-CAPABILITY-BUDGETS)
	BudgetReport   *BudgetReport         // Budget usage report (--budget-report flag, M-DX25)
	DisableBudgets bool                  // Bypass budget enforcement (--no-budgets flag)
	EnvSnapshot    map[string]string     // Env effect: immutable snapshot of environment variables
	EnvAllowlist   []string              // Env effect: allowed variable names (nil = allow all)
	Args           []string              // CLI arguments passed to the program (excluding program name)
	Trace          *trace.Collector      // Semantic trace collector (--emit-trace flag, M-TRACE-EXPORT)
	IOWriter       io.Writer             // Override for IO effect output (nil = os.Stdout)
	IOReader       io.Reader             // Override for IO effect input (nil = os.Stdin)

	// M-DX25: Scoped budget charging
	DeclaredBudgets map[string]int // Callee's declared @limit values (for charging caller on return)
	CallerContext   *EffContext    // Reference to caller's context (for charging on scope exit)

	// M-STREAM-BIDI: Function caller for stream event handlers
	// Set by the evaluator; allows effects to call AILANG functions without import cycles.
	FnCaller func(fn eval.Value, arg eval.Value) (eval.Value, error)

	// M-ITERATIVE-LIST: Multi-arg function caller for iterative builtins (e.g., foldl callback).
	// Set by the evaluator alongside FnCaller.
	FnCallerN func(fn eval.Value, args []eval.Value) (eval.Value, error)

	// OTEL effect tracing: callback injection to avoid effects→telemetry import cycle.
	// GoCtx carries the Go context with OTEL trace propagation.
	// SpanWrapper wraps each effect operation with an OTEL span (nil = no tracing).
	GoCtx       context.Context
	SpanWrapper SpanWrapperFunc
	// contains filtered or unexported fields
}

EffContext holds runtime capability grants and environment configuration

The effect context is the central runtime structure for effect execution. It tracks which capabilities have been granted and provides environment configuration for deterministic effect execution.

Thread-safety: EffContext is typically created once per evaluation and should not be mutated concurrently.

func NewEffContext

func NewEffContext(args []string) *EffContext

NewEffContext creates a new effect context with command-line arguments

The context is initialized with no capabilities granted (deny-by-default) and environment loaded from OS environment variables.

Parameters:

  • args: Command-line arguments passed to the program (excluding program name)

Returns:

  • A new EffContext ready to use

Example:

ctx := NewEffContext([]string{"arg1", "arg2"})
ctx.Grant(NewCapability("IO"))
ctx.Grant(NewCapability("FS"))
ctx.Grant(NewCapability("Clock"))
ctx.Grant(NewCapability("Net"))
ctx.Grant(NewCapability("Env"))

func (*EffContext) CheckEnsures

func (ctx *EffContext) CheckEnsures(cond bool, msg, location string) error

CheckEnsures delegates postcondition checking to ContractContext

M-VERIFY-CONTRACTS: Called by the evaluator when checking function postconditions. If no ContractContext is configured, this is a no-op.

Parameters:

  • cond: The boolean result of evaluating the postcondition
  • msg: User-provided message or auto-generated predicate string
  • location: Source location "file.ail:42"

Returns:

  • nil if check passes or contracts disabled
  • Error in Panic mode if check fails

func (*EffContext) CheckMinimums

func (ctx *EffContext) CheckMinimums(position string) error

CheckMinimums verifies all minimum requirements are met

M-DX25 M4: Implements eval.MinimumChecker interface. Called by evaluator on scope exit to ensure effects were exercised.

Parameters:

  • position: Source position for error reporting

Returns:

  • nil if all minimums satisfied
  • BudgetUnderrunError if any minimum is not met

func (*EffContext) CheckRequires

func (ctx *EffContext) CheckRequires(cond bool, msg, location string) error

CheckRequires delegates precondition checking to ContractContext

M-VERIFY-CONTRACTS: Called by the evaluator when checking function preconditions. If no ContractContext is configured, this is a no-op.

Parameters:

  • cond: The boolean result of evaluating the precondition
  • msg: User-provided message or auto-generated predicate string
  • location: Source location "file.ail:42"

Returns:

  • nil if check passes or contracts disabled
  • Error in Panic mode if check fails

func (*EffContext) Clone

func (ctx *EffContext) Clone() interface{}

Clone creates a shallow copy of the EffContext for per-request isolation. Config fields (Caps, Env, Clock, Net, etc.) are shared by reference. FnCaller/FnCallerN will be re-wired by the forked evaluator.

func (*EffContext) GetIOReader

func (ctx *EffContext) GetIOReader() *bufio.Reader

GetIOReader returns a persistent buffered reader for IO effect input. Returns a bufio.Reader wrapping IOReader (or os.Stdin if not overridden). The reader is lazily initialized and reused across calls, so buffered data is preserved between readLine() invocations.

func (*EffContext) GetIOWriter

func (ctx *EffContext) GetIOWriter() io.Writer

HasTraceCollector returns true if semantic trace collection is active. GetIOWriter returns the writer for IO effect output. Returns os.Stdout unless overridden (e.g., when --emit-trace redirects program output to stderr).

func (*EffContext) Grant

func (ctx *EffContext) Grant(cap Capability)

Grant adds a capability to the context

Once granted, the capability allows execution of the corresponding effect operations. Granting is idempotent - granting the same capability twice has no additional effect.

Parameters:

  • cap: The capability to grant

Example:

ctx.Grant(NewCapability("IO"))

func (*EffContext) HasCap

func (ctx *EffContext) HasCap(name string) bool

HasCap checks if a capability is granted

Parameters:

  • name: The capability name to check (e.g., "IO", "FS")

Returns:

  • true if the capability is granted, false otherwise

Example:

if ctx.HasCap("IO") {
    // IO operations allowed
}

func (*EffContext) HasTraceCollector

func (ctx *EffContext) HasTraceCollector() bool

func (*EffContext) IsContractCheckingEnabled

func (ctx *EffContext) IsContractCheckingEnabled() bool

IsContractCheckingEnabled returns true if contract checking is enabled

M-VERIFY-CONTRACTS: Contracts are enabled when a ContractContext exists and its Mode is not ContractModeOff.

func (*EffContext) PopScopeAndChargeCaller

func (ctx *EffContext) PopScopeAndChargeCaller()

PopScopeAndChargeCaller charges the caller context with declared semantic budgets

M-DX25: When a scoped function returns, the caller is charged the callee's declared budget (not the actual physical usage). This implements the "charge declared amount" semantic charging model.

This method should be called when restoring the old effect context after evaluating a function with declared budgets.

If this context has no CallerContext (pass-through mode), this is a no-op.

func (*EffContext) RecordEffect

func (ctx *EffContext) RecordEffect(effectName, opName string, args []string, result string)

RecordEffect delegates to trace collector if present.

func (*EffContext) RecordFunctionEnter

func (ctx *EffContext) RecordFunctionEnter(name string, args []string)

RecordFunctionEnter delegates to trace collector if present.

func (*EffContext) RecordFunctionExit

func (ctx *EffContext) RecordFunctionExit(name string, result string)

RecordFunctionExit delegates to trace collector if present.

func (*EffContext) RequireCap

func (ctx *EffContext) RequireCap(name string) error

RequireCap checks for a capability and returns an error if missing

This is the primary capability check used by effect operations. It provides a consistent error type (CapabilityError) when a capability is not granted.

Parameters:

  • name: The required capability name

Returns:

  • nil if the capability is granted
  • CapabilityError if the capability is missing

Example:

if err := ctx.RequireCap("FS"); err != nil {
    return nil, err
}
// FS operations allowed here

func (*EffContext) RequireCapWithBudget

func (ctx *EffContext) RequireCapWithBudget(name, position string) error

RequireCapWithBudget checks for capability and budget, consuming one budget unit

This combines capability checking with budget enforcement. Use this instead of RequireCap when budgets are configured.

Parameters:

  • name: The required capability name
  • position: Source position for error reporting (optional)

Returns:

  • nil if the capability is granted and budget is available
  • CapabilityError if the capability is missing
  • BudgetExhaustedError if the budget is exhausted

Example:

if err := ctx.RequireCapWithBudget("IO", "file.ail:10:5"); err != nil {
    return nil, err
}
// IO operation allowed here

func (*EffContext) SetBudget

func (ctx *EffContext) SetBudget(budget *BudgetContext)

SetBudget configures the budget context

Parameters:

  • budget: The budget context to use, or nil to disable budgets

func (*EffContext) SetFnCaller

func (ctx *EffContext) SetFnCaller(fn func(eval.Value, eval.Value) (eval.Value, error))

SetFnCaller sets the single-arg function caller callback. M-ITERATIVE-LIST: Used by embed.Engine to wire callbacks without importing effects.

func (*EffContext) SetFnCallerN

func (ctx *EffContext) SetFnCallerN(fn func(eval.Value, []eval.Value) (eval.Value, error))

SetFnCallerN sets the multi-arg function caller callback. M-ITERATIVE-LIST: Used by embed.Engine to wire callbacks without importing effects.

func (*EffContext) SetMinBudgets

func (ctx *EffContext) SetMinBudgets(minLimits map[string]int)

SetMinBudgets sets minimum usage requirements on the context's budget

M-DX25 M4: Implements eval.MinBudgetEnforcer interface. Called by evaluator after WithBudgetLimits to set minimum constraints.

Parameters:

  • minLimits: Map of effect name to minimum required usage

func (*EffContext) WithBudget

func (ctx *EffContext) WithBudget(budget *BudgetContext) *EffContext

WithBudget creates a copy of the context with the specified budget

This is useful for function invocations that need fresh budgets.

Parameters:

  • budget: The budget context to use

Returns:

  • A new EffContext with the specified budget

func (*EffContext) WithBudgetLimits

func (ctx *EffContext) WithBudgetLimits(limits map[string]int) interface{}

WithBudgetLimits creates a new context with budget limits from a map[string]int This implements the eval.BudgetEnforcer interface to avoid import cycles.

M-DX25: This creates a scoped budget context that tracks: - DeclaredBudgets: the callee's declared limits (for charging caller on return) - CallerContext: reference to caller for semantic charging on scope exit

Parameters:

  • limits: Map of effect name to budget limit (e.g., {"IO": 5, "Rand": 10})

Returns:

  • A new EffContext with the specified budget limits (as interface{})

type EffEnv

type EffEnv struct {
	Seed    int64  // AILANG_SEED for reproducible randomness
	TZ      string // TZ for deterministic time operations
	Locale  string // LANG for deterministic string operations
	Sandbox string // Root directory for FS operations (empty = no sandbox)
}

EffEnv provides deterministic effect execution configuration

The environment holds configuration from OS environment variables that control effect behavior:

  • AILANG_SEED: Seed for reproducible randomness
  • TZ: Timezone for deterministic time operations
  • LANG: Locale for deterministic string operations
  • AILANG_FS_SANDBOX: Root directory for sandboxed FS operations

type EffOp

type EffOp func(ctx *EffContext, args []eval.Value) (eval.Value, error)

EffOp is a function that implements an effect operation

Effect operations are native Go implementations of effectful functions. They receive a capability context and arguments, perform side effects, and return a result value.

Parameters:

  • ctx: The effect context (for capability checking and environment)
  • args: The arguments passed from AILANG code

Returns:

  • The result value
  • An error if the operation fails

Example implementation:

func ioPrint(ctx *EffContext, args []eval.Value) (eval.Value, error) {
    if len(args) != 1 {
        return nil, fmt.Errorf("print: expected 1 argument")
    }
    str := args[0].(*eval.StringValue)
    fmt.Print(str.Value)
    return &eval.UnitValue{}, nil
}

type EffectBudgetJSON

type EffectBudgetJSON struct {
	Semantic int  `json:"semantic"`        // Semantic usage (charged to caller)
	Physical int  `json:"physical"`        // Physical usage (actual calls)
	Limit    *int `json:"limit,omitempty"` // Semantic limit (nil = unlimited)
}

EffectBudgetJSON represents budget usage for a single effect

type Embedder

type Embedder interface {
	Embed(text string) ([]float32, error)
	EmbedBatch(texts []string) ([][]float32, error)
	Dimension() int
	ModelName() string
}

Embedder is the interface for generating text embeddings. Matches messaging.Embedder but defined here to avoid circular imports.

type EventSource

type EventSource interface {
	// Name returns a human-readable identifier for this source (e.g. "stdin", "ws:echo.example.com").
	Name() string

	// Priority returns the dispatch priority (higher = checked first in multiplexer).
	// When multiple sources have events ready, the highest priority wins.
	Priority() int

	// Events returns the read-only channel delivering events from this source.
	Events() <-chan streamEvent

	// Close signals the source to stop producing events and clean up resources.
	// Must be safe to call multiple times.
	Close()
}

EventSource represents a readable event source for the selectEvents multiplexer. Any type that can produce streamEvent values through a Go channel implements this.

M-ASYNC-IO: This is the core abstraction enabling multi-source event multiplexing. Sources are consumed by selectEventsLoop which dispatches events to a handler.

func NewConnSource

func NewConnSource(conn *StreamConnection, name string, priority int) EventSource

NewConnSource creates an EventSource from an existing StreamConnection. The source reads from the connection's eventBuffer channel.

func NewProcessSource

func NewProcessSource(parentCtx context.Context, cmdPath string, args []string, name string, priority int, chunkSize int) (EventSource, error)

NewProcessSource creates an EventSource that reads subprocess stdout in fixed-size chunks.

The subprocess is started immediately. A background goroutine reads stdout in chunkSize-byte increments and sends SourceBytes events to the channel.

Parameters:

  • parentCtx: parent context (cancelled when selectEvents loop exits)
  • cmdPath: resolved absolute path to the binary
  • args: command arguments (no shell expansion)
  • name: source name for SourceBytes(name, data) matching
  • priority: dispatch priority in selectEvents (higher = checked first)
  • chunkSize: bytes per SourceBytes event (determines streaming latency)

The subprocess is killed (SIGTERM → 5s grace → SIGKILL) when Close() is called or when the parent context is cancelled.

func NewStdinSource

func NewStdinSource(reader io.Reader, name string, priority int) EventSource

NewStdinSource creates an EventSource that reads lines from the given reader. Typically called with os.Stdin. The goroutine reads until the reader is exhausted, the source is closed, or an error occurs.

type FunctionBudgetJSON

type FunctionBudgetJSON struct {
	Effects map[string]EffectBudgetJSON `json:"effects"`
}

FunctionBudgetJSON represents budget usage for a single function

type InMemorySharedCache

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

InMemorySharedCache is a thread-safe in-memory implementation of SharedCache.

Features:

  • Thread-safe: all operations protected by RWMutex
  • Copy-on-read: Get returns a copy to prevent buffer sharing bugs
  • Copy-on-write: Put copies input to prevent caller mutations
  • Atomic CAS: compare-and-swap is a single locked operation

Memory management:

  • No automatic eviction (suitable for bounded workloads)
  • Use Delete to manually remove entries
  • For production, consider Redis-backed implementation

func NewInMemorySharedCache

func NewInMemorySharedCache() *InMemorySharedCache

NewInMemorySharedCache creates a new empty in-memory cache.

Returns:

  • A new InMemorySharedCache ready for use

func (*InMemorySharedCache) CAS

func (c *InMemorySharedCache) CAS(key string, oldValue, newValue []byte) bool

CAS performs an atomic compare-and-swap operation.

The operation atomically:

  1. Reads the current value
  2. Compares it to oldValue (byte-for-byte)
  3. If equal, writes newValue and returns true
  4. If not equal, returns false (no write)

Special case: if oldValue is nil, CAS creates the key only if it doesn't exist. This enables "create-if-absent" semantics for new entries.

Parameters:

  • key: The key to update
  • oldValue: Expected current value (nil = key must not exist)
  • newValue: New value to write if oldValue matches

Returns:

  • true if the swap succeeded
  • false if the current value didn't match oldValue

func (*InMemorySharedCache) Delete

func (c *InMemorySharedCache) Delete(key string)

Delete removes a value by key.

No-op if the key doesn't exist.

func (*InMemorySharedCache) Get

func (c *InMemorySharedCache) Get(key string) ([]byte, bool)

Get retrieves a value by key.

Returns (nil, false) if the key doesn't exist. Returns (copy of value, true) if the key exists.

The returned bytes are a defensive copy - callers can safely modify them without affecting the cached value.

func (*InMemorySharedCache) Keys

func (c *InMemorySharedCache) Keys() []string

Keys returns all keys in the cache.

The returned slice is a snapshot - adding/removing keys after this call won't affect the returned slice, and modifying the slice won't affect the cache.

func (*InMemorySharedCache) Len

func (c *InMemorySharedCache) Len() int

Len returns the number of entries in the cache.

func (*InMemorySharedCache) Put

func (c *InMemorySharedCache) Put(key string, value []byte)

Put stores a value at the given key.

Overwrites any existing value. The value is copied before storage - callers can safely modify the input after Put returns.

type InMemorySharedIndex

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

InMemorySharedIndex is the default in-memory implementation of SharedIndex.

Thread-safe for concurrent read/write access. Uses namespace-scoped maps with RWMutex for fine-grained locking.

Implementation details:

  • Entries stored in map[namespace]map[key]*IndexEntry
  • FindSimilarSimHash scans all entries in namespace (O(N) with maxScan limit)
  • Strict mode sorts by (score DESC, key ASC) for deterministic results

func NewInMemorySharedIndex

func NewInMemorySharedIndex() *InMemorySharedIndex

NewInMemorySharedIndex creates a new in-memory index.

func (*InMemorySharedIndex) Delete

func (idx *InMemorySharedIndex) Delete(namespace, key string)

Delete removes an entry from the index.

func (*InMemorySharedIndex) EntryCount

func (idx *InMemorySharedIndex) EntryCount(namespace string) int

EntryCount returns the number of entries in a namespace.

func (*InMemorySharedIndex) FindSimilarByEmbedding

func (idx *InMemorySharedIndex) FindSimilarByEmbedding(
	namespace string,
	queryEmbedding []float64,
	topK, maxScan int,
	mode DeterminismMode,
) []SearchResult

FindSimilarByEmbedding finds entries similar to the query embedding using cosine similarity.

Only entries with non-empty embeddings are considered. Uses cosine similarity normalized to [0, 1] for scoring.

In Strict mode, results are sorted deterministically by (score DESC, key ASC). In BestEffort mode, results may vary in ordering when scores are equal.

func (*InMemorySharedIndex) FindSimilarSimHash

func (idx *InMemorySharedIndex) FindSimilarSimHash(
	namespace string,
	querySimHash int64,
	topK, maxScan int,
	mode DeterminismMode,
) []SearchResult

FindSimilarSimHash finds entries similar to the query simhash.

Uses hamming distance for similarity scoring:

score = 1.0 - (hamming_distance / 64.0)

In Strict mode, results are sorted deterministically by (score DESC, key ASC). In BestEffort mode, results may vary in ordering when scores are equal.

func (*InMemorySharedIndex) Namespaces

func (idx *InMemorySharedIndex) Namespaces() []string

Namespaces returns all namespace names in the index.

func (*InMemorySharedIndex) Upsert

func (idx *InMemorySharedIndex) Upsert(namespace, key string, simhash, version, timestamp int64)

Upsert adds or updates an entry in the index.

func (*InMemorySharedIndex) UpsertWithEmbedding

func (idx *InMemorySharedIndex) UpsertWithEmbedding(namespace, key string, simhash int64, embedding []float64, version, timestamp int64)

UpsertWithEmbedding adds or updates an entry with a neural embedding.

type IndexEntry

type IndexEntry struct {
	Namespace string    // Namespace for index partitioning
	Key       string    // Unique key within namespace
	SimHash   int64     // 64-bit SimHash for similarity matching
	Embedding []float64 // Optional: neural embedding vector (e.g., 768-dim from EmbeddingGemma)
	Version   int64     // Version for optimistic locking
	Timestamp int64     // Unix epoch timestamp in milliseconds
	Score     float64   // Similarity score (set by FindSimilar* methods)
}

IndexEntry represents a single entry in the SharedIndex. Entries are keyed by (namespace, key) and contain similarity search metadata.

type LogEntry

type LogEntry struct {
	Message   string
	Location  string // "file.ail:42" (auto-injected by compiler)
	Timestamp int64  // Logical time (host-defined: tick, test index, etc.)
}

LogEntry represents a single log message

type NetContext

type NetContext struct {
	Timeout        time.Duration // HTTP request timeout
	MaxBytes       int64         // Max response body size
	MaxRedirects   int           // Max HTTP redirects
	AllowHTTP      bool          // Allow http:// (default: false, https only)
	AllowLocalhost bool          // Allow localhost/127.x/::1 (default: false)
	AllowMetadata  bool          // Allow cloud metadata server at 169.254.169.254 (default: false)
	AllowedDomains []string      // Domain allowlist (empty = all allowed)
	UserAgent      string        // User-Agent header
}

NetContext provides configuration for Net effect security

The net context holds security settings for HTTP requests:

  • Timeout enforcement (default: 30s)
  • Body size limits (default: 5MB)
  • Redirect limits (default: 5)
  • Protocol allowlist (https always, http opt-in)
  • Domain allowlist (optional)
  • Localhost override (default: blocked)

func NewNetContext

func NewNetContext() *NetContext

NewNetContext creates a new net context with secure defaults

Default configuration:

  • Timeout: 30 seconds
  • MaxBytes: 5 MB
  • MaxRedirects: 5
  • AllowHTTP: false (https only)
  • AllowLocalhost: false (localhost blocked)
  • AllowedDomains: empty (all domains allowed)
  • UserAgent: "ailang/0.3.0"

Returns:

  • A new NetContext with secure defaults

type ProcessContext

type ProcessContext struct {
	Timeout      time.Duration     // Maximum execution time
	MaxOutput    int64             // Maximum stdout/stderr bytes before kill
	Allowlist    map[string]string // Allowed commands: name → resolved absolute path (nil = all)
	HasAllowlist bool              // True if allowlist was explicitly set
	// contains filtered or unexported fields
}

ProcessContext provides configuration for Process effect security

The process context holds security settings for command execution:

  • Timeout enforcement (default: 30s)
  • Output size limits (default: 10MB)
  • Command allowlist with path-pinned resolution
  • Working directory from sandbox
  • Managed process tracking (M-ASYNC-IO Phase 3)

func NewProcessContext

func NewProcessContext() *ProcessContext

NewProcessContext creates a new process context with secure defaults

func (*ProcessContext) AcquireManagedProcess

func (pc *ProcessContext) AcquireManagedProcess(mp *managedProcess) int

AcquireManagedProcess registers a managed process and returns its ID.

func (*ProcessContext) CloseAllManaged

func (pc *ProcessContext) CloseAllManaged()

CloseAllManaged kills all tracked managed processes. Used for graceful shutdown.

func (*ProcessContext) GetManagedProcess

func (pc *ProcessContext) GetManagedProcess(id int) (*managedProcess, bool)

GetManagedProcess retrieves a managed process by ID.

func (*ProcessContext) ReleaseManagedProcess

func (pc *ProcessContext) ReleaseManagedProcess(id int)

ReleaseManagedProcess removes a managed process from tracking.

func (*ProcessContext) ResolveAllowlist

func (pc *ProcessContext) ResolveAllowlist(allowlistStr string) error

ResolveAllowlist resolves command names to absolute paths via exec.LookPath. This pins paths at startup to prevent TOCTOU attacks.

type SQLiteSharedCache

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

SQLiteSharedCache is a persistent implementation of SharedCache backed by SQLite.

Features:

  • Persistent: data survives process restarts
  • Thread-safe: SQLite WAL mode + Go-level serialization
  • SimHash search: find similar frames by hamming distance
  • FTS5 keyword search: full-text search on content
  • Embedding search: cosine similarity over float32 vectors
  • TTL support: automatic expiration via GarbageCollect
  • Namespace support: partition frames by namespace

The schema uses the same pragmas as internal/messaging/schema.go:

  • WAL mode, NORMAL synchronous, 5s busy timeout, 64MB cache

func NewSQLiteSharedCache

func NewSQLiteSharedCache(dbPath string, opts ...CacheOption) (*SQLiteSharedCache, error)

NewSQLiteSharedCache opens or creates a SQLite-backed SharedCache at the given path.

The database is configured with WAL mode and the same pragmas as the messaging system. If the database doesn't exist, it is created with the brain_frames schema. Optional CacheOption values can configure the cache (e.g., WithEmbedder).

func (*SQLiteSharedCache) BackfillEmbeddings

func (c *SQLiteSharedCache) BackfillEmbeddings(namespace string) (int, int)

BackfillEmbeddings computes embeddings for all frames that have content but no embedding. Returns (processed, errors) counts.

func (*SQLiteSharedCache) CAS

func (c *SQLiteSharedCache) CAS(key string, oldValue, newValue []byte) bool

CAS performs an atomic compare-and-swap operation.

If oldValue is nil, creates the key only if it doesn't exist. Otherwise, updates only if the current value matches oldValue byte-for-byte.

func (*SQLiteSharedCache) Close

func (c *SQLiteSharedCache) Close() error

Close closes the underlying database connection.

func (*SQLiteSharedCache) DB

func (c *SQLiteSharedCache) DB() *sql.DB

DB returns the underlying database connection for advanced operations.

func (*SQLiteSharedCache) Delete

func (c *SQLiteSharedCache) Delete(key string)

Delete removes a value by key. No-op if the key doesn't exist.

func (*SQLiteSharedCache) DeleteNamespace added in v0.14.2

func (c *SQLiteSharedCache) DeleteNamespace(namespace string) (int64, error)

DeleteNamespace removes all frames in the given namespace. Used by the micro-rag indexer to support release-tied corpus reset. Returns the number of frames removed.

func (*SQLiteSharedCache) EmbeddingStats

func (c *SQLiteSharedCache) EmbeddingStats() (total, withEmbedding int, models map[string]int)

EmbeddingStats returns embedding coverage statistics.

func (*SQLiteSharedCache) GarbageCollect

func (c *SQLiteSharedCache) GarbageCollect() (int64, error)

GarbageCollect removes expired frames (where expires_at < now). Returns the number of frames removed.

func (*SQLiteSharedCache) GarbageCollectOlderThan

func (c *SQLiteSharedCache) GarbageCollectOlderThan(namespace string, age time.Duration) (int64, error)

GarbageCollectOlderThan removes frames older than the given duration in a namespace. Returns the number of frames removed.

func (*SQLiteSharedCache) Get

func (c *SQLiteSharedCache) Get(key string) ([]byte, bool)

Get retrieves a value by key.

func (*SQLiteSharedCache) GetEmbedder

func (c *SQLiteSharedCache) GetEmbedder() Embedder

GetEmbedder returns the configured embedder (may be nil).

func (*SQLiteSharedCache) Keys

func (c *SQLiteSharedCache) Keys() []string

Keys returns all keys in the cache.

func (*SQLiteSharedCache) Len

func (c *SQLiteSharedCache) Len() int

Len returns the number of entries in the cache.

func (*SQLiteSharedCache) ListRecent

func (c *SQLiteSharedCache) ListRecent(namespace string, limit int) []BrainFrame

ListRecent returns the most recently updated frames.

func (*SQLiteSharedCache) Put

func (c *SQLiteSharedCache) Put(key string, value []byte)

Put stores a value at the given key, overwriting any existing value.

func (*SQLiteSharedCache) PutFrame

func (c *SQLiteSharedCache) PutFrame(f BrainFrame) error

PutFrame stores a frame with full metadata. If an embedder is configured (via WithEmbedder) and the frame has content but no embedding, the embedding is computed automatically. Embedder errors are silently ignored — the frame is stored with SimHash only.

func (*SQLiteSharedCache) PutVector

func (c *SQLiteSharedCache) PutVector(key, namespace string, embedding []float32, model string, payload []byte) error

PutVector stores a frame with an embedding but no text content. Used for machine-to-machine vector communication.

func (*SQLiteSharedCache) SearchByEmbedding

func (c *SQLiteSharedCache) SearchByEmbedding(queryEmbedding []float32, namespace string, limit int) []BrainSearchResult

SearchByEmbedding performs brute-force cosine similarity scan over all frames with embeddings. Returns results sorted by cosine similarity descending, key ascending (deterministic).

func (*SQLiteSharedCache) SearchBySimHash

func (c *SQLiteSharedCache) SearchBySimHash(namespace string, queryHash int64, limit int) []BrainSearchResult

SearchBySimHash finds frames with similar SimHash values in a given namespace. Returns results sorted by score descending, key ascending (deterministic).

func (*SQLiteSharedCache) SearchByText

func (c *SQLiteSharedCache) SearchByText(query string, namespace string, limit int) []BrainSearchResult

SearchByText performs keyword search across all namespaces (or a specific one). If namespace is empty, searches all namespaces.

func (*SQLiteSharedCache) SetEmbedder

func (c *SQLiteSharedCache) SetEmbedder(e Embedder)

SetEmbedder configures the embedder for auto-embedding on PutFrame.

func (*SQLiteSharedCache) Stats

func (c *SQLiteSharedCache) Stats() BrainStats

Stats returns aggregate statistics about the brain.

type SearchResult

type SearchResult struct {
	Key       string  // The matching key
	Score     float64 // Similarity score: 1.0 - (hamming_distance / 64.0)
	Version   int64   // Version at time of index entry
	Timestamp int64   // Timestamp at time of index entry
}

SearchResult represents a single result from similarity search. Returns key metadata only - caller loads full frame from SharedMem.

type SharedCache

type SharedCache interface {
	// Get retrieves a value by key.
	// Returns (nil, false) if the key doesn't exist.
	// Returns (value, true) if the key exists.
	// The returned bytes are a copy - callers can safely modify them.
	Get(key string) ([]byte, bool)

	// Put stores a value at the given key.
	// Overwrites any existing value.
	// The value is copied - callers can safely modify the input after Put returns.
	Put(key string, value []byte)

	// Delete removes a value by key.
	// No-op if the key doesn't exist.
	Delete(key string)

	// CAS performs a compare-and-swap operation.
	// Returns true if the swap succeeded (oldValue matched current value).
	// Returns false if the current value != oldValue (another writer modified it).
	//
	// Special case: if oldValue is nil, CAS creates the key only if it doesn't exist.
	// This enables "create-if-absent" semantics for new entries.
	CAS(key string, oldValue, newValue []byte) bool

	// Keys returns all keys in the cache.
	// The returned slice is a snapshot - modifications don't affect the cache.
	Keys() []string

	// Len returns the number of entries in the cache.
	Len() int
}

SharedCache is the interface for shared memory cache backends.

All operations are thread-safe. Implementations must ensure:

  • Get/Put/Delete are atomic
  • CAS (compare-and-swap) is atomic
  • Copy-on-read/write to prevent buffer sharing bugs

The cache stores arbitrary bytes keyed by string IDs. For sem_frame storage, use JSON encoding via std/sem.encode_frame.

type SharedIndex

type SharedIndex interface {
	// Upsert adds or updates an entry in the index.
	// If the key exists in the namespace, it is updated.
	// If the key doesn't exist, it is created.
	//
	// Parameters:
	//   - namespace: Index partition (e.g., "beliefs", "goals")
	//   - key: Unique identifier within namespace
	//   - simhash: 64-bit SimHash for similarity matching
	//   - version: Version number for optimistic locking
	//   - timestamp: Unix epoch timestamp in milliseconds
	Upsert(namespace, key string, simhash, version, timestamp int64)

	// Delete removes an entry from the index.
	// No-op if the key doesn't exist in the namespace.
	//
	// Parameters:
	//   - namespace: Index partition
	//   - key: Key to delete
	Delete(namespace, key string)

	// FindSimilarSimHash finds entries similar to the query simhash.
	//
	// Parameters:
	//   - namespace: Index partition to search (required)
	//   - querySimHash: The SimHash to find similar entries for
	//   - topK: Maximum number of results to return
	//   - maxScan: Maximum entries to scan (0 = unlimited, bounds O(N) search)
	//   - mode: DeterminismStrict or DeterminismBestEffort
	//
	// Returns:
	//   - Slice of SearchResult, ordered by score DESC (Strict: then key ASC)
	//   - Empty slice if no entries found
	//
	// Scoring formula: score = 1.0 - (hamming_distance / 64.0)
	// Score of 1.0 = identical SimHash, 0.0 = maximally different
	FindSimilarSimHash(namespace string, querySimHash int64, topK, maxScan int, mode DeterminismMode) []SearchResult

	// EntryCount returns the number of entries in a namespace.
	// Returns 0 if namespace doesn't exist.
	EntryCount(namespace string) int

	// Namespaces returns all namespace names in the index.
	Namespaces() []string

	// UpsertWithEmbedding adds or updates an entry with a neural embedding.
	// The embedding is stored alongside the SimHash for hybrid search.
	//
	// Parameters:
	//   - namespace: Index partition (e.g., "beliefs", "goals")
	//   - key: Unique identifier within namespace
	//   - simhash: 64-bit SimHash for fast similarity matching
	//   - embedding: Neural embedding vector (e.g., 768 floats from EmbeddingGemma)
	//   - version: Version number for optimistic locking
	//   - timestamp: Unix epoch timestamp in milliseconds
	UpsertWithEmbedding(namespace, key string, simhash int64, embedding []float64, version, timestamp int64)

	// FindSimilarByEmbedding finds entries similar to the query embedding using cosine similarity.
	//
	// Parameters:
	//   - namespace: Index partition to search (required)
	//   - queryEmbedding: The embedding vector to find similar entries for
	//   - topK: Maximum number of results to return
	//   - maxScan: Maximum entries to scan (0 = unlimited, bounds O(N) search)
	//   - mode: DeterminismStrict or DeterminismBestEffort
	//
	// Returns:
	//   - Slice of SearchResult, ordered by score DESC (Strict: then key ASC)
	//   - Empty slice if no entries found or no entries have embeddings
	//
	// Scoring: cosine similarity normalized to [0, 1]
	// Score of 1.0 = identical vectors, 0.0 = orthogonal vectors
	FindSimilarByEmbedding(namespace string, queryEmbedding []float64, topK, maxScan int, mode DeterminismMode) []SearchResult
}

SharedIndex is the interface for similarity index backends.

All operations are thread-safe. Implementations must ensure:

  • Upsert/Delete are atomic within a namespace
  • FindSimilarSimHash returns deterministic results in Strict mode
  • Namespace isolation is enforced (operations don't cross namespace boundaries)

The index stores minimal metadata for similarity search. Full frame data is stored in SharedMem; the index provides fast lookup.

type SharedIndexContext

type SharedIndexContext struct {
	Index SharedIndex // The underlying index implementation

	UpsertCount  int64 // Number of Upsert operations
	DeleteCount  int64 // Number of Delete operations
	SearchCount  int64 // Number of FindSimilarSimHash operations
	ScannedTotal int64 // Total entries scanned across all searches

	// Trace logging (optional, for debugging/replay)
	TraceEnabled bool         // If true, operations are logged to Trace
	Trace        []TraceEntry // Recorded operations (append-only)
	// contains filtered or unexported fields
}

SharedIndexContext holds the runtime state for the SharedIndex effect.

The context provides access to the shared index and tracks statistics for debugging and monitoring.

func NewSharedIndexContext

func NewSharedIndexContext(index SharedIndex) *SharedIndexContext

NewSharedIndexContext creates a new SharedIndex context with the given index.

If index is nil, a new InMemorySharedIndex is created.

Parameters:

  • index: The SharedIndex implementation to use (nil for default in-memory)

Returns:

  • A new SharedIndexContext ready for use

func (*SharedIndexContext) ClearTrace

func (c *SharedIndexContext) ClearTrace()

ClearTrace removes all trace entries.

func (*SharedIndexContext) DisableTracing

func (c *SharedIndexContext) DisableTracing()

DisableTracing disables trace logging for this context.

func (*SharedIndexContext) EnableTracing

func (c *SharedIndexContext) EnableTracing()

EnableTracing enables trace logging for this context.

func (*SharedIndexContext) GetTrace

func (c *SharedIndexContext) GetTrace() []TraceEntry

GetTrace returns a copy of the trace entries.

func (*SharedIndexContext) IncrementDelete

func (c *SharedIndexContext) IncrementDelete()

IncrementDelete increments the delete counter (thread-safe).

func (*SharedIndexContext) IncrementSearch

func (c *SharedIndexContext) IncrementSearch(scanned int64)

IncrementSearch increments the search counter (thread-safe).

func (*SharedIndexContext) IncrementUpsert

func (c *SharedIndexContext) IncrementUpsert()

IncrementUpsert increments the upsert counter (thread-safe).

func (*SharedIndexContext) TraceDelete

func (c *SharedIndexContext) TraceDelete(namespace, key string)

TraceDelete records a delete operation if tracing is enabled.

func (*SharedIndexContext) TraceFindByEmbedding

func (c *SharedIndexContext) TraceFindByEmbedding(namespace string, embeddingDim, topK, maxScan int, mode DeterminismMode, resultCount int)

TraceFindByEmbedding records an embedding-based similarity search operation if tracing is enabled.

func (*SharedIndexContext) TraceFindSimHash

func (c *SharedIndexContext) TraceFindSimHash(namespace string, queryHash int64, topK, maxScan int, mode DeterminismMode, resultCount int)

TraceFindSimHash records a similarity search operation if tracing is enabled.

func (*SharedIndexContext) TraceResolveBestMatch

func (c *SharedIndexContext) TraceResolveBestMatch(namespace string, queryHash int64, chosenKey string)

TraceResolveBestMatch records a resolve operation with the chosen key if tracing is enabled.

func (*SharedIndexContext) TraceUpsert

func (c *SharedIndexContext) TraceUpsert(namespace, key string)

TraceUpsert records an upsert operation if tracing is enabled.

func (*SharedIndexContext) TraceUpsertWithEmbedding

func (c *SharedIndexContext) TraceUpsertWithEmbedding(namespace, key string, embeddingDim int)

TraceUpsertWithEmbedding records an upsert with embedding operation if tracing is enabled.

type SharedMemContext

type SharedMemContext struct {
	Cache SharedCache // The underlying cache implementation

	GetCount   int64 // Number of Get operations
	PutCount   int64 // Number of Put operations
	CASCount   int64 // Number of CAS operations
	CASSuccess int64 // Number of successful CAS operations
	// contains filtered or unexported fields
}

SharedMemContext holds the runtime state for the SharedMem effect.

The context provides access to the shared cache and tracks statistics for debugging and monitoring.

func NewSharedMemContext

func NewSharedMemContext(cache SharedCache) *SharedMemContext

NewSharedMemContext creates a new SharedMem context with the given cache.

If cache is nil, a new InMemorySharedCache is created.

Parameters:

  • cache: The SharedCache implementation to use (nil for default in-memory)

Returns:

  • A new SharedMemContext ready for use

func (*SharedMemContext) IncrCASCount

func (ctx *SharedMemContext) IncrCASCount(success bool)

IncrCASCount increments the CAS counter and optionally the success counter (thread-safe)

func (*SharedMemContext) IncrGetCount

func (ctx *SharedMemContext) IncrGetCount()

IncrGetCount increments the Get counter (thread-safe)

func (*SharedMemContext) IncrPutCount

func (ctx *SharedMemContext) IncrPutCount()

IncrPutCount increments the Put counter (thread-safe)

func (*SharedMemContext) Stats

func (ctx *SharedMemContext) Stats() (gets, puts, cas, casSuccess int64)

Stats returns a snapshot of the statistics

type SpanWrapperFunc

type SpanWrapperFunc func(
	goCtx context.Context,
	effectName, opName string,
	args []eval.Value,
	fn func() (eval.Value, error),
) (eval.Value, error)

SpanWrapperFunc wraps an effect operation with an OTEL span. Called by Call() if non-nil. The wrapper starts a span, calls fn(), sets span attributes/status, and ends the span. Defined here (in effects) so telemetry can implement it without import cycles.

type StreamConnection

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

StreamConnection holds per-connection state for a WebSocket or SSE connection.

func (*StreamConnection) Close

func (sc *StreamConnection) Close()

Close gracefully shuts down the connection.

func (*StreamConnection) Status

func (sc *StreamConnection) Status() StreamStatus

Status returns the current connection status.

type StreamContext

type StreamContext struct {
	// Connection limits
	MaxConnections int   // Default: 4 (prevent resource exhaustion)
	MaxMessageSize int64 // Default: 1MB per message (reassembled from frames)
	MaxFrameSize   int64 // Default: 64KB per frame (gorilla ReadLimit)

	// Timeouts
	ConnectTimeout time.Duration // Default: 30s
	IdleTimeout    time.Duration // Default: 60s (close if no messages; reset on any activity)
	MaxDuration    time.Duration // Default: 5min (hard ceiling)

	// Security
	AllowHTTP       bool     // Default: false (wss:// only)
	AllowLocalhost  bool     // Default: false
	BlockPrivateIPs bool     // Default: true (RFC1918 + link-local)
	AllowedDomains  []string // Domain allowlist (empty = all allowed)

	// Event buffer
	EventBufferSize int // Default: 1000 (bounded; backpressure when full)
	// contains filtered or unexported fields
}

StreamContext provides configuration for Stream effect security

M-STREAM-BIDI: The stream context holds security settings for persistent WebSocket connections, following the same patterns as NetContext.

Security features:

  • Protocol validation (wss:// enforced by default, ws:// requires flag)
  • Domain allowlist (optional)
  • Private IP blocking (RFC1918 + link-local, default: on)
  • Connection count limits (default: 4 concurrent)
  • Message size limits (default: 1MB per message, 64KB per frame)
  • Idle timeout (default: 60s)
  • Hard duration ceiling (default: 5min)

func NewStreamContext

func NewStreamContext() *StreamContext

NewStreamContext creates a new stream context with secure defaults

func (*StreamContext) AcquireConnection

func (sc *StreamContext) AcquireConnection(conn *StreamConnection) (int, error)

AcquireConnection registers a new connection and returns its ID. Returns an error if the connection limit is reached.

func (*StreamContext) AcquireSource

func (sc *StreamContext) AcquireSource(source EventSource) int

AcquireSource registers a new event source and returns its ID.

func (*StreamContext) CloseAll

func (sc *StreamContext) CloseAll()

CloseAll closes all active connections. Used for graceful shutdown.

func (*StreamContext) ConnectionCount

func (sc *StreamContext) ConnectionCount() int

ConnectionCount returns the number of active connections.

func (*StreamContext) GetConnection

func (sc *StreamContext) GetConnection(id int) (*StreamConnection, bool)

GetConnection retrieves a connection by ID.

func (*StreamContext) GetSource

func (sc *StreamContext) GetSource(id int) (EventSource, bool)

GetSource retrieves an event source by ID.

func (*StreamContext) ReleaseConnection

func (sc *StreamContext) ReleaseConnection(id int)

ReleaseConnection removes a connection from tracking.

func (*StreamContext) ReleaseSource

func (sc *StreamContext) ReleaseSource(id int)

ReleaseSource removes a source from tracking.

func (*StreamContext) ValidateURL

func (sc *StreamContext) ValidateURL(rawURL string) error

ValidateURL checks a URL against the stream security policy. Returns nil if the URL passes all checks.

type StreamStatus

type StreamStatus int

StreamStatus represents the connection state

const (
	StreamStatusConnecting StreamStatus = iota
	StreamStatusOpen
	StreamStatusClosing
	StreamStatusClosed
)

func (StreamStatus) String

func (s StreamStatus) String() string

type StubAIHandler

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

StubAIHandler returns deterministic placeholder responses

Use for testing and development. Supports:

  • Default response for all inputs
  • Per-input canned responses

func NewStubAIHandler

func NewStubAIHandler() *StubAIHandler

NewStubAIHandler creates a stub handler with a sensible default

func (*StubAIHandler) Call

func (h *StubAIHandler) Call(input string) (string, error)

Call returns the configured response for the input

func (*StubAIHandler) CallImage

func (h *StubAIHandler) CallImage(prompt, outputPath, options string) (string, error)

CallImage returns a stub image path (writes a minimal 1x1 PNG to disk).

func (*StubAIHandler) CallImageBase64

func (h *StubAIHandler) CallImageBase64(prompt, options string) (string, error)

CallImageBase64 returns a stub base64 JSON response.

func (*StubAIHandler) CallJson

func (h *StubAIHandler) CallJson(input string, schema string) (string, error)

CallJson returns valid JSON for structured output requests. The stub returns the default response (which is valid JSON).

func (*StubAIHandler) SetDefaultResponse

func (h *StubAIHandler) SetDefaultResponse(response string)

SetDefaultResponse sets the fallback for unmatched inputs

func (*StubAIHandler) SetResponse

func (h *StubAIHandler) SetResponse(input, response string)

SetResponse sets a canned response for an exact input match

type TraceEntry

type TraceEntry struct {
	Operation   string          `json:"op"`        // "upsert", "delete", "find_simhash"
	Namespace   string          `json:"namespace"` // Index partition
	Key         string          `json:"key,omitempty"`
	QueryHash   int64           `json:"query_hash,omitempty"`
	TopK        int             `json:"top_k,omitempty"`
	MaxScan     int             `json:"max_scan,omitempty"`
	Mode        DeterminismMode `json:"mode,omitempty"`
	ResultCount int             `json:"result_count,omitempty"`
	ChosenKey   string          `json:"chosen_key,omitempty"` // Only for resolve_best_match
	Timestamp   int64           `json:"ts"`                   // Unix nano timestamp
}

TraceEntry captures details of a SharedIndex operation for debugging and replay. Trace entries are only recorded when TraceEnabled is true.

type TraceRegistry

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

TraceRegistry provides a simple in-memory registry for recording span names during program execution. This enables trace verification in tests.

The registry is designed for testing scenarios where you want to verify that certain trace spans were created during execution.

Thread-safe for concurrent access.

func GlobalTraceRegistry

func GlobalTraceRegistry() *TraceRegistry

GlobalTraceRegistry returns the global trace registry. This is used by the _trace_check builtin.

func NewTraceRegistry

func NewTraceRegistry() *TraceRegistry

NewTraceRegistry creates a new empty trace registry.

func (*TraceRegistry) All

func (r *TraceRegistry) All() map[string]int

All returns a copy of all recorded span names and their counts.

func (*TraceRegistry) Clear

func (r *TraceRegistry) Clear()

Clear removes all recorded spans from the registry. Useful for resetting state between tests.

func (*TraceRegistry) Count

func (r *TraceRegistry) Count(name string) int

Count returns the number of times a span with the given name was recorded. Returns 0 if the span doesn't exist.

func (*TraceRegistry) Exists

func (r *TraceRegistry) Exists(name string) bool

Exists returns true if a span with the given name exists in the registry. Supports prefix matching: "compile" matches "compile.parse", "compile.typecheck", etc.

func (*TraceRegistry) Record

func (r *TraceRegistry) Record(name string)

Record records that a span with the given name was created. Can be called multiple times for the same name to track count.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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