@elizaos/python 2.0.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +239 -0
  3. package/elizaos/__init__.py +280 -0
  4. package/elizaos/action_docs.py +149 -0
  5. package/elizaos/advanced_capabilities/__init__.py +85 -0
  6. package/elizaos/advanced_capabilities/actions/__init__.py +54 -0
  7. package/elizaos/advanced_capabilities/actions/add_contact.py +139 -0
  8. package/elizaos/advanced_capabilities/actions/follow_room.py +151 -0
  9. package/elizaos/advanced_capabilities/actions/image_generation.py +148 -0
  10. package/elizaos/advanced_capabilities/actions/mute_room.py +164 -0
  11. package/elizaos/advanced_capabilities/actions/remove_contact.py +145 -0
  12. package/elizaos/advanced_capabilities/actions/roles.py +207 -0
  13. package/elizaos/advanced_capabilities/actions/schedule_follow_up.py +154 -0
  14. package/elizaos/advanced_capabilities/actions/search_contacts.py +145 -0
  15. package/elizaos/advanced_capabilities/actions/send_message.py +187 -0
  16. package/elizaos/advanced_capabilities/actions/settings.py +151 -0
  17. package/elizaos/advanced_capabilities/actions/unfollow_room.py +164 -0
  18. package/elizaos/advanced_capabilities/actions/unmute_room.py +164 -0
  19. package/elizaos/advanced_capabilities/actions/update_contact.py +164 -0
  20. package/elizaos/advanced_capabilities/actions/update_entity.py +161 -0
  21. package/elizaos/advanced_capabilities/evaluators/__init__.py +18 -0
  22. package/elizaos/advanced_capabilities/evaluators/reflection.py +134 -0
  23. package/elizaos/advanced_capabilities/evaluators/relationship_extraction.py +203 -0
  24. package/elizaos/advanced_capabilities/providers/__init__.py +36 -0
  25. package/elizaos/advanced_capabilities/providers/agent_settings.py +60 -0
  26. package/elizaos/advanced_capabilities/providers/contacts.py +77 -0
  27. package/elizaos/advanced_capabilities/providers/facts.py +82 -0
  28. package/elizaos/advanced_capabilities/providers/follow_ups.py +113 -0
  29. package/elizaos/advanced_capabilities/providers/knowledge.py +83 -0
  30. package/elizaos/advanced_capabilities/providers/relationships.py +112 -0
  31. package/elizaos/advanced_capabilities/providers/roles.py +97 -0
  32. package/elizaos/advanced_capabilities/providers/settings.py +51 -0
  33. package/elizaos/advanced_capabilities/services/__init__.py +18 -0
  34. package/elizaos/advanced_capabilities/services/follow_up.py +138 -0
  35. package/elizaos/advanced_capabilities/services/rolodex.py +244 -0
  36. package/elizaos/advanced_memory/__init__.py +3 -0
  37. package/elizaos/advanced_memory/evaluators.py +97 -0
  38. package/elizaos/advanced_memory/memory_service.py +556 -0
  39. package/elizaos/advanced_memory/plugin.py +30 -0
  40. package/elizaos/advanced_memory/prompts.py +12 -0
  41. package/elizaos/advanced_memory/providers.py +90 -0
  42. package/elizaos/advanced_memory/types.py +65 -0
  43. package/elizaos/advanced_planning/__init__.py +10 -0
  44. package/elizaos/advanced_planning/actions.py +145 -0
  45. package/elizaos/advanced_planning/message_classifier.py +127 -0
  46. package/elizaos/advanced_planning/planning_service.py +712 -0
  47. package/elizaos/advanced_planning/plugin.py +40 -0
  48. package/elizaos/advanced_planning/prompts.py +4 -0
  49. package/elizaos/basic_capabilities/__init__.py +66 -0
  50. package/elizaos/basic_capabilities/actions/__init__.py +24 -0
  51. package/elizaos/basic_capabilities/actions/choice.py +140 -0
  52. package/elizaos/basic_capabilities/actions/ignore.py +66 -0
  53. package/elizaos/basic_capabilities/actions/none.py +56 -0
  54. package/elizaos/basic_capabilities/actions/reply.py +120 -0
  55. package/elizaos/basic_capabilities/providers/__init__.py +54 -0
  56. package/elizaos/basic_capabilities/providers/action_state.py +113 -0
  57. package/elizaos/basic_capabilities/providers/actions.py +263 -0
  58. package/elizaos/basic_capabilities/providers/attachments.py +76 -0
  59. package/elizaos/basic_capabilities/providers/capabilities.py +62 -0
  60. package/elizaos/basic_capabilities/providers/character.py +113 -0
  61. package/elizaos/basic_capabilities/providers/choice.py +73 -0
  62. package/elizaos/basic_capabilities/providers/context_bench.py +44 -0
  63. package/elizaos/basic_capabilities/providers/current_time.py +58 -0
  64. package/elizaos/basic_capabilities/providers/entities.py +99 -0
  65. package/elizaos/basic_capabilities/providers/evaluators.py +54 -0
  66. package/elizaos/basic_capabilities/providers/providers_list.py +55 -0
  67. package/elizaos/basic_capabilities/providers/recent_messages.py +85 -0
  68. package/elizaos/basic_capabilities/providers/time.py +45 -0
  69. package/elizaos/basic_capabilities/providers/world.py +93 -0
  70. package/elizaos/basic_capabilities/services/__init__.py +18 -0
  71. package/elizaos/basic_capabilities/services/embedding.py +122 -0
  72. package/elizaos/basic_capabilities/services/task.py +178 -0
  73. package/elizaos/bootstrap/__init__.py +12 -0
  74. package/elizaos/bootstrap/actions/__init__.py +68 -0
  75. package/elizaos/bootstrap/actions/add_contact.py +149 -0
  76. package/elizaos/bootstrap/actions/choice.py +147 -0
  77. package/elizaos/bootstrap/actions/follow_room.py +151 -0
  78. package/elizaos/bootstrap/actions/ignore.py +80 -0
  79. package/elizaos/bootstrap/actions/image_generation.py +135 -0
  80. package/elizaos/bootstrap/actions/mute_room.py +151 -0
  81. package/elizaos/bootstrap/actions/none.py +71 -0
  82. package/elizaos/bootstrap/actions/remove_contact.py +159 -0
  83. package/elizaos/bootstrap/actions/reply.py +140 -0
  84. package/elizaos/bootstrap/actions/roles.py +193 -0
  85. package/elizaos/bootstrap/actions/schedule_follow_up.py +164 -0
  86. package/elizaos/bootstrap/actions/search_contacts.py +159 -0
  87. package/elizaos/bootstrap/actions/send_message.py +173 -0
  88. package/elizaos/bootstrap/actions/settings.py +165 -0
  89. package/elizaos/bootstrap/actions/unfollow_room.py +151 -0
  90. package/elizaos/bootstrap/actions/unmute_room.py +151 -0
  91. package/elizaos/bootstrap/actions/update_contact.py +178 -0
  92. package/elizaos/bootstrap/actions/update_entity.py +175 -0
  93. package/elizaos/bootstrap/autonomy/__init__.py +18 -0
  94. package/elizaos/bootstrap/autonomy/action.py +197 -0
  95. package/elizaos/bootstrap/autonomy/providers.py +165 -0
  96. package/elizaos/bootstrap/autonomy/routes.py +171 -0
  97. package/elizaos/bootstrap/autonomy/service.py +562 -0
  98. package/elizaos/bootstrap/autonomy/types.py +18 -0
  99. package/elizaos/bootstrap/evaluators/__init__.py +19 -0
  100. package/elizaos/bootstrap/evaluators/reflection.py +118 -0
  101. package/elizaos/bootstrap/evaluators/relationship_extraction.py +192 -0
  102. package/elizaos/bootstrap/plugin.py +140 -0
  103. package/elizaos/bootstrap/providers/__init__.py +80 -0
  104. package/elizaos/bootstrap/providers/action_state.py +71 -0
  105. package/elizaos/bootstrap/providers/actions.py +256 -0
  106. package/elizaos/bootstrap/providers/agent_settings.py +63 -0
  107. package/elizaos/bootstrap/providers/attachments.py +76 -0
  108. package/elizaos/bootstrap/providers/capabilities.py +66 -0
  109. package/elizaos/bootstrap/providers/character.py +128 -0
  110. package/elizaos/bootstrap/providers/choice.py +77 -0
  111. package/elizaos/bootstrap/providers/contacts.py +78 -0
  112. package/elizaos/bootstrap/providers/context_bench.py +49 -0
  113. package/elizaos/bootstrap/providers/current_time.py +56 -0
  114. package/elizaos/bootstrap/providers/entities.py +99 -0
  115. package/elizaos/bootstrap/providers/evaluators.py +58 -0
  116. package/elizaos/bootstrap/providers/facts.py +86 -0
  117. package/elizaos/bootstrap/providers/follow_ups.py +116 -0
  118. package/elizaos/bootstrap/providers/knowledge.py +73 -0
  119. package/elizaos/bootstrap/providers/providers_list.py +59 -0
  120. package/elizaos/bootstrap/providers/recent_messages.py +85 -0
  121. package/elizaos/bootstrap/providers/relationships.py +106 -0
  122. package/elizaos/bootstrap/providers/roles.py +95 -0
  123. package/elizaos/bootstrap/providers/settings.py +55 -0
  124. package/elizaos/bootstrap/providers/time.py +45 -0
  125. package/elizaos/bootstrap/providers/world.py +97 -0
  126. package/elizaos/bootstrap/services/__init__.py +26 -0
  127. package/elizaos/bootstrap/services/embedding.py +122 -0
  128. package/elizaos/bootstrap/services/follow_up.py +138 -0
  129. package/elizaos/bootstrap/services/rolodex.py +244 -0
  130. package/elizaos/bootstrap/services/task.py +585 -0
  131. package/elizaos/bootstrap/types.py +54 -0
  132. package/elizaos/bootstrap/utils/__init__.py +7 -0
  133. package/elizaos/bootstrap/utils/xml.py +69 -0
  134. package/elizaos/character.py +149 -0
  135. package/elizaos/logger.py +179 -0
  136. package/elizaos/media/__init__.py +45 -0
  137. package/elizaos/media/mime.py +315 -0
  138. package/elizaos/media/search.py +161 -0
  139. package/elizaos/media/tests/__init__.py +1 -0
  140. package/elizaos/media/tests/test_mime.py +117 -0
  141. package/elizaos/media/tests/test_search.py +156 -0
  142. package/elizaos/plugin.py +191 -0
  143. package/elizaos/prompts.py +1071 -0
  144. package/elizaos/py.typed +0 -0
  145. package/elizaos/runtime.py +2572 -0
  146. package/elizaos/services/__init__.py +49 -0
  147. package/elizaos/services/hook_service.py +511 -0
  148. package/elizaos/services/message_service.py +1248 -0
  149. package/elizaos/settings.py +182 -0
  150. package/elizaos/streaming_context.py +159 -0
  151. package/elizaos/trajectory_context.py +18 -0
  152. package/elizaos/types/__init__.py +512 -0
  153. package/elizaos/types/agent.py +31 -0
  154. package/elizaos/types/components.py +208 -0
  155. package/elizaos/types/database.py +64 -0
  156. package/elizaos/types/environment.py +46 -0
  157. package/elizaos/types/events.py +47 -0
  158. package/elizaos/types/memory.py +45 -0
  159. package/elizaos/types/model.py +393 -0
  160. package/elizaos/types/plugin.py +188 -0
  161. package/elizaos/types/primitives.py +100 -0
  162. package/elizaos/types/runtime.py +460 -0
  163. package/elizaos/types/service.py +113 -0
  164. package/elizaos/types/service_interfaces.py +244 -0
  165. package/elizaos/types/state.py +188 -0
  166. package/elizaos/types/task.py +29 -0
  167. package/elizaos/utils/__init__.py +108 -0
  168. package/elizaos/utils/spec_examples.py +48 -0
  169. package/elizaos/utils/streaming.py +426 -0
  170. package/elizaos_atropos_shared/__init__.py +1 -0
  171. package/elizaos_atropos_shared/canonical_eliza.py +282 -0
  172. package/package.json +19 -0
  173. package/pyproject.toml +143 -0
  174. package/requirements-dev.in +11 -0
  175. package/requirements-dev.lock +134 -0
  176. package/requirements.in +9 -0
  177. package/requirements.lock +64 -0
  178. package/tests/__init__.py +0 -0
  179. package/tests/test_action_parameters.py +154 -0
  180. package/tests/test_actions_provider_examples.py +39 -0
  181. package/tests/test_advanced_memory_behavior.py +96 -0
  182. package/tests/test_advanced_memory_flag.py +30 -0
  183. package/tests/test_advanced_planning_behavior.py +225 -0
  184. package/tests/test_advanced_planning_flag.py +26 -0
  185. package/tests/test_autonomy.py +445 -0
  186. package/tests/test_bootstrap_initialize.py +37 -0
  187. package/tests/test_character.py +163 -0
  188. package/tests/test_character_provider.py +231 -0
  189. package/tests/test_dynamic_prompt_exec.py +561 -0
  190. package/tests/test_logger_redaction.py +43 -0
  191. package/tests/test_plugin.py +117 -0
  192. package/tests/test_runtime.py +422 -0
  193. package/tests/test_salt_production_enforcement.py +22 -0
  194. package/tests/test_settings_crypto.py +118 -0
  195. package/tests/test_streaming.py +295 -0
  196. package/tests/test_types.py +221 -0
  197. package/tests/test_uuid_parity.py +46 -0
