@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,178 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import TYPE_CHECKING
5
+
6
+ from elizaos.bootstrap.utils.xml import parse_key_value_xml
7
+ from elizaos.generated.spec_helpers import require_action_spec
8
+ from elizaos.prompts import UPDATE_CONTACT_TEMPLATE
9
+ from elizaos.types import (
10
+ Action,
11
+ ActionExample,
12
+ ActionResult,
13
+ Content,
14
+ ModelType,
15
+ )
16
+
17
+ if TYPE_CHECKING:
18
+ from elizaos.types import (
19
+ HandlerCallback,
20
+ HandlerOptions,
21
+ IAgentRuntime,
22
+ Memory,
23
+ State,
24
+ )
25
+
26
+ # Get text content from centralized specs
27
+ _spec = require_action_spec("UPDATE_CONTACT")
28
+
29
+
30
+ def _convert_spec_examples() -> list[list[ActionExample]]:
31
+ """Convert spec examples to ActionExample format."""
32
+ spec_examples = _spec.get("examples", [])
33
+ if spec_examples:
34
+ return [
35
+ [
36
+ ActionExample(
37
+ name=msg.get("name", ""),
38
+ content=Content(
39
+ text=msg.get("content", {}).get("text", ""),
40
+ actions=msg.get("content", {}).get("actions"),
41
+ ),
42
+ )
43
+ for msg in example
44
+ ]
45
+ for example in spec_examples
46
+ ]
47
+ return []
48
+
49
+
50
+ @dataclass
51
+ class UpdateContactAction:
52
+ name: str = _spec["name"]
53
+ similes: list[str] = field(default_factory=lambda: list(_spec.get("similes", [])))
54
+ description: str = _spec["description"]
55
+
56
+ async def validate(
57
+ self, runtime: IAgentRuntime, _message: Memory, _state: State | None = None
58
+ ) -> bool:
59
+ rolodex_service = runtime.get_service("rolodex")
60
+ return rolodex_service is not None
61
+
62
+ async def handler(
63
+ self,
64
+ runtime: IAgentRuntime,
65
+ message: Memory,
66
+ state: State | None = None,
67
+ options: HandlerOptions | None = None,
68
+ callback: HandlerCallback | None = None,
69
+ responses: list[Memory] | None = None,
70
+ ) -> ActionResult:
71
+ from elizaos.bootstrap.services.rolodex import RolodexService
72
+
73
+ rolodex_service = runtime.get_service("rolodex")
74
+ if not rolodex_service or not isinstance(rolodex_service, RolodexService):
75
+ return ActionResult(
76
+ text="Rolodex service not available",
77
+ success=False,
78
+ values={"error": True},
79
+ data={"error": "RolodexService not available"},
80
+ )
81
+
82
+ state = await runtime.compose_state(message, ["RECENT_MESSAGES", "ENTITIES"])
83
+
84
+ prompt = runtime.compose_prompt_from_state(
85
+ state=state,
86
+ template=UPDATE_CONTACT_TEMPLATE,
87
+ )
88
+
89
+ response = await runtime.use_model(ModelType.TEXT_SMALL, {"prompt": prompt})
90
+ parsed = parse_key_value_xml(response)
91
+
92
+ if not parsed or not parsed.get("contactName"):
93
+ return ActionResult(
94
+ text="Could not determine which contact to update",
95
+ success=False,
96
+ values={"error": True},
97
+ data={"error": "No contact name provided"},
98
+ )
99
+
100
+ contact_name = str(parsed.get("contactName", ""))
101
+ operation = str(parsed.get("operation", "replace"))
102
+
103
+ contacts = await rolodex_service.search_contacts(search_term=contact_name)
104
+
105
+ if not contacts:
106
+ return ActionResult(
107
+ text=f"Could not find a contact named '{contact_name}'",
108
+ success=False,
109
+ values={"error": True},
110
+ data={"error": "Contact not found"},
111
+ )
112
+
113
+ contact = contacts[0]
114
+
115
+ categories = None
116
+ tags = None
117
+
118
+ if parsed.get("categories"):
119
+ new_categories = [c.strip() for c in str(parsed["categories"]).split(",") if c.strip()]
120
+ if operation == "add_to" and contact.categories:
121
+ categories = list(set(contact.categories + new_categories))
122
+ else:
123
+ categories = new_categories
124
+
125
+ if parsed.get("tags"):
126
+ new_tags = [t.strip() for t in str(parsed["tags"]).split(",") if t.strip()]
127
+ if operation == "add_to" and contact.tags:
128
+ tags = list(set(contact.tags + new_tags))
129
+ else:
130
+ tags = new_tags
131
+
132
+ updated = await rolodex_service.update_contact(
133
+ entity_id=contact.entity_id,
134
+ categories=categories,
135
+ tags=tags,
136
+ )
137
+
138
+ if updated:
139
+ response_text = f"I've updated {contact_name}'s contact information."
140
+ if categories:
141
+ response_text += f" Categories: {', '.join(categories)}."
142
+ if tags:
143
+ response_text += f" Tags: {', '.join(tags)}."
144
+
145
+ if callback:
146
+ await callback(Content(text=response_text, actions=["UPDATE_CONTACT"]))
147
+
148
+ return ActionResult(
149
+ text=response_text,
150
+ success=True,
151
+ values={
152
+ "contactId": str(contact.entity_id),
153
+ "categoriesStr": ",".join(categories) if categories else "",
154
+ "tagsStr": ",".join(tags) if tags else "",
155
+ },
156
+ data={"success": True},
157
+ )
158
+ else:
159
+ return ActionResult(
160
+ text="Failed to update contact",
161
+ success=False,
162
+ values={"error": True},
163
+ data={"error": "Update operation failed"},
164
+ )
165
+
166
+ @property
167
+ def examples(self) -> list[list[ActionExample]]:
168
+ return _convert_spec_examples()
169
+
170
+
171
+ update_contact_action = Action(
172
+ name=UpdateContactAction.name,
173
+ similes=UpdateContactAction().similes,
174
+ description=UpdateContactAction.description,
175
+ validate=UpdateContactAction().validate,
176
+ handler=UpdateContactAction().handler,
177
+ examples=UpdateContactAction().examples,
178
+ )
@@ -0,0 +1,175 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import TYPE_CHECKING
5
+ from uuid import UUID
6
+
7
+ from elizaos.bootstrap.utils.xml import parse_key_value_xml
8
+ from elizaos.generated.spec_helpers import require_action_spec
9
+ from elizaos.prompts import UPDATE_ENTITY_TEMPLATE
10
+ from elizaos.types import Action, ActionExample, ActionResult, Content, ModelType
11
+
12
+ if TYPE_CHECKING:
13
+ from elizaos.types import HandlerCallback, HandlerOptions, IAgentRuntime, Memory, State
14
+
15
+ # Get text content from centralized specs
16
+ _spec = require_action_spec("UPDATE_ENTITY")
17
+
18
+
19
+ def _convert_spec_examples() -> list[list[ActionExample]]:
20
+ """Convert spec examples to ActionExample format."""
21
+ spec_examples = _spec.get("examples", [])
22
+ if spec_examples:
23
+ return [
24
+ [
25
+ ActionExample(
26
+ name=msg.get("name", ""),
27
+ content=Content(
28
+ text=msg.get("content", {}).get("text", ""),
29
+ actions=msg.get("content", {}).get("actions"),
30
+ ),
31
+ )
32
+ for msg in example
33
+ ]
34
+ for example in spec_examples
35
+ ]
36
+ return []
37
+
38
+
39
+ @dataclass
40
+ class UpdateEntityAction:
41
+ name: str = _spec["name"]
42
+ similes: list[str] = field(default_factory=lambda: list(_spec.get("similes", [])))
43
+ description: str = _spec["description"]
44
+
45
+ async def validate(
46
+ self, runtime: IAgentRuntime, message: Memory, _state: State | None = None
47
+ ) -> bool:
48
+ return message.entity_id is not None
49
+
50
+ async def handler(
51
+ self,
52
+ runtime: IAgentRuntime,
53
+ message: Memory,
54
+ state: State | None = None,
55
+ options: HandlerOptions | None = None,
56
+ callback: HandlerCallback | None = None,
57
+ responses: list[Memory] | None = None,
58
+ ) -> ActionResult:
59
+ if state is None:
60
+ raise ValueError("State is required for UPDATE_ENTITY action")
61
+
62
+ entity_id = message.entity_id
63
+ if not entity_id:
64
+ return ActionResult(
65
+ text="No entity specified to update",
66
+ values={"success": False, "error": "no_entity_id"},
67
+ data={"actionName": "UPDATE_ENTITY"},
68
+ success=False,
69
+ )
70
+
71
+ entity = await runtime.get_entity(entity_id)
72
+ if entity is None:
73
+ return ActionResult(
74
+ text="Entity not found",
75
+ values={"success": False, "error": "entity_not_found"},
76
+ data={"actionName": "UPDATE_ENTITY"},
77
+ success=False,
78
+ )
79
+
80
+ state = await runtime.compose_state(
81
+ message, ["RECENT_MESSAGES", "ACTION_STATE", "ENTITY_INFO"]
82
+ )
83
+
84
+ entity_info = f"""
85
+ Entity ID: {entity.id}
86
+ Name: {entity.name or "Unknown"}
87
+ Type: {entity.entity_type or "Unknown"}
88
+ """
89
+ if entity.metadata:
90
+ entity_info += f"Metadata: {entity.metadata}"
91
+
92
+ template = (
93
+ runtime.character.templates.get("updateEntityTemplate")
94
+ if runtime.character.templates and "updateEntityTemplate" in runtime.character.templates
95
+ else UPDATE_ENTITY_TEMPLATE
96
+ )
97
+ prompt = runtime.compose_prompt(state=state, template=template)
98
+ prompt = prompt.replace("{{entityInfo}}", entity_info)
99
+
100
+ response_text = await runtime.use_model(ModelType.TEXT_LARGE, prompt=prompt)
101
+ parsed_xml = parse_key_value_xml(response_text)
102
+
103
+ if parsed_xml is None:
104
+ raise ValueError("Failed to parse XML response")
105
+
106
+ thought = str(parsed_xml.get("thought", ""))
107
+ target_entity_id_str = str(parsed_xml.get("entity_id", str(entity_id)))
108
+
109
+ target_entity_id = UUID(target_entity_id_str)
110
+
111
+ updates_raw = parsed_xml.get("updates", {})
112
+ updated_fields: list[str] = []
113
+
114
+ if isinstance(updates_raw, dict):
115
+ field_list = updates_raw.get("field", [])
116
+ if isinstance(field_list, dict):
117
+ field_list = [field_list]
118
+ for field_update in field_list:
119
+ if isinstance(field_update, dict):
120
+ field_name = str(field_update.get("name", ""))
121
+ field_value = str(field_update.get("value", ""))
122
+ if field_name and field_value:
123
+ if entity.metadata is None:
124
+ entity.metadata = {}
125
+ entity.metadata[field_name] = field_value
126
+ updated_fields.append(field_name)
127
+
128
+ if not updated_fields:
129
+ return ActionResult(
130
+ text="No fields to update",
131
+ values={"success": True, "noChanges": True},
132
+ data={"actionName": "UPDATE_ENTITY", "thought": thought},
133
+ success=True,
134
+ )
135
+
136
+ await runtime.update_entity(entity)
137
+
138
+ response_content = Content(
139
+ text=f"Updated entity fields: {', '.join(updated_fields)}",
140
+ actions=["UPDATE_ENTITY"],
141
+ )
142
+
143
+ if callback:
144
+ await callback(response_content)
145
+
146
+ return ActionResult(
147
+ text=f"Updated entity: {', '.join(updated_fields)}",
148
+ values={
149
+ "success": True,
150
+ "entityUpdated": True,
151
+ "entityId": str(target_entity_id),
152
+ "updatedFields": ", ".join(updated_fields),
153
+ },
154
+ data={
155
+ "actionName": "UPDATE_ENTITY",
156
+ "entityId": str(target_entity_id),
157
+ "updatedFields": updated_fields,
158
+ "thought": thought,
159
+ },
160
+ success=True,
161
+ )
162
+
163
+ @property
164
+ def examples(self) -> list[list[ActionExample]]:
165
+ return _convert_spec_examples()
166
+
167
+
168
+ update_entity_action = Action(
169
+ name=UpdateEntityAction.name,
170
+ similes=UpdateEntityAction().similes,
171
+ description=UpdateEntityAction.description,
172
+ validate=UpdateEntityAction().validate,
173
+ handler=UpdateEntityAction().handler,
174
+ examples=UpdateEntityAction().examples,
175
+ )
@@ -0,0 +1,18 @@
1
+ from __future__ import annotations
2
+
3
+ from .action import send_to_admin_action
4
+ from .providers import admin_chat_provider, autonomy_status_provider
5
+ from .routes import autonomy_routes
6
+ from .service import AUTONOMY_SERVICE_TYPE, AutonomyService
7
+ from .types import AutonomyConfig, AutonomyStatus
8
+
9
+ __all__ = [
10
+ "AutonomyConfig",
11
+ "AutonomyStatus",
12
+ "AutonomyService",
13
+ "AUTONOMY_SERVICE_TYPE",
14
+ "send_to_admin_action",
15
+ "admin_chat_provider",
16
+ "autonomy_status_provider",
17
+ "autonomy_routes",
18
+ ]
@@ -0,0 +1,197 @@
1
+ from __future__ import annotations
2
+
3
+ import time
4
+ import uuid
5
+ from typing import TYPE_CHECKING
6
+
7
+ from elizaos.types.components import Action, ActionResult, HandlerOptions
8
+ from elizaos.types.memory import Memory
9
+ from elizaos.types.primitives import UUID, Content, as_uuid
10
+
11
+ from .service import AUTONOMY_SERVICE_TYPE, AutonomyService
12
+
13
+ if TYPE_CHECKING:
14
+ from collections.abc import Awaitable, Callable
15
+
16
+ from elizaos.types.runtime import IAgentRuntime
17
+ from elizaos.types.state import State
18
+
19
+
20
+ ADMIN_KEYWORDS = (
21
+ "admin",
22
+ "user",
23
+ "tell",
24
+ "notify",
25
+ "inform",
26
+ "update",
27
+ "message",
28
+ "send",
29
+ "communicate",
30
+ "report",
31
+ "alert",
32
+ )
33
+
34
+
35
+ async def _validate_send_to_admin(
36
+ runtime: IAgentRuntime,
37
+ message: Memory,
38
+ _state: State | None = None,
39
+ ) -> bool:
40
+ autonomy_service = runtime.get_service(AUTONOMY_SERVICE_TYPE)
41
+ if not autonomy_service or not isinstance(autonomy_service, AutonomyService):
42
+ return False
43
+
44
+ autonomous_room_id = autonomy_service.get_autonomous_room_id()
45
+ if not autonomous_room_id or message.room_id != autonomous_room_id:
46
+ return False
47
+
48
+ admin_user_id = runtime.get_setting("ADMIN_USER_ID")
49
+ if not admin_user_id:
50
+ return False
51
+
52
+ text = (message.content.text or "").lower() if message.content else ""
53
+ if not text:
54
+ return False
55
+ return any(keyword in text for keyword in ADMIN_KEYWORDS)
56
+
57
+
58
+ async def _handle_send_to_admin(
59
+ runtime: IAgentRuntime,
60
+ message: Memory,
61
+ state: State | None = None,
62
+ options: HandlerOptions | None = None,
63
+ callback: Callable[[Content], Awaitable[None]] | None = None,
64
+ responses: list[Memory] | None = None,
65
+ ) -> ActionResult:
66
+ autonomy_service = runtime.get_service(AUTONOMY_SERVICE_TYPE)
67
+ if not autonomy_service or not isinstance(autonomy_service, AutonomyService):
68
+ return ActionResult(
69
+ success=False,
70
+ text="Autonomy service not available",
71
+ data={"error": "Service unavailable"},
72
+ )
73
+
74
+ autonomous_room_id = autonomy_service.get_autonomous_room_id()
75
+ if not autonomous_room_id or message.room_id != autonomous_room_id:
76
+ return ActionResult(
77
+ success=False,
78
+ text="Send to admin only available in autonomous context",
79
+ data={"error": "Invalid context"},
80
+ )
81
+
82
+ admin_user_id = runtime.get_setting("ADMIN_USER_ID")
83
+ if not admin_user_id:
84
+ return ActionResult(
85
+ success=False,
86
+ text="No admin user configured. Set ADMIN_USER_ID in settings.",
87
+ data={"error": "No admin configured"},
88
+ )
89
+
90
+ admin_messages = await runtime.get_memories(
91
+ {
92
+ "roomId": runtime.agent_id,
93
+ "count": 10,
94
+ "tableName": "memories",
95
+ }
96
+ )
97
+
98
+ target_room_id: UUID
99
+ if admin_messages and len(admin_messages) > 0:
100
+ target_room_id = admin_messages[-1].room_id or runtime.agent_id
101
+ else:
102
+ target_room_id = runtime.agent_id
103
+
104
+ autonomous_thought = message.content.text or "" if message.content else ""
105
+
106
+ if "completed" in autonomous_thought or "finished" in autonomous_thought:
107
+ message_to_admin = (
108
+ f"I've completed a task and wanted to update you. My thoughts: {autonomous_thought}"
109
+ )
110
+ elif (
111
+ "problem" in autonomous_thought
112
+ or "issue" in autonomous_thought
113
+ or "error" in autonomous_thought
114
+ ):
115
+ message_to_admin = (
116
+ f"I encountered something that might need your attention: {autonomous_thought}"
117
+ )
118
+ elif "question" in autonomous_thought or "unsure" in autonomous_thought:
119
+ message_to_admin = (
120
+ f"I have a question and would appreciate your guidance: {autonomous_thought}"
121
+ )
122
+ else:
123
+ message_to_admin = f"Autonomous update: {autonomous_thought}"
124
+
125
+ current_time_ms = int(time.time() * 1000)
126
+ admin_message = Memory(
127
+ id=as_uuid(str(uuid.uuid4())),
128
+ entity_id=runtime.agent_id,
129
+ room_id=target_room_id,
130
+ content=Content(
131
+ text=message_to_admin,
132
+ source="autonomy-to-admin",
133
+ ),
134
+ created_at=current_time_ms,
135
+ )
136
+
137
+ await runtime.create_memory(admin_message, "memories")
138
+
139
+ # Emit MESSAGE_SENT event after creating the admin message
140
+ await runtime.emit_event(
141
+ "MESSAGE_SENT",
142
+ {
143
+ "runtime": runtime,
144
+ "source": "autonomy-to-admin",
145
+ "message": admin_message,
146
+ },
147
+ )
148
+
149
+ success_message = f"Message sent to admin in room {str(target_room_id)[:8]}..."
150
+
151
+ if callback:
152
+ await callback(
153
+ Content(
154
+ text=success_message,
155
+ data={
156
+ "adminUserId": str(admin_user_id),
157
+ "targetRoomId": str(target_room_id),
158
+ "messageContent": message_to_admin,
159
+ },
160
+ )
161
+ )
162
+
163
+ return ActionResult(
164
+ success=True,
165
+ text=success_message,
166
+ data={
167
+ "adminUserId": str(admin_user_id),
168
+ "targetRoomId": str(target_room_id),
169
+ "messageContent": message_to_admin,
170
+ "sent": True,
171
+ },
172
+ )
173
+
174
+
175
+ send_to_admin_action = Action(
176
+ name="SEND_TO_ADMIN",
177
+ description="Send a message directly to the admin user from autonomous context",
178
+ examples=[
179
+ [
180
+ {
181
+ "name": "Agent",
182
+ "content": {
183
+ "text": "I need to update the admin about my progress on the task.",
184
+ "action": "SEND_TO_ADMIN",
185
+ },
186
+ },
187
+ {
188
+ "name": "Agent",
189
+ "content": {
190
+ "text": "Message sent to admin successfully.",
191
+ },
192
+ },
193
+ ],
194
+ ],
195
+ validate=_validate_send_to_admin,
196
+ handler=_handle_send_to_admin,
197
+ )