Documentation
¶
Overview ¶
Package agentic provides shared types for the SemStreams agentic processing system.
Overview ¶
The agentic package defines the foundational types used by the agentic component family: agentic-loop (orchestration), agentic-model (LLM integration), and agentic-tools (tool execution). These components work together to enable autonomous, tool-using AI agents that can execute complex multi-step tasks.
This package provides:
- Request/Response types for agent communication (AgentRequest, AgentResponse)
- State machine types for loop lifecycle (LoopState, LoopEntity)
- Tool system types (ToolDefinition, ToolCall, ToolResult)
- Trajectory tracking for observability (Trajectory, TrajectoryStep)
Architecture Context ¶
The agentic system uses a three-component architecture communicating over NATS JetStream:
┌─────────────────┐
│ agentic-loop │ Orchestrates the agent lifecycle
│ (state machine)│ Manages state, routes messages, captures trajectory
└────────┬────────┘
│
┌────┴────┐
│ │
▼ ▼
┌────────┐ ┌────────────┐
│agentic-│ │agentic- │
│model │ │tools │
│(LLM) │ │(execution) │
└────────┘ └────────────┘
All three components share the types defined in this package, ensuring consistent serialization and validation across the system.
Request/Response Types ¶
AgentRequest encapsulates a request to an LLM endpoint:
request := agentic.AgentRequest{
RequestID: "req_001",
LoopID: "loop_123",
Role: "general", // or "architect", "editor"
Model: "gpt-4",
Messages: []agentic.ChatMessage{
{Role: "system", Content: "You are a helpful assistant."},
{Role: "user", Content: "Analyze this code for bugs."},
},
Tools: []agentic.ToolDefinition{
{Name: "read_file", Description: "Read file contents", Parameters: schema},
},
}
AgentResponse captures the LLM's response:
response := agentic.AgentResponse{
RequestID: "req_001",
Status: "complete", // or "tool_call", "error"
Message: agentic.ChatMessage{
Role: "assistant",
Content: "I found 3 potential issues...",
},
TokenUsage: agentic.TokenUsage{
PromptTokens: 150,
CompletionTokens: 200,
},
}
ChatMessage Roles ¶
The ChatMessage type supports four roles following the OpenAI convention:
- "system": System instructions that shape agent behavior
- "user": User input or task descriptions
- "assistant": Agent responses (from the LLM)
- "tool": Results from tool execution
Messages with tool calls use the ToolCalls field instead of Content:
assistantMessage := agentic.ChatMessage{
Role: "assistant",
ToolCalls: []agentic.ToolCall{
{ID: "call_001", Name: "read_file", Arguments: map[string]any{"path": "main.go"}},
},
}
Agent Roles ¶
The agentic system supports three agent roles for different task patterns:
- "general": Standard single-agent execution for most tasks
- "architect": High-level planning and design (used with editor split)
- "editor": Implementation based on architect's plan
The architect/editor split enables complex tasks where planning and execution benefit from separation. The loop orchestrator handles spawning editor loops when an architect completes.
State Machine ¶
LoopState represents the lifecycle of an agentic loop with seven states:
exploring → planning → architecting → executing → reviewing → complete
↘ failed
States are fluid checkpoints, not gates. The loop can move backward (e.g., from executing back to exploring if the agent needs to rethink). Only the terminal states (complete, failed) prevent further transitions.
Create and manage loop entities:
entity := agentic.NewLoopEntity("loop_123", "task_456", "general", "gpt-4")
// State transitions
entity.TransitionTo(agentic.LoopStatePlanning)
entity.TransitionTo(agentic.LoopStateExecuting)
// Iteration tracking (with guard)
if err := entity.IncrementIteration(); err != nil {
// Max iterations reached
}
// Check terminal state
if entity.State.IsTerminal() {
// Loop has finished
}
Tool System ¶
The tool system enables agents to interact with external systems through a well-defined interface.
ToolDefinition describes an available tool:
toolDef := agentic.ToolDefinition{
Name: "read_file",
Description: "Read the contents of a file",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"path": map[string]any{"type": "string", "description": "File path"},
},
"required": []string{"path"},
},
}
ToolCall represents a request from the LLM to execute a tool:
call := agentic.ToolCall{
ID: "call_001",
Name: "read_file",
Arguments: map[string]any{"path": "/etc/hosts"},
}
ToolResult returns the outcome of tool execution:
result := agentic.ToolResult{
CallID: "call_001",
Content: "127.0.0.1 localhost\n...",
// Or on error:
// Error: "file not found",
}
Tool Validation ¶
The ValidateToolsAllowed function checks tool calls against an allowlist:
allowed := []string{"read_file", "write_file", "list_dir"}
calls := []agentic.ToolCall{{ID: "1", Name: "delete_file"}}
if err := agentic.ValidateToolsAllowed(calls, allowed); err != nil {
// Error: "disallowed tools: delete_file"
}
Trajectory Tracking ¶
Trajectories capture the complete execution path of an agentic loop for observability, debugging, and compliance.
Create and populate a trajectory:
trajectory := agentic.NewTrajectory("loop_123")
// Record a model call
trajectory.AddStep(agentic.TrajectoryStep{
Timestamp: time.Now(),
StepType: "model_call",
RequestID: "req_001",
Prompt: "Analyze this code...",
Response: "I found 3 issues...",
TokensIn: 150,
TokensOut: 200,
Duration: 1250, // milliseconds
})
// Record a tool call
trajectory.AddStep(agentic.TrajectoryStep{
Timestamp: time.Now(),
StepType: "tool_call",
ToolName: "read_file",
ToolArguments: map[string]any{"path": "main.go"},
ToolResult: "package main...",
Duration: 50,
})
// Complete the trajectory
trajectory.Complete("complete")
Trajectories automatically track:
- Total input/output tokens across all model calls
- Cumulative duration of all steps
- Start and end times with final outcome
Token Usage ¶
TokenUsage tracks token consumption for cost monitoring and rate limiting:
usage := agentic.TokenUsage{
PromptTokens: 500,
CompletionTokens: 250,
}
total := usage.Total() // 750
Model Configuration ¶
ModelConfig provides default values for LLM parameters:
config := agentic.ModelConfig{
Temperature: 0.7,
MaxTokens: 2048,
}
// Apply defaults (Temperature: 0.2, MaxTokens: 4096)
config = config.WithDefaults()
Validation ¶
All types include Validate() methods for input validation:
request := agentic.AgentRequest{...}
if err := request.Validate(); err != nil {
// Handle validation error
}
Validation rules:
- AgentRequest: requires request_id, at least one message, valid role
- AgentResponse: requires valid status (complete, tool_call, error)
- ChatMessage: requires valid role, either content, reasoning_content, or tool_calls
- ToolDefinition: requires name and parameters
- ToolCall: requires id and name
- ToolResult: requires call_id
- TrajectoryStep: requires valid step_type and timestamp
- LoopEntity: requires id, valid state, positive max_iterations
Integration with NATS ¶
The agentic components communicate via NATS JetStream using these subject patterns:
agent.task.* - Task requests (external → loop) agent.request.* - Model requests (loop → model) agent.response.* - Model responses (model → loop) tool.execute.* - Tool execution (loop → tools) tool.result.* - Tool results (tools → loop) agent.complete.* - Completions (loop → external)
All subjects use the AGENT stream for reliable delivery.
KV Storage ¶
Loop state and trajectories are persisted to NATS KV buckets:
- AGENT_LOOPS: LoopEntity per loop ID
- AGENT_TRAJECTORIES: Trajectory per loop ID
This enables recovery after restarts and provides queryable execution history.
Thread Safety ¶
Types in this package are not inherently thread-safe. When used concurrently, external synchronization is required. The agentic-loop component provides thread-safe managers (LoopManager, TrajectoryManager) that wrap these types.
Error Handling ¶
Validation errors are returned as standard Go errors with descriptive messages. Tool execution errors are captured in ToolResult.Error rather than returned as Go errors, allowing the agent to handle them gracefully.
Limitations ¶
Current limitations of the agentic type system:
- No streaming support (responses are complete documents)
- Tool parameters use map[string]any (no strong typing)
- Trajectory steps are append-only (no editing)
- Maximum trajectory size limited by NATS KV (1MB default)
See Also ¶
Related packages:
- processor/agentic-loop: Loop orchestration and state management
- processor/agentic-model: LLM endpoint integration
- processor/agentic-tools: Tool execution framework
Package agentic provides shared types for the agentic components system. This includes loop state management, tool execution interfaces, and trajectory tracking.
Index ¶
- Constants
- Variables
- func AdvertisedToolsFromMetadata(m map[string]any) (tools []string, present bool)
- func AgentLessonEntityID(org, platform, id string) string
- func AgentLessonMessageType() message.Type
- func AgentLessonRecordPrefix(org, platform string) string
- func AuthorizeLineageTriplePredicate(producer, predicate string) error
- func BashStepURLs(toolName string, toolArgs map[string]any) []string
- func ChainExecutionEntityID(org, platform, chainID string) string
- func ExtractFetchedURLs(command string) []string
- func FilesystemPolicyFromMetadata(m map[string]any) (policy string, scratch []string)
- func IsApprovalRejected(reason string) bool
- func IsApprovalRequired(reason string) bool
- func IsKnownFilesystemPolicy(policy string) bool
- func IsReadOnlyPolicy(policy string) bool
- func LineageTriplePredicate(roleKey string) (string, error)
- func LoopExecutionEntityID(org, platform, loopID string) string
- func LoopExecutionMessageType() message.Type
- func LoopIDFromExecutionEntityID(entityID string) (string, bool)
- func ModelEndpointEntityID(org, platform, endpointName string) string
- func ModelEndpointMessageType() message.Type
- func OpsDiagnosisEntityID(org, platform, id string) string
- func OpsDiagnosisMessageType() message.Type
- func RegisterPayloads(reg *payloadregistry.Registry) error
- func TrajectoryStepEntityID(org, platform, loopID string, stepIndex int) string
- func TrajectoryStepMessageType() message.Type
- func TryChainExecutionEntityID(org, platform, chainID string) (string, error)
- func TryLoopExecutionEntityID(org, platform, loopID string) (string, error)
- func TryWebObservationEntityID(org, platform, rawURL string) (entityID, canonicalURL string, err error)
- func ValidateToolsAllowed(calls []ToolCall, allowed []string) error
- type AgentRequest
- type AgentResponse
- type ApprovalPendingEvent
- type ApprovalResponse
- type Attachment
- type ChatMessage
- type ConstructedContext
- type ContextEvent
- type ContextSource
- type GraphContextSpec
- type LoopCancelledEvent
- type LoopCompletedEvent
- type LoopCreatedEvent
- type LoopEntity
- func (e *LoopEntity) BeginAwaitingApproval(callID, toolName string, arguments map[string]any, reason string, ...) error
- func (e *LoopEntity) IncrementIteration() error
- func (e *LoopEntity) ResolveApproval() error
- func (e *LoopEntity) TransitionTo(newState LoopState) error
- func (e *LoopEntity) Validate() error
- type LoopExecutionEntity
- type LoopFailedEvent
- type LoopState
- type ModelConfig
- type PendingApprovalState
- type ReasoningCarrierKind
- type ReasoningRecord
- type ResponseAction
- type ResponseBlock
- type ResponseFormat
- type TaskMessage
- type TokenUsage
- type ToolCall
- type ToolChoice
- type ToolDefinition
- type ToolErrorKind
- type ToolResult
- type ToolResultHint
- type Trajectory
- type TrajectoryListItem
- type TrajectoryListResponse
- type TrajectoryStep
- type TrajectoryStepEntity
- func (e *TrajectoryStepEntity) ContentFields() map[string]string
- func (e *TrajectoryStepEntity) EntityID() string
- func (e *TrajectoryStepEntity) RawContent() map[string]string
- func (e *TrajectoryStepEntity) SetStorageRef(ref *message.StorageReference)
- func (e *TrajectoryStepEntity) StorageRef() *message.StorageReference
- func (e *TrajectoryStepEntity) Triples() []message.Triple
- type UserMessage
- type UserResponse
- type UserSignal
Constants ¶
const ( Domain = "agentic" SchemaVersion = "v1" )
Domain and version constants for message type identification.
const ( CategoryTask = "task" CategoryUserMessage = "user_message" CategorySignal = "signal" CategoryUserResponse = "user_response" CategoryRequest = "request" CategoryResponse = "response" CategoryToolCall = "tool_call" CategoryToolResult = "tool_result" CategoryLoopCreated = "loop_created" CategoryLoopCompleted = "loop_completed" CategoryLoopFailed = "loop_failed" CategoryLoopCancelled = "loop_cancelled" CategoryContextEvent = "context_event" CategorySignalMessage = "signal_message" CategoryApprovalPending = "approval_pending" CategoryApprovalResponse = "approval_response" )
Category constants for message types.
const ( ApprovalDecisionApprove = "approve" ApprovalDecisionReject = "reject" ApprovalDecisionModify = "modify" )
Decision values for ApprovalResponse — what the human-in-the-loop approver decided about a tool call gated by approval_required.
const ( OutcomeSuccess = "success" OutcomeFailed = "failed" OutcomeCancelled = "cancelled" OutcomeTruncated = "truncated" )
Outcome values for loop completion events.
const ( StatusComplete = "complete" StatusToolCall = "tool_call" StatusError = "error" StatusLengthTruncated = "length_truncated" )
Response status values from model responses.
const ( FinishReasonStop = "stop" FinishReasonLength = "length" FinishReasonToolCalls = "tool_calls" )
Finish reason values from model responses (OpenAI-compatible).
const ( ContextEventCompactionStarting = "compaction_starting" ContextEventCompactionComplete = "compaction_complete" // ContextEventCompactionRetry is emitted when a length-truncated // response triggers an inline compaction + within-iteration retry // (see beta.21). Carries pre-compaction Utilization and TokensSaved // so operators can see the recovery in the loop's history. ContextEventCompactionRetry = "compaction_retry" )
ContextEvent type values.
const ( RoleArchitect = "architect" RoleEditor = "editor" RoleGeneral = "general" RoleQualifier = "qualifier" RoleDeveloper = "developer" RoleReviewer = "reviewer" )
Role values for agent loops.
const ( // ResponseFormatJSONObject — legacy bare JSON validity (no schema). ResponseFormatJSONObject = "json_object" // ResponseFormatJSONSchema — strict-mode schema-constrained output (current standard). ResponseFormatJSONSchema = "json_schema" )
Response format type values for AgentRequest.ResponseFormat. See ADR-034.
const ( FilesystemPolicyReadOnly = "read_only" FilesystemPolicyWorkspaceWrite = "workspace_write" FilesystemPolicyHostWrite = "host_write" // valid enum; permissive, no v1 enforcement meaning (substrate concern) )
Filesystem policy values. Deliberately match the sandbox-substrate filesystem enum (ADR-052 vocabulary/sandbox: filesystem.read_only / filesystem.workspace_write) so there is ONE filesystem-policy model across the framework and the future substrate. host_write has no v1 meaning here (an environment-level concern the substrate owns).
const ( SignalCancel = "cancel" // Stop execution immediately SignalPause = "pause" // Pause at next checkpoint SignalResume = "resume" // Continue paused loop SignalApprove = "approve" // Approve pending result SignalReject = "reject" // Reject with optional reason SignalFeedback = "feedback" // Add feedback without decision SignalRetry = "retry" // Retry failed loop )
Signal type constants for user control signals
const ( ResponseTypeText = "text" // Plain text response ResponseTypeStatus = "status" // Status update ResponseTypeResult = "result" // Final result ResponseTypeError = "error" // Error message ResponseTypePrompt = "prompt" // Awaiting user input (approval, etc.) ResponseTypeStream = "stream" // Streaming partial content )
Response type constants
const ApprovalRejectedPrefix = "approval_rejected: "
ApprovalRejectedPrefix is prepended to rejection reasons synthesised by the loop after an ApprovalResponse with Decision == reject. The distinct prefix prevents the loop from re-triggering the awaiting-approval branch when the rejection result flows through HandleToolResult.
const ApprovalRequiredPrefix = "approval_required: "
ApprovalRequiredPrefix is prepended to rejection reasons when a tool requires human approval. The agentic-loop detects this prefix and transitions the loop to LoopStateAwaitingApproval instead of storing a normal error result.
const CategoryAgentLesson = "agent_lesson"
CategoryAgentLesson is the message category for the agent-lesson-record entity origin contract (ADR-080). It names the ENTITY type born when the ops agent's emit_lesson tool distils a reusable lesson into the graph — distinct from any event payload. Mirrors CategoryOpsDiagnosis / CategoryLoopExecution / CategoryModelEndpoint.
Lessons unify with the rest of agent memory under the agent.* entity-ID domain; diagnosis stays ops.* (it is an observability artifact, not memory). See ADR-080 decision 1.
const ( // CategoryLoopExecution is the message category for the loop-execution // entity origin contract (ADR-056 W0 4c-pre-1). Distinct from // CategoryLoopCreated (the event payload) — this category names the // entity type, not the event. CategoryLoopExecution = "loop_execution" )
const CategoryModelEndpoint = "model_endpoint"
CategoryModelEndpoint is the message category for the model-endpoint entity origin contract. It names the ENTITY type born when the agentic loop registers a model registry endpoint in the graph — distinct from any event payload. Mirrors CategoryLoopExecution.
const CategoryOpsDiagnosis = "ops_diagnosis"
CategoryOpsDiagnosis is the message category for the ops-diagnosis-finding entity origin contract. It names the ENTITY type born when the ops agent's emit_diagnosis tool records a finding in the graph — distinct from any event payload. Mirrors CategoryLoopExecution / CategoryModelEndpoint.
const CategoryTrajectoryStep = "trajectory_step"
CategoryTrajectoryStep is the message category for the trajectory-step entity origin contract. It names the ENTITY type born when the agentic loop records a trajectory step in the graph — distinct from any event payload. Mirrors CategoryLoopExecution / CategoryModelEndpoint.
const LineageTripleNamespace = "agent.lineage"
LineageTripleNamespace is the fixed framework-owned namespace for cross-arc loop-ID lineage triples. Each entry in TaskMessage.Metadata[MetadataKeyRelatedLoops] becomes a triple of the form:
subject: <spawned loop entity ID> predicate: agent.lineage.<role-key> // e.g. agent.lineage.research-reviewer object: <upstream loop ID string>
Downstream rules that fire on the spawned entity read these via the existing $entity.triple.<predicate> substitution, e.g. $entity.triple.agent.lineage.researcher resolves to the upstream loop ID without any new substitution-token, tool, or persona-driven echo forwarding.
Stable namespace: ops-agent (ADR-027) and the operating-curve observability primitives (ADR-033) aggregate cross-arc lineage by scanning predicates with this prefix. Codifying as a public constant keeps producers and consumers aligned without string-literal drift.
const LineageTripleProducer = "agentic-loop-lineage"
LineageTripleProducer is the stable trusted producer identity granted the exact agent.lineage namespace. It names the framework integration boundary, not Triple.Source or caller-controlled task metadata.
const MetadataKeyAdvertisedTools = "agent.tools.advertised"
MetadataKeyAdvertisedTools is the ToolCall.Metadata key under which agentic-loop dispatch stamps the loop's ADVERTISED tool set — the names of the tool definitions cached for the loop at spawn (per-task task.Tools, or global discovery when unset) — so the tool executor can enforce advertise-and-enforce per loop (gh#551): a model that emits a tool outside its advertised set is rejected at execution even when the tool is in the executor's global AllowedTools. Defense-in-depth: providers normally only emit advertised tools, but one executor multiplexing many roles (semdev's coordinator/developer/reviewer) must not execute a call the loop never advertised.
[]string when stamped; comes back from BaseMessage decode as []any (each element still a Go string) — readers coerce via AdvertisedToolsFromMetadata. Absent == loop has no cached tool set; executor applies only the global allowlist (back-compat). Present-but-empty/malformed == executor fails closed (rejects), per the IsKnownFilesystemPolicy precedent: an unrecognized value on a security control must not degrade to permissive.
Deliberately NOT a member of DispatchEnforcedMetadataKeys: that set's contract is "stamped from the loop's cached TASK metadata" (LoopManager.GetCachedMetadata), while this key is stamped from the loop's tools CACHE (LoopManager.GetCachedTools) — like the MetadataKeyRunID stamp, it is a framework fact dispatch derives itself, authoritatively (OVERWRITE), at the same shared dispatchToolCall seam (ADR-067).
const MetadataKeyAgentRole = "agent.role"
MetadataKeyAgentRole is the ToolCall.Metadata key under which agentic-loop dispatch stamps the emitting loop's role (LoopEntity.Role). A tool executor reads it to attribute a role to its output WITHOUT the model supplying (and therefore being able to spoof) an identity parameter — e.g. emit_lesson derives agent.lesson.observed-role from it (ADR-080: "attribution is derived, not supplied").
Stamped authoritatively (overwrite) when the loop has a role, and DELETED when the role is empty, exactly like the run anchor: the role is a framework fact derived from the loop entity, not a caller-routable hint, so a caller/model-injected value must never survive. Absent for a roleless loop.
const MetadataKeyDecideActionAllowlist = "agent.decide.action_allowlist"
MetadataKeyDecideActionAllowlist is the TaskMessage.Metadata / ToolCall.Metadata key under which a closed action vocabulary for the decide tool flows from the spawning rule down to the executor.
When set, the decide executor validates its `action` argument against the contained []string and rejects non-members with ToolErrorInvalidArgs (the message names the valid set so the LLM can correct on retry).
Empty/missing leaves decide free-form (back-compat).
Set by rule.executePublishAgent from rule.Action.ActionAllowlist. Belt-and-suspenders for persona prose: the persona enumerates the vocabulary in the LLM's system prompt; this allowlist enforces it structurally on the wire.
const MetadataKeyFilesystemPolicy = "agent.exec.filesystem_policy"
MetadataKeyFilesystemPolicy is the TaskMessage.Metadata / ToolCall.Metadata key under which a task's filesystem write-scope flows to write-capable tool executors (v1: bash). Values are the sandbox-substrate filesystem enum (ADR-052 vocabulary): FilesystemPolicyReadOnly | FilesystemPolicyWorkspaceWrite. Absent/empty == workspace_write (unchanged, back-compat).
Under read_only the bash executor enforces a pre+post git worktree-and-HEAD non-mutation proof and returns a typed violation on mutation. The value is model-uncontrollable: stamped by dispatchToolCall as an authoritative overwrite (a framework enforcement fact), never read from tool Arguments.
Set by the product's role→policy mapping when spawning an inspect-role loop (role→policy is product-layer; the framework owns the enforcement floor). Per-task, NOT inherited by spawned sub-loops — a product must re-assert it on each spawned inspect child (see ADR-067 §5).
const MetadataKeyHasMore = "has_more"
MetadataKeyHasMore is the ToolResult.Metadata key set to a bool true when more pages of results remain after the current call. Always set when an executor opts into pagination (ToolDefinition.Paginated=true); absence on a paginated tool's result is a contract violation worth a Warn log. Mutually paired with either MetadataKeyNextOffset (for byte/index-based paging) or MetadataKeyNextCursor (for opaque-keyset paging) — never both.
The agent loop reads this in buildToolMessages and appends a canonical continuation hint to the model's next message so the model knows it can call the same tool again with the supplied continuation token, instead of having to re-narrow blind.
Names are unprefixed because they're the canonical wire shape read_loop_result has already shipped under (semspec is already integrated against these strings). Lifting the existing names into constants is the contract semspec asked for — promotion, not rename.
const MetadataKeyNextCursor = "next_cursor"
MetadataKeyNextCursor is the ToolResult.Metadata key carrying an opaque server-format-controlled cursor token to pass back on the next call. Use for keyset-paginated result SETS where there is no natural byte offset (graph_search-style result iteration). The agent must never inspect or modify the cursor — server owns the format, so the backend can change encoding without breaking in-flight pagination. Mutually exclusive with NextOffset.
const MetadataKeyNextOffset = "next_offset"
MetadataKeyNextOffset is the ToolResult.Metadata key carrying the byte/index offset to pass back on the next call to continue paging. Use for offset-stable result sources where bytes-from-the-start is meaningful (read_loop_result's byte-paging of a single string is the canonical example). Mutually exclusive with NextCursor.
const MetadataKeyRelatedLoops = "agent.related_loops"
MetadataKeyRelatedLoops is the TaskMessage.Metadata / ToolCall.Metadata key under which cross-arc loop-ID lineage flows from a spawning rule down to the spawned loop and into its tool calls. The value is a map[string]string whose keys are exact static lower-kebab predicate segments (maximum 64 bytes) and whose values are related loop IDs. Keys are neither substituted nor normalized.
Use case: a downstream role needs to read_loop_result against an upstream loop without the loop ID being baked into the spawn prompt. Architect needing the researcher's loop ID for harness selection (semteams smoke #8 run-2 wedge cause); challenger cross-grounding back to planner; ops-agent / ADR-033 chain_id stability.
String-to-string only by design. Non-string values are out of scope: if a future case needs structured data, it earns a dedicated typed field (the Tools / ToolChoice / Timeout precedent), not a generalized escape hatch through this map.
Empty/missing leaves no lineage threaded (back-compat).
Set by rule.executePublishAgent from rule.Action.RelatedLoops. JSON round-trip note: like ActionAllowlist, the value comes back from BaseMessage decode as map[string]any (with each value still a Go string) — readers coerce on access.
const MetadataKeyRunEntityID = "agent.run_entity_id"
MetadataKeyRunEntityID is the ToolCall.Metadata key carrying the resolved 6-part chain execution entity ID (org.platform.agent.chain.execution.<runID>) for the loop's run anchor. Mirrors LoopCreatedEvent.RunEntityID / LoopCompletedEvent. RunEntityID so a tool executor and an event subscriber resolve the same run/chain entity.
Stamped alongside MetadataKeyRunID by agentic-loop dispatch when the loop belongs to a run AND the handler has a valid platform identity (org+platform). Absent when RunID is empty or the platform identity is missing; a consumer that needs the entity ID under a missing platform can reconstruct it from MetadataKeyRunID plus its own org/platform via agentic.ChainExecutionEntityID.
const MetadataKeyRunID = "agent.run_id"
MetadataKeyRunID is the ToolCall.Metadata key under which agentic-loop dispatch stamps the loop's run anchor — the bare run loop-id (ADR-053 D7/D8) — when the loop belongs to a run. A tool executor reads it to obtain the run/chain identity directly, instead of re-deriving it by walking agent.loop.parent ancestry triples from its LoopID back to the chain root over graph.query.entity (the hand-rolled ancestry resolver the semteams product shell carried for ADR-053 Phase 5, issue #250).
Empty/absent when the loop is not part of a run — a standalone loop stamps neither this nor MetadataKeyRunEntityID (back-compat). Paired with MetadataKeyRunEntityID, which carries the resolved 6-part chain execution entity ID for the same run.
Stamped authoritatively (overwrite), unlike the loop_id soft-fallback: the run anchor is a framework fact derived from the loop's typed RunID (set via LoopManager.SetRunID at loop creation), and there is no legitimate caller-override use case, so dispatch always supplies it.
const MetadataKeyScratchPaths = "agent.exec.scratch_paths"
MetadataKeyScratchPaths is the TaskMessage.Metadata / ToolCall.Metadata key under which IN-WORKTREE paths exempt from the read_only proof flow (e.g. a declared ".scratch/" build dir). Out-of-worktree paths (the common case: /tmp) are exempt automatically — git in the worktree never reports them, so probes there are invisible to the proof by construction.
[]string. Comes back from BaseMessage decode as []any (each element still a Go string); readers coerce on access, like MetadataKeyDecideActionAllowlist. Empty == only out-of-worktree paths are writable.
const MetadataKeyTotalBytes = "total_bytes"
MetadataKeyTotalBytes is the OPTIONAL ToolResult.Metadata key carrying the total result count, when the executor can compute it cheaply (no expensive secondary round-trip). Useful for UIs that want progress bars; not load-bearing for the agent. Absent when the executor can't compute it without extra work.
Named `total_bytes` rather than `total_available` to match the existing read_loop_result wire — promotion of the shipped shape, not rename. Executors with non-byte units can set their own unit-specific key and document it; this one is reserved for byte-paging consistency.
Variables ¶
var DispatchEnforcedMetadataKeys = []string{ MetadataKeyFilesystemPolicy, MetadataKeyScratchPaths, MetadataKeyDecideActionAllowlist, }
DispatchEnforcedMetadataKeys is the set of task-scoped enforcement keys that dispatchToolCall stamps AUTHORITATIVELY (overwrite) from the loop's cached task metadata onto every tool call — so they reach every dispatch path (main, approval re-dispatch, queue dequeue), not just the main-path merge. These are framework enforcement facts; a fill-only merge would let a stray pre-existing value defeat the control. Absent keys stay absent (back-compat).
MetadataKeyDecideActionAllowlist is included because it had the identical approval-redispatch gap (an approved decide call silently lost its closed vocabulary) — fixed at the same seam (ADR-067 §2).
var ErrMaxIterationsReached = errors.New("max iterations reached")
ErrMaxIterationsReached is the typed sentinel LoopEntity.IncrementIteration returns when the loop's iteration counter has already met or exceeded its configured budget (gh#529). Callers MUST branch on errors.Is against this sentinel rather than treating any non-nil IncrementIteration error as budget exhaustion — processor/agentic-loop.LoopManager.IncrementIteration can also fail with an unrelated "loop not found" error, which is a distinct operational failure and must not be misreported as max_iterations. processor/agentic-loop.ErrMaxIterationsReached aliases this value (the import direction only works agentic → agenticloop) so both packages compare against the exact same sentinel.
var ErrToolNotFound = errors.New("tool not found")
ErrToolNotFound is the sentinel returned by tool registries when a requested tool name has no executor. Callers use errors.Is to detect the miss without parsing error strings — the previous string-match fallback in agentic-tools/component.go was the source of repeated extension friction. Lives here (alongside ToolCall/ToolResult) rather than in agentic-tools so consumers in other processor packages can check it without importing the executor package.
Functions ¶
func AdvertisedToolsFromMetadata ¶
AdvertisedToolsFromMetadata resolves the per-loop advertised tool set from a tool call's metadata. present reports whether the key exists at all — callers MUST branch on it, because key-absent (no per-loop restriction, back-compat) and key-present-but-empty/malformed (fail closed) have opposite enforcement outcomes. tools is the coerced name list: JSON decode lands it as []any (string elements); a native []string (in-process path) is also accepted; non-string and empty elements are dropped; any other value type yields (nil, true) so the executor rejects rather than silently allowing.
func AgentLessonEntityID ¶
AgentLessonEntityID returns the canonical entity ID for an agent lesson record. The id argument is the content-derived unique identifier (a UUIDv5 without dots, generated by the emit_lesson tool from the lesson's category + scope + summary + evidence — see emit_lesson.go).
Format: {org}.{platform}.agent.lesson.record.{id}
Example: AgentLessonEntityID("acme", "ops", "2c5acb9b-8283-5b34-a4d1-4b1c9f8502ca") Returns: "acme.ops.agent.lesson.record.2c5acb9b-8283-5b34-a4d1-4b1c9f8502ca"
This is a 6-part entity ID (org.platform / agent / lesson / record / id); the domain+system axes (agent.lesson) align the entity's identity with its predicate family (agent.lesson.*), exactly as ops.diagnosis.finding aligns with ops.diagnosis.*.
Panics if any input part is empty or contains a dot, as these represent programming errors — the caller is responsible for supplying well-formed identifiers. The id must be a UUID or equivalent unique token with no dots.
func AgentLessonMessageType ¶
AgentLessonMessageType returns the message.Type for the agent-lesson-record entity origin contract — key "agentic.agent_lesson.v1".
Registry decision (mirrors OpsDiagnosisMessageType / LoopExecutionMessageType, ADR-056 typed-origin): MUTATION-ONLY. Stamped on CreateEntityWithTriplesRequest.Entity.MessageType when EmitLessonExecutor births a lesson entity; NEVER published as a BaseMessage payload, NOT registered in the payload registry, never decoded. Each emit_lesson call mints a content-derived agent.lesson.record.{uuid5} entity that MUST be CREATED with this envelope, not auto-vivified by triple.add — graph-ingest enforces must-exist and would reject the lesson's triples otherwise (the gh#390 failure shape emit_diagnosis already hit).
func AgentLessonRecordPrefix ¶
AgentLessonRecordPrefix returns the 5-part entity-ID prefix shared by every lesson record for an org/platform: "{org}.{platform}.agent.lesson.record".
It is the query prefix a reader passes to graph.ingest.query.prefix to list all lesson entities (the brief-assembly LessonReader uses it). Keeping the "agent.lesson.record" segment string here — beside AgentLessonEntityID — prevents it drifting from the entity-ID format. Panics on empty/dotted parts, as those are programming errors.
func AuthorizeLineageTriplePredicate ¶
AuthorizeLineageTriplePredicate applies the fixed lineage namespace policy for a producer supplied by a trusted framework boundary.
func BashStepURLs ¶
BashStepURLs returns the external URLs a bash trajectory step fetched, or nil for any other tool or when no fetch URL is present. toolArgs is the step's ToolArguments map; the bash command lives under the "command" key.
func ChainExecutionEntityID ¶
ChainExecutionEntityID constructs a 6-part entity ID for a cross-arc agent chain. Format: {org}.{platform}.agent.chain.execution.{chainID}
Example: ChainExecutionEntityID("c360", "ops", "abc123") Returns: "c360.ops.agent.chain.execution.abc123"
A chain entity is the canonical anchor for cross-arc data flow: rules and product subscribers stamp milestone triples (chain.dispatched_at, chain.research_artifact_loop, chain.spec_artifact_loop, chain.paused.*, chain.decision.*, ...) on this entity. The chain_id is the dispatch loop's UUID — no new ID generation required at chain start. See semteams ADR-038 for the chain-anchor pattern. `chain` is a sibling component to `agentic-loop` within the `agent` domain.
Panics if any input part is empty or contains a dot, as these represent programming errors — the caller is responsible for supplying well-formed identifiers.
Runtime callers where a panic would silently crash a goroutine (event- construction in publish goroutines, tool executors) should use TryChainExecutionEntityID instead and surface the error explicitly.
func ExtractFetchedURLs ¶
ExtractFetchedURLs derives the external URLs a bash command fetches. It inspects only sub-commands whose leading program is a known fetch verb (curl/wget/httpie), so URLs that appear as data — echo strings, grep patterns, filesystem paths — are excluded. Returns nil when no fetch URL is detected (no false-positive noise); results are order-preserving and de-duplicated. This is a cheap observability heuristic, not a shell parser: it inspects only the leading token, so wrapped/prefixed fetches — `sudo curl ...`, `time curl ...`, `HTTPS_PROXY=x curl ...`, `echo url | curl -` — are known misses (under-count, never over-count).
func FilesystemPolicyFromMetadata ¶
FilesystemPolicyFromMetadata resolves the read-only execution policy from a tool call's metadata. It returns the policy string (FilesystemPolicyReadOnly or "" for the default workspace_write) and the in-worktree scratch-path exemptions. Nil/absent metadata yields ("", nil) — the back-compat default.
scratch values arrive from BaseMessage JSON decode as []any (each element a Go string); this coerces them, dropping non-string / empty entries.
func IsApprovalRejected ¶
IsApprovalRejected reports whether a rejection reason originates from a denied ApprovalResponse. Distinct from IsApprovalRequired so the loop can flow these through normally without re-entering the awaiting-approval branch.
func IsApprovalRequired ¶
IsApprovalRequired reports whether a rejection reason indicates the tool needs human approval rather than being a genuine error.
func IsKnownFilesystemPolicy ¶
IsKnownFilesystemPolicy reports whether policy is a recognized filesystem enum value. An UNRECOGNIZED non-empty value (a product typo like "readonly") must NOT silently degrade to permissive — for a security control that is a fail-open. Callers fail closed (refuse) + log on an unknown value; the empty string is the back-compat default (workspace_write) and IS known.
func IsReadOnlyPolicy ¶
IsReadOnlyPolicy reports whether a resolved policy string means read_only.
func LineageTriplePredicate ¶
LineageTriplePredicate returns the canonical predicate for a RelatedLoops role key. The key is one static lower-kebab predicate segment; validating the complete candidate through vocabulary.ParsePredicate keeps this narrow delegation from becoming unchecked authority to mint arbitrary agent predicates.
Centralising construction keeps producers (rule.executePublishAgent / agentic-loop loop-creation path) and consumers (rule authors using $entity.triple.agent.lineage.<key>, ops-agent aggregations) cannot drift on the format.
func LoopExecutionEntityID ¶
LoopExecutionEntityID constructs a 6-part entity ID for an agentic loop execution. Format: {org}.{platform}.agent.agentic-loop.execution.{loopID}
Example: LoopExecutionEntityID("c360", "ops", "abc123") Returns: "c360.ops.agent.agentic-loop.execution.abc123"
Panics if any input part is empty or contains a dot. Suitable for boot-time and post-completion paths where invalid input represents a programming error that should fail loud (operator config check at startup; framework bug in event-payload construction).
Runtime tool executors (where a panic silently kills the dispatch goroutine and the agent fails opaquely) should use TryLoopExecutionEntityID and surface the error as ToolErrorInternal instead. ADR-036 Stage 3.8 documents the panic-class concern; the beta.36 read_loop_result wedge is the in-tree precedent.
func LoopExecutionMessageType ¶
LoopExecutionMessageType returns the message.Type for the loop-execution entity origin contract — key "agentic.loop_execution.v1" (snake_case category, matching the agentic convention: tool_call, loop_created, approval_pending).
Registry decision (ADR-056, intentionally pinned — not implicit): this type is MUTATION-ONLY. It is stamped on CreateEntityWithTriplesRequest.Entity.MessageType at birth purely as PRODUCER IDENTITY for ownership arbitration; it is NEVER published as a BaseMessage payload and is therefore NOT registered in the payload registry (payload_registry.go) and never round-trips through payload decoding. This mirrors core.identity.stub.v1 (the referential-integrity stub envelope): both are envelope-bearing graph-origin markers, not wire payloads. If a future producer needs to PUBLISH a loop_execution message over NATS (not merely stamp it at create), that producer must add the init() registration at that point — until then, registering it would advertise a decode path that does not exist.
func LoopIDFromExecutionEntityID ¶
LoopIDFromExecutionEntityID extracts the loop_id segment from a 6-part entity ID matching the LoopExecutionEntityID shape: {org}.{platform}.agent.agentic-loop.execution.{loopID}
Returns ("", false) when the input is not a valid 6-part entity ID, or when it doesn't match the agent.agentic-loop.execution.* shape (e.g. model-registry endpoints, trajectory steps, chain entities, or non-agent entity IDs). Used by the rule engine's publish_agent action to set task.ParentLoopID when a rule fires on a loop-execution-shaped trigger entity, so rule-fanned chains carry their parent linkage natively (semteams ADR-038 §D2).
Pure parser: no validation of the loop_id's content beyond IsValidEntityID's alphanumeric/hyphen/underscore guard.
func ModelEndpointEntityID ¶
ModelEndpointEntityID constructs a 6-part entity ID for a model registry endpoint. Format: {org}.{platform}.agent.model-registry.endpoint.{endpointName}
Example: ModelEndpointEntityID("c360", "ops", "claude-sonnet") Returns: "c360.ops.agent.model-registry.endpoint.claude-sonnet"
Panics if any input part is empty or contains a dot, as these represent programming errors — the caller is responsible for supplying well-formed identifiers.
func ModelEndpointMessageType ¶
ModelEndpointMessageType returns the message.Type for the model-endpoint entity origin contract — key "agentic.model_endpoint.v1".
Registry decision (mirrors LoopExecutionMessageType, ADR-056 typed-origin — intentionally pinned, not implicit): this type is MUTATION-ONLY. It is stamped on CreateEntityWithTriplesRequest.Entity.MessageType when WriteModelEndpoints births the endpoint entity, purely as PRODUCER IDENTITY for ownership arbitration; it is NEVER published as a BaseMessage payload and is therefore NOT registered in the payload registry (payload_registry.go) and never round-trips through payload decoding. A model endpoint is a config-derived FACT about the world, born once at startup via create_with_triples — not a wire message. If a future producer needs to PUBLISH a model_endpoint message over NATS (not merely stamp it at create), that producer must add the init() registration at that point — until then, registering it would advertise a decode path that does not exist.
func OpsDiagnosisEntityID ¶
OpsDiagnosisEntityID returns the canonical entity ID for an ops diagnosis finding. The id argument is a unique per-finding identifier (e.g., a UUID without dots generated by the emit_diagnosis tool).
Format: {org}.{platform}.ops.diagnosis.finding.{id}
Example: OpsDiagnosisEntityID("acme", "ops", "550e8400-e29b-41d4-a716-446655440000") Returns: "acme.ops.ops.diagnosis.finding.550e8400-e29b-41d4-a716-446655440000"
Panics if any input part is empty or contains a dot, as these represent programming errors — the caller is responsible for supplying well-formed identifiers. The id must be a UUID or equivalent unique token with no dots.
func OpsDiagnosisMessageType ¶
OpsDiagnosisMessageType returns the message.Type for the ops-diagnosis-finding entity origin contract — key "agentic.ops_diagnosis.v1".
Registry decision (mirrors LoopExecutionMessageType, ADR-056 typed-origin): MUTATION-ONLY. Stamped on CreateEntityWithTriplesRequest.Entity.MessageType when EmitDiagnosisExecutor births a finding entity; NEVER published as a BaseMessage payload, NOT registered in the payload registry, never decoded. Each emit_diagnosis call mints a fresh ops.diagnosis.finding.{uuid} entity that MUST be CREATED with this envelope, not auto-vivified by triple.add — graph-ingest enforces must-exist and would reject the finding's triples otherwise (gh#390).
func RegisterPayloads ¶
func RegisterPayloads(reg *payloadregistry.Registry) error
RegisterPayloads registers all agentic payload types with the supplied registry. Called from payloadbuiltins.Register during process bootstrap. Returns aggregated errors via errors.Join so misconfigured deployments see every collision on a single boot.
Builders are intentionally omitted — the PayloadRegistry's JSON fallback (Factory + json.Unmarshal) handles payload construction for workflow variable interpolation without requiring duplicate field-mapping code.
func TrajectoryStepEntityID ¶
TrajectoryStepEntityID constructs a 6-part entity ID for a trajectory step. Format: {org}.{platform}.agent.agentic-loop.step.{loopID}-{stepIndex}
Example: TrajectoryStepEntityID("c360", "ops", "abc123", 0) Returns: "c360.ops.agent.agentic-loop.step.abc123-0"
Panics if any input part is empty or contains a dot, as these represent programming errors — the caller is responsible for supplying well-formed identifiers.
func TrajectoryStepMessageType ¶
TrajectoryStepMessageType returns the message.Type for the trajectory-step entity origin contract — key "agentic.trajectory_step.v1".
Registry decision (mirrors LoopExecutionMessageType, ADR-056 typed-origin): MUTATION-ONLY. Stamped on CreateEntityWithTriplesRequest.Entity.MessageType when WriteTrajectorySteps births a step entity; NEVER published as a BaseMessage payload, NOT registered in the payload registry, never decoded. A trajectory step is a metadata fact born once via create_with_triples (large content lives in ObjectStore via ContentStorable) — not a wire message. The step entity MUST be created with this envelope, not auto-vivified by triple.add: graph-ingest enforces must-exist and would reject the step's metadata triples otherwise (gh#390).
func TryChainExecutionEntityID ¶
TryChainExecutionEntityID is the error-returning variant of ChainExecutionEntityID. Use this from runtime hot paths (event- construction in publish goroutines, milestone subscribers) where a panic would silently crash the goroutine. Boot-time and post-completion callers that own their inputs can keep using the panicking form.
Returns ("", error) when any input part is empty or contains a dot, or when the constructed ID fails IsValidEntityID.
func TryLoopExecutionEntityID ¶
TryLoopExecutionEntityID is the error-returning variant of LoopExecutionEntityID. Use this from runtime hot paths (tool executors, per-iteration prompt assembly) where a panic would silently crash the dispatch goroutine and the agent would fail opaquely. Boot-time and post-completion callers can keep using the panicking LoopExecutionEntityID.
Returns ("", error) when any input part is empty or contains a dot, or when the constructed ID fails IsValidEntityID.
func TryWebObservationEntityID ¶
func TryWebObservationEntityID(org, platform, rawURL string) (entityID, canonicalURL string, err error)
TryWebObservationEntityID returns the canonical 6-part entity ID for a URL observed by an agent (web_search) or fetched by an agent (http_request), along with the canonical URL the entity represents. Same URL across loops → same entity ID, so observations naturally dedup: rule queries against agent.web.observation entities see one vertex per URL with whatever predicates the system has so far accumulated.
Format: {org}.{platform}.agent.web.observation.{sha256-hex-16}
Returns ("", "", error) when org/platform are empty or contain dots, when rawURL fails to parse, or when the constructed ID fails IsValidEntityID. The Try-variant naming follows the beta.36 precedent for runtime tool executors: a panic in a tool handler silently kills the dispatch goroutine, so runtime code MUST use this error-returning form.
Canonicalisation (V1, conservative — easily extended in a follow-up without changing the entity hash for URLs that didn't hit the new rules):
- lowercase scheme and host
- strip default port (:80 for http, :443 for https)
- strip fragment (#section)
- strip trailing slash on bare-host URLs (preserve internal slashes)
- preserve query string as-is (tracking-param stripping and query-param sorting are V2 candidates; query-param semantics are too domain-specific to canonicalise generically)
Note that canonicalisation is one-way: callers who need to display the agent's original input URL should keep it from the tool call, not try to reverse the hash.
func ValidateToolsAllowed ¶
ValidateToolsAllowed validates that all tool calls are in the allowed list
Types ¶
type AgentRequest ¶
type AgentRequest struct {
RequestID string `json:"request_id"`
LoopID string `json:"loop_id"`
Role string `json:"role"`
Messages []ChatMessage `json:"messages"`
Model string `json:"model"`
MaxTokens int `json:"max_tokens,omitempty"`
Temperature float64 `json:"temperature,omitempty"`
Tools []ToolDefinition `json:"tools,omitempty"`
ToolChoice *ToolChoice `json:"tool_choice,omitempty"`
// ResponseFormat constrains output to JSON or JSON-schema-conformant
// structure. Optional; nil means no structuring constraint (current
// behavior; tool-calling remains the structured-output primitive when
// nil). Honored on OpenAI-compat providers and Ollama; stubbed
// (warn-once log) on Gemini and generic adapters for v1. See ADR-034.
ResponseFormat *ResponseFormat `json:"response_format,omitempty"`
// Timeout caps this specific request. Go duration string (e.g. "30s").
// Empty means fall through to endpoint, capability, or component-level
// timeout. Takes precedence over all other timeout sources when set.
Timeout string `json:"timeout,omitempty"`
}
AgentRequest represents a request to an agentic service
func (*AgentRequest) MarshalJSON ¶
func (r *AgentRequest) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler
func (*AgentRequest) Schema ¶
func (r *AgentRequest) Schema() message.Type
Schema implements message.Payload
func (*AgentRequest) UnmarshalJSON ¶
func (r *AgentRequest) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler
func (AgentRequest) Validate ¶
func (r AgentRequest) Validate() error
Validate checks if the AgentRequest is valid
type AgentResponse ¶
type AgentResponse struct {
RequestID string `json:"request_id"`
Status string `json:"status"`
FinishReason string `json:"finish_reason,omitempty"` // Raw finish_reason from provider (stop, length, tool_calls)
Message ChatMessage `json:"message,omitempty"`
Error string `json:"error,omitempty"`
TokenUsage TokenUsage `json:"token_usage,omitempty"`
RetryCount int `json:"retry_count,omitempty"`
}
AgentResponse represents a response from an agentic service
func (*AgentResponse) MarshalJSON ¶
func (r *AgentResponse) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler
func (*AgentResponse) Schema ¶
func (r *AgentResponse) Schema() message.Type
Schema implements message.Payload
func (*AgentResponse) UnmarshalJSON ¶
func (r *AgentResponse) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler
func (AgentResponse) Validate ¶
func (r AgentResponse) Validate() error
Validate checks if the AgentResponse is valid
type ApprovalPendingEvent ¶
type ApprovalPendingEvent struct {
LoopID string `json:"loop_id"`
CallID string `json:"call_id"`
ToolName string `json:"tool_name"`
Arguments map[string]any `json:"arguments,omitempty"`
Reason string `json:"reason"` // Original "approval_required: ..." rejection reason
RequestedAt time.Time `json:"requested_at"`
// Timeout is the duration after which the loop will auto-reject if
// no response arrives. Zero means wait indefinitely.
Timeout time.Duration `json:"timeout,omitempty"`
// TraceID propagates from the original ToolCall so consumers can
// correlate the approval prompt with the originating loop trace.
TraceID string `json:"trace_id,omitempty"`
}
ApprovalPendingEvent is published by agentic-loop when a tool call is gated by Config.ApprovalRequired and the loop has transitioned to LoopStateAwaitingApproval. Product-layer approval UIs subscribe to agent.approval_pending.<loop_id> and surface the request for human review. The corresponding response is published as agent.approval_response.<loop_id> with an ApprovalResponse payload.
func (*ApprovalPendingEvent) MarshalJSON ¶
func (e *ApprovalPendingEvent) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler.
func (*ApprovalPendingEvent) Schema ¶
func (e *ApprovalPendingEvent) Schema() message.Type
Schema implements message.Payload.
func (*ApprovalPendingEvent) UnmarshalJSON ¶
func (e *ApprovalPendingEvent) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler.
func (*ApprovalPendingEvent) Validate ¶
func (e *ApprovalPendingEvent) Validate() error
Validate implements message.Payload.
type ApprovalResponse ¶
type ApprovalResponse struct {
LoopID string `json:"loop_id"`
CallID string `json:"call_id"`
Decision string `json:"decision"` // ApprovalDecisionApprove | ApprovalDecisionReject | ApprovalDecisionModify
// ModifiedArguments replaces the original tool-call arguments when
// Decision == ApprovalDecisionModify. Ignored for approve/reject.
ModifiedArguments map[string]any `json:"modified_arguments,omitempty"`
// Reason is an optional human-readable explanation for the
// decision. Carried into the audit trail and into the synthesized
// rejection result when Decision == reject.
Reason string `json:"reason,omitempty"`
// ApprovedBy identifies the approver. Required for approve and
// modify decisions so the audit trail knows who said yes; optional
// for reject. Propagated to ToolCall.ApprovedBy on re-dispatch so
// the agentic-tools approval filter recognises the bypass.
ApprovedBy string `json:"approved_by,omitempty"`
DecidedAt time.Time `json:"decided_at"`
}
ApprovalResponse is published by a product-layer approval UI in reply to an ApprovalPendingEvent. The agentic-loop subscribes to agent.approval_response.<loop_id>; the matching loop transitions out of LoopStateAwaitingApproval and either re-dispatches the tool (Decision == approve | modify) or synthesizes a rejection result for the LLM (Decision == reject).
func (*ApprovalResponse) MarshalJSON ¶
func (r *ApprovalResponse) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler.
func (*ApprovalResponse) Schema ¶
func (r *ApprovalResponse) Schema() message.Type
Schema implements message.Payload.
func (*ApprovalResponse) UnmarshalJSON ¶
func (r *ApprovalResponse) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler.
func (*ApprovalResponse) Validate ¶
func (r *ApprovalResponse) Validate() error
Validate implements message.Payload.
type Attachment ¶
type Attachment struct {
Type string `json:"type"` // file, image, code, url
Name string `json:"name"` // filename or title
URL string `json:"url,omitempty"` // URL to fetch content
Content string `json:"content,omitempty"` // inline content if small
MimeType string `json:"mime_type,omitempty"`
Size int64 `json:"size,omitempty"`
}
Attachment represents a file or other media attached to a message
type ChatMessage ¶
type ChatMessage struct {
Role string `json:"role"`
Content string `json:"content,omitempty"`
Name string `json:"name,omitempty"` // Function name for tool role messages (required by Gemini)
ReasoningContent string `json:"reasoning_content,omitempty"` // Thinking model chain-of-thought
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
ToolCallID string `json:"tool_call_id,omitempty"` // Required for tool role messages
IsError bool `json:"is_error,omitempty"` // Tool result contains an error — preserved during context GC
// ReasoningRecords are provider-opaque reasoning blobs captured
// from the model's response, to be echoed back on subsequent
// turns. Loop owns cross-turn propagation; adapters reshape into
// wire format at the seam. See ReasoningRecord (agentic/reasoning.go)
// and ADR-051.
ReasoningRecords []ReasoningRecord `json:"reasoning_records,omitempty"`
}
ChatMessage represents a message in a conversation
func (*ChatMessage) UnmarshalJSON ¶
func (m *ChatMessage) UnmarshalJSON(data []byte) error
UnmarshalJSON accepts both "reasoning" (Ollama) and "reasoning_content" (DeepSeek/canonical). If both are present, reasoning_content wins.
func (ChatMessage) Validate ¶
func (m ChatMessage) Validate() error
Validate checks if the ChatMessage is valid
type ConstructedContext ¶
type ConstructedContext = types.ConstructedContext
ConstructedContext is an alias for types.ConstructedContext. The canonical type is defined in pkg/types/context.go.
type ContextEvent ¶
type ContextEvent struct {
Type string `json:"type"` // ContextEventCompactionStarting, ContextEventCompactionComplete, ContextEventGCComplete
LoopID string `json:"loop_id"`
UserID string `json:"user_id,omitempty"` // owning user (provenance hook); lets a consumer scope user-scoped artifacts without a separate KV lookup
Iteration int `json:"iteration"`
Utilization float64 `json:"utilization,omitempty"`
TokensSaved int `json:"tokens_saved,omitempty"`
Summary string `json:"summary,omitempty"`
}
ContextEvent represents a context management event (compaction, GC).
func (*ContextEvent) MarshalJSON ¶
func (e *ContextEvent) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler
func (*ContextEvent) Schema ¶
func (e *ContextEvent) Schema() message.Type
Schema implements message.Payload
func (*ContextEvent) UnmarshalJSON ¶
func (e *ContextEvent) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler
func (*ContextEvent) Validate ¶
func (e *ContextEvent) Validate() error
Validate implements message.Payload
type ContextSource ¶
type ContextSource = types.ContextSource
ContextSource is an alias for types.ContextSource. The canonical type is defined in pkg/types/context.go.
type GraphContextSpec ¶
type GraphContextSpec = types.GraphContextSpec
GraphContextSpec is an alias for types.GraphContextSpec. The canonical type is defined in pkg/types/context.go.
type LoopCancelledEvent ¶
type LoopCancelledEvent struct {
LoopID string `json:"loop_id"`
TaskID string `json:"task_id"`
Outcome string `json:"outcome"` // OutcomeCancelled
CancelledBy string `json:"cancelled_by"`
// ParentLoopID enables ancestry walks from cancelled loops (parity with
// LoopFailedEvent.ParentLoopID). Populated from LoopEntity.ParentLoopID
// at cancellation construction time (ADR-053 D8).
ParentLoopID string `json:"parent_loop,omitempty"`
WorkflowSlug string `json:"workflow_slug,omitempty"`
WorkflowStep string `json:"workflow_step,omitempty"`
CancelledAt time.Time `json:"cancelled_at"`
Metadata map[string]any `json:"metadata,omitempty"`
// RunID is the bare run loop-id this loop belongs to (ADR-053 D8).
// Empty when the loop is not part of a run.
RunID string `json:"run_id,omitempty"`
// RunEntityID is the full 6-part chain execution entity ID for the run.
// Empty when RunID is empty.
RunEntityID string `json:"run_entity_id,omitempty"`
}
LoopCancelledEvent is published when a loop is cancelled by user action.
func (*LoopCancelledEvent) MarshalJSON ¶
func (e *LoopCancelledEvent) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler
func (*LoopCancelledEvent) Schema ¶
func (e *LoopCancelledEvent) Schema() message.Type
Schema implements message.Payload
func (*LoopCancelledEvent) UnmarshalJSON ¶
func (e *LoopCancelledEvent) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler
func (*LoopCancelledEvent) Validate ¶
func (e *LoopCancelledEvent) Validate() error
Validate implements message.Payload
type LoopCompletedEvent ¶
type LoopCompletedEvent struct {
LoopID string `json:"loop_id"`
TaskID string `json:"task_id"`
Outcome string `json:"outcome"` // OutcomeSuccess
Role string `json:"role"`
Result string `json:"result"`
Prompt string `json:"prompt,omitempty"` // Original user task prompt; enables NL/BM25 search
Model string `json:"model"`
Iterations int `json:"iterations"`
TokensIn int `json:"tokens_in"`
TokensOut int `json:"tokens_out"`
ParentLoopID string `json:"parent_loop,omitempty"`
WorkflowSlug string `json:"workflow_slug,omitempty"`
WorkflowStep string `json:"workflow_step,omitempty"`
CompletedAt time.Time `json:"completed_at"`
// User routing info for response delivery
ChannelType string `json:"channel_type,omitempty"`
ChannelID string `json:"channel_id,omitempty"`
UserID string `json:"user_id,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
// RunID is the bare run loop-id this loop belongs to (ADR-053 D8).
// Empty when the loop is not part of a run.
RunID string `json:"run_id,omitempty"`
// RunEntityID is the full 6-part chain execution entity ID for the run.
// Empty when RunID is empty.
RunEntityID string `json:"run_entity_id,omitempty"`
}
LoopCompletedEvent is published when a loop completes successfully.
func (*LoopCompletedEvent) MarshalJSON ¶
func (e *LoopCompletedEvent) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler
func (*LoopCompletedEvent) Schema ¶
func (e *LoopCompletedEvent) Schema() message.Type
Schema implements message.Payload
func (*LoopCompletedEvent) UnmarshalJSON ¶
func (e *LoopCompletedEvent) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler
func (*LoopCompletedEvent) Validate ¶
func (e *LoopCompletedEvent) Validate() error
Validate implements message.Payload
type LoopCreatedEvent ¶
type LoopCreatedEvent struct {
LoopID string `json:"loop_id"`
TaskID string `json:"task_id"`
Role string `json:"role"`
Model string `json:"model"`
WorkflowSlug string `json:"workflow_slug,omitempty"`
WorkflowStep string `json:"workflow_step,omitempty"`
ContextRequestID string `json:"context_request_id,omitempty"`
MaxIterations int `json:"max_iterations"`
CreatedAt time.Time `json:"created_at"`
Metadata map[string]any `json:"metadata,omitempty"`
// RunID is the bare run loop-id this loop belongs to (ADR-053 D8).
// Empty when the loop is not part of a run.
RunID string `json:"run_id,omitempty"`
// RunEntityID is the full 6-part chain execution entity ID for the run
// (e.g. "org.platform.agent.chain.execution.<runID>"). Empty when RunID is empty.
RunEntityID string `json:"run_entity_id,omitempty"`
}
LoopCreatedEvent is published when a new agentic loop is created.
func (*LoopCreatedEvent) MarshalJSON ¶
func (e *LoopCreatedEvent) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler
func (*LoopCreatedEvent) Schema ¶
func (e *LoopCreatedEvent) Schema() message.Type
Schema implements message.Payload
func (*LoopCreatedEvent) UnmarshalJSON ¶
func (e *LoopCreatedEvent) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler
func (*LoopCreatedEvent) Validate ¶
func (e *LoopCreatedEvent) Validate() error
Validate implements message.Payload
type LoopEntity ¶
type LoopEntity struct {
ID string `json:"id"`
TaskID string `json:"task_id"`
State LoopState `json:"state"`
Role string `json:"role"`
Model string `json:"model"`
Iterations int `json:"iterations"`
MaxIterations int `json:"max_iterations"`
PendingToolResults map[string]ToolResult `json:"pending_tool_results,omitempty"` // Accumulated tool results by call ID
StartedAt time.Time `json:"started_at,omitempty"` // When the loop was created
TimeoutAt time.Time `json:"timeout_at,omitempty"` // When the loop should timeout
ParentLoopID string `json:"parent_loop_id,omitempty"` // Parent loop ID for architect->editor relationship
// RunID is the 6-part-derived run anchor; the run loop-id this loop belongs to.
// Empty for loops not in a run. Inherited at spawn (ADR-053 D7).
RunID string `json:"run_id,omitempty"`
// Multi-agent depth tracking
Depth int `json:"depth,omitempty"` // Current depth in agent tree (0 = root)
MaxDepth int `json:"max_depth,omitempty"` // Maximum allowed depth for spawned agents
// Signal support fields
PauseRequested bool `json:"pause_requested,omitempty"` // Pause requested, will pause at next checkpoint
PauseRequestedBy string `json:"pause_requested_by,omitempty"` // User who requested pause
StateBeforePause LoopState `json:"state_before_pause,omitempty"` // State before pause (for resume)
CancelledBy string `json:"cancelled_by,omitempty"` // User who cancelled the loop
CancelledAt time.Time `json:"cancelled_at,omitempty"` // When the loop was cancelled
// Approval-gating fields (set when a tool call is rejected by the
// agentic-tools approval filter). The loop transitions to
// LoopStateAwaitingApproval and persists the pending call here so
// it can be re-dispatched on approval. StateBeforeApproval lets us
// restore the prior workflow state once the approval response
// arrives.
PendingApproval *PendingApprovalState `json:"pending_approval,omitempty"`
StateBeforeApproval LoopState `json:"state_before_approval,omitempty"`
// User context (for routing responses)
UserID string `json:"user_id,omitempty"` // User who initiated the loop
ChannelType string `json:"channel_type,omitempty"` // cli, slack, discord, web
ChannelID string `json:"channel_id,omitempty"` // Channel/session ID for routing responses
// Workflow context (for loops created by workflow commands)
WorkflowSlug string `json:"workflow_slug,omitempty"` // e.g., "add-user-auth"
WorkflowStep string `json:"workflow_step,omitempty"` // e.g., "design"
// Completion data (populated when loop completes)
// These fields enable SSE delivery of results via KV watch
Outcome string `json:"outcome,omitempty"` // success, failed, cancelled
Result string `json:"result,omitempty"` // LLM response content
Error string `json:"error,omitempty"` // Error message on failure
CompletedAt time.Time `json:"completed_at,omitempty"` // When the loop completed
// Domain context propagated from TaskMessage through lifecycle events
Metadata map[string]any `json:"metadata,omitempty"`
}
LoopEntity represents an agentic loop instance
func NewLoopEntity ¶
func NewLoopEntity(id, taskID, role, model string, maxIterations ...int) LoopEntity
NewLoopEntity creates a new LoopEntity with default values
func (*LoopEntity) BeginAwaitingApproval ¶
func (e *LoopEntity) BeginAwaitingApproval(callID, toolName string, arguments map[string]any, reason string, timeout time.Duration, traceID string) error
BeginAwaitingApproval transitions the loop into LoopStateAwaitingApproval and stores the pending call. Returns an error if the loop is already terminal or already awaiting approval for a different call (which would indicate a logic bug — two rejections for the same loop shouldn't be possible while the first is still pending).
func (*LoopEntity) IncrementIteration ¶
func (e *LoopEntity) IncrementIteration() error
IncrementIteration increments the iteration counter
func (*LoopEntity) ResolveApproval ¶
func (e *LoopEntity) ResolveApproval() error
ResolveApproval clears the pending approval and restores the prior state so the loop can resume normal iteration. Caller is responsible for re-dispatching the tool (approve/modify) or synthesizing a rejection (reject) before invoking this.
func (*LoopEntity) TransitionTo ¶
func (e *LoopEntity) TransitionTo(newState LoopState) error
TransitionTo transitions the entity to a new state
func (*LoopEntity) Validate ¶
func (e *LoopEntity) Validate() error
Validate checks if the LoopEntity is valid
type LoopExecutionEntity ¶
type LoopExecutionEntity struct {
Org string
Platform string
LoopID string
Task *TaskMessage
}
LoopExecutionEntity is the Graphable origin contract for an agentic loop execution entity (ADR-056 W0 4c-pre-1). It encodes the spawn-identity triples that give the entity its typed origin: role, task, parent, run, reply_to, workflow, workflow_step, user, and description.
EntityID() and Triples() together form the typed origin contract — the same data set that processor/agentic-loop's buildSpawnIdentityTriples emitted, now expressed through graph.Graphable so it can be born via create_with_triples instead of triple.add_batch auto-vivification.
This type lives in the agentic package (below processor/agentic-loop in the import graph) to keep the dependency direction agentic → processor (never the reverse).
func (*LoopExecutionEntity) EntityID ¶
func (e *LoopExecutionEntity) EntityID() string
EntityID returns the canonical 6-part entity ID for this loop execution.
func (*LoopExecutionEntity) Triples ¶
func (e *LoopExecutionEntity) Triples() []message.Triple
Triples returns the spawn-identity origin triples for this loop execution. The predicate set is identical to what buildSpawnIdentityTriples in processor/agentic-loop produced: always-on (role, task), conditionally-on (parent, run, run.entity_id, reply_to, workflow, workflow_step, user, description) when the corresponding TaskMessage field is non-empty.
All triples share a single timestamp (captured once at call time) so that every triple in one batch is guaranteed to have the same wall-clock value — mirrors the shared-timestamp invariant preserved by buildSpawnIdentityTriples.
Returns nil when Task is nil.
type LoopFailedEvent ¶
type LoopFailedEvent struct {
LoopID string `json:"loop_id"`
TaskID string `json:"task_id"`
Outcome string `json:"outcome"` // OutcomeFailed
Reason string `json:"reason"`
Error string `json:"error"`
Role string `json:"role"`
Prompt string `json:"prompt,omitempty"` // Original user task prompt; enables NL/BM25 search
Model string `json:"model"`
Iterations int `json:"iterations"`
TokensIn int `json:"tokens_in"`
TokensOut int `json:"tokens_out"`
// ParentLoopID enables ancestry walks from failed loops — required by
// chain-aware failure handlers (e.g. semteams chainpause writing
// chain.paused.* triples to the canonical chain entity per ADR-038).
// Without this, the agent.loop.parent triple wasn't stamped on the
// failed loop, so an ancestry walk terminated at the failure and
// returned chain_id == failed_loop_id even when the failed loop
// wasn't the chain root. Populated at construction the same way
// LoopCompletedEvent.ParentLoopID is — entity.ParentLoopID flows
// through.
ParentLoopID string `json:"parent_loop,omitempty"`
WorkflowSlug string `json:"workflow_slug,omitempty"`
WorkflowStep string `json:"workflow_step,omitempty"`
FailedAt time.Time `json:"failed_at"`
// User routing info for error notifications
ChannelType string `json:"channel_type,omitempty"`
ChannelID string `json:"channel_id,omitempty"`
UserID string `json:"user_id,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
// RunID is the bare run loop-id this loop belongs to (ADR-053 D8).
// Empty when the loop is not part of a run.
RunID string `json:"run_id,omitempty"`
// RunEntityID is the full 6-part chain execution entity ID for the run.
// Empty when RunID is empty.
RunEntityID string `json:"run_entity_id,omitempty"`
}
LoopFailedEvent is published when a loop fails.
func (*LoopFailedEvent) MarshalJSON ¶
func (e *LoopFailedEvent) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler
func (*LoopFailedEvent) Schema ¶
func (e *LoopFailedEvent) Schema() message.Type
Schema implements message.Payload
func (*LoopFailedEvent) UnmarshalJSON ¶
func (e *LoopFailedEvent) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler
func (*LoopFailedEvent) Validate ¶
func (e *LoopFailedEvent) Validate() error
Validate implements message.Payload
type LoopState ¶
type LoopState string
LoopState represents the current state of an agentic loop
const ( // Standard workflow states LoopStateExploring LoopState = "exploring" LoopStatePlanning LoopState = "planning" LoopStateArchitecting LoopState = "architecting" LoopStateExecuting LoopState = "executing" LoopStateReviewing LoopState = "reviewing" // Terminal states LoopStateComplete LoopState = "complete" LoopStateFailed LoopState = "failed" LoopStateCancelled LoopState = "cancelled" // Cancelled by user signal // Signal-related states LoopStatePaused LoopState = "paused" // Paused by user signal LoopStateAwaitingApproval LoopState = "awaiting_approval" // Waiting for user approval )
Loop states for the agentic state machine. The state machine supports fluid transitions (can move backward) except from terminal states.
func (LoopState) IsTerminal ¶
IsTerminal returns true if the state is a terminal state
type ModelConfig ¶
type ModelConfig struct {
Temperature float64 `json:"temperature,omitempty"`
MaxTokens int `json:"max_tokens,omitempty"`
}
ModelConfig represents configuration for a language model
func (ModelConfig) WithDefaults ¶
func (c ModelConfig) WithDefaults() ModelConfig
WithDefaults returns a copy of the config with default values applied
type PendingApprovalState ¶
type PendingApprovalState struct {
CallID string `json:"call_id"`
ToolName string `json:"tool_name"`
Arguments map[string]any `json:"arguments,omitempty"`
Reason string `json:"reason,omitempty"` // Original "approval_required: ..." rejection reason
RequestedAt time.Time `json:"requested_at"` // When the rejection arrived and the loop paused
Timeout time.Duration `json:"timeout,omitempty"` // Auto-reject deadline; zero means wait indefinitely
TraceID string `json:"trace_id,omitempty"` // Propagated for audit correlation
}
PendingApprovalState captures the gated tool call so the loop can re-dispatch (or reject) it once a human approval response arrives. Persisted on LoopEntity so a process restart mid-approval still remembers what the human is reviewing.
type ReasoningCarrierKind ¶
type ReasoningCarrierKind string
ReasoningCarrierKind enumerates the structural attachment shapes a provider's reasoning blob can take on the wire. Closed set: adapters must handle every value, and unknown values are an authoring error.
const ( // ReasoningCarrierToolCall indicates the blob attaches to a // specific tool call on the wire. Used by Gemini (thought_signature // per tool_call). ReasoningCarrierToolCall ReasoningCarrierKind = "tool_call" // ReasoningCarrierStandaloneItem indicates the blob is a sibling // output item with no attachment to messages or tool calls. Used // by OpenAI Responses (reasoning items in the output array). ReasoningCarrierStandaloneItem ReasoningCarrierKind = "standalone_item" // ReasoningCarrierAssistantContent indicates the blob is a content // part inside the assistant message. Reserved for Anthropic's // thinking blocks when/if we take that on. ReasoningCarrierAssistantContent ReasoningCarrierKind = "assistant_content" )
type ReasoningRecord ¶
type ReasoningRecord struct {
// Provider names the carrier provider. The adapter uses this to
// decide on-the-wire reconstruction. Known values: "google",
// "openai". Future providers extend the set.
Provider string `json:"provider"`
// ItemID is the provider-assigned identity for this record.
// Used for cross-turn echo when the provider needs it. OpenAI
// Responses uses it (id of the reasoning item); Gemini does not
// (signatures are carried per-tool-call, not by id).
ItemID string `json:"item_id,omitempty"`
// SummaryText is a human-readable description of the reasoning,
// when the provider exposes one. Safe to log. Used for trajectory
// and operator-facing trace.
SummaryText string `json:"summary_text,omitempty"`
// Opaque is the provider-specific blob that must be echoed back
// verbatim. Treat as bytes; do not parse, do not log in full.
// - Gemini: the base64 thought_signature string (bytes of UTF-8)
// - OpenAI: encrypted_content blob (when store:false)
// - Anthropic (future): thinking block content
Opaque []byte `json:"opaque,omitempty"`
// CarrierKind names the structural attachment constraint on the
// wire. Adapters use this to reshape correctly. The set is closed:
// unknown values are an authoring error and must fail validation,
// not silently pass.
CarrierKind ReasoningCarrierKind `json:"carrier_kind"`
// ToolCallID is set when CarrierKind == ReasoningCarrierToolCall.
// The signature belongs with this specific tool call; the adapter
// re-binds them on send.
ToolCallID string `json:"tool_call_id,omitempty"`
}
ReasoningRecord is the provider-neutral carrier for opaque reasoning state that must be echoed back on the next turn. Captured on response, attached to the next request. Provider-specific reshape happens at the adapter seam — loop code stays shape-neutral.
Replaces the per-provider MetadataKeyGoogleThoughtSignature carrier retired in ADR-051 Phase 1. The semantic role ("opaque blob the model wants echoed on the next turn for reasoning continuity") is stable across providers; the wire shape is not, so the abstraction lives at the role layer.
type ResponseAction ¶
type ResponseAction struct {
ID string `json:"id"`
Type string `json:"type"` // button, reaction
Label string `json:"label"`
Signal string `json:"signal"` // signal to send if clicked
Style string `json:"style"` // primary, danger, secondary
}
ResponseAction represents an interactive action in a response
type ResponseBlock ¶
type ResponseBlock struct {
Type string `json:"type"` // text, code, diff, file, progress
Content string `json:"content"`
Lang string `json:"lang,omitempty"` // for code blocks
}
ResponseBlock represents a block of content in a rich response
type ResponseFormat ¶
type ResponseFormat struct {
// Type: ResponseFormatJSONObject (legacy, bare JSON validity) or
// ResponseFormatJSONSchema (current standard, strict-mode schema
// adherence). Empty is invalid; callers must set one.
Type string `json:"type"`
// Schema is the JSON Schema document. Required when Type ==
// ResponseFormatJSONSchema; ignored when Type ==
// ResponseFormatJSONObject. Must be a valid OpenAI Structured Outputs
// schema (subset of JSON Schema — no $ref to external schemas, no
// anyOf at root, every property in required, additionalProperties:
// false). Adapters do not validate locally; trust the caller.
Schema map[string]any `json:"schema,omitempty"`
// Name is required by OpenAI when Type == ResponseFormatJSONSchema.
// Should describe the output (e.g. "decide_action_args"). Adapters
// that don't need a name ignore it.
Name string `json:"name,omitempty"`
// Strict enables OpenAI's strict mode (response is guaranteed
// schema-conformant; sampling is constrained). Defaults true via
// NewJSONSchemaFormat. Set false to opt into permissive mode (rare;
// mostly for compat testing).
Strict bool `json:"strict"`
}
ResponseFormat constrains the model's output to a JSON object or JSON-schema-conformant structure. Maps to OpenAI's response_format on OpenAI-compatible providers; translated to provider-specific equivalents on others (Ollama: native format field; Gemini: stubbed for v1; Anthropic: stubbed for v1, future translation to forced single-tool-call). See ADR-034.
Nil on AgentRequest means no structuring constraint — current behavior; tool-calling remains the structured-output primitive for personas that work fine on cloud providers without response_format.
func NewJSONObjectFormat ¶
func NewJSONObjectFormat() *ResponseFormat
NewJSONObjectFormat constructs a ResponseFormat that requires valid JSON output without imposing a schema. Less reliable than JSON-schema mode on small models; prefer NewJSONSchemaFormat when a schema is available.
func NewJSONSchemaFormat ¶
func NewJSONSchemaFormat(name string, schema map[string]any) *ResponseFormat
NewJSONSchemaFormat constructs a strict-mode JSON-schema ResponseFormat. Strict defaults to true — the OpenAI Structured Outputs guarantee. Pass the schema directly; do not wrap in an outer json_schema envelope (the adapter layer handles wire-shape translation per provider).
func (*ResponseFormat) Validate ¶
func (rf *ResponseFormat) Validate() error
Validate checks if the ResponseFormat has a valid type and required fields.
type TaskMessage ¶
type TaskMessage struct {
LoopID string `json:"loop_id,omitempty"` // loop to continue, or empty for new
TaskID string `json:"task_id"`
Role string `json:"role"`
Model string `json:"model"`
Prompt string `json:"prompt"`
// Workflow context (optional, set by workflow commands)
WorkflowSlug string `json:"workflow_slug,omitempty"` // e.g., "add-user-auth"
WorkflowStep string `json:"workflow_step,omitempty"` // e.g., "design"
// User routing info (optional, for error notifications)
ChannelType string `json:"channel_type,omitempty"` // e.g., "http", "cli", "slack"
ChannelID string `json:"channel_id,omitempty"` // session/channel identifier
UserID string `json:"user_id,omitempty"` // user who initiated the request
// Multi-agent hierarchy (optional, for parallel/nested agents)
ParentLoopID string `json:"parent_loop_id,omitempty"` // Parent loop ID for nested agents
// RunID is the 6-part-derived run anchor: the run loop-id this loop belongs to.
// Empty for loops not in a run. Inherited at spawn (ADR-053 D7).
RunID string `json:"run_id,omitempty"`
Depth int `json:"depth,omitempty"` // Current depth in agent tree (0 = root)
MaxDepth int `json:"max_depth,omitempty"` // Maximum allowed depth
// MaxIterations is an optional per-spawn iteration budget (gh#528). Nil
// means "use the component default" (agentic-loop's Config.MaxIterations).
// A non-nil value narrows the spawned loop's budget: agentic-loop computes
// the effective ceiling as min(*MaxIterations, component ceiling) at loop
// creation — a spawn may narrow its budget, never widen it past the
// operator-configured ceiling. Validate rejects a non-nil value below 1.
// The publish_agent rule action exposes this as loop_max_iterations
// (distinct from the action's own firing-cap max_iterations field).
MaxIterations *int `json:"max_iterations,omitempty"`
// InReplyTo is the bare loop-id this task is a reply to (gh#256). When
// set, agentic-loop stamps an agent.loop.reply_to triple (a 6-part loop
// entity reference, mirroring agent.loop.parent) on the spawned loop so a
// rule can detect a reply via $entity.triple.agent.loop.reply_to. Empty
// for non-reply tasks. Distinct from ParentLoopID (tree ancestry) — a
// reply re-enters a paused run rather than nesting under a parent.
InReplyTo string `json:"in_reply_to,omitempty"`
// Pre-constructed context (optional, skips discovery if present)
// When set, the agent loop uses this context directly instead of hydrating
Context *types.ConstructedContext `json:"context,omitempty"`
// Context assembly reference (links to assembled context)
ContextRequestID string `json:"context_request_id,omitempty"`
// Tools is a per-task tool override. The spawner sets this to scope
// which tools the agent may call; the loop consumes the field with
// nil-vs-empty semantics:
// - nil → no override, loop falls back to global discovery.
// - non-nil → explicit allowlist. Empty slice means "no tools".
// `omitempty` is deliberately omitted so an explicit empty slice
// round-trips as `"tools": []` and the receiver can distinguish it
// from an absent field.
Tools []ToolDefinition `json:"tools"`
// ToolChoice controls how the model selects tools for this task.
// Nil means "auto" (model decides). Cached for all iterations in the loop.
ToolChoice *ToolChoice `json:"tool_choice,omitempty"`
// Domain context propagated to all tool calls in this loop
Metadata map[string]any `json:"metadata,omitempty"`
// Timeout caps LLM calls issued for this task. Go duration string
// (e.g. "30s"). Empty means fall through to endpoint, capability, or
// component-level timeout. Highest precedence when set.
Timeout string `json:"timeout,omitempty"`
// ResponseFormat constrains the model's output to a JSON object or
// JSON-schema-conformant JSON for this task. ADR-034. The agentic-loop
// caches it on initial build and threads it onto every AgentRequest in
// the loop. Nil means tool-calling behaviour is unchanged. Set this
// from a rule.Action (publish_agent path) or from a dispatcher when
// the task needs structured output.
ResponseFormat *ResponseFormat `json:"response_format,omitempty"`
}
TaskMessage represents a task to be executed by an agentic loop
func (*TaskMessage) MarshalJSON ¶
func (t *TaskMessage) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler
func (*TaskMessage) Schema ¶
func (t *TaskMessage) Schema() message.Type
Schema implements message.Payload
func (*TaskMessage) UnmarshalJSON ¶
func (t *TaskMessage) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler
func (TaskMessage) Validate ¶
func (t TaskMessage) Validate() error
Validate checks if the TaskMessage is valid
type TokenUsage ¶
type TokenUsage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
}
TokenUsage tracks token consumption for a request
func (TokenUsage) Total ¶
func (u TokenUsage) Total() int
Total returns the total number of tokens used
type ToolCall ¶
type ToolCall struct {
ID string `json:"id"`
Name string `json:"name"`
Arguments map[string]any `json:"arguments,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"` // Domain context, propagated from task
LoopID string `json:"loop_id,omitempty"`
TraceID string `json:"trace_id,omitempty"`
// ApprovedBy is set by the loop when re-dispatching a previously
// gated tool call after receiving an ApprovalResponse. The
// agentic-tools approval filter recognises a non-empty ApprovedBy
// as the explicit bypass token (see C5). Empty means the call has
// not been through human approval — normal filter rules apply.
ApprovedBy string `json:"approved_by,omitempty"`
}
ToolCall represents a request to call a tool
func (*ToolCall) MarshalJSON ¶
MarshalJSON implements json.Marshaler
func (*ToolCall) UnmarshalJSON ¶
UnmarshalJSON implements json.Unmarshaler
type ToolChoice ¶
type ToolChoice struct {
Mode string `json:"mode"` // "auto", "required", "none", "function"
FunctionName string `json:"function_name,omitempty"` // required when Mode is "function"
}
ToolChoice controls how the model selects tools. Mode is one of: "auto" (default), "required", "none", or "function". When Mode is "function", FunctionName specifies which function to call.
func (ToolChoice) Validate ¶
func (tc ToolChoice) Validate() error
Validate checks if the ToolChoice has a valid mode and required fields.
type ToolDefinition ¶
type ToolDefinition struct {
Name string `json:"name"`
Description string `json:"description"`
Parameters map[string]any `json:"parameters"`
// Strict enables OpenAI's strict-mode tool calling: the model is
// constrained to emit tool_calls[].function.arguments that conform to
// Parameters. Symmetric to ResponseFormat.Strict — same provider table
// applies (see ADR-034): honored on OpenAI proper / vLLM / OpenRouter /
// sparky / any OpenAI-compat runtime under provider:"openai"; silently
// ignored on Anthropic and Gemini OpenAI-compat (adapters clear it +
// Warn). Best-effort on Ollama /v1 (model-dependent — gemma3 ignores
// per ADR-034 §gh#10001).
//
// Requires Parameters to satisfy OpenAI's strict-mode subset:
// additionalProperties:false at every object level, every property
// listed in required, no $ref/anyOf at the root, max nesting 5. A
// non-conforming schema returns 400 from the upstream — caller bug,
// not a framework concern.
Strict bool `json:"strict,omitempty"`
// Paginated declares that this tool supports continuation paging via
// the agentic.MetadataKey{HasMore,NextOffset,NextCursor} contract.
// When true, the executor MUST set MetadataKeyHasMore on every
// successful result (bool false for last page, bool true with one
// of NextOffset or NextCursor for intermediate pages). The agent
// loop reads has_more in buildToolMessages and appends a canonical
// continuation hint to the model's next message — telling the
// model it can call the same tool again with the supplied
// continuation token instead of having to re-narrow blind.
//
// Informational at the wire level today: the loop branches on the
// actual has_more value in result metadata, not on this flag. Future
// uses include operator introspection ("which tools paginate?") and
// loop-side contract-violation warnings when has_more arrives from
// a tool that didn't declare Paginated.
Paginated bool `json:"paginated,omitempty"`
}
ToolDefinition represents the definition of a tool that can be called
func (ToolDefinition) Validate ¶
func (t ToolDefinition) Validate() error
Validate checks if the ToolDefinition is valid
type ToolErrorKind ¶
type ToolErrorKind string
ToolErrorKind classifies the source or nature of a tool execution failure. It is the structured counterpart to ToolResult.Error and feeds the agent.step.error_category graph predicate for queryable failure analysis.
const ( // ToolErrorTimeout means the tool exceeded its execution deadline // (context.DeadlineExceeded observed after the executor returned). ToolErrorTimeout ToolErrorKind = "timeout" // ToolErrorNotFound means the tool was not registered, the requested // resource did not exist, or the caller was not permitted to invoke it // via the component allowlist. ToolErrorNotFound ToolErrorKind = "not_found" // ToolErrorInvalidArgs means tool arguments failed validation // (missing required field, wrong type, schema violation). ToolErrorInvalidArgs ToolErrorKind = "invalid_args" // ToolErrorPermission means the request was refused on authorization // grounds — an external system's auth failure (e.g., HTTP 401/403) or an // internal framework policy refusal (approval filter, per-loop advertised // tool set). ToolErrorPermission ToolErrorKind = "permission" // ToolErrorNetwork means a transport-level failure occurred // (dial error, connection reset, DNS failure). ToolErrorNetwork ToolErrorKind = "network" // ToolErrorExternal means an external service returned a failure // that does not fall into the other categories (5xx, 429 rate limit, // operation-specific failure from an upstream API). ToolErrorExternal ToolErrorKind = "external" // ToolErrorInternal means an executor-internal bug // (marshal/unmarshal failure, unexpected nil, invariant violation). ToolErrorInternal ToolErrorKind = "internal" // ToolErrorUnknown means the failure was not classified. Used as the // default when a ToolResult has a non-empty Error but no ErrorKind. ToolErrorUnknown ToolErrorKind = "unknown" )
type ToolResult ¶
type ToolResult struct {
CallID string `json:"call_id"`
Name string `json:"name,omitempty"` // Tool function name (required by Gemini on tool result messages)
Content string `json:"content,omitempty"`
Error string `json:"error,omitempty"`
ErrorKind ToolErrorKind `json:"error_kind,omitempty"` // Structured classification of the failure
ResultHint ToolResultHint `json:"result_hint,omitempty"` // Structured action recommendation when call worked but agent should refine
Metadata map[string]any `json:"metadata,omitempty"`
LoopID string `json:"loop_id,omitempty"`
TraceID string `json:"trace_id,omitempty"`
StopLoop bool `json:"stop_loop,omitempty"` // Signal loop termination; Content becomes the completion result
}
ToolResult represents the result of a tool call
func (*ToolResult) MarshalJSON ¶
func (t *ToolResult) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler
func (*ToolResult) Schema ¶
func (t *ToolResult) Schema() message.Type
Schema implements message.Payload
func (*ToolResult) UnmarshalJSON ¶
func (t *ToolResult) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler
func (ToolResult) Validate ¶
func (t ToolResult) Validate() error
Validate checks if the ToolResult is valid
type ToolResultHint ¶
type ToolResultHint string
ToolResultHint classifies non-error conditions on a SUCCESSFUL tool call where the agent should refine its approach before continuing. It is the structured sibling of ToolErrorKind: ErrorKind classifies errors (the tool failed), Hint classifies successes that returned data the agent should treat as a signal to adjust.
Distinct from ToolErrorKind because the call worked — there's nothing to "retry" in the failure sense. The cases are advisory: the model should narrow, broaden, or introspect on the next turn. The agent loop reads Hint in buildToolMessages and prepends a canonical hint line to the model's next message so small/mid-tier models don't have to parse free-form English advice from a successful result body.
Pre-2026-05-11, the only in-band signaling pattern was ApprovalRequiredPrefix — a stringly-typed magic-string sniffed off the Error field. ResultHint replaces that pattern's spirit with a typed enum: producers set it directly; consumers branch on the typed value.
const ( // HintTooLarge means the call returned more data than the executor // or framework permitted and the content was truncated (or the // raw response would have exceeded an internal cap). Action: the // model should narrow its query — add a filter, an entity_id, or // a smaller limit. Composes with the pagination contract // (MetadataKeyHasMore) when both are set: the model gets BOTH // "narrow your query" AND "or continue with cursor=..." in one // shot. HintTooLarge ToolResultHint = "too_large" // HintEmpty means the call succeeded with an empty result set // (no entities matched the filter, search returned zero hits). // Action: the model should try a broader filter, drop one of // the predicates, or invoke a different tool to find candidate // entities. Distinct from ToolErrorNotFound — empty results // from a well-formed query is not an error. HintEmpty ToolResultHint = "empty" // HintSyntaxError means the tool's query-language parser // rejected the request. Distinct from ToolErrorInvalidArgs — // InvalidArgs is the AGENT's arguments failing JSON-schema // validation at the framework boundary; SyntaxError is the // TOOL's deeper parse of the argument content (e.g. the // graph-query DSL itself rejecting a malformed expression). // Action: the model should call an introspect/help facility // on the tool before retrying with the same shape. HintSyntaxError ToolResultHint = "syntax_error" )
type Trajectory ¶
type Trajectory struct {
LoopID string `json:"loop_id"`
StartTime time.Time `json:"start_time"`
EndTime *time.Time `json:"end_time,omitempty"`
Steps []TrajectoryStep `json:"steps"`
Outcome string `json:"outcome,omitempty"`
TotalTokensIn int `json:"total_tokens_in"`
TotalTokensOut int `json:"total_tokens_out"`
Duration int64 `json:"duration"` // milliseconds
}
Trajectory represents the complete execution path of an agentic loop
func NewTrajectory ¶
func NewTrajectory(loopID string) Trajectory
NewTrajectory creates a new Trajectory with initialized values
func (*Trajectory) AddStep ¶
func (t *Trajectory) AddStep(step TrajectoryStep)
AddStep adds a step to the trajectory and updates totals. Compaction steps are excluded from token totals because their TokensIn/Out represent evicted/summarized tokens, not new LLM API consumption. Including them would double-count tokens already tallied by prior model_call steps.
func (*Trajectory) Complete ¶
func (t *Trajectory) Complete(outcome string)
Complete marks the trajectory as complete and calculates final duration
type TrajectoryListItem ¶
type TrajectoryListItem struct {
LoopID string `json:"loop_id"`
TaskID string `json:"task_id"`
Outcome string `json:"outcome,omitempty"`
Role string `json:"role"`
Model string `json:"model"`
WorkflowSlug string `json:"workflow_slug,omitempty"`
WorkflowStep string `json:"workflow_step,omitempty"`
Iterations int `json:"iterations"`
TotalTokensIn int `json:"total_tokens_in"`
TotalTokensOut int `json:"total_tokens_out"`
Duration int64 `json:"duration"`
StartTime time.Time `json:"start_time"`
EndTime *time.Time `json:"end_time,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
}
TrajectoryListItem is a summary of a trajectory for list responses. Contains loop metadata and aggregate metrics but no individual steps.
type TrajectoryListResponse ¶
type TrajectoryListResponse struct {
Trajectories []TrajectoryListItem `json:"trajectories"`
Total int `json:"total"`
}
TrajectoryListResponse is the response format for trajectory list queries.
type TrajectoryStep ¶
type TrajectoryStep struct {
Timestamp time.Time `json:"timestamp"`
StepType string `json:"step_type"`
RequestID string `json:"request_id,omitempty"`
Prompt string `json:"prompt,omitempty"`
Response string `json:"response,omitempty"`
TokensIn int `json:"tokens_in,omitempty"`
TokensOut int `json:"tokens_out,omitempty"`
ToolName string `json:"tool_name,omitempty"`
ToolArguments map[string]any `json:"tool_arguments,omitempty"`
ToolResult string `json:"tool_result,omitempty"`
Duration int64 `json:"duration"` // milliseconds
Messages []ChatMessage `json:"messages,omitempty"` // Full request messages (detail=full)
ToolCalls []ToolCall `json:"tool_calls,omitempty"` // Assistant tool calls (detail=full)
Model string `json:"model,omitempty"` // Model used
Provider string `json:"provider,omitempty"` // LLM provider (anthropic, openai, etc.)
Capability string `json:"capability,omitempty"` // Role/purpose (coding, planning, reviewing, etc.)
RetryCount int `json:"retry_count,omitempty"` // Number of retries before success
Utilization float64 `json:"utilization,omitempty"` // Context utilization at compaction trigger (0.0-1.0)
// Tool outcome fields, populated for tool_call steps. Emitted as graph
// triples (agent.step.tool_status / error_message / error_category) so
// ops agents can query failure rates by tool and category.
ToolStatus string `json:"tool_status,omitempty"` // "success" | "failed"
ErrorMessage string `json:"error_message,omitempty"` // Raw error text; omitted on success
ErrorCategory string `json:"error_category,omitempty"` // ToolErrorKind string form; omitted on success
// URLsFetched lists external URLs a bash step fetched (curl/wget/httpie),
// derived from the command string (gh#146). Lets citation/audit/governance
// dashboards filter and count external reach independently of generic shell
// activity. Empty/omitted when the step fetched no URL.
URLsFetched []string `json:"url_fetched,omitempty"`
}
TrajectoryStep represents a single step in an agentic trajectory
func (TrajectoryStep) Validate ¶
func (s TrajectoryStep) Validate() error
Validate checks if the TrajectoryStep is valid
type TrajectoryStepEntity ¶
type TrajectoryStepEntity struct {
Step TrajectoryStep
Org string
Platform string
LoopID string
StepIndex int
// contains filtered or unexported fields
}
TrajectoryStepEntity wraps a TrajectoryStep with the context needed to produce a graph entity. It implements message.ContentStorable so that large content (tool results, model responses) is stored in ObjectStore while metadata-only triples go into the graph.
func (*TrajectoryStepEntity) ContentFields ¶
func (e *TrajectoryStepEntity) ContentFields() map[string]string
ContentFields returns the semantic role to field name mapping. This tells embedding workers which fields to use for text extraction.
func (*TrajectoryStepEntity) EntityID ¶
func (e *TrajectoryStepEntity) EntityID() string
EntityID returns the 6-part entity ID for this trajectory step.
func (*TrajectoryStepEntity) RawContent ¶
func (e *TrajectoryStepEntity) RawContent() map[string]string
RawContent returns the content to store in ObjectStore. Field names here match the values in ContentFields().
func (*TrajectoryStepEntity) SetStorageRef ¶
func (e *TrajectoryStepEntity) SetStorageRef(ref *message.StorageReference)
SetStorageRef sets the ObjectStore reference after content is stored.
func (*TrajectoryStepEntity) StorageRef ¶
func (e *TrajectoryStepEntity) StorageRef() *message.StorageReference
StorageRef returns the reference to stored content in ObjectStore.
func (*TrajectoryStepEntity) Triples ¶
func (e *TrajectoryStepEntity) Triples() []message.Triple
Triples returns metadata-only triples for this step. Large content (tool args/results, model responses) is NOT included — that goes to ObjectStore via RawContent/ContentFields.
type UserMessage ¶
type UserMessage struct {
// Identity
MessageID string `json:"message_id"`
ChannelType string `json:"channel_type"` // cli, slack, discord, web
ChannelID string `json:"channel_id"` // specific conversation/channel
UserID string `json:"user_id"`
// Content
Content string `json:"content"`
Attachments []Attachment `json:"attachments,omitempty"`
// Context
ReplyTo string `json:"reply_to,omitempty"` // loop_id if continuing
ThreadID string `json:"thread_id,omitempty"` // for threaded channels
Metadata map[string]string `json:"metadata,omitempty"` // channel-specific
ContextRequestID string `json:"context_request_id,omitempty"` // links to assembled context
// Resumable-reply context (gh#256). These are distinct from ReplyTo:
// ReplyTo routes the message to a loop to continue; the two below let a
// reply re-enter and resume a *paused run*.
//
// RunID is the bare run anchor the reply should re-attach to. A client
// resuming a paused run (ADR-053) echoes the RunID it held from the pause
// state so the resumed loop carries agent.loop.run / agent.run.entity-id even
// when the prior loop entity was evicted during the pause. Empty for
// non-run submissions.
RunID string `json:"run_id,omitempty"`
// InReplyTo marks this message as a reply to a specific loop's question
// (e.g. an ask_user clarification), stamped onto the resumed loop as the
// agent.loop.reply_to triple so a rule can fire on it. Deliberately
// separate from ReplyTo so ordinary continuations are NOT marked as
// replies. Empty for non-reply submissions.
InReplyTo string `json:"in_reply_to,omitempty"`
// Timing
Timestamp time.Time `json:"timestamp"`
}
UserMessage represents normalized input from any channel (CLI, Slack, Discord, web)
func (*UserMessage) MarshalJSON ¶
func (m *UserMessage) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler
func (*UserMessage) Schema ¶
func (m *UserMessage) Schema() message.Type
Schema implements message.Payload
func (*UserMessage) UnmarshalJSON ¶
func (m *UserMessage) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler
func (UserMessage) Validate ¶
func (m UserMessage) Validate() error
Validate checks if the UserMessage is valid
type UserResponse ¶
type UserResponse struct {
ResponseID string `json:"response_id"`
ChannelType string `json:"channel_type"`
ChannelID string `json:"channel_id"`
UserID string `json:"user_id"` // who to respond to
// What we're responding to
InReplyTo string `json:"in_reply_to,omitempty"` // message_id or loop_id
ThreadID string `json:"thread_id,omitempty"`
// Content
Type string `json:"type"` // text, status, result, error, prompt, stream
Content string `json:"content"`
// Rich content (optional)
Blocks []ResponseBlock `json:"blocks,omitempty"`
Actions []ResponseAction `json:"actions,omitempty"`
Timestamp time.Time `json:"timestamp"`
}
UserResponse is sent back to users via their channel
func (*UserResponse) MarshalJSON ¶
func (r *UserResponse) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler
func (*UserResponse) Schema ¶
func (r *UserResponse) Schema() message.Type
Schema implements message.Payload
func (*UserResponse) UnmarshalJSON ¶
func (r *UserResponse) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler
func (UserResponse) Validate ¶
func (r UserResponse) Validate() error
Validate checks if the UserResponse is valid
type UserSignal ¶
type UserSignal struct {
SignalID string `json:"signal_id"`
Type string `json:"type"` // cancel, pause, resume, approve, reject, feedback, retry
LoopID string `json:"loop_id"`
UserID string `json:"user_id"`
ChannelType string `json:"channel_type"`
ChannelID string `json:"channel_id"`
Payload any `json:"payload,omitempty"` // signal-specific data (e.g., rejection reason)
Timestamp time.Time `json:"timestamp"`
}
UserSignal represents a control signal from user to affect loop execution
func (*UserSignal) MarshalJSON ¶
func (s *UserSignal) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler
func (*UserSignal) Schema ¶
func (s *UserSignal) Schema() message.Type
Schema implements message.Payload
func (*UserSignal) UnmarshalJSON ¶
func (s *UserSignal) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler
func (UserSignal) Validate ¶
func (s UserSignal) Validate() error
Validate checks if the UserSignal is valid
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package agentrun implements the AgentRun lifecycle Participant (ADR-053 D1–D6).
|
Package agentrun implements the AgentRun lifecycle Participant (ADR-053 D1–D6). |
|
Package identity provides local DID-based cryptographic identity primitives.
|
Package identity provides local DID-based cryptographic identity primitives. |
|
Package research defines the payload types for the ADR-045 graph search rule chain: Intent, SearchResult, and RouteDecision.
|
Package research defines the payload types for the ADR-045 graph search rule chain: Intent, SearchResult, and RouteDecision. |