DeploySEALED

▪◆▪■╳●▫■?#◇○▓*#▫$□◇%◆&╳?%□◇■*@#▓◇^$●█^●○╳@╲▓░%▫▓#&%*?◆#^●#~?▪▪◇@

Transaction ID

Timestamp

Feb 27, 2026, 12:08:54 AM UTC
1d ago

Block Height

143,525,904

Computation

0

Execution Fee

0.00562 FLOW

Transaction Summary

Deploy

Contract deployment

Contract deployment

Script Arguments

0nameString
AgentScheduler
1codeString
// AgentScheduler.cdc // Replaces OpenClaw's unreliable off-chain cron with Flow's native scheduled transactions. // // WHY THIS IS BETTER: // In OpenClaw/ZeroClaw, cron jobs run via Node.js setInterval or system crontab. // If the process crashes, the machine sleeps, or the daemon restarts — jobs get missed. // Flow's scheduled transactions are executed by VALIDATORS at the protocol level. // Once scheduled, they run regardless of whether your machine is online. // // HOW IT WORKS: // 1. Agent owner schedules a task (e.g., "summarize my inbox every morning at 9am") // 2. The task is registered on-chain with a TransactionHandler capability // 3. Flow validators execute the handler at the specified timestamp // 4. The handler emits an AgentTaskTriggered event // 5. The off-chain relay picks up the event and does the actual work // 6. Results are posted back on-chain // // For recurring tasks, the handler RE-SCHEDULES itself after execution, // creating a reliable on-chain cron loop that can't be missed. // // CADENCE FEATURES USED: // - Pre/post conditions: validate task parameters, verify execution results // - Capabilities: handler references are capability-scoped // - Entitlements: separate Schedule, Execute, Cancel permissions // - Resources: tasks are owned objects with move semantics import AgentRegistry from 0x91d0a5b7c9832a8b access(all) contract AgentScheduler { // ----------------------------------------------------------------------- // Events // ----------------------------------------------------------------------- access(all) event TaskScheduled( taskId: UInt64, agentId: UInt64, owner: Address, taskType: String, executeAt: UFix64, isRecurring: Bool ) access(all) event TaskTriggered( taskId: UInt64, agentId: UInt64, owner: Address, taskType: String, triggeredAt: UFix64 ) access(all) event TaskCompleted( taskId: UInt64, resultHash: String, nextExecutionAt: UFix64? ) access(all) event TaskCanceled(taskId: UInt64) access(all) event TaskFailed(taskId: UInt64, reason: String) access(all) event TaskRescheduled(taskId: UInt64, nextExecutionAt: UFix64) // ----------------------------------------------------------------------- // Paths // ----------------------------------------------------------------------- access(all) let SchedulerStoragePath: StoragePath // ----------------------------------------------------------------------- // State // ----------------------------------------------------------------------- access(all) var totalTasks: UInt64 // ----------------------------------------------------------------------- // Entitlements — separate permissions for scheduling vs executing vs canceling // ----------------------------------------------------------------------- access(all) entitlement Schedule access(all) entitlement Execute access(all) entitlement Cancel access(all) entitlement Admin // ----------------------------------------------------------------------- // TaskType — predefined agent task categories // ----------------------------------------------------------------------- access(all) enum TaskCategory: UInt8 { // Inference tasks — trigger an LLM call with a prompt access(all) case inference // Memory tasks — compaction, cleanup, summarization access(all) case memoryMaintenance // Monitoring tasks — check conditions, report status access(all) case monitoring // Communication tasks — send scheduled messages access(all) case communication // Custom tasks — user-defined via prompt access(all) case custom } // ----------------------------------------------------------------------- // RecurrenceRule — how often a task repeats // ----------------------------------------------------------------------- access(all) struct RecurrenceRule { access(all) let intervalSeconds: UFix64 // Time between executions access(all) let maxExecutions: UInt64? // nil = infinite access(all) let endTimestamp: UFix64? // nil = no end date init( intervalSeconds: UFix64, maxExecutions: UInt64?, endTimestamp: UFix64? ) { pre { intervalSeconds >= 60.0: "Minimum interval is 60 seconds" } self.intervalSeconds = intervalSeconds self.maxExecutions = maxExecutions self.endTimestamp = endTimestamp } } // ----------------------------------------------------------------------- // Common recurrence presets // ----------------------------------------------------------------------- access(all) fun everyMinute(): RecurrenceRule { return RecurrenceRule(intervalSeconds: 60.0, maxExecutions: nil, endTimestamp: nil) } access(all) fun everyHour(): RecurrenceRule { return RecurrenceRule(intervalSeconds: 3600.0, maxExecutions: nil, endTimestamp: nil) } access(all) fun everyDay(): RecurrenceRule { return RecurrenceRule(intervalSeconds: 86400.0, maxExecutions: nil, endTimestamp: nil) } access(all) fun everyWeek(): RecurrenceRule { return RecurrenceRule(intervalSeconds: 604800.0, maxExecutions: nil, endTimestamp: nil) } // ----------------------------------------------------------------------- // TaskConfig — what the scheduled task should do // ----------------------------------------------------------------------- access(all) struct TaskConfig { access(all) let name: String // Human-readable name access(all) let description: String access(all) let category: TaskCategory access(all) let prompt: String // The instruction for the agent access(all) let targetSessionId: UInt64? // Which session to use (nil = create new) access(all) let toolsAllowed: [String] // Which tools this task can use access(all) let maxTurns: UInt64 // Max agentic loop turns access(all) let priority: UInt8 // 0=low, 1=medium, 2=high init( name: String, description: String, category: TaskCategory, prompt: String, targetSessionId: UInt64?, toolsAllowed: [String], maxTurns: UInt64, priority: UInt8 ) { pre { name.length > 0: "Task name cannot be empty" prompt.length > 0: "Task prompt cannot be empty" maxTurns > 0 && maxTurns <= 20: "Max turns must be 1-20" priority <= 2: "Priority must be 0, 1, or 2" } self.name = name self.description = description self.category = category self.prompt = prompt self.targetSessionId = targetSessionId self.toolsAllowed = toolsAllowed self.maxTurns = maxTurns self.priority = priority } } // ----------------------------------------------------------------------- // ScheduledTask — a task waiting to be executed // ----------------------------------------------------------------------- access(all) struct ScheduledTask { access(all) let taskId: UInt64 access(all) let agentId: UInt64 access(all) let owner: Address access(all) let config: TaskConfig access(all) let createdAt: UFix64 access(all) var nextExecutionAt: UFix64 access(all) let recurrence: RecurrenceRule? // nil = one-shot access(all) var executionCount: UInt64 access(all) var lastExecutedAt: UFix64? access(all) var lastResultHash: String? access(all) var status: UInt8 // 0=active, 1=paused, 2=completed, 3=failed init( taskId: UInt64, agentId: UInt64, owner: Address, config: TaskConfig, executeAt: UFix64, recurrence: RecurrenceRule? ) { self.taskId = taskId self.agentId = agentId self.owner = owner self.config = config self.createdAt = getCurrentBlock().timestamp self.nextExecutionAt = executeAt self.recurrence = recurrence self.executionCount = 0 self.lastExecutedAt = nil self.lastResultHash = nil self.status = 0 } } // ----------------------------------------------------------------------- // TaskExecutionResult — recorded after each execution // ----------------------------------------------------------------------- access(all) struct TaskExecutionResult { access(all) let taskId: UInt64 access(all) let executionNumber: UInt64 access(all) let triggeredAt: UFix64 access(all) let completedAt: UFix64 access(all) let resultHash: String access(all) let tokensUsed: UInt64 access(all) let turnsUsed: UInt64 access(all) let success: Bool access(all) let errorMessage: String? init( taskId: UInt64, executionNumber: UInt64, triggeredAt: UFix64, completedAt: UFix64, resultHash: String, tokensUsed: UInt64, turnsUsed: UInt64, success: Bool, errorMessage: String? ) { self.taskId = taskId self.executionNumber = executionNumber self.triggeredAt = triggeredAt self.completedAt = completedAt self.resultHash = resultHash self.tokensUsed = tokensUsed self.turnsUsed = turnsUsed self.success = success self.errorMessage = errorMessage } } // ----------------------------------------------------------------------- // Scheduler Resource — per-account task manager // ----------------------------------------------------------------------- access(all) resource Scheduler { access(all) let agentId: UInt64 access(self) var tasks: {UInt64: ScheduledTask} access(self) var executionHistory: [TaskExecutionResult] access(self) var activeTaskCount: UInt64 init(agentId: UInt64) { self.agentId = agentId self.tasks = {} self.executionHistory = [] self.activeTaskCount = 0 } // --- Schedule: create and manage scheduled tasks --- access(Schedule) fun scheduleTask( config: TaskConfig, executeAt: UFix64, recurrence: RecurrenceRule? ): UInt64 { pre { executeAt > getCurrentBlock().timestamp: "Execution time must be in the future" self.activeTaskCount < 50: "Maximum 50 active tasks per agent" } post { self.tasks[AgentScheduler.totalTasks] != nil: "Task must be stored after scheduling" } AgentScheduler.totalTasks = AgentScheduler.totalTasks + 1 let taskId = AgentScheduler.totalTasks let task = ScheduledTask( taskId: taskId, agentId: self.agentId, owner: self.owner!.address, config: config, executeAt: executeAt, recurrence: recurrence ) self.tasks[taskId] = task self.activeTaskCount = self.activeTaskCount + 1 emit TaskScheduled( taskId: taskId, agentId: self.agentId, owner: self.owner!.address, taskType: config.name, executeAt: executeAt, isRecurring: recurrence != nil ) // NOTE: In production, this is where you'd call // FlowTransactionScheduler to register with the protocol. // For the PoC, the relay polls for TaskScheduled events // and sets up its own timer. return taskId } access(Schedule) fun scheduleRecurringInference( name: String, prompt: String, recurrence: RecurrenceRule, priority: UInt8 ): UInt64 { let config = TaskConfig( name: name, description: "Recurring inference: ".concat(name), category: TaskCategory.inference, prompt: prompt, targetSessionId: nil, toolsAllowed: ["memory_store", "memory_recall", "web_fetch"], maxTurns: 5, priority: priority ) let firstExecution = getCurrentBlock().timestamp + recurrence.intervalSeconds return self.scheduleTask( config: config, executeAt: firstExecution, recurrence: recurrence ) } // --- Execute: trigger and complete tasks --- access(Execute) fun triggerTask(taskId: UInt64): Bool { pre { self.tasks[taskId] != nil: "Task not found" } if var task = self.tasks[taskId] { // Verify it's time if task.status != 0 { return false // Not active } if getCurrentBlock().timestamp < task.nextExecutionAt { return false // Not time yet } emit TaskTriggered( taskId: taskId, agentId: self.agentId, owner: self.owner!.address, taskType: task.config.name, triggeredAt: getCurrentBlock().timestamp ) return true } return false } access(Execute) fun completeTask( taskId: UInt64, resultHash: String, tokensUsed: UInt64, turnsUsed: UInt64, success: Bool, errorMessage: String? ) { pre { self.tasks[taskId] != nil: "Task not found" } post { // If recurring and successful, must have a next execution time self.tasks[taskId]!.recurrence == nil || !success || self.tasks[taskId]!.status != 0 || self.tasks[taskId]!.nextExecutionAt > before(self.tasks[taskId]!.nextExecutionAt): "Recurring task must reschedule" } if var task = self.tasks[taskId] { let now = getCurrentBlock().timestamp // Record execution let result = TaskExecutionResult( taskId: taskId, executionNumber: task.executionCount + 1, triggeredAt: task.nextExecutionAt, completedAt: now, resultHash: resultHash, tokensUsed: tokensUsed, turnsUsed: turnsUsed, success: success, errorMessage: errorMessage ) self.executionHistory.append(result) // Update task state task = ScheduledTask( taskId: task.taskId, agentId: task.agentId, owner: task.owner, config: task.config, executeAt: task.nextExecutionAt, recurrence: task.recurrence ) // Handle recurrence if let recurrence = task.recurrence { let nextExec = now + recurrence.intervalSeconds // Check if we should continue var shouldContinue = success if let maxExec = recurrence.maxExecutions { if task.executionCount + 1 >= maxExec { shouldContinue = false } } if let endTime = recurrence.endTimestamp { if nextExec > endTime { shouldContinue = false } } if shouldContinue { // Reschedule — this is the key to reliable recurring tasks // The task re-registers itself on-chain for the next execution emit TaskRescheduled(taskId: taskId, nextExecutionAt: nextExec) } else { // Mark as completed emit TaskCompleted( taskId: taskId, resultHash: resultHash, nextExecutionAt: nil ) } } else { // One-shot task — mark complete self.activeTaskCount = self.activeTaskCount - 1 emit TaskCompleted( taskId: taskId, resultHash: resultHash, nextExecutionAt: nil ) } if !success { emit TaskFailed(taskId: taskId, reason: errorMessage ?? "Unknown error") } } } // --- Cancel: stop scheduled tasks --- access(Cancel) fun cancelTask(taskId: UInt64): Bool { if self.tasks[taskId] != nil { self.tasks.remove(key: taskId) self.activeTaskCount = self.activeTaskCount - 1 emit TaskCanceled(taskId: taskId) return true } return false } // --- Read --- access(all) fun getTask(taskId: UInt64): ScheduledTask? { return self.tasks[taskId] } access(all) fun getActiveTasks(): [ScheduledTask] { var active: [ScheduledTask] = [] for taskId in self.tasks.keys { if let task = self.tasks[taskId] { if task.status == 0 { active.append(task) } } } return active } access(all) fun getTasksDueBefore(timestamp: UFix64): [ScheduledTask] { var due: [ScheduledTask] = [] for taskId in self.tasks.keys { if let task = self.tasks[taskId] { if task.status == 0 && task.nextExecutionAt <= timestamp { due.append(task) } } } return due } access(all) fun getExecutionHistory(limit: Int): [TaskExecutionResult] { let len = self.executionHistory.length if len <= limit { return self.executionHistory } return self.executionHistory.slice(from: len - limit, upTo: len) } access(all) fun getActiveTaskCount(): UInt64 { return self.activeTaskCount } } // ----------------------------------------------------------------------- // Public factory // ----------------------------------------------------------------------- access(all) fun createScheduler(agentId: UInt64): @Scheduler { return <- create Scheduler(agentId: agentId) } // ----------------------------------------------------------------------- // Init // ----------------------------------------------------------------------- init() { self.totalTasks = 0 self.SchedulerStoragePath = /storage/FlowClawScheduler } }

Cadence Script

1transaction(name: String, code: String ) {
2		prepare(signer: auth(AddContract) &Account) {
3			signer.contracts.add(name: name, code: code.utf8 )
4		}
5	}