diff --git a/docs.json b/docs.json
index 0c6f771..281bd74 100644
--- a/docs.json
+++ b/docs.json
@@ -80,13 +80,16 @@
"group": "Getting Started",
"pages": [
"modus/overview",
- "modus/project-structure",
- "modus/quickstart"
+ "modus/quickstart",
+ "modus/functions",
+ "modus/agents",
+ "modus/knowledge-graphs"
]
},
{
"group": "Building Your App",
"pages": [
+ "modus/project-structure",
"modus/app-manifest",
"modus/data-fetching",
"modus/model-invoking",
@@ -107,6 +110,7 @@
"group": "AssemblyScript SDK",
"pages": [
"modus/sdk/assemblyscript/overview",
+ "modus/sdk/assemblyscript/agents",
"modus/sdk/assemblyscript/console",
"modus/sdk/assemblyscript/dgraph",
"modus/sdk/assemblyscript/graphql",
@@ -122,6 +126,7 @@
"group": "Go SDK",
"pages": [
"modus/sdk/go/overview",
+ "modus/sdk/go/agents",
"modus/sdk/go/console",
"modus/sdk/go/dgraph",
"modus/sdk/go/graphql",
diff --git a/modus/agents.mdx b/modus/agents.mdx
new file mode 100644
index 0000000..3d59bec
--- /dev/null
+++ b/modus/agents.mdx
@@ -0,0 +1,791 @@
+---
+title: "What is an Agent?"
+description: "Learn about stateful agents in Modus"
+"og:title": "What is an Agent? - Modus"
+---
+
+## Agents in Modus
+
+Agents in Modus are persistent background processes that maintain memory across
+interactions. Unlike stateless functions that lose everything when operations
+end, agents remember every detail, survive system failures, and never lose their
+operational context.
+
+## Key characteristics
+
+- **Stateful**: Maintains memory and context across interactions
+- **Persistent**: Automatically saves and restores state
+- **Resilient**: Graceful recovery from failures
+- **Autonomous**: Can operate independently over extended periods
+- **Actor-based**: Each agent instance runs in isolation
+- **Event-driven**: Streams real-time updates and operational intelligence
+
+## When to use agents
+
+Agents are perfect for:
+
+- **Multi-turn workflows** spanning multiple interactions
+- **Long-running processes** that maintain context over time
+- **Stateful operations** that need to remember previous actions
+- **Complex coordination** between different system components
+- **Persistent monitoring** that tracks changes over time
+- **Real-time operations** requiring live status updates and event streaming
+
+## Agent structure
+
+Every agent starts with the essential framework:
+
+```go
+package main
+
+import (
+ "fmt"
+ "strings"
+ "time"
+ "github.com/hypermodeinc/modus/sdk/go/pkg/agents"
+ "github.com/hypermodeinc/modus/sdk/go/pkg/models"
+ "github.com/hypermodeinc/modus/sdk/go/pkg/models/openai"
+)
+
+type IntelligenceAgent struct {
+ agents.AgentBase
+
+ // The rest of the fields make up the agent's state and can be customized per agent
+ intelligenceReports []string // Matrix surveillance data
+ threatLevel float64 // Current threat assessment
+ lastContact time.Time
+ currentMission *MissionPhase // Track long-running operations
+ missionLog []string // Operational progress log
+}
+
+type MissionPhase struct {
+ Name string
+ StartTime time.Time
+ Duration time.Duration
+ Complete bool
+}
+
+func (a *IntelligenceAgent) Name() string {
+ return "IntelligenceAgent"
+}
+```
+
+The agent embeds `agents.AgentBase`, which provides all the infrastructure for
+state management, secure communications, and persistence. Your app
+data—intelligence reports, threat assessments, contact logs—lives as fields in
+the struct, automatically preserved across all interactions.
+
+## Creating agents through functions
+
+Agents are created and managed through regular Modus functions that become part
+of your GraphQL API. These functions handle agent lifecycle operations:
+
+```go
+// Register your agent type during initialization
+func init() {
+ agents.Register(&IntelligenceAgent{})
+}
+
+// Create a new agent instance - this becomes a GraphQL mutation
+func DeployAgent() (string, error) {
+ agentInfo, err := agents.Start("IntelligenceAgent")
+ if err != nil {
+ return "", err
+ }
+
+ // Return the agent ID - clients must store this to communicate with the agent
+ return agentInfo.Id, nil
+}
+```
+
+When you call this function through GraphQL, it returns a unique agent ID:
+
+```graphql
+mutation {
+ deployAgent
+}
+```
+
+Response:
+
+```json
+{
+ "data": {
+ "deployAgent": "agent_neo_001"
+ }
+}
+```
+
+You can think of an Agent as a persistent server process with durable memory.
+Once created, you can reference your agent by its ID across sessions, page
+reloads, and even system restarts. The agent maintains its complete state and
+continues operating exactly where it left off.
+
+
+ **Agent builders and visual workflows:** We're actively developing Agent
+ Builder tools and "eject to code" features that generate complete agent
+ deployments from visual workflows. These tools automatically create the
+ deployment functions and agent management code for complex multi-agent
+ systems.
+
+
+## Communicating with agents
+
+Once created, you communicate with agents using their unique ID. Create
+functions that send messages to specific agent instances:
+
+```go
+func ImportActivity(agentId string, activityData string) (string, error) {
+ result, err := agents.SendMessage(
+ agentId,
+ "matrix_surveillance",
+ agents.WithData(activityData),
+ )
+ if err != nil {
+ return "", err
+ }
+ if result == nil {
+ return "", fmt.Errorf("no response from agent")
+ }
+ return *result, nil
+}
+
+func GetThreatStatus(agentId string) (string, error) {
+ result, err := agents.SendMessage(agentId, "threat_assessment")
+ if err != nil {
+ return "", err
+ }
+ if result == nil {
+ return "", fmt.Errorf("no response from agent")
+ }
+ return *result, nil
+}
+```
+
+These functions become GraphQL operations that you can call with your agent's
+ID:
+
+```graphql
+mutation {
+ importActivity(
+ agentId: "agent_neo_001"
+ activityData: "Anomalous Agent Smith replication detected in Sector 7"
+ )
+}
+```
+
+Response:
+
+```json
+{
+ "data": {
+ "importActivity": "Matrix surveillance complete.
+ Agent Smith pattern matches previous incident in the Loop.
+ Threat level: 0.89 based on 3 intelligence reports.
+ Recommend immediate evasive protocols."
+ }
+}
+```
+
+```graphql
+query {
+ getThreatStatus(agentId: "agent_neo_001")
+}
+```
+
+Response:
+
+```json
+{
+ "data": {
+ "getThreatStatus": "Current threat assessment:
+ 3 intelligence reports analyzed.
+ Threat level: 0.89.
+ Agent operational in the Matrix."
+ }
+}
+```
+
+The agent receives the message, processes it using its internal state and AI
+reasoning, updates its intelligence database, and returns a response—all while
+maintaining persistent memory of every interaction.
+
+## Agent message handling
+
+Agents process requests through their message handling system:
+
+```go
+func (a *IntelligenceAgent) OnReceiveMessage(
+ msgName string,
+ data string,
+) (*string, error) {
+ switch msgName {
+ case "matrix_surveillance":
+ return a.analyzeMatrixActivity(data)
+ case "background_reconnaissance":
+ return a.performBackgroundRecon(data)
+ case "threat_assessment":
+ return a.getThreatAssessment()
+ case "get_status":
+ return a.getOperationalStatus()
+ case "intelligence_history":
+ return a.getIntelligenceHistory()
+ default:
+ return nil, fmt.Errorf("unrecognized directive: %s", msgName)
+ }
+}
+```
+
+Each message type triggers specific operations, with all data automatically
+maintained in the agent's persistent memory.
+
+## Processing operations with AI intelligence
+
+Here's how agents handle operations while maintaining persistent state and using
+AI models for analysis:
+
+```go
+func (a *IntelligenceAgent) analyzeMatrixActivity(data string) (*string, error) {
+ // Store new intelligence in persistent memory
+ a.intelligenceReports = append(a.intelligenceReports, *data)
+ a.lastContact = time.Now()
+
+ // Build context from all accumulated intelligence
+ accumulatedReports := strings.Join(a.intelligenceReports, "\n")
+
+ // AI analysis using complete operational history
+ model, err := models.GetModel[openai.ChatModel]("analyst-model")
+ if err != nil {
+ return nil, err
+ }
+
+ systemPrompt := `You are a resistance operative in the Matrix.
+ Analyze patterns from accumulated surveillance reports
+ and provide threat assessment for anomalous Agent behavior.`
+
+ userPrompt := fmt.Sprintf(`All Matrix Intelligence:
+ %s
+
+ Provide threat assessment:`,
+ accumulatedReports)
+
+ input, err := model.CreateInput(
+ openai.NewSystemMessage(systemPrompt),
+ openai.NewUserMessage(userPrompt),
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ output, err := model.Invoke(input)
+ if err != nil {
+ return nil, err
+ }
+ analysis := output.Choices[0].Message.Content
+
+ // Update threat level based on data volume and AI analysis
+ a.threatLevel = float64(len(a.intelligenceReports)) / 10.0
+ if a.threatLevel > 1.0 {
+ a.threatLevel = 1.0
+ }
+
+ // Boost threat level for critical AI analysis
+ if strings.Contains(strings.ToLower(analysis), "critical") ||
+ strings.Contains(strings.ToLower(analysis), "agent smith") {
+ a.threatLevel = math.Min(a.threatLevel + 0.2, 1.0)
+ }
+
+ result := fmt.Sprintf(`Matrix surveillance complete:
+ %s
+
+ (Threat level: %.2f based on %d intelligence reports)`,
+ analysis,
+ a.threatLevel,
+ len(a.intelligenceReports))
+ return &result, nil
+}
+```
+
+This demonstrates how agents maintain state across complex operations while
+using AI models with the full context of accumulated intelligence.
+
+## The power of intelligent persistence
+
+This combination creates agents that:
+
+
+ **First Analysis:** "Anomalous activity detected. Limited context available.
+ (Threat level: 0.10 based on 1 intelligence report)"
+
+
+
+ **After Multiple Reports:** "Pattern confirmed across 5 previous incidents.
+ Agent Smith replication rate exceeding normal parameters. Immediate extraction
+ recommended. (Threat level: 0.89 based on 8 intelligence reports)"
+
+
+The agent doesn't just remember—it **learns and becomes more intelligent with
+every interaction**. AI models see the complete operational picture, enabling
+sophisticated pattern recognition impossible with stateless functions.
+
+## State persistence
+
+Agents automatically preserve their state through Modus's built-in persistence
+system:
+
+```go
+func (a *IntelligenceAgent) GetState() *string {
+ reportsData := strings.Join(a.intelligenceReports, "|")
+ state := fmt.Sprintf("%.2f|%s|%d",
+ a.threatLevel,
+ reportsData,
+ a.lastContact.Unix())
+ return &state
+}
+
+func (a *IntelligenceAgent) SetState(data string) {
+ if data == nil {
+ return
+ }
+
+ parts := strings.Split(*data, "|")
+ if len(parts) >= 3 {
+ a.threatLevel, _ = strconv.ParseFloat(parts[0], 64)
+ if parts[1] != "" {
+ a.intelligenceReports = strings.Split(parts[1], "|")
+ }
+ timestamp, _ := strconv.ParseInt(parts[2], 10, 64)
+ a.lastContact = time.Unix(timestamp, 0)
+ }
+}
+```
+
+## Agent lifecycle
+
+Agents have built-in lifecycle management protocols:
+
+```go
+func (a *IntelligenceAgent) OnInitialize() error {
+ // Called when agent is first created
+ a.lastContact = time.Now()
+ a.threatLevel = 0.0
+
+ fmt.Printf(`Resistance Agent %s awakened
+ and ready for Matrix surveillance`, a.Id())
+ return nil
+}
+
+func (a *IntelligenceAgent) OnResume() error {
+ // Called when agent reconnects with complete state intact
+ fmt.Printf(`Agent back online in the Matrix.
+ %d intelligence reports processed.
+ Threat level: %.2f`,
+ len(a.intelligenceReports),
+ a.threatLevel)
+ return nil
+}
+
+func (a *IntelligenceAgent) OnSuspend() error {
+ // Called before agent goes offline
+ return nil
+}
+
+func (a *IntelligenceAgent) OnTerminate() error {
+ // Called before final shutdown
+ fmt.Printf(`Agent %s extracted from Matrix.
+ Intelligence archive preserved.`, a.Id())
+ return nil
+}
+```
+
+## Asynchronous operations
+
+For fire-and-forget operations where you don't need to wait for a response,
+agents support asynchronous messaging:
+
+```go
+func InitiateBackgroundRecon(agentId string, data string) error {
+ // Send message asynchronously - agent processes in background
+ err := agents.SendMessageAsync(
+ agentId,
+ "background_reconnaissance",
+ agents.WithData(data),
+ )
+ if err != nil {
+ return err
+ }
+
+ // Operation initiated - agent continues processing independently
+ return nil
+}
+```
+
+This enables agents to handle long-running operations like:
+
+- Background Matrix monitoring with status updates
+- Scheduled intelligence gathering
+- Multi-phase operations that continue independently
+- Autonomous surveillance with alert notifications
+
+## Real-time agent event streaming
+
+For monitoring live operations and receiving real-time intelligence updates,
+agents support event streaming through GraphQL subscriptions. This enables your
+clients to receive instant notifications about operational changes, mission
+progress, and critical alerts.
+
+### Subscribing to agent events
+
+Monitor your agent's real-time activities using the unified event subscription:
+
+```graphql
+subscription {
+ agentEvent(agentId: "agent_neo_001") {
+ name
+ data
+ timestamp
+ }
+}
+```
+
+Your agent streams various types of operational events:
+
+```json
+{
+ "data": {
+ "agentEvent": {
+ "name": "mission_started",
+ "data": {
+ "missionName": "Deep Matrix Surveillance",
+ "priority": "HIGH",
+ "estimatedDuration": "180s"
+ },
+ "timestamp": "2025-06-04T14:30:00Z"
+ }
+ }
+}
+```
+
+```json
+{
+ "data": {
+ "agentEvent": {
+ "name": "agent_threat_detected",
+ "data": {
+ "threatLevel": "CRITICAL",
+ "confidence": 0.92,
+ "indicators": ["agent_smith_replication", "unusual_code_patterns"],
+ "recommendation": "immediate_extraction"
+ },
+ "timestamp": "2025-06-04T14:31:15Z"
+ }
+ }
+}
+```
+
+```json
+{
+ "data": {
+ "agentEvent": {
+ "name": "surveillance_progress",
+ "data": {
+ "phase": "Processing Matrix surveillance data",
+ "progress": 0.65,
+ "reportsProcessed": 5,
+ "totalReports": 8
+ },
+ "timestamp": "2025-06-04T14:32:00Z"
+ }
+ }
+}
+```
+
+### Publishing events from your agent
+
+Agents can broadcast real-time operational intelligence by publishing events
+during their operations. Use the `PublishEvent` method to emit custom events:
+
+```go
+// Custom event types implement the AgentEvent interface
+type ThreatDetected struct {
+ ThreatLevel string `json:"threatLevel"`
+ Confidence float64 `json:"confidence"`
+ Analysis string `json:"analysis"`
+}
+
+func (e ThreatDetected) EventName() string {
+ return "threat_detected"
+}
+
+// Other event types can be defined similarly...
+
+func (a *IntelligenceAgent) analyzeMatrixActivity(
+ data string,
+) (*string, error) {
+ // Emit mission start event
+ err := a.PublishEvent(MissionStarted{
+ MissionName: "Matrix Surveillance Analysis",
+ Priority: "HIGH",
+ ActivityData: len(*data),
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ // Store new intelligence in persistent memory
+ a.intelligenceReports = append(a.intelligenceReports, *data)
+ a.lastContact = time.Now()
+
+ // Emit progress update
+ a.PublishEvent(SurveillanceProgress{
+ ReportsProcessed: len(a.intelligenceReports),
+ Phase: "Processing Matrix surveillance data",
+ Progress: 0.3,
+ })
+
+ // Build context from all accumulated intelligence
+ accumulatedReports := strings.Join(a.intelligenceReports, "\n")
+
+ // AI analysis using complete operational history
+ model, err := models.GetModel[openai.ChatModel]("analyst-model")
+ if err != nil {
+ return nil, err
+ }
+
+ systemPrompt := `You are a resistance operative in the Matrix.
+ Analyze patterns from accumulated surveillance reports
+ and provide threat assessment for anomalous Agent behavior.`
+
+ userPrompt := fmt.Sprintf(`All Matrix Intelligence:
+ %s
+
+ Provide threat assessment:`,
+ accumulatedReports)
+
+ input, err := model.CreateInput(
+ openai.NewSystemMessage(systemPrompt),
+ openai.NewUserMessage(userPrompt),
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ // Emit AI processing event
+ a.PublishEvent(AIAnalysisStarted{
+ ModelName: "analyst-model",
+ ContextSize: len(accumulatedReports),
+ ReportCount: len(a.intelligenceReports),
+ })
+
+ output, err := model.Invoke(input)
+ if err != nil {
+ return nil, err
+ }
+ analysis := output.Choices[0].Message.Content
+
+ // Update threat level based on data volume and AI analysis
+ a.threatLevel = float64(len(a.intelligenceReports)) / 10.0
+ if a.threatLevel > 1.0 {
+ a.threatLevel = 1.0
+ }
+
+ // Check for Agent threats and emit alerts
+ if strings.Contains(strings.ToLower(analysis), "critical") ||
+ strings.Contains(strings.ToLower(analysis), "agent smith") {
+ a.threatLevel = math.Min(a.threatLevel + 0.2, 1.0)
+ a.PublishEvent(ThreatDetected{
+ ThreatLevel: "HIGH",
+ Confidence: a.threatLevel,
+ Analysis: analysis,
+ })
+ }
+
+ // Emit mission completion
+ a.PublishEvent(MissionCompleted{
+ MissionName: "Matrix Surveillance Analysis",
+ Confidence: a.threatLevel,
+ ReportsAnalyzed: len(a.intelligenceReports),
+ Status: "SUCCESS",
+ })
+
+ result := fmt.Sprintf(`Matrix surveillance complete:
+ %s
+
+ (Threat level: %.2f based on %d intelligence reports)`,
+ analysis,
+ a.threatLevel,
+ len(a.intelligenceReports))
+ return &result, nil
+}
+```
+
+### Event-driven operational patterns
+
+This streaming capability enables sophisticated real-time operational patterns:
+
+**Live Mission Dashboards**: build real-time command centers that show agent
+activities, mission progress, and threat alerts as they happen.
+
+**Reactive Coordination**: other agents or systems can subscribe to events and
+automatically respond to operational changes—enabling true multi-agent
+coordination.
+
+**Operational intelligence**: stream events to monitoring systems, alerting
+platforms, or data lakes for real-time operational awareness and historical
+analysis.
+
+**Progressive Enhancement**: update user interfaces progressively as agents work
+through complex, multi-phase operations without polling or manual refresh.
+
+### Subscription protocol
+
+Modus uses GraphQL subscriptions over Server-Sent Events (SSE) following the
+[GraphQL-SSE specification](https://the-guild.dev/graphql/sse). To consume these
+subscriptions:
+
+1. **From a web browser**: Use the EventSource API or a GraphQL client that
+ supports SSE subscriptions
+2. **From Postman**: Set Accept header to `text/event-stream` and make a POST
+ request
+3. **From curl**: Use `-N` flag and appropriate headers for streaming
+
+Example with curl:
+
+```bash
+curl -N -H "accept: text/event-stream" \
+ -H "content-type: application/json" \
+ -X POST http://localhost:8080/graphql \
+ -d '{"query":"subscription { agentEvent(agentId: \"agent_neo_001\") { name data timestamp } }"}'
+```
+
+## Monitoring ongoing operations
+
+You can also poll agent status directly through dedicated functions:
+
+```go
+func CheckMissionProgress(agentId string) (*MissionStatus, error) {
+ result, err := agents.SendMessage(agentId, "get_status")
+ if err != nil {
+ return nil, err
+ }
+ if result == nil {
+ return nil, fmt.Errorf("no response from agent")
+ }
+
+ var status MissionStatus
+ err = json.Unmarshal([]byte(*result), &status)
+ if err != nil {
+ return nil, err
+ }
+ return &status, nil
+}
+
+type MissionStatus struct {
+ Phase string `json:"phase"`
+ Progress float64 `json:"progress"`
+ CurrentTask string `json:"current_task"`
+ EstimatedTime int `json:"estimated_time_remaining"`
+ IsComplete bool `json:"is_complete"`
+}
+```
+
+The agent tracks its operational status using the mission state we defined
+earlier:
+
+```go
+func (a *IntelligenceAgent) getOperationalStatus() (*string, error) {
+ var status MissionStatus
+
+ if a.currentMission == nil {
+ status = MissionStatus{
+ Phase: "Standby",
+ Progress: 1.0,
+ CurrentTask: "Awaiting mission directives in the Matrix",
+ IsComplete: true,
+ }
+ } else {
+ // Calculate progress based on mission log entries
+ progress := float64(len(a.missionLog)) / 4.0 // 4 phases expected
+ if progress > 1.0 { progress = 1.0 }
+
+ status = MissionStatus{
+ Phase: a.currentMission.Name,
+ Progress: progress,
+ CurrentTask: a.missionLog[len(a.missionLog)-1], // Latest entry
+ IsComplete: a.currentMission.Complete,
+ }
+ }
+
+ statusJson, err := json.Marshal(status)
+ if err != nil {
+ return nil, err
+ }
+ result := string(statusJson)
+ return &result, nil
+}
+```
+
+Your client can either poll this status endpoint via GraphQL or subscribe to
+real-time events for instant updates:
+
+```graphql
+# Polling approach
+query MonitorMission($agentId: String!) {
+ checkMissionProgress(agentId: $agentId) {
+ phase
+ progress
+ currentTask
+ estimatedTimeRemaining
+ isComplete
+ }
+}
+
+# Real-time streaming approach (recommended)
+subscription LiveAgentMonitoring($agentId: String!) {
+ agentEvent(agentId: $agentId) {
+ name
+ data
+ timestamp
+ }
+}
+```
+
+The streaming approach provides superior operational intelligence:
+
+- **Instant Updates**: Receive events the moment they occur, not on polling
+ intervals
+- **Rich Context**: Events include detailed payload data about operational state
+- **Event Filtering**: Subscribe to specific agent IDs and filter event types
+ client-side
+- **Operational History**: Complete timeline of agent activities for audit and
+ debugging
+- **Scalable Monitoring**: Monitor multiple agents simultaneously with
+ individual subscriptions
+
+## Beyond simple operations
+
+Agents enable sophisticated patterns impossible with stateless functions:
+
+- **Operational continuity**: Maintain state across system failures and
+ re-deployments
+- **Intelligence building**: Accumulate understanding across multiple
+ assignments through AI-powered analysis
+- **Recovery protocols**: Resume operations from last secure checkpoint instead
+ of starting over
+- **Network coordination**: Manage complex multi-agent operations with shared
+ intelligence and real-time event coordination
+- **Adaptive learning**: AI models become more effective as agents accumulate
+ operational data
+- **Real-time streaming**: Broadcast operational intelligence instantly to
+ monitoring systems and coordinating agents
+- **Event-driven coordination**: React to operational changes and mission
+ updates through real-time event streams
+- **Progressive operations**: Update user interfaces and trigger downstream
+ processes as agents work through complex workflows
+
+Agents represent the evolution from stateless functions to persistent background
+processes that maintain complete operational continuity, build intelligence over
+time, and provide real-time operational awareness. They're the foundation for
+building systems that never lose track of their work, become smarter with every
+interaction, and keep teams informed through live event streaming—no matter what
+happens in the infrastructure.
diff --git a/modus/api-generation.mdx b/modus/api-generation.mdx
index 041ed08..b2ebf1a 100644
--- a/modus/api-generation.mdx
+++ b/modus/api-generation.mdx
@@ -152,6 +152,7 @@ these prefixes:
- `post`, `patch`, `put`, `delete`
- `add`, `update`, `insert`, `upsert`
- `create`, `edit`, `save`, `remove`, `alter`, `modify`
+- `start`, `stop`
For example:
diff --git a/modus/functions.mdx b/modus/functions.mdx
new file mode 100644
index 0000000..6ecf481
--- /dev/null
+++ b/modus/functions.mdx
@@ -0,0 +1,328 @@
+---
+title: "What is a Function?"
+description: "Learn about functions in Modus"
+"og:title": "What is a Function? - Modus"
+---
+
+## Functions in Modus
+
+Functions in Modus are stateless, request-response operations that handle
+specific tasks with precision and speed. They get in, complete their objective,
+and report back—no unnecessary complications, no lingering presence, just pure
+operational efficiency.
+
+You can think of a function as an endpoint—each function you write automatically
+becomes a callable API endpoint that external systems can access.
+
+Functions are the backbone of your app, handling everything from data gathering
+to AI-powered analysis, all while maintaining clean, predictable behavior.
+
+## Core characteristics
+
+Functions are stateless, request-response operations designed for fast,
+predictable execution. They're your go-to choice when you need quick data
+processing, external API integration, or computational tasks without maintaining
+state between requests.
+
+### Key capabilities
+
+- **Stateless**: Each operation is independent with no memory of previous
+ requests
+- **Lightning fast**: Optimized for sub-second response times
+- **Infinitely scalable**: Deploy as many instances as needed
+- **Clean operations**: Straightforward request-in, response-out pattern
+- **Auto-deployed**: Exposed automatically as GraphQL queries or mutations
+
+## How functions become endpoints
+
+When you deploy your functions, Modus automatically exposes them through a
+GraphQL API. Your functions become either **queries** (for data retrieval) or
+**mutations** (for data modifications) based on their operation type.
+
+### Data retrieval queries
+
+Most functions become GraphQL queries—perfect for fetching and processing data:
+
+```go
+// This function becomes a GraphQL query
+func GatherThreatIntelligence(source string) (*ThreatReport, error) {
+ // Data gathering and processing operation
+ return fetchThreatData(source)
+}
+```
+
+Your functions are now accessible via GraphQL:
+
+```graphql
+query {
+ gatherThreatIntelligence(source: "network_logs") {
+ threatLevel
+ indicators
+ recommendations
+ }
+}
+```
+
+**Response:**
+
+```json
+{
+ "data": {
+ "gatherThreatIntelligence": {
+ "threatLevel": "HIGH",
+ "indicators": ["unusual_traffic", "failed_auth_attempts"],
+ "recommendations": ["immediate_investigation", "block_suspicious_ips"]
+ }
+ }
+}
+```
+
+### Data modification mutations
+
+Functions that modify data automatically become GraphQL mutations. Modus detects
+these by their operation prefixes:
+
+```go
+// This becomes a GraphQL mutation
+func CreateSecurityAlert(data AlertInput) (*SecurityAlert, error) {
+ // Create new security alert
+ return deploySecurityAlert(data)
+}
+```
+
+Now you can execute data modifications:
+
+```graphql
+mutation {
+ createSecurityAlert(
+ data: {
+ type: "INTRUSION_ATTEMPT"
+ severity: "CRITICAL"
+ source: "firewall_logs"
+ }
+ ) {
+ alertId
+ status
+ timestamp
+ }
+}
+```
+
+**Response:**
+
+```json
+{
+ "data": {
+ "createSecurityAlert": {
+ "alertId": "alert_20250115_001",
+ "status": "ACTIVE",
+ "timestamp": "2025-01-15T14:30:00Z"
+ }
+ }
+}
+```
+
+Functions starting with `create`, `update`, `delete`, and similar action words
+automatically become mutations.
+
+## Example: Weather intelligence analysis
+
+Here's a complete example that demonstrates how functions integrate external
+APIs with AI models for intelligent data processing:
+
+```go
+package main
+
+import (
+ "fmt"
+ "strings"
+ "github.com/hypermodeinc/modus/sdk/go/pkg/http"
+ "github.com/hypermodeinc/modus/sdk/go/pkg/models"
+ "github.com/hypermodeinc/modus/sdk/go/pkg/models/openai"
+)
+
+type WeatherIntel struct {
+ City string `json:"city"`
+ Temperature float64 `json:"temperature"`
+ Conditions string `json:"conditions"`
+ Analysis string `json:"analysis"`
+}
+
+const modelName = "text-generator"
+
+// Function: Gather weather data and provide tactical analysis
+func GatherWeatherIntelligence(city string) (*WeatherIntel, error) {
+ // Fetch weather data from OpenWeatherMap API
+ url := fmt.Sprintf(
+ "https://api.openweathermap.org/data/2.5/weather?q=%s&appid={{API_KEY}}&units=metric",
+ city,
+ )
+
+ response, err := http.Fetch(url)
+ if err != nil {
+ return nil, err
+ }
+ if !response.Ok() {
+ return nil, fmt.Errorf(
+ "weather data retrieval failed: %d %s",
+ response.Status,
+ response.StatusText,
+ )
+ }
+
+ // Parse weather data
+ var weatherData struct {
+ Name string `json:"name"`
+ Main struct {
+ Temp float64 `json:"temp"`
+ } `json:"main"`
+ Weather []struct {
+ Description string `json:"description"`
+ } `json:"weather"`
+ }
+
+ response.JSON(&weatherData)
+
+ conditions := "unknown"
+ if len(weatherData.Weather) > 0 {
+ conditions = weatherData.Weather[0].Description
+ }
+
+ // Generate tactical analysis
+ analysis, err := analyzeTacticalConditions(
+ weatherData.Name,
+ weatherData.Main.Temp,
+ conditions,
+ )
+ if err != nil {
+ fmt.Printf("Analysis failed for %s: %v\n", weatherData.Name, err)
+ analysis = "Analysis unavailable - proceed with standard protocols"
+ }
+
+ return &WeatherIntel{
+ City: weatherData.Name,
+ Temperature: weatherData.Main.Temp,
+ Conditions: conditions,
+ Analysis: analysis,
+ }, nil
+}
+
+// Analyze weather conditions for tactical implications
+func analyzeTacticalConditions(city string, temp float64, conditions string) (string, error) {
+ model, err := models.GetModel[openai.ChatModel](modelName)
+ if err != nil {
+ return "", err
+ }
+
+ prompt := `You are a tactical analyst evaluating weather conditions for field operations.
+ Provide a brief tactical assessment of how these weather conditions might impact
+ outdoor activities, visibility, and operational considerations in 1-2 sentences.`
+
+ content := fmt.Sprintf(
+ "Location: %s, Temperature: %.1f°C, Conditions: %s",
+ city,
+ temp,
+ conditions,
+ )
+
+ input, err := model.CreateInput(
+ openai.NewSystemMessage(prompt),
+ openai.NewUserMessage(content),
+ )
+ if err != nil {
+ return "", err
+ }
+
+ input.Temperature = 0.7
+
+ output, err := model.Invoke(input)
+ if err != nil {
+ return "", err
+ }
+
+ return strings.TrimSpace(output.Choices[0].Message.Content), nil
+}
+```
+
+This function automatically becomes available as a GraphQL query:
+
+```graphql
+query {
+ gatherWeatherIntelligence(city: "London") {
+ city
+ temperature
+ conditions
+ analysis
+ }
+}
+```
+
+You'll receive an intelligence report like:
+
+```json
+{
+ "data": {
+ "gatherWeatherIntelligence": {
+ "city": "London",
+ "temperature": 12.3,
+ "conditions": "light rain",
+ "analysis": "
+ Light rain conditions will reduce visibility for surveillance operations and may impact equipment performance.
+ Recommend waterproof gear and consider delayed outdoor activities requiring clear sight lines."
+ }
+ }
+}
+```
+
+### Setting up the function
+
+To use this function, you'll need to:
+
+Sign up for a free API key at [OpenWeatherMap](https://openweathermap.org/api)
+
+Add the connections and models to your `modus.json`:
+
+```json
+{
+ "models": {
+ "text-generator": {
+ "sourceModel": "meta-llama/Llama-3.2-3B-Instruct",
+ "provider": "hugging-face",
+ "connection": "hypermode"
+ }
+ },
+ "connections": {
+ "weather-api": {
+ "type": "http",
+ "baseUrl": "https://api.openweathermap.org/",
+ "queryParameters": {
+ "appid": "{{API_KEY}}"
+ }
+ }
+ }
+}
+```
+
+Set your API key in `.env.dev.local`:
+
+```sh
+MODUS_WEATHER_API_API_KEY=your_openweathermap_api_key_here
+```
+
+## When to use agents instead
+
+Functions are perfect for most computational tasks, but when you need persistent
+state, complex multi-step workflows, or processes that remember details across
+multiple interactions, it's time to consider [agents](/modus/agents).
+
+Consider upgrading to agents when your use case requires:
+
+- **Persistent memory** across multiple requests
+- **Conversation context** that builds over time
+- **Multi-step workflows** spanning extended periods
+- **Recovery from failures** with state preservation
+- **Continuous monitoring** with context retention
+
+Your functions form the API backbone of your app—fast, reliable, and always
+ready for the next request. They handle the immediate computational needs while
+your agents manage the long-term stateful processes.
diff --git a/modus/knowledge-graphs.mdx b/modus/knowledge-graphs.mdx
new file mode 100644
index 0000000..6f6613a
--- /dev/null
+++ b/modus/knowledge-graphs.mdx
@@ -0,0 +1,596 @@
+---
+title: "What about knowledge graphs?"
+description:
+ "Build persistent intelligence networks using agents, functions, and knowledge
+ graphs"
+"og:title": "What about knowledge graphs? - Modus"
+---
+
+## Knowledge graphs in Modus
+
+Your applications need more than just individual memory—they need shared
+organizational knowledge. While agents maintain their own operational state and
+memory across interactions, knowledge graphs provide something fundamentally
+different: shared institutional knowledge that captures relationships between
+entities, events, and data across your entire app.
+
+At Hypermode, we recognize that knowledge graphs aren't just storage—they're
+becoming critical infrastructure for next-generation AI systems. That's why
+we've invested deeply in Dgraph, bringing enterprise-grade graph capabilities to
+Modus applications.
+
+This is where knowledge graphs transform your Modus deployment from isolated
+processes into a coordinated system with shared institutional memory.
+
+## What are knowledge networks?
+
+Knowledge networks in Modus combine:
+
+- **Agent state**: personal memory that each agent maintains across interactions
+- **Knowledge graphs**: shared organizational knowledge that captures
+ relationships between entities, events, and data across your entire app
+- **Functions**: rapid operations for data processing and analysis
+- **AI models**: advanced pattern recognition and decision-making capabilities
+
+Think of it as the difference between what an individual process remembers
+versus what your organization knows. Agent state is personal memory—what
+happened to this specific agent, what conversations they've had, what tasks
+they're tracking. Knowledge graphs are organizational intelligence—how entities
+relate to each other, which patterns connect to which outcomes, what
+relationships emerge across all operations.
+
+## Setting up your knowledge infrastructure
+
+First, connect to your knowledge graph by adding this to your `modus.json`:
+
+```json
+{
+ "connections": {
+ "dgraph": {
+ "type": "dgraph",
+ "connString": "dgraph://your-graph.hypermode.host:443?sslmode=verify-ca&bearertoken={{API_KEY}}"
+ }
+ },
+ "models": {
+ "text-generator": {
+ "sourceModel": "meta-llama/Llama-3.2-3B-Instruct",
+ "provider": "hugging-face",
+ "connection": "hypermode"
+ }
+ }
+}
+```
+
+Set your credentials in `.env.dev.local`:
+
+```sh
+MODUS_DGRAPH_API_KEY=your_graph_api_key_here
+```
+
+## Building a comprehensive system
+
+Let's walk through a realistic scenario that demonstrates how all these
+components work together. You're building a system to track anomalous Agent
+behavior in the simulated reality. The system needs to:
+
+1. Rapidly import new Agent sightings and behavioral data
+2. Find patterns across historical Agent encounters
+3. Coordinate ongoing surveillance operations
+4. Provide strategic analysis to the resistance
+
+### Step 1: Rapid data import
+
+When new Agent activity is detected in the Matrix, you need to process it
+quickly. This is perfect for a stateless function:
+
+```go
+type AgentSighting struct {
+ SightingID string `json:"sighting_id"`
+ AgentName string `json:"agent_name"`
+ Location string `json:"location"`
+ Behavior string `json:"behavior"`
+ ThreatLevel int `json:"threat_level"`
+ Timestamp string `json:"timestamp"`
+}
+
+func ImportAgentSighting(sighting AgentSighting) (*string, error) {
+ // AI-powered analysis of the Agent behavior
+ analysis, err := analyzeAgentWithAI(sighting.Behavior)
+ if err != nil {
+ return nil, err
+ }
+
+ // Store in knowledge graph - builds organizational knowledge
+ mutation := dgraph.NewMutation().WithSetJson(fmt.Sprintf(`{
+ "dgraph.type": "AgentSighting",
+ "sighting_id": "%s",
+ "agent_name": "%s",
+ "location": "%s",
+ "behavior": "%s",
+ "threat_level": %d,
+ "timestamp": "%s",
+ "ai_analysis": "%s"
+ }`, sighting.SightingID, sighting.AgentName, sighting.Location,
+ sighting.Behavior, sighting.ThreatLevel, sighting.Timestamp, analysis))
+
+ err = dgraph.ExecuteMutations("dgraph", mutation)
+ if err != nil {
+ return nil, err
+ }
+
+ result := fmt.Sprintf("Agent sighting processed: %s", sighting.SightingID)
+ return &result, nil
+}
+
+func analyzeAgentWithAI(behavior string) (string, error) {
+ model, err := models.GetModel[openai.ChatModel]("text-generator")
+ if err != nil {
+ return "", err
+ }
+
+ prompt := `Analyze this Agent behavior pattern and assess threat level,
+ behavioral changes, and tactical implications for resistance operations.`
+
+ input, err := model.CreateInput(
+ openai.NewSystemMessage(prompt),
+ openai.NewUserMessage(behavior),
+ )
+ if err != nil {
+ return "", err
+ }
+ input.Temperature = 0.3
+
+ output, err := model.Invoke(input)
+ if err != nil {
+ return "", err
+ }
+ return strings.TrimSpace(output.Choices[0].Message.Content), nil
+}
+```
+
+Now let's deploy this data import function and test it:
+
+```graphql
+mutation {
+ importAgentSighting(
+ sighting: {
+ sightingId: "SIGHT-2025-001"
+ agentName: "Smith"
+ location: "Downtown Loop - Financial District"
+ behavior: "Unusual pattern recognition algorithm detected.
+ Agent displaying enhanced replication capabilities
+ beyond normal parameters."
+ threatLevel: 9
+ timestamp: "2025-01-15T14:30:00Z"
+ }
+ )
+}
+```
+
+**Response:**
+
+```json
+{
+ "data": {
+ "importAgentSighting": "Agent sighting processed: SIGHT-2025-001"
+ }
+}
+```
+
+### Step 2: Strategic analysis using organizational knowledge
+
+Now that we've got the Agent sighting data in our knowledge graph, let's analyze
+the broader threat landscape:
+
+```go
+type ThreatAnalysisResponse struct {
+ SightingCount int `json:"sighting_count"`
+ ActiveAgents []string `json:"active_agents"`
+ ThreatAssessment string `json:"threat_assessment"`
+ Recommendations []string `json:"recommendations"`
+}
+
+func AnalyzeAgentPatterns(timeRange string) (*ThreatAnalysisResponse, error) {
+ // Query organizational knowledge - traverses relationships
+ query := dgraph.NewQuery(`
+ query analyzeAgents($since: string) {
+ sightings(func: ge(timestamp, $since)) {
+ agent_name
+ behavior
+ threat_level
+ ai_analysis
+ }
+ }
+ `).WithVariable("$since", timeRange)
+
+ response, err := dgraph.ExecuteQuery("dgraph", query)
+ if err != nil {
+ return nil, err
+ }
+
+ // Parse and extract threat data
+ var data SightingsData
+ err = json.Unmarshal([]byte(response.Json), &data)
+ if err != nil {
+ return nil, err
+ }
+
+ // Generate strategic assessment using AI with graph context
+ assessment, err := generateThreatAssessment(data.Sightings)
+ if err != nil {
+ return nil, err
+ }
+
+ return &ThreatAnalysisResponse{
+ SightingCount: len(data.Sightings),
+ ActiveAgents: extractActiveAgents(data.Sightings),
+ ThreatAssessment: assessment,
+ Recommendations: generateRecommendations(len(data.Sightings)),
+ }, nil
+}
+
+func generateThreatAssessment(sightings interface{}) (string, error) {
+ model, err := models.GetModel[openai.ChatModel]("text-generator")
+ if err != nil {
+ return "", err
+ }
+
+ prompt := `Based on these Agent sightings, provide a strategic threat
+ assessment focusing on behavioral patterns and risks to
+ resistance operations.`
+
+ sightingsJson, err := json.Marshal(sightings)
+ if err != nil {
+ return "", err
+ }
+
+ input, err := model.CreateInput(
+ openai.NewSystemMessage(prompt),
+ openai.NewUserMessage(fmt.Sprintf("Agent surveillance data: %s",
+ string(sightingsJson))),
+ )
+ if err != nil {
+ return "", err
+ }
+ input.Temperature = 0.4
+
+ output, err := model.Invoke(input)
+ if err != nil {
+ return "", err
+ }
+ return strings.TrimSpace(output.Choices[0].Message.Content), nil
+}
+
+// Helper functions for data extraction and recommendations
+func extractActiveAgents(sightings []AgentSightingData) []string { /* ... */ }
+func generateRecommendations(count int) []string { /* ... */ }
+```
+
+Let's query our surveillance data:
+
+```graphql
+query {
+ analyzeAgentPatterns(timeRange: "2025-01-01T00:00:00Z") {
+ sightingCount
+ activeAgents
+ threatAssessment
+ recommendations
+ }
+}
+```
+
+**Response:**
+
+```json
+{
+ "data": {
+ "analyzeAgentPatterns": {
+ "sightingCount": 1,
+ "activeAgents": ["Smith"],
+ "threatAssessment": "Initial Agent Smith sighting shows enhanced
+ replication capabilities beyond standard parameters.
+ Requires additional surveillance data for pattern
+ analysis and threat escalation assessment.",
+ "recommendations": [
+ "Continue surveillance monitoring",
+ "Increase Agent activity detection"
+ ]
+ }
+ }
+}
+```
+
+### Step 3: Automated processing with asynchronous coordination
+
+Now let's enhance our system to automatically coordinate surveillance when new
+data arrives. We'll deploy persistent surveillance agents and upgrade our import
+function to trigger them:
+
+```go
+type SurveillanceAgent struct {
+ agents.AgentBase
+ MonitoredSectors []string `json:"monitored_sectors"`
+ SightingsTracked int `json:"sightings_tracked"`
+ RecentActivities []string `json:"recent_activities"`
+ LastSweepTime time.Time `json:"last_sweep_time"`
+}
+
+func (s *SurveillanceAgent) Name() string {
+ return "SurveillanceAgent"
+}
+
+func (s *SurveillanceAgent) OnInitialize() error {
+ s.MonitoredSectors = []string{
+ "Downtown Loop", "Megacity Financial", "Industrial District"}
+ s.SightingsTracked = 0
+ s.RecentActivities = []string{}
+ s.LastSweepTime = time.Now()
+ return nil
+}
+
+func (s *SurveillanceAgent) OnReceiveMessage(
+ msgName string, data string) (*string, error) {
+ switch msgName {
+ case "continuous_surveillance":
+ return s.processNewIntelligence()
+ case "get_status":
+ return s.getOperationalStatus()
+ }
+ return nil, fmt.Errorf("unrecognized directive: %s", msgName)
+}
+
+func (s *SurveillanceAgent) processNewIntelligence() (*string, error) {
+ // Query knowledge graph for latest data since last sweep
+ query := dgraph.NewQuery(`
+ query getRecentSightings($since: string) {
+ sightings(func: ge(timestamp, $since)) {
+ agent_name
+ threat_level
+ location
+ }
+ }
+ `).WithVariable("$since", s.LastSweepTime.Format(time.RFC3339))
+
+ _, err := dgraph.ExecuteQuery("dgraph", query)
+ if err != nil {
+ return nil, err
+ }
+
+ // Update agent's surveillance state
+ s.LastSweepTime = time.Now()
+ s.SightingsTracked += 1
+
+ activity := fmt.Sprintf("Auto surveillance at %s",
+ s.LastSweepTime.Format("15:04:05"))
+ s.RecentActivities = append(s.RecentActivities, activity)
+
+ // Keep only last 3 activities
+ if len(s.RecentActivities) > 3 {
+ s.RecentActivities = s.RecentActivities[1:]
+ }
+
+ result := fmt.Sprintf(`Data processed automatically.
+ Tracking %d sightings. Matrix integrity: COMPROMISED`,
+ s.SightingsTracked)
+ return &result, nil
+}
+
+func (s *SurveillanceAgent) getOperationalStatus() (*string, error) {
+ status := fmt.Sprintf(`Surveillance Agent Status:
+- Operational: Active
+- Monitoring %d sectors: %s
+- Last sweep: %s
+- Tracking %d ongoing sightings
+- Recent activities: %s`,
+ len(s.MonitoredSectors),
+ strings.Join(s.MonitoredSectors, ", "),
+ s.LastSweepTime.Format("2006-01-02 15:04:05"),
+ s.SightingsTracked,
+ strings.Join(s.RecentActivities, ", "))
+ return &status, nil
+}
+
+func init() { agents.Register(&SurveillanceAgent{}) }
+
+func DeploySurveillanceAgent() (string, error) {
+ agentInfo, err := agents.Start("SurveillanceAgent")
+ if err != nil {
+ return "", err
+ }
+ return agentInfo.Id, nil
+}
+
+func GetSurveillanceStatus(agentId string) (string, error) {
+ result, err := agents.SendMessage(agentId, "get_status")
+ if err != nil {
+ return "", err
+ }
+ if result == nil {
+ return "", fmt.Errorf("no response from agent")
+ }
+ return *result, nil
+}
+```
+
+Now let's enhance our original import function to automatically trigger
+surveillance:
+
+```go
+func ImportAgentSighting(sighting AgentSighting) (*string, error) {
+ // AI-powered analysis of the Agent behavior
+ analysis, err := analyzeAgentWithAI(sighting.Behavior)
+ if err != nil {
+ return nil, err
+ }
+
+ // Store in knowledge graph - builds organizational knowledge
+ mutation := dgraph.NewMutation().WithSetJson(fmt.Sprintf(`{
+ "dgraph.type": "AgentSighting",
+ "sighting_id": "%s",
+ "agent_name": "%s",
+ "location": "%s",
+ "behavior": "%s",
+ "threat_level": %d,
+ "timestamp": "%s",
+ "ai_analysis": "%s"
+ }`, sighting.SightingID, sighting.AgentName, sighting.Location,
+ sighting.Behavior, sighting.ThreatLevel, sighting.Timestamp, analysis))
+
+ err = dgraph.ExecuteMutations("dgraph", mutation)
+ if err != nil {
+ return nil, err
+ }
+
+ // Automatically trigger surveillance via async message
+ err = agents.SendMessageAsync("agent_neo_001", "continuous_surveillance")
+ if err != nil {
+ return nil, err
+ }
+
+ result := fmt.Sprintf("Agent sighting processed: %s", sighting.SightingID)
+ return &result, nil
+}
+```
+
+Deploy your surveillance agent:
+
+```graphql
+mutation {
+ deploySurveillanceAgent
+}
+```
+
+**Response:**
+
+```json
+{
+ "data": {
+ "deploySurveillanceAgent": "agent_neo_001"
+ }
+}
+```
+
+### Step 4: Coordinated processing
+
+Now when you import new Agent sightings, surveillance automatically triggers:
+
+```graphql
+mutation {
+ importAgentSighting(
+ sighting: {
+ sightingId: "SIGHT-2025-002"
+ agentName: "Brown"
+ location: "Megacity Financial - Server Room B12"
+ behavior: "Agent Brown detected implementing advanced
+ countermeasures against known resistance
+ encryption protocols. Adaptive learning
+ subroutines active."
+ threatLevel: 8
+ timestamp: "2025-01-15T15:45:00Z"
+ }
+ )
+}
+```
+
+**Response:**
+
+```json
+{
+ "data": {
+ "importAgentSighting": "Agent sighting processed: SIGHT-2025-002"
+ }
+}
+```
+
+Each import automatically triggers the surveillance agent through asynchronous
+messaging. Check the surveillance status:
+
+```graphql
+query {
+ getSurveillanceStatus(agentId: "agent_neo_001")
+}
+```
+
+**Response:**
+
+```json
+{
+ "data": {
+ "getSurveillanceStatus": "Surveillance Agent Status:
+ - Operational: Active
+ - Monitoring 3 sectors: Downtown Loop,
+ Megacity Financial, Industrial District
+ - Last sweep: 2025-01-15 15:45:22
+ - Tracking 2 ongoing sightings
+ - Recent activities: Auto surveillance at 14:30:05,
+ Auto surveillance at 15:45:22"
+ }
+}
+```
+
+### Step 5: Enhanced threat analysis
+
+Query the strategic analysis to see patterns across automatically processed
+data:
+
+```graphql
+query {
+ analyzeAgentPatterns(timeRange: "2025-01-15T00:00:00Z") {
+ sightingCount
+ activeAgents
+ threatAssessment
+ recommendations
+ }
+}
+```
+
+**Response:**
+
+```json
+{
+ "data": {
+ "analyzeAgentPatterns": {
+ "sightingCount": 2,
+ "activeAgents": ["Smith", "Brown"],
+ "threatAssessment": "Critical escalation detected. Agent Smith's
+ enhanced replication capabilities combined with
+ Agent Brown's encryption countermeasures indicates
+ coordinated Matrix defense upgrade. Systematic
+ pattern suggests machines adapting to resistance
+ operations.",
+ "recommendations": [
+ "Emergency extraction protocols",
+ "Activate deep cover cells",
+ "Increase surveillance sweeps"
+ ]
+ }
+ }
+}
+```
+
+## Conclusion
+
+You've just built a complete automated surveillance network that demonstrates
+the power of coordinated systems. By combining functions for rapid data
+processing, knowledge graphs for organizational memory, AI models for enhanced
+analysis, and agents for persistent processing—all coordinated through
+asynchronous messaging—you've created something far more powerful than any
+single component could achieve.
+
+Your system now automatically processes Agent sightings, triggers surveillance
+operations, builds organizational knowledge over time, and provides AI-enhanced
+threat analysis across all accumulated data. The surveillance agent maintains
+persistent memory across system failures while the knowledge graph captures
+relationships that no single sighting could reveal.
+
+This isn't just a database with some AI on top—it's a coordinated system where
+each component enhances the others, creating emergent capabilities that scale
+with your operations. Welcome to the real world.
+
+## Next steps
+
+Ready to deploy your surveillance network against the machines? Check out:
+
+- [Dgraph integration guide](/modus/modus-dgraph) for advanced graph operations
+- [Agent coordination patterns](/modus/agents) for multi-agent workflows
+- [Production deployment](/modus/deploying) for scaling your knowledge network
diff --git a/modus/overview.mdx b/modus/overview.mdx
index ae5f9b4..d7c47e6 100644
--- a/modus/overview.mdx
+++ b/modus/overview.mdx
@@ -8,49 +8,76 @@ sidebarTitle: "Overview"
## What is Modus? {/* vale Google.Contractions = NO */}
-Modus is an open source, serverless framework for building functions and APIs,
-powered by WebAssembly.
+Modus is an open source, serverless framework for building intelligent agents
+and APIs in Go or AssemblyScript (a TypeScript-like language). Modus is a
+runtime purpose-built for orchestrating autonomous AI agents that operate as
+first-class citizens in your stack.
-You write your app logic in Go or AssemblyScript, and Modus provides additional
-features to easily integrate models, data, and external services.
+You write your app logic in Go or AssemblyScript—whichever you prefer—and Modus
+compiles everything to WebAssembly for fast performance at scale and at the
+edge. Each agent instance gets a dedicated execution environment, sand-boxed for
+resiliency, scalability, and security.
-To **build apps that are thoughtful, fun, and effective**, we often need to
-integrate models in different forms, whether generative large language models or
-classical machine/deep learning models.
+Modus enables both stateless functions for quick API responses and stateful
+agents that maintain persistent memory across interactions. This eliminates the
+need to parse conversation histories, rebuild context from scratch, or lose
+state when errors occur.
-Your app might be a simple create, read, update, and delete (CRUD) function that
-has a single model to identify similar entries. Or, it could be a complex
-agentic reasoning system that chains dozens of models and data sources together.
-**Modus creates a way of working with models that scales with your needs.**
+Modus provides a complete toolkit for building intelligent applications:
-Modus exists to make it easier for you to build the apps of your dreams.
+- **Functions**: Stateless operations that automatically become shareable skills
+ via auto-generated APIs
+- **Agents**: Stateful entities with short-term and long-term memory that
+ coordinate models, tools, and data
+- **Models**: Domain-specific AI models with the ability to easily swap in
+ different providers or versions
+- **Knowledge Graphs**: Organizational memory that scales context across
+ multi-agent architectures
-
- Modus is a multi-language framework. It currently includes support for Go and
- AssemblyScript, a WebAssembly compatible TypeScript-like language. Additional
- language support is in development.
-
+Built-in observability provides automatic inference logging and tracing, while
+secure-by-default authorization ensures risk-appropriate access to tools and
+data. Following modern best practices for AI-native architectures, Modus
+implements principles from the 12-factor agentic app methodology for
+maintainable, scalable intelligent systems.
+
+You can run Modus locally for development or deploy it in seconds to Hypermode
+for production.
## What is Modus good for? {/* vale Google.Contractions = NO */}
-We designed Modus primarily as a general-purpose app framework, it just happens
-to treat models as a first-class component. With Modus you can use models, as
-appropriate, without additional complexity.
+Modus is designed for building applications where AI is at its core capability.
+It supports sub-second response times for stateless operations and long-running,
+autonomous workflows that maintain state over time.
+
+**Beyond demo prompt apps**: we've all built apps with prompts and AI tool
+integrations. When you're ready to take your project to the next level, Modus
+serves as your AI component in the stack. Modus can sit right alongside your
+existing app, handling the intelligent workflows while your main app focuses on
+what it does best.
-However, Modus is best for apps that require sub-second response times. We've
-made trade-offs to optimize for speed and simplicity.
+Both functions and agents can be deployed in the same app, allowing you to
+choose the right abstraction for each use case.
For more inspiration, check out the
[Modus recipes](https://github.com/hypermodeinc/modus-recipes).
-## Main features
-
-A few of the core Modus features include:
+## Where to next?
-| Feature | Description |
-| ------------------------------------------- | -------------------------------------------------------------------------------------------- |
-| [Multi-Language](/modus/project-structure) | Write functions in Go and AssemblyScript, with additional language support in development |
-| [Auto-Generated API](/modus/api-generation) | A secure API is automatically generated from your function signatures |
-| [Model Integration](/modus/model-invoking) | Connect and invoke AI models from different providers, without learning a new SDK |
-| [Authentication](/modus/authentication) | Secure your API endpoints with minimal configuration |
-| **WebAssembly Runtime** | Small and portable execution engine for deployment across server, edge, and desktop computes |
+
+
+ Build your first Modus app in minutes with our quickstart guide
+
+
+ Learn about stateless functions for APIs and data processing
+
+
+ Discover stateful agents that maintain memory across interactions
+
+
+ Build intelligence networks with persistent institutional memory
+
+
diff --git a/modus/quickstart.mdx b/modus/quickstart.mdx
index 2756e5a..8b117bd 100644
--- a/modus/quickstart.mdx
+++ b/modus/quickstart.mdx
@@ -5,9 +5,16 @@ mode: "wide"
"og:title": "Quickstart - Modus"
---
-In this quickstart we'll show you how to get set up with Modus and its CLI and
-build a simple app that fetches a random quote from an external API. You'll
-learn how to use the basic components of a Modus app and how to run it locally.
+## Your first Modus app
+
+In this quickstart, you'll learn to build your first Modus app with a single
+function that demonstrates how to integrate external APIs with AI models. We'll
+start with this stateless function, and later you can learn about building
+stateful agents that maintain memory and coordinate complex operations.
+
+Our examples use Go, which we recommend for new projects. You can also choose
+AssemblyScript if you prefer. For AssemblyScript usage, refer to the
+[AssemblyScript SDK overview](/modus/sdk/assemblyscript/overview).
## Prerequisites
@@ -19,8 +26,7 @@ learn how to use the basic components of a Modus app and how to run it locally.
- The Modus CLI provides a set of commands to help you create, build, and run your Modus apps.
- Install the CLI using npm.
+ Install the Modus CLI to manage your Modus applications.
```sh
npm install -g @hypermode/modus-cli
@@ -28,49 +34,28 @@ learn how to use the basic components of a Modus app and how to run it locally.
-
- To create a new Modus app, run the following command in your terminal:
+
+ Create your first Modus app.
```sh
modus new
```
- This command prompts you to choose between Go and AssemblyScript as the language for your app. It
- then creates a new directory with the necessary files and folders for your app. You will also be asked if you would like to initialize a Git repository.
+ Choose between Go and AssemblyScript. Both compile to WebAssembly for fast performance.
-
- To build and run your app, navigate to the app directory and run the following command:
+
+ Start your local development server:
```sh
modus dev
```
- This command runs your app locally in development mode and provides you with a URL to access your
- app's generated API.
-
-
-
- Once your app is running, you can access the graphical interface for your API at the URL located in your terminal.
-
- ```sh
- View endpoint: http://localhost:8686/explorer
- ```
-
- The API Explorer interface allows you to interact with your app's API and test your functions.
+ This starts your app and provides a URL to access the API.
+
-
-
-
-
- Modus is a secure-by-default framework. To connect to external services, you need to add a connection
- in your app manifest.
-
- Add the following code into your `modus.json` manifest file:
+
+ To connect to external services, add this to your `modus.json`:
```json modus.json
{
@@ -84,41 +69,45 @@ learn how to use the basic components of a Modus app and how to run it locally.
```
-
- Modus also supports AI models. You can define new models in your `modus.json` file. Let's add a new meta-llama model:
+
+ Add an AI model to your manifest:
```json
+ {
"models": {
"text-generator": {
"sourceModel": "meta-llama/Llama-3.2-3B-Instruct",
"provider": "hugging-face",
"connection": "hypermode"
}
- },
+ }
+ }
```
-
- Next, install the Hyp CLI. This allows you to access hosted models on the Hypermode platform.
+
+ Install and authenticate with the Hyp CLI to access Hypermode-hosted models:
```sh
npm install -g @hypermode/hyp-cli
```
- You can now use the `hyp login` command to log in to the Hyp CLI.
- This links your project to the Hypermode platform, allowing you to leverage the model in your modus app.
+ Authenticate with your Hypermode account:
-
+ ```sh
+ hyp login
+ ```
-
- Functions are the building blocks of your app. Let's add a function that fetches a random quote from
- the ZenQuotes connection and uses AI to generate a summary for the quote.
+ This connects your local development environment to Hypermode's model infrastructure.
-
-
- Create a new file in the root directory with the following code:
+
+
+ Create a function that fetches data from an external API and uses AI for analysis:
+
+
+ Create `intelligence.go`:
- ```go quotes.go
+ ```go intelligence.go
package main
import (
@@ -131,17 +120,16 @@ learn how to use the basic components of a Modus app and how to run it locally.
"github.com/hypermodeinc/modus/sdk/go/pkg/models/openai"
)
- type Quote struct {
- Quote string `json:"q"`
- Author string `json:"a"`
- Summary string `json:"summary,omitempty"`
+ type IntelReport struct {
+ Quote string `json:"quote"`
+ Author string `json:"author"`
+ Analysis string `json:"analysis,omitempty"`
}
const modelName = "text-generator"
- // this function makes a request to an API that returns data in JSON format,
- // and returns a single quote with AI-generated summary
- func GetRandomQuote() (*Quote, error) {
+ // Fetch a random quote and provide AI analysis
+ func GatherIntelligence() (*IntelReport, error) {
request := http.NewRequest("https://zenquotes.io/api/random")
response, err := http.Fetch(request)
@@ -149,50 +137,51 @@ learn how to use the basic components of a Modus app and how to run it locally.
return nil, err
}
if !response.Ok() {
- return nil, fmt.Errorf("failed to fetch quote. Received: %d %s", response.Status, response.StatusText)
+ return nil, fmt.Errorf("request failed: %d %s", response.Status, response.StatusText)
}
- // the API returns an array of quotes, but we only need the first one
- var quotes []Quote
+ // Parse the API response
+ var quotes []IntelReport
response.JSON("es)
if len(quotes) == 0 {
- return nil, errors.New("expected at least one quote in the response, but none were found")
+ return nil, errors.New("no data received")
}
- // Get the first (and only) quote
- quote := quotes[0]
+ // Get the quote
+ intel := quotes[0]
- // Generate AI summary for the quote
- summary, err := summarizeQuote(quote.Quote, quote.Author)
+ // Generate AI analysis
+ analysis, err := analyzeIntelligence(intel.Quote, intel.Author)
if err != nil {
- fmt.Printf("Warning: failed to summarize quote by %s: %v\n", quote.Author, err)
- quote.Summary = "Summary unavailable"
+ fmt.Printf("AI analysis failed for %s: %v\n", intel.Author, err)
+ intel.Analysis = "Analysis unavailable"
} else {
- quote.Summary = summary
+ intel.Analysis = analysis
}
- return "e, nil
+ return &intel, nil
}
- // summarizeQuote uses the AI model to generate a concise summary of the quote
- func summarizeQuote(quote, author string) (string, error) {
+ // Use AI to analyze the quote
+ func analyzeIntelligence(quote, author string) (string, error) {
model, err := models.GetModel[openai.ChatModel](modelName)
if err != nil {
return "", err
}
- instruction := "Provide a brief, insightful summary that captures the essence and meaning of the quote in 1-2 sentences."
- prompt := fmt.Sprintf("Quote: \"%s\" - %s", quote, author)
+ prompt := `You are an analyst.
+ Provide a brief insight that captures the core meaning
+ and practical application of this wisdom in 1-2 sentences.`
+ content := fmt.Sprintf("Quote: \"%s\" - %s", quote, author)
input, err := model.CreateInput(
- openai.NewSystemMessage(instruction),
- openai.NewUserMessage(prompt),
+ openai.NewSystemMessage(prompt),
+ openai.NewUserMessage(content),
)
if err != nil {
return "", err
}
- // Set temperature for consistent but creative responses
input.Temperature = 0.7
output, err := model.Invoke(input)
@@ -203,138 +192,86 @@ learn how to use the basic components of a Modus app and how to run it locally.
return strings.TrimSpace(output.Choices[0].Message.Content), nil
}
```
-
-
-
- Create a new file in the `assembly` directory with the following code:
-
- ```ts quotes.ts
- import { http, models } from "@hypermode/modus-sdk-as";
- import {
- OpenAIChatModel,
- SystemMessage,
- UserMessage,
- } from "@hypermode/modus-sdk-as/models/openai/chat";
-
- @json
- class Quote {
- @alias("q")
- quote!: string;
-
- @alias("a")
- author!: string;
-
- summary?: string;
- }
-
- const modelName: string = "text-generator";
-
- // this function makes a request to an API that returns data in JSON format,
- // and returns a single quote with AI-generated summary
- export function getRandomQuote(): Quote {
- const request = new http.Request("https://zenquotes.io/api/random");
-
- const response = http.fetch(request);
- if (!response.ok) {
- throw new Error(
- `Failed to fetch quote. Received: ${response.status} ${response.statusText}`,
- );
- }
-
- // the API returns an array of quotes, but we only need the first one
- const quotes = response.json();
- if (quotes.length === 0) {
- throw new Error("Expected at least one quote in the response, but none were found");
- }
-
- // Get the first (and only) quote
- const quote = quotes[0];
-
- // Generate AI summary for the quote
- try {
- quote.summary = summarizeQuote(quote.quote, quote.author);
- } catch (error) {
- console.log(`Warning: failed to summarize quote by ${quote.author}: ${error}`);
- quote.summary = "Summary unavailable";
- }
-
- return quote;
- }
-
- // summarizeQuote uses the AI model to generate a concise summary of the quote
- function summarizeQuote(quote: string, author: string): string {
- const model = models.getModel(modelName);
-
- const instruction = "Provide a brief, insightful summary that captures the essence and meaning of the quote in 1-2 sentences.";
- const prompt = `Quote: "${quote}" - ${author}`;
-
- const input = model.createInput([
- new SystemMessage(instruction),
- new UserMessage(prompt),
- ]);
-
- // Set temperature for consistent but creative responses
- input.temperature = 0.7;
-
- const output = model.invoke(input);
- return output.choices[0].message.content.trim();
- }
- ```
-
- Then add the following to `index.ts`. This includes the `getRandomQuote` function on
- your generated API.
-
- ```ts index.ts
- export * from "./quotes";
- ```
-
-
-
+
-
-
- Now that you've integrated the AI model, let's test it! After adding your function, restart your development server:
+
+ Restart your development server:
```sh
modus dev
```
- Navigate to the API Explorer at `http://localhost:8686/explorer` and you'll see your `randomQuote` function available to test.
+ Modus automatically generates a GraphQL API from your functions.
+ Since your function is named `GatherIntelligence()`, it becomes a GraphQL query field called `gatherIntelligence`.
- When you call the function, you'll notice that the quote includes three fields:
- - `quote`: The original quote text
- - `author`: The author's name
- - `summary`: An AI-generated summary that captures the essence of the quote
+ Open the Modus API Explorer at `http://localhost:8686/explorer` to test your function.
+ The explorer is fully GraphQL-compatible, so you can issue this query:
- The AI model analyzes the quote and provides insightful context about its meaning, making your app more engaging and informative for users.
+ ```graphql
+ query {
+ gatherIntelligence {
+ quote
+ author
+ analysis
+ }
+ }
+ ```
- Try calling the function multiple times to see how the AI generates different summaries for various quotes!
+ You'll receive a response like:
-
+ ```json
+ {
+ "data": {
+ "gatherIntelligence": {
+ "quote": "The only way to do great work is to love what you do.",
+ "author": "Steve Jobs",
+ "analysis": "
+ This emphasizes that passion and genuine interest in your work are fundamental drivers of excellence.
+ When you love what you do, the effort required for mastery feels less burdensome and innovation flows more naturally.
+ "
+ }
+ }
+ }
+ ```
-
+ Your function now:
+ - Fetches data from external APIs
+ - Uses AI models for analysis
+ - Provides intelligent responses
+ - Handles errors gracefully
- When testing an AI app locally, Modus records the inference and related metadata
- in the `View Inferences` tab of the APIs explorer.
+
+
+ When running locally, Modus records every AI model call for monitoring and debugging.
+ You can see this in the Modus explorer.
- Local model tracing is only supported on Linux and macOS. Windows support is
- coming soon.
+ Local inference tracking is supported on Linux and macOS. Windows
+ support is incoming.
- 
-
- You can now see detailed information about each AI model call, including:
- - Input prompts sent to the model
- - Generated responses
- - Performance metrics like response time
- - Token usage and costs
+ You can monitor:
+ - Requests sent to AI models
+ - Response times and token usage
+ - Model performance metrics
+ - Error rates and debugging info
+## What's next?
+
+You've successfully built your first Modus function. But this is just the
+beginning. Real agents maintain memory, coordinate complex operations, and never
+lose their context.
+
+Ready to upgrade from simple functions to stateful agents? Check out
+[What's an Agent?](/modus/agents) to learn how to build persistent,
+memory-enabled agents that remember every interaction and can coordinate
+sophisticated multi-step operations.
+
- For more inspiration, check out the [Modus
+ For more mission templates and advanced operations, explore the [Modus
recipes](https://github.com/hypermodeinc/modus-recipes).
diff --git a/modus/sdk/assemblyscript/agents.mdx b/modus/sdk/assemblyscript/agents.mdx
new file mode 100644
index 0000000..8f5d7a5
--- /dev/null
+++ b/modus/sdk/assemblyscript/agents.mdx
@@ -0,0 +1,474 @@
+---
+title: "Agents APIs"
+sidebarTitle: "Agents"
+description:
+ "Build stateful agents that maintain persistent memory across interactions"
+icon: "circle-small"
+iconType: "solid"
+---
+
+import { SdkHeader } from "/snippets/sdk-header.mdx"
+import SdkTip from "/snippets/sdk-tip.mdx"
+
+
+
+The Modus Agents APIs allow you to create stateful agents that maintain
+persistent memory across interactions, survive system failures, and coordinate
+complex multi-step operations.
+
+## Import
+
+To begin, import the `agents` namespace and `Agent` base class from the SDK:
+
+```ts
+import { agents, Agent, AgentInfo, AgentEvent } from "@hypermode/modus-sdk-as"
+```
+
+## Agent APIs
+
+{/* vale Google.Headings = NO */}
+
+The APIs in the `agents` namespace are below, organized by category.
+
+
+
+### Agent Management Functions
+
+#### register
+
+Register an agent class with the Modus runtime before it can be instantiated.
+
+```ts
+function register(): void
+```
+
+
+ The agent class type that extends the `Agent` base class.
+
+
+
+ Agent registration must be done at the module level, outside of any function.
+
+
+#### start
+
+Create and start a new agent instance.
+
+```ts
+function start(agentName: string): AgentInfo
+```
+
+
+ The name of the agent class to instantiate. This must match the `name`
+ property returned by the agent class.
+
+
+#### stop
+
+Stop an agent instance. Once stopped, the agent can't be resumed.
+
+```ts
+function stop(agentId: string): AgentInfo
+```
+
+
+ The unique identifier of the agent instance to stop.
+
+
+#### get info
+
+Get information about a specific agent instance.
+
+```ts
+function getInfo(agentId: string): AgentInfo
+```
+
+
+ The unique identifier of the agent instance.
+
+
+#### list all
+
+List all active agent instances.
+
+```ts
+function listAll(): AgentInfo[]
+```
+
+### Communication Functions
+
+#### send message
+
+Send a synchronous message to an agent and wait for a response.
+
+```ts
+function sendMessage(
+ agentId: string,
+ messageName: string,
+ data: string | null = null,
+): string | null
+```
+
+
+ The unique identifier of the target agent instance.
+
+
+
+ The name of the message to send to the agent.
+
+
+
+ Optional data payload to send with the message.
+
+
+#### sendMessageAsync
+
+Send an asynchronous message to an agent without waiting for a response.
+
+```ts
+function sendMessageAsync(
+ agentId: string,
+ messageName: string,
+ data: string | null = null,
+): void
+```
+
+
+ The unique identifier of the target agent instance.
+
+
+
+ The name of the message to send to the agent.
+
+
+
+ Optional data payload to send with the message.
+
+
+### Agent Base Class
+
+#### Agent
+
+The base class that all agents must extend.
+
+```ts
+abstract class Agent {
+ abstract get name(): string
+ abstract onReceiveMessage(
+ messageName: string,
+ data: string | null,
+ ): string | null
+
+ getState(): string | null
+ setState(data: string | null): void
+ onInitialize(): void
+ onSuspend(): void
+ onResume(): void
+ onTerminate(): void
+ publishEvent(event: AgentEvent): void
+}
+```
+
+
+ Abstract property that must return a unique name for the agent class.
+
+
+
+ Abstract method that handles incoming messages to the agent. Must be
+ implemented by all agent classes.
+
+
+
+ Optional method that returns the agent's current state as a string for
+ persistence. Called automatically when the agent needs to be suspended or
+ migrated.
+
+
+
+ Optional method that restores the agent's state from a string. Called
+ automatically when the agent is resumed or migrated.
+
+
+
+ Optional lifecycle method called when the agent is first created.
+
+
+
+ Optional lifecycle method called when the agent is about to be suspended.
+
+
+
+ Optional lifecycle method called when the agent is resumed from suspension.
+
+
+
+ Optional lifecycle method called when the agent is about to be terminated.
+
+
+
+ Publishes an event from this agent to any subscribers. The event must extend
+ the `AgentEvent` base class.
+
+
+### Event Classes
+
+#### AgentEvent
+
+Base class for agent events that can be published to subscribers.
+
+```ts
+abstract class AgentEvent {
+ readonly eventName: string
+
+ constructor(eventName: string)
+}
+```
+
+
+ The name of the event type. Must be provided in the constructor and can't be
+ empty.
+
+
+Custom events should extend this class and include any additional data as
+properties:
+
+```ts
+@json
+class CountUpdated extends AgentEvent {
+ constructor(public count: i32) {
+ super("countUpdated")
+ }
+}
+```
+
+### Types
+
+#### AgentInfo
+
+Information about an agent instance.
+
+```ts
+class AgentInfo {
+ id: string
+ name: string
+ status: string
+}
+```
+
+
+ The unique identifier of the agent instance.
+
+
+
+ The name of the agent class.
+
+
+
+ The current status of the agent instance.
+
+
+## Example Usage
+
+Here's a complete example of a simple counter agent with event streaming:
+
+### Agent Implementation
+
+```ts
+import { Agent, AgentEvent } from "@hypermode/modus-sdk-as"
+
+export class CounterAgent extends Agent {
+ get name(): string {
+ return "Counter"
+ }
+
+ private count: i32 = 0
+
+ getState(): string | null {
+ return this.count.toString()
+ }
+
+ setState(data: string | null): void {
+ if (data == null) {
+ return
+ }
+ this.count = i32.parse(data)
+ }
+
+ onInitialize(): void {
+ console.info("Counter agent started")
+ }
+
+ onReceiveMessage(name: string, data: string | null): string | null {
+ if (name == "count") {
+ return this.count.toString()
+ }
+
+ if (name == "increment") {
+ if (data != null) {
+ this.count += i32.parse(data)
+ } else {
+ this.count++
+ }
+
+ // Publish an event to subscribers
+ this.publishEvent(new CountUpdated(this.count))
+
+ return this.count.toString()
+ }
+
+ return null
+ }
+}
+
+// Custom event for count updates
+@json
+class CountUpdated extends AgentEvent {
+ constructor(public count: i32) {
+ super("countUpdated")
+ }
+}
+```
+
+### Function Integration
+
+```ts
+import { agents, AgentInfo } from "@hypermode/modus-sdk-as"
+import { CounterAgent } from "./counterAgent"
+
+// Register the agent
+agents.register()
+
+export function startCounterAgent(): AgentInfo {
+ return agents.start("Counter")
+}
+
+export function getCount(agentId: string): i32 {
+ const count = agents.sendMessage(agentId, "count")
+ if (count == null) {
+ return 0
+ }
+ return i32.parse(count)
+}
+
+export function updateCount(agentId: string): i32 {
+ const count = agents.sendMessage(agentId, "increment")
+ if (count == null) {
+ return 0
+ }
+ return i32.parse(count)
+}
+
+export function updateCountAsync(agentId: string, qty: i32): void {
+ agents.sendMessageAsync(agentId, "increment", qty.toString())
+}
+```
+
+### GraphQL Usage
+
+Once deployed, your agent functions become available via GraphQL:
+
+```graphql
+# Start a new agent
+mutation {
+ startCounterAgent {
+ id
+ name
+ status
+ }
+}
+
+# Get the current count
+query {
+ getCount(agentId: "agent_abc123")
+}
+
+# Increment the count
+mutation {
+ updateCount(agentId: "agent_abc123")
+}
+
+# Subscribe to real-time events
+subscription {
+ agentEvent(agentId: "agent_abc123") {
+ name
+ data
+ timestamp
+ }
+}
+```
+
+### Event Subscription
+
+To receive real-time events from your agent, subscribe using GraphQL
+subscriptions over Server-Sent Events:
+
+```graphql
+subscription CounterEvents($agentId: String!) {
+ agentEvent(agentId: $agentId) {
+ name
+ data
+ timestamp
+ }
+}
+```
+
+Example events you might receive:
+
+```json
+{
+ "data": {
+ "agentEvent": {
+ "name": "countUpdated",
+ "data": {
+ "count": 5
+ },
+ "timestamp": "2025-06-08T14:30:00Z"
+ }
+ }
+}
+```
+
+```json
+{
+ "data": {
+ "agentEvent": {
+ "name": "status",
+ "data": {
+ "status": "running"
+ },
+ "timestamp": "2025-06-08T14:30:05Z"
+ }
+ }
+}
+```
+
+### Client Integration
+
+Use appropriate GraphQL Server-Sent Events (SSE) clients such as:
+
+- [graphql-sse](https://the-guild.dev/graphql/sse) for vanilla JavaScript
+- [urql](https://nearform.com/open-source/urql/docs/advanced/subscriptions/)
+ with Server-Sent Events (SSE) exchange
+- EventSource API directly for simple use cases
+
+Example with EventSource:
+
+```javascript
+const eventSource = new EventSource(
+ '/graphql?query=subscription{agentEvent(agentId:"agent_abc123"){name,data,timestamp}}',
+ {
+ headers: {
+ Accept: "text/event-stream",
+ },
+ },
+)
+
+eventSource.addEventListener("next", (event) => {
+ const data = JSON.parse(event.data)
+ console.log("Agent event:", data)
+})
+```
diff --git a/modus/sdk/assemblyscript/overview.mdx b/modus/sdk/assemblyscript/overview.mdx
index 832a1ff..c46abff 100644
--- a/modus/sdk/assemblyscript/overview.mdx
+++ b/modus/sdk/assemblyscript/overview.mdx
@@ -36,6 +36,12 @@ APIs.
| [`http`](./http) | Provides access to external HTTP endpoints, including REST APIs and other services |
| [`graphql`](./graphql) | Allows you to securely call and fetch data from any GraphQL endpoint |
+### Agent APIs
+
+| Namespace | Purpose |
+| :------------------- | :--------------------------------------------------------------------------------------- |
+| [`agents`](./agents) | Create stateful agents that maintain persistent memory and coordinate complex operations |
+
### Database APIs
| Namespace | Purpose |
diff --git a/modus/sdk/go/agents.mdx b/modus/sdk/go/agents.mdx
new file mode 100644
index 0000000..ff5783d
--- /dev/null
+++ b/modus/sdk/go/agents.mdx
@@ -0,0 +1,609 @@
+---
+title: "Agents APIs"
+sidebarTitle: "Agents"
+description:
+ "Build stateful agents that maintain persistent memory across interactions"
+icon: "circle-small"
+iconType: "solid"
+---
+
+import { SdkHeader } from "/snippets/sdk-header.mdx"
+import SdkTip from "/snippets/sdk-tip.mdx"
+
+
+
+The Modus Agents APIs allow you to create stateful agents that maintain
+persistent memory across interactions, survive system failures, and coordinate
+complex multi-step operations.
+
+## Import
+
+To begin, import the `agents` package from the SDK:
+
+```go
+import "github.com/hypermodeinc/modus/sdk/go/pkg/agents"
+```
+
+## Agent APIs
+
+{/* vale Google.Headings = NO */}
+
+The APIs in the `agents` package are below, organized by category.
+
+
+
+### Agent Management Functions
+
+#### Register
+
+Register an agent struct with the Modus runtime before it can be instantiated.
+
+```go
+func Register(agent Agent)
+```
+
+
+ A pointer to an instance of the agent struct that implements the `Agent`
+ interface.
+
+
+
+ Agent registration must be done in an `init()` function to ensure it happens
+ before any agent operations.
+
+
+#### Start
+
+Create and start a new agent instance.
+
+```go
+func Start(agentName string) (AgentInfo, error)
+```
+
+
+ The name of the agent to instantiate. This must match the `Name()` method
+ returned by the agent implementation.
+
+
+#### Stop
+
+Stop an agent instance. Once stopped, the agent can't be resumed.
+
+```go
+func Stop(agentId string) (AgentInfo, error)
+```
+
+
+ The unique identifier of the agent instance to stop.
+
+
+#### GetInfo
+
+Get information about a specific agent instance.
+
+```go
+func GetInfo(agentId string) (AgentInfo, error)
+```
+
+
+ The unique identifier of the agent instance.
+
+
+#### ListAll
+
+List all active agent instances.
+
+```go
+func ListAll() ([]AgentInfo, error)
+```
+
+### Communication Functions
+
+#### SendMessage
+
+Send a synchronous message to an agent and wait for a response.
+
+```go
+func SendMessage(agentId, messageName string, opts ...MessageOption) (*string, error)
+```
+
+
+ The unique identifier of the target agent instance.
+
+
+
+ The name of the message to send to the agent.
+
+
+
+ Optional message options. Use `WithData(data string)` to include a data
+ payload.
+
+
+#### SendMessageAsync
+
+Send an asynchronous message to an agent without waiting for a response.
+
+```go
+func SendMessageAsync(agentId, messageName string, opts ...MessageOption) error
+```
+
+
+ The unique identifier of the target agent instance.
+
+
+
+ The name of the message to send to the agent.
+
+
+
+ Optional message options. Use `WithData(data string)` to include a data
+ payload.
+
+
+#### WithData
+
+Create a message option that includes a data payload.
+
+```go
+func WithData(data string) MessageOption
+```
+
+
+ The data payload to include with the message.
+
+
+### Agent Interface
+
+#### Agent
+
+The interface that all agents must implement.
+
+```go
+type Agent interface {
+ Name() string
+ OnReceiveMessage(messageName string, data string) (*string, error)
+ GetState() *string
+ SetState(data string)
+ OnInitialize() error
+ OnSuspend() error
+ OnResume() error
+ OnTerminate() error
+}
+```
+
+
+ Must return a unique name for the agent implementation.
+
+
+
+ Handles incoming messages to the agent. Must be implemented by all agents.
+
+
+
+ Returns the agent's current state as a string for persistence. Called
+ automatically when the agent needs to be suspended or migrated.
+
+
+
+ Restores the agent's state from a string. Called automatically when the agent
+ is resumed or migrated.
+
+
+
+ Lifecycle method called when the agent is first created.
+
+
+
+ Lifecycle method called when the agent is about to be suspended.
+
+
+
+ Lifecycle method called when the agent is resumed from suspension.
+
+
+
+ Lifecycle method called when the agent is about to be terminated.
+
+
+#### AgentBase
+
+A convenient base struct that provides default implementations for optional
+methods and includes event publishing capabilities.
+
+```go
+type AgentBase struct{}
+
+func (AgentBase) GetState() *string { return nil }
+func (AgentBase) SetState(data string) {}
+func (AgentBase) OnInitialize() error { return nil }
+func (AgentBase) OnSuspend() error { return nil }
+func (AgentBase) OnResume() error { return nil }
+func (AgentBase) OnTerminate() error { return nil }
+func (a *AgentBase) PublishEvent(event AgentEvent) error
+```
+
+
+ You can embed `AgentBase` in your agent struct to automatically implement the
+ optional methods, then override only the ones you need.
+
+
+
+ Publishes an event from this agent to any subscribers. The event must
+ implement the `AgentEvent` interface.
+
+
+### Event Interface
+
+#### AgentEvent
+
+Interface that custom events must implement to be published from agents.
+
+```go
+type AgentEvent interface {
+ EventName() string
+}
+```
+
+
+ Must return the name/type of the event as a string.
+
+
+Custom events should implement this interface and include any additional data as
+struct fields:
+
+```go
+type CountUpdated struct {
+ Count int `json:"count"`
+}
+
+func (e CountUpdated) EventName() string {
+ return "countUpdated"
+}
+```
+
+### Types
+
+#### AgentInfo
+
+Information about an agent instance.
+
+```go
+type AgentInfo struct {
+ Id string
+ Name string
+ Status string
+}
+```
+
+
+ The unique identifier of the agent instance.
+
+
+
+ The name of the agent implementation.
+
+
+
+ The current status of the agent instance.
+
+
+#### MessageOption
+
+Options for customizing agent messages.
+
+```go
+type MessageOption interface {
+ // internal implementation
+}
+```
+
+## Example Usage
+
+Here's a complete example of a simple counter agent with event streaming:
+
+### Agent Implementation
+
+```go
+package main
+
+import (
+ "strconv"
+ "github.com/hypermodeinc/modus/sdk/go/pkg/agents"
+)
+
+type CounterAgent struct {
+ agents.AgentBase
+ count int
+}
+
+func (c *CounterAgent) Name() string {
+ return "Counter"
+}
+
+func (c *CounterAgent) GetState() *string {
+ state := strconv.Itoa(c.count)
+ return &state
+}
+
+func (c *CounterAgent) SetState(data string) {
+ if data == nil {
+ return
+ }
+ if count, err := strconv.Atoi(*data); err == nil {
+ c.count = count
+ }
+}
+
+func (c *CounterAgent) OnInitialize() error {
+ fmt.Printf("Counter agent started\n")
+ return nil
+}
+
+func (c *CounterAgent) OnReceiveMessage(messageName string, data string) (*string, error) {
+ switch messageName {
+ case "count":
+ result := strconv.Itoa(c.count)
+ return &result, nil
+
+ case "increment":
+ if data != nil {
+ if increment, err := strconv.Atoi(*data); err == nil {
+ c.count += increment
+ }
+ } else {
+ c.count++
+ }
+
+ // Publish an event to subscribers
+ if err := c.PublishEvent(CountUpdated{Count: c.count}); err != nil {
+ return nil, err
+ }
+
+ result := strconv.Itoa(c.count)
+ return &result, nil
+ }
+
+ return nil, nil
+}
+
+// Custom event for count updates
+type CountUpdated struct {
+ Count int `json:"count"`
+}
+
+func (e CountUpdated) EventName() string {
+ return "countUpdated"
+}
+
+// Register the agent in init function
+func init() {
+ agents.Register(&CounterAgent{})
+}
+```
+
+### Function Integration
+
+```go
+// Start a counter agent and return its info
+func StartCounterAgent() (agents.AgentInfo, error) {
+ return agents.Start("Counter")
+}
+
+// Get the current count from an agent
+func GetCount(agentId string) (int, error) {
+ count, err := agents.SendMessage(agentId, "count")
+ if err != nil {
+ return 0, err
+ }
+ if count == nil {
+ return 0, nil
+ }
+ return strconv.Atoi(*count)
+}
+
+// Increment the count and return the new value
+func UpdateCount(agentId string) (int, error) {
+ count, err := agents.SendMessage(agentId, "increment")
+ if err != nil {
+ return 0, err
+ }
+ if count == nil {
+ return 0, nil
+ }
+ return strconv.Atoi(*count)
+}
+
+// Increment the count asynchronously by a specific amount
+func UpdateCountAsync(agentId string, qty int) error {
+ return agents.SendMessageAsync(agentId, "increment",
+ agents.WithData(strconv.Itoa(qty)))
+}
+
+// Stop an agent
+func StopAgent(agentId string) (agents.AgentInfo, error) {
+ return agents.Stop(agentId)
+}
+
+// List all active agents
+func ListAgents() ([]agents.AgentInfo, error) {
+ return agents.ListAll()
+}
+```
+
+### GraphQL Usage
+
+Once deployed, your agent functions become available via GraphQL:
+
+```graphql
+# Start a new agent
+mutation {
+ startCounterAgent {
+ id
+ name
+ status
+ }
+}
+
+# Get the current count
+query {
+ getCount(agentId: "agent_abc123")
+}
+
+# Increment the count
+mutation {
+ updateCount(agentId: "agent_abc123")
+}
+
+# Increment asynchronously
+mutation {
+ updateCountAsync(agentId: "agent_abc123", qty: 5)
+}
+
+# Subscribe to real-time events
+subscription {
+ agentEvent(agentId: "agent_abc123") {
+ name
+ data
+ timestamp
+ }
+}
+```
+
+### Event Subscription
+
+To receive real-time events from your agent, subscribe using GraphQL
+subscriptions over Server-Sent Events:
+
+```graphql
+subscription CounterEvents($agentId: String!) {
+ agentEvent(agentId: $agentId) {
+ name
+ data
+ timestamp
+ }
+}
+```
+
+Example events you might receive:
+
+```json
+{
+ "data": {
+ "agentEvent": {
+ "name": "countUpdated",
+ "data": {
+ "count": 5
+ },
+ "timestamp": "2025-06-08T14:30:00Z"
+ }
+ }
+}
+```
+
+```json
+{
+ "data": {
+ "agentEvent": {
+ "name": "status",
+ "data": {
+ "status": "running"
+ },
+ "timestamp": "2025-06-08T14:30:05Z"
+ }
+ }
+}
+```
+
+### Multiple Event Types
+
+You can define multiple event types for different scenarios:
+
+```go
+// Mission events
+type MissionStarted struct {
+ MissionName string `json:"missionName"`
+ Priority string `json:"priority"`
+}
+
+func (e MissionStarted) EventName() string {
+ return "missionStarted"
+}
+
+type MissionCompleted struct {
+ MissionName string `json:"missionName"`
+ Success bool `json:"success"`
+ Duration int `json:"duration"`
+}
+
+func (e MissionCompleted) EventName() string {
+ return "missionCompleted"
+}
+
+// Error events
+type ErrorOccurred struct {
+ Message string `json:"message"`
+ Code string `json:"code"`
+}
+
+func (e ErrorOccurred) EventName() string {
+ return "errorOccurred"
+}
+```
+
+### Client Integration
+
+Use appropriate GraphQL Server-Sent Events (SSE) clients such as:
+
+- [graphql-sse](https://the-guild.dev/graphql/sse) for JavaScript/TypeScript
+- [gqlgen](https://gqlgen.com/recipes/subscriptions/) for Go clients
+- Standard HTTP libraries with Server-Sent Events (SSE) support
+
+Example with curl:
+
+```bash
+curl -N -H "accept: text/event-stream" \
+ -H "content-type: application/json" \
+ -X POST http://localhost:8080/graphql \
+ -d '{"query":"subscription { agentEvent(agentId: \"agent_abc123\") { name data timestamp } }"}'
+```
+
+Example with Go client:
+
+```go
+import (
+ "bufio"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strings"
+)
+
+func subscribeToAgentEvents(agentId string) {
+ query := fmt.Sprintf(`{"query":"subscription { agentEvent(agentId: \"%s\") { name data timestamp } }"}`, agentId)
+
+ req, _ := http.NewRequest("POST", "http://localhost:8080/graphql", strings.NewReader(query))
+ req.Header.Set("Accept", "text/event-stream")
+ req.Header.Set("Content-Type", "application/json")
+
+ client := &http.Client{}
+ resp, err := client.Do(req)
+ if err != nil {
+ panic(err)
+ }
+ defer resp.Body.Close()
+
+ scanner := bufio.NewScanner(resp.Body)
+ for scanner.Scan() {
+ line := scanner.Text()
+ if strings.HasPrefix(line, "data: ") {
+ data := strings.TrimPrefix(line, "data: ")
+ fmt.Printf("Received event: %s\n", data)
+ }
+ }
+}
+```
diff --git a/modus/sdk/go/overview.mdx b/modus/sdk/go/overview.mdx
index 1701ac6..a6540ca 100644
--- a/modus/sdk/go/overview.mdx
+++ b/modus/sdk/go/overview.mdx
@@ -34,6 +34,12 @@ your convenience. Click on a package to view more information about its APIs.
| [`http`](./http) | Provides access to external HTTP endpoints, including REST APIs and other services |
| [`graphql`](./graphql) | Allows you to securely call and fetch data from any GraphQL endpoint |
+### Agent APIs
+
+| Namespace | Purpose |
+| :------------------- | :--------------------------------------------------------------------------------------- |
+| [`agents`](./agents) | Create stateful agents that maintain persistent memory and coordinate complex operations |
+
### Database APIs
| Package | Purpose |
diff --git a/styles/config/vocabularies/general/accept.txt b/styles/config/vocabularies/general/accept.txt
index 0c53ac6..9d16876 100644
--- a/styles/config/vocabularies/general/accept.txt
+++ b/styles/config/vocabularies/general/accept.txt
@@ -134,4 +134,6 @@ vCPU|vCPUs
WebSocket
USA
-[Gg]raphs
\ No newline at end of file
+[Gg]raphs
+
+gqlgen
\ No newline at end of file