Smart Contract

FlowClaw

A.91d0a5b7c9832a8b.FlowClaw

Valid From

143,525,964

Deployed

1d ago
Feb 27, 2026, 12:09:53 AM UTC

Dependents

0 imports
1// FlowClaw.cdc
2// Main orchestrator contract — the agentic harness on Flow.
3// Ties together AgentRegistry, AgentSession, InferenceOracle, ToolRegistry, and AgentMemory.
4// Each Flow account gets a complete, private agent stack.
5//
6// Architecture:
7// ┌──────────────────────────────────────────────────────┐
8// │  Flow Account (Owner)                                │
9// │  ┌─────────────┐  ┌──────────────┐  ┌────────────┐  │
10// │  │   Agent      │  │  Sessions    │  │  Memory    │  │
11// │  │  (Resource)  │  │  (Resource)  │  │  (Resource)│  │
12// │  │             │  │              │  │            │  │
13// │  │ - config    │  │ - history    │  │ - k/v store│  │
14// │  │ - security  │  │ - messages   │  │ - tags     │  │
15// │  │ - rate lim  │  │ - inference  │  │ - search   │  │
16// │  └─────────────┘  └──────────────┘  └────────────┘  │
17// │  ┌──────────────┐  ┌──────────────┐                  │
18// │  │  Tools       │  │  Oracle      │                  │
19// │  │  (Resource)  │  │  Config      │                  │
20// │  │             │  │  (Resource)  │                  │
21// │  │ - registry  │  │ - relays     │                  │
22// │  │ - exec log  │  │ - dedup      │                  │
23// │  └──────────────┘  └──────────────┘                  │
24// └──────────────────────────────────────────────────────┘
25//           │ Events ↓            ↑ Transactions
26// ┌──────────────────────────────────────────────────────┐
27// │  Off-Chain Inference Relay (per-account)             │
28// │  - Listens for InferenceRequested events             │
29// │  - Calls LLM provider with account's config         │
30// │  - Posts results back via completeInference tx       │
31// │  - Executes tool calls in sandboxed environment      │
32// └──────────────────────────────────────────────────────┘
33
34import AgentRegistry from 0x91d0a5b7c9832a8b
35import AgentSession from 0x91d0a5b7c9832a8b
36import InferenceOracle from 0x91d0a5b7c9832a8b
37import ToolRegistry from 0x91d0a5b7c9832a8b
38import AgentMemory from 0x91d0a5b7c9832a8b
39
40access(all) contract FlowClaw {
41
42    // -----------------------------------------------------------------------
43    // Events
44    // -----------------------------------------------------------------------
45    access(all) event AccountInitialized(owner: Address, agentId: UInt64)
46    access(all) event AgentLoopStarted(agentId: UInt64, sessionId: UInt64, owner: Address)
47    access(all) event AgentLoopCompleted(agentId: UInt64, sessionId: UInt64, turnsUsed: UInt64)
48    access(all) event UserMessageSent(sessionId: UInt64, contentHash: String)
49    access(all) event AgentResponseReceived(sessionId: UInt64, contentHash: String)
50
51    // -----------------------------------------------------------------------
52    // Paths
53    // -----------------------------------------------------------------------
54    access(all) let FlowClawStoragePath: StoragePath
55
56    // -----------------------------------------------------------------------
57    // State
58    // -----------------------------------------------------------------------
59    access(all) var totalAccounts: UInt64
60    access(all) var totalMessages: UInt64
61    access(all) let version: String
62
63    // -----------------------------------------------------------------------
64    // Entitlements
65    // -----------------------------------------------------------------------
66    access(all) entitlement Owner
67    access(all) entitlement Operate
68
69    // -----------------------------------------------------------------------
70    // AgentStack — the complete per-account agent infrastructure
71    // -----------------------------------------------------------------------
72    access(all) resource AgentStack {
73        access(all) let agentId: UInt64
74        access(all) var isInitialized: Bool
75
76        // References to the per-account resources (stored separately)
77        // The AgentStack coordinates between them
78
79        init(agentId: UInt64) {
80            self.agentId = agentId
81            self.isInitialized = true
82        }
83
84        // --- Send a user message and request inference ---
85        // This is the main entry point for the agentic loop
86        access(Operate) fun sendMessage(
87            sessionManager: auth(AgentSession.Manage) &AgentSession.SessionManager,
88            agent: auth(AgentRegistry.Execute) &AgentRegistry.Agent,
89            sessionId: UInt64,
90            content: String,
91            contentHash: String
92        ): UInt64? {
93            // 1. Validate agent is active and within rate limits
94            if !agent.isActive {
95                return nil
96            }
97            if !agent.checkRateLimits() {
98                return nil
99            }
100
101            // 2. Get session
102            let session = sessionManager.borrowSession(sessionId: sessionId)
103                ?? panic("Session not found")
104
105            // 3. Add user message to session
106            session.addMessage(
107                role: "user",
108                content: content,
109                contentHash: contentHash,
110                toolName: nil,
111                toolCallId: nil,
112                tokensEstimate: UInt64(content.length / 4) // rough estimate
113            )
114
115            FlowClaw.totalMessages = FlowClaw.totalMessages + 1
116            emit UserMessageSent(sessionId: sessionId, contentHash: contentHash)
117
118            // 4. Request inference — emits event for relay
119            let requestId = session.requestInference(
120                agentRef: agent,
121                messagesHash: contentHash
122            )
123
124            emit AgentLoopStarted(
125                agentId: self.agentId,
126                sessionId: sessionId,
127                owner: self.owner!.address
128            )
129
130            return requestId
131        }
132
133        // --- Complete an inference (called by relay transaction) ---
134        access(Operate) fun completeInference(
135            sessionManager: auth(AgentSession.Manage) &AgentSession.SessionManager,
136            agent: auth(AgentRegistry.Execute) &AgentRegistry.Agent,
137            oracleConfig: auth(InferenceOracle.Relay) &InferenceOracle.OracleConfig,
138            sessionId: UInt64,
139            requestId: UInt64,
140            responseContent: String,
141            responseHash: String,
142            tokensUsed: UInt64,
143            relayAddress: Address
144        ) {
145            // 1. Verify relay is authorized for this account
146            assert(
147                oracleConfig.isRelayAuthorized(relayAddress: relayAddress),
148                message: "Relay not authorized for this account"
149            )
150
151            // 2. Verify request hasn't already been completed (dedup)
152            assert(
153                !oracleConfig.isRequestCompleted(requestId: requestId),
154                message: "Request already completed"
155            )
156
157            // 3. Get session and complete inference
158            let session = sessionManager.borrowSession(sessionId: sessionId)
159                ?? panic("Session not found")
160
161            session.completeInference(
162                requestId: requestId,
163                responseContent: responseContent,
164                responseHash: responseHash,
165                tokensUsed: tokensUsed
166            )
167
168            // 4. Mark as completed in oracle (dedup)
169            oracleConfig.markRequestCompleted(requestId: requestId)
170
171            // 5. Record cost (rough estimate: $0.001 per 1000 tokens)
172            let costEstimate = UFix64(tokensUsed) * 0.000001
173            agent.recordCost(amount: costEstimate)
174
175            FlowClaw.totalMessages = FlowClaw.totalMessages + 1
176
177            emit AgentResponseReceived(sessionId: sessionId, contentHash: responseHash)
178        }
179
180        // --- Process tool call result from relay ---
181        access(Operate) fun processToolResult(
182            sessionManager: auth(AgentSession.Manage) &AgentSession.SessionManager,
183            toolCollection: auth(ToolRegistry.ExecuteTools) &ToolRegistry.ToolCollection,
184            sessionId: UInt64,
185            toolCallId: String,
186            toolName: String,
187            output: String,
188            outputHash: String,
189            agentId: UInt64,
190            executionTimeMs: UInt64
191        ) {
192            // 1. Log the execution
193            toolCollection.logExecution(
194                toolName: toolName,
195                agentId: agentId,
196                sessionId: sessionId,
197                inputHash: toolCallId,
198                outputHash: outputHash,
199                status: 1, // completed
200                executionTimeMs: executionTimeMs
201            )
202
203            // 2. Add tool result to session as a message
204            let session = sessionManager.borrowSession(sessionId: sessionId)
205                ?? panic("Session not found")
206
207            session.addMessage(
208                role: "tool",
209                content: output,
210                contentHash: outputHash,
211                toolName: toolName,
212                toolCallId: toolCallId,
213                tokensEstimate: UInt64(output.length / 4)
214            )
215        }
216
217        // --- Store to memory (on-chain) ---
218        access(Operate) fun storeMemory(
219            memoryVault: auth(AgentMemory.Store) &AgentMemory.MemoryVault,
220            key: String,
221            content: String,
222            contentHash: String,
223            tags: [String],
224            source: String
225        ): UInt64 {
226            return memoryVault.store(
227                key: key,
228                content: content,
229                contentHash: contentHash,
230                tags: tags,
231                source: source
232            )
233        }
234
235        // --- Recall from memory ---
236        access(Operate) fun recallMemory(
237            memoryVault: auth(AgentMemory.Recall) &AgentMemory.MemoryVault,
238            key: String
239        ): AgentMemory.MemoryEntry? {
240            return memoryVault.getByKey(key: key)
241        }
242
243        access(Operate) fun recallMemoryByTag(
244            memoryVault: auth(AgentMemory.Recall) &AgentMemory.MemoryVault,
245            tag: String
246        ): [AgentMemory.MemoryEntry] {
247            return memoryVault.getByTag(tag: tag)
248        }
249    }
250
251    // -----------------------------------------------------------------------
252    // AccountStatus — public view of a FlowClaw account
253    // -----------------------------------------------------------------------
254    access(all) struct AccountStatus {
255        access(all) let owner: Address
256        access(all) let agentInfo: AgentRegistry.AgentPublicInfo
257        access(all) let sessionCount: Int
258        access(all) let memoryCount: UInt64
259        access(all) let toolCount: Int
260        access(all) let isRelayConfigured: Bool
261
262        init(
263            owner: Address,
264            agentInfo: AgentRegistry.AgentPublicInfo,
265            sessionCount: Int,
266            memoryCount: UInt64,
267            toolCount: Int,
268            isRelayConfigured: Bool
269        ) {
270            self.owner = owner
271            self.agentInfo = agentInfo
272            self.sessionCount = sessionCount
273            self.memoryCount = memoryCount
274            self.toolCount = toolCount
275            self.isRelayConfigured = isRelayConfigured
276        }
277    }
278
279    // -----------------------------------------------------------------------
280    // Public factory
281    // -----------------------------------------------------------------------
282    access(all) fun createAgentStack(ownerAddress: Address, agentId: UInt64): @AgentStack {
283        self.totalAccounts = self.totalAccounts + 1
284        let stack <- create AgentStack(agentId: agentId)
285        emit AccountInitialized(owner: ownerAddress, agentId: agentId)
286        return <- stack
287    }
288
289    // -----------------------------------------------------------------------
290    // Version info
291    // -----------------------------------------------------------------------
292    access(all) fun getVersion(): String {
293        return self.version
294    }
295
296    // -----------------------------------------------------------------------
297    // Init
298    // -----------------------------------------------------------------------
299    init() {
300        self.totalAccounts = 0
301        self.totalMessages = 0
302        self.version = "0.1.0-alpha"
303        self.FlowClawStoragePath = /storage/FlowClawStack
304    }
305}
306