@@ -0,0 +1,165 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime
4
+ from typing import TYPE_CHECKING
5
+
6
+ from elizaos.types.components import Provider, ProviderResult
7
+ from elizaos.types.primitives import as_uuid
8
+
9
+ from .service import AUTONOMY_SERVICE_TYPE, AutonomyService
10
+
11
+ if TYPE_CHECKING:
12
+ from elizaos.types.memory import Memory
13
+ from elizaos.types.runtime import IAgentRuntime
14
+ from elizaos.types.state import State
15
+
16
+
17
+ async def _get_admin_chat_history(
18
+ runtime: IAgentRuntime,
19
+ message: Memory,
20
+ _state: State | None = None,
21
+ ) -> ProviderResult:
22
+ autonomy_service = runtime.get_service(AUTONOMY_SERVICE_TYPE)
23
+ if not autonomy_service or not isinstance(autonomy_service, AutonomyService):
24
+ return ProviderResult(text="")
25
+
26
+ autonomous_room_id = autonomy_service.get_autonomous_room_id()
27
+ if not autonomous_room_id or message.room_id != autonomous_room_id:
28
+ return ProviderResult(text="")
29
+
30
+ admin_user_id = runtime.get_setting("ADMIN_USER_ID")
31
+ if not admin_user_id:
32
+ return ProviderResult(
33
+ text="[ADMIN_CHAT_HISTORY]\nNo admin user configured. Set ADMIN_USER_ID in character settings.\n[/ADMIN_CHAT_HISTORY]",
34
+ data={"adminConfigured": False},
35
+ )
36
+
37
+ admin_uuid = as_uuid(str(admin_user_id))
38
+
39
+ admin_messages = await runtime.get_memories(
40
+ {
41
+ "entityId": admin_uuid,
42
+ "count": 15,
43
+ "unique": False,
44
+ "tableName": "memories",
45
+ }
46
+ )
47
+
48
+ if not admin_messages:
49
+ return ProviderResult(
50
+ text="[ADMIN_CHAT_HISTORY]\nNo recent messages found with admin user.\n[/ADMIN_CHAT_HISTORY]",
51
+ data={
52
+ "adminConfigured": True,
53
+ "messageCount": 0,
54
+ "adminUserId": str(admin_user_id),
55
+ },
56
+ )
57
+
58
+ sorted_messages = sorted(admin_messages, key=lambda m: m.created_at or 0)[-10:]
59
+
60
+ history_lines = []
61
+ for msg in sorted_messages:
62
+ is_from_admin = msg.entity_id == admin_uuid
63
+ is_from_agent = msg.entity_id == runtime.agent_id
64
+
65
+ sender = "Admin" if is_from_admin else "Agent" if is_from_agent else "Other"
66
+ text = msg.content.text if msg.content and msg.content.text else "[No text content]"
67
+ timestamp = datetime.fromtimestamp((msg.created_at or 0) / 1000).strftime("%H:%M:%S")
68
+
69
+ history_lines.append(f"{timestamp} {sender}: {text}")
70
+
71
+ conversation_history = "\n".join(history_lines)
72
+
73
+ recent_admin_messages = [msg for msg in sorted_messages if msg.entity_id == admin_uuid][-3:]
74
+
75
+ last_admin_message = recent_admin_messages[-1] if recent_admin_messages else None
76
+ admin_mood_context = (
77
+ f'Last admin message: "{last_admin_message.content.text if last_admin_message and last_admin_message.content else "N/A"}"'
78
+ if recent_admin_messages
79
+ else "No recent admin messages"
80
+ )
81
+
82
+ return ProviderResult(
83
+ text=f"[ADMIN_CHAT_HISTORY]\nRecent conversation with admin user ({len(admin_messages)} total messages):\n\n{conversation_history}\n\n{admin_mood_context}\n[/ADMIN_CHAT_HISTORY]",
84
+ data={
85
+ "adminConfigured": True,
86
+ "messageCount": len(admin_messages),
87
+ "adminUserId": str(admin_user_id),
88
+ "recentMessageCount": len(recent_admin_messages),
89
+ "lastAdminMessage": last_admin_message.content.text
90
+ if last_admin_message and last_admin_message.content
91
+ else "",
92
+ "conversationActive": any(
93
+ (datetime.now().timestamp() * 1000) - (m.created_at or 0) < 3600000
94
+ for m in admin_messages
95
+ ),
96
+ },
97
+ )
98
+
99
+
100
+ async def _get_autonomy_status(
101
+ runtime: IAgentRuntime,
102
+ message: Memory,
103
+ _state: State | None = None,
104
+ ) -> ProviderResult:
105
+ autonomy_service = runtime.get_service(AUTONOMY_SERVICE_TYPE)
106
+ if not autonomy_service or not isinstance(autonomy_service, AutonomyService):
107
+ return ProviderResult(text="")
108
+
109
+ autonomous_room_id = autonomy_service.get_autonomous_room_id()
110
+ if autonomous_room_id and message.room_id == autonomous_room_id:
111
+ return ProviderResult(text="")
112
+
113
+ setting_value = runtime.get_setting("AUTONOMY_ENABLED")
114
+ autonomy_enabled = (
115
+ setting_value is True
116
+ or (isinstance(setting_value, str) and setting_value.strip().lower() == "true")
117
+ or runtime.enable_autonomy is True
118
+ )
119
+ service_running = autonomy_service.is_loop_running()
120
+ interval = autonomy_service.get_loop_interval()
121
+
122
+ if service_running:
123
+ status = "running autonomously"
124
+ status_icon = "🤖"
125
+ elif autonomy_enabled:
126
+ status = "autonomy enabled but not running"
127
+ status_icon = "⏸️"
128
+ else:
129
+ status = "autonomy disabled"
130
+ status_icon = "🔕"
131
+
132
+ interval_seconds = interval // 1000
133
+ interval_unit = (
134
+ f"{interval_seconds} seconds"
135
+ if interval_seconds < 60
136
+ else f"{interval_seconds // 60} minutes"
137
+ )
138
+
139
+ return ProviderResult(
140
+ text=f"[AUTONOMY_STATUS]\nCurrent status: {status_icon} {status}\nThinking interval: {interval_unit}\n[/AUTONOMY_STATUS]",
141
+ data={
142
+ "autonomyEnabled": bool(autonomy_enabled),
143
+ "serviceRunning": service_running,
144
+ "interval": interval,
145
+ "intervalSeconds": interval_seconds,
146
+ "status": "running"
147
+ if service_running
148
+ else "enabled"
149
+ if autonomy_enabled
150
+ else "disabled",
151
+ },
152
+ )
153
+
154
+
155
+ admin_chat_provider = Provider(
156
+ name="ADMIN_CHAT_HISTORY",
157
+ description="Provides recent conversation history with the admin user for autonomous context",
158
+ get=_get_admin_chat_history,
159
+ )
160
+
161
+ autonomy_status_provider = Provider(
162
+ name="AUTONOMY_STATUS",
163
+ description="Provides current autonomy status for agent awareness in conversations",
164
+ get=_get_autonomy_status,
165
+ )
@@ -0,0 +1,171 @@
1
+ """
2
+ Autonomy Routes for elizaOS Python runtime.
3
+
4
+ API routes for controlling autonomy via REST.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from elizaos.types.plugin import Route, RouteRequest, RouteResponse
10
+ from elizaos.types.runtime import IAgentRuntime
11
+
12
+ from .service import AUTONOMY_SERVICE_TYPE, AutonomyService
13
+
14
+ Route.model_rebuild()
15
+
16
+
17
+ def _get_autonomy_service(runtime: IAgentRuntime) -> AutonomyService | None:
18
+ """Get autonomy service from runtime with fallback."""
19
+ service = runtime.get_service(AUTONOMY_SERVICE_TYPE)
20
+ if service is None:
21
+ service = runtime.get_service("autonomy")
22
+ return service # type: ignore[return-value]
23
+
24
+
25
+ async def _status_handler(_req: RouteRequest, res: RouteResponse, runtime: IAgentRuntime) -> None:
26
+ """GET /autonomy/status - Get current autonomy status."""
27
+ autonomy_service = _get_autonomy_service(runtime)
28
+
29
+ if not autonomy_service:
30
+ res.status(503).json({"error": "Autonomy service not available"})
31
+ return
32
+
33
+ status = autonomy_service.get_status()
34
+
35
+ res.json(
36
+ {
37
+ "success": True,
38
+ "data": {
39
+ "enabled": status.enabled,
40
+ "running": status.running,
41
+ "interval": status.interval,
42
+ "intervalSeconds": round(status.interval / 1000),
43
+ "autonomousRoomId": status.autonomous_room_id,
44
+ "agentId": str(runtime.agent_id),
45
+ "characterName": runtime.character.name if runtime.character else "Agent",
46
+ },
47
+ }
48
+ )
49
+
50
+
51
+ async def _enable_handler(_req: RouteRequest, res: RouteResponse, runtime: IAgentRuntime) -> None:
52
+ """POST /autonomy/enable - Enable autonomy."""
53
+ autonomy_service = _get_autonomy_service(runtime)
54
+
55
+ if not autonomy_service:
56
+ res.status(503).json({"success": False, "error": "Autonomy service not available"})
57
+ return
58
+
59
+ await autonomy_service.enable_autonomy()
60
+ status = autonomy_service.get_status()
61
+
62
+ res.json(
63
+ {
64
+ "success": True,
65
+ "message": "Autonomy enabled",
66
+ "data": {
67
+ "enabled": status.enabled,
68
+ "running": status.running,
69
+ "interval": status.interval,
70
+ },
71
+ }
72
+ )
73
+
74
+
75
+ async def _disable_handler(_req: RouteRequest, res: RouteResponse, runtime: IAgentRuntime) -> None:
76
+ """POST /autonomy/disable - Disable autonomy."""
77
+ autonomy_service = _get_autonomy_service(runtime)
78
+
79
+ if not autonomy_service:
80
+ res.status(503).json({"success": False, "error": "Autonomy service not available"})
81
+ return
82
+
83
+ await autonomy_service.disable_autonomy()
84
+ status = autonomy_service.get_status()
85
+
86
+ res.json(
87
+ {
88
+ "success": True,
89
+ "message": "Autonomy disabled",
90
+ "data": {
91
+ "enabled": status.enabled,
92
+ "running": status.running,
93
+ "interval": status.interval,
94
+ },
95
+ }
96
+ )
97
+
98
+
99
+ async def _toggle_handler(_req: RouteRequest, res: RouteResponse, runtime: IAgentRuntime) -> None:
100
+ """POST /autonomy/toggle - Toggle autonomy state."""
101
+ autonomy_service = _get_autonomy_service(runtime)
102
+
103
+ if not autonomy_service:
104
+ res.status(503).json({"success": False, "error": "Autonomy service not available"})
105
+ return
106
+
107
+ current_status = autonomy_service.get_status()
108
+
109
+ if current_status.enabled:
110
+ await autonomy_service.disable_autonomy()
111
+ else:
112
+ await autonomy_service.enable_autonomy()
113
+
114
+ new_status = autonomy_service.get_status()
115
+
116
+ res.json(
117
+ {
118
+ "success": True,
119
+ "message": "Autonomy enabled" if new_status.enabled else "Autonomy disabled",
120
+ "data": {
121
+ "enabled": new_status.enabled,
122
+ "running": new_status.running,
123
+ "interval": new_status.interval,
124
+ },
125
+ }
126
+ )
127
+
128
+
129
+ async def _interval_handler(req: RouteRequest, res: RouteResponse, runtime: IAgentRuntime) -> None:
130
+ """POST /autonomy/interval - Set loop interval."""
131
+ autonomy_service = _get_autonomy_service(runtime)
132
+
133
+ if not autonomy_service:
134
+ res.status(503).json({"success": False, "error": "Autonomy service not available"})
135
+ return
136
+
137
+ body = req.body or {}
138
+ interval = body.get("interval") if isinstance(body, dict) else None
139
+
140
+ if not isinstance(interval, (int, float)) or interval < 5000 or interval > 600000:
141
+ res.status(400).json(
142
+ {
143
+ "success": False,
144
+ "error": "Interval must be a number between 5000ms (5s) and 600000ms (10m)",
145
+ }
146
+ )
147
+ return
148
+
149
+ autonomy_service.set_loop_interval(int(interval))
150
+ status = autonomy_service.get_status()
151
+
152
+ res.json(
153
+ {
154
+ "success": True,
155
+ "message": "Interval updated",
156
+ "data": {
157
+ "interval": status.interval,
158
+ "intervalSeconds": round(status.interval / 1000),
159
+ },
160
+ }
161
+ )
162
+
163
+
164
+ # Autonomy API routes
165
+ autonomy_routes: list[Route] = [
166
+ Route(type="GET", path="/autonomy/status", handler=_status_handler),
167
+ Route(type="POST", path="/autonomy/enable", handler=_enable_handler),
168
+ Route(type="POST", path="/autonomy/disable", handler=_disable_handler),
169
+ Route(type="POST", path="/autonomy/toggle", handler=_toggle_handler),
170
+ Route(type="POST", path="/autonomy/interval", handler=_interval_handler),
171
+ ]