@elizaos/plugin-agent-orchestrator 2.0.0-alpha.1

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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +248 -0
  3. package/package.json +83 -0
  4. package/python/README.md +95 -0
  5. package/python/dist/elizaos_plugin_agent_orchestrator-2.0.0-py3-none-any.whl +0 -0
  6. package/python/dist/elizaos_plugin_agent_orchestrator-2.0.0.tar.gz +0 -0
  7. package/python/elizaos_plugin_agent_orchestrator/__init__.py +84 -0
  8. package/python/elizaos_plugin_agent_orchestrator/__pycache__/__init__.cpython-313.pyc +0 -0
  9. package/python/elizaos_plugin_agent_orchestrator/__pycache__/config.cpython-313.pyc +0 -0
  10. package/python/elizaos_plugin_agent_orchestrator/__pycache__/service.cpython-313.pyc +0 -0
  11. package/python/elizaos_plugin_agent_orchestrator/__pycache__/types.cpython-313.pyc +0 -0
  12. package/python/elizaos_plugin_agent_orchestrator/actions/__init__.py +23 -0
  13. package/python/elizaos_plugin_agent_orchestrator/actions/__pycache__/__init__.cpython-313.pyc +0 -0
  14. package/python/elizaos_plugin_agent_orchestrator/actions/__pycache__/task_management.cpython-313.pyc +0 -0
  15. package/python/elizaos_plugin_agent_orchestrator/actions/task_management.py +404 -0
  16. package/python/elizaos_plugin_agent_orchestrator/config.py +28 -0
  17. package/python/elizaos_plugin_agent_orchestrator/providers/__init__.py +7 -0
  18. package/python/elizaos_plugin_agent_orchestrator/providers/__pycache__/__init__.cpython-313.pyc +0 -0
  19. package/python/elizaos_plugin_agent_orchestrator/providers/__pycache__/task_context.cpython-313.pyc +0 -0
  20. package/python/elizaos_plugin_agent_orchestrator/providers/task_context.py +58 -0
  21. package/python/elizaos_plugin_agent_orchestrator/py.typed +0 -0
  22. package/python/elizaos_plugin_agent_orchestrator/service.py +649 -0
  23. package/python/elizaos_plugin_agent_orchestrator/types.py +309 -0
  24. package/python/elizaos_plugin_agent_orchestrator.egg-info/PKG-INFO +119 -0
  25. package/python/elizaos_plugin_agent_orchestrator.egg-info/SOURCES.txt +17 -0
  26. package/python/elizaos_plugin_agent_orchestrator.egg-info/dependency_links.txt +1 -0
  27. package/python/elizaos_plugin_agent_orchestrator.egg-info/requires.txt +5 -0
  28. package/python/elizaos_plugin_agent_orchestrator.egg-info/top_level.txt +1 -0
  29. package/python/elizaos_plugin_discord/generated/specs/__init__.py +1 -0
  30. package/python/elizaos_plugin_discord/generated/specs/specs.py +77 -0
  31. package/python/pyproject.toml +56 -0
  32. package/python/tests/__init__.py +1 -0
  33. package/python/tests/__pycache__/__init__.cpython-313.pyc +0 -0
  34. package/python/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc +0 -0
  35. package/python/tests/__pycache__/test_service.cpython-313-pytest-9.0.2.pyc +0 -0
  36. package/python/tests/conftest.py +130 -0
  37. package/python/tests/test_service.py +140 -0
  38. package/rust/Cargo.toml +33 -0
  39. package/rust/README.md +112 -0
  40. package/rust/src/actions/mod.rs +173 -0
  41. package/rust/src/config.rs +111 -0
  42. package/rust/src/error.rs +30 -0
  43. package/rust/src/generated/specs/mod.rs +3 -0
  44. package/rust/src/generated/specs/specs.rs +27 -0
  45. package/rust/src/lib.rs +48 -0
  46. package/rust/src/providers/mod.rs +5 -0
  47. package/rust/src/providers/task_context.rs +50 -0
  48. package/rust/src/service.rs +771 -0
  49. package/rust/src/types.rs +275 -0
  50. package/typescript/dist/index.d.ts +9 -0
  51. package/typescript/dist/index.d.ts.map +1 -0
  52. package/typescript/dist/index.js +817 -0
  53. package/typescript/dist/index.js.map +18 -0
  54. package/typescript/dist/src/actions/task-management.d.ts +9 -0
  55. package/typescript/dist/src/actions/task-management.d.ts.map +1 -0
  56. package/typescript/dist/src/config.d.ts +4 -0
  57. package/typescript/dist/src/config.d.ts.map +1 -0
  58. package/typescript/dist/src/providers/task-context.d.ts +3 -0
  59. package/typescript/dist/src/providers/task-context.d.ts.map +1 -0
  60. package/typescript/dist/src/services/agent-orchestrator-service.d.ts +59 -0
  61. package/typescript/dist/src/services/agent-orchestrator-service.d.ts.map +1 -0
  62. package/typescript/dist/src/types.d.ts +113 -0
  63. package/typescript/dist/src/types.d.ts.map +1 -0
@@ -0,0 +1,404 @@
1
+ """
2
+ Task management actions for the Agent Orchestrator plugin.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import re
8
+ from typing import Any
9
+
10
+ from ..service import AgentOrchestratorService
11
+
12
+
13
+ def _get_service(runtime: Any) -> AgentOrchestratorService:
14
+ """Get the orchestrator service from runtime."""
15
+ svc = runtime.get_service("CODE_TASK")
16
+ if svc is None:
17
+ raise RuntimeError("AgentOrchestratorService not available (CODE_TASK)")
18
+ return svc
19
+
20
+
21
+ def _extract_query(text: str) -> str:
22
+ """Extract search query from text."""
23
+ result = text.lower()
24
+ result = re.sub(
25
+ r"\b(switch|select|go|change|search|find|pause|stop|halt|resume|restart|continue|start|run|begin|cancel|delete|remove|list|show|view)\b",
26
+ "",
27
+ result,
28
+ )
29
+ result = re.sub(r"\b(about|for|named|called|with|to|my|your|our|this|current)\b", "", result)
30
+ result = re.sub(r"\b(task|tasks|the|a|an)\b", "", result)
31
+ result = re.sub(r"\s+", " ", result)
32
+ return result.strip()
33
+
34
+
35
+ # ============================================================================
36
+ # CREATE_TASK
37
+ # ============================================================================
38
+
39
+ create_task_action = {
40
+ "name": "CREATE_TASK",
41
+ "similes": ["START_TASK", "SPAWN_TASK", "NEW_TASK", "BEGIN_TASK"],
42
+ "description": "Create an orchestrated background task to be executed by a selected agent provider.",
43
+ }
44
+
45
+
46
+ async def validate_create_task(runtime: Any, message: Any) -> bool:
47
+ """Validate CREATE_TASK action."""
48
+ text = (getattr(message.content, "text", "") or "").lower()
49
+ has_explicit = "create task" in text or "new task" in text or "start a task" in text
50
+ has_intent = any(
51
+ word in text
52
+ for word in ["implement", "build", "create", "develop", "refactor", "fix", "add"]
53
+ )
54
+ return has_explicit or has_intent
55
+
56
+
57
+ async def handle_create_task(
58
+ runtime: Any,
59
+ message: Any,
60
+ state: Any | None = None,
61
+ options: dict[str, Any] | None = None,
62
+ callback: Any | None = None,
63
+ ) -> dict[str, Any]:
64
+ """Handle CREATE_TASK action."""
65
+ svc = _get_service(runtime)
66
+ raw = getattr(message.content, "text", "") or ""
67
+
68
+ opts = options or {}
69
+ name = (opts.get("title") or raw.split("\n")[0] or "New Task").strip()[:100] or "New Task"
70
+ description = (opts.get("description") or raw).strip()[:4000] or name
71
+
72
+ room_id = getattr(message, "room_id", None) or getattr(message, "roomId", None)
73
+ task = await svc.create_task(name, description, room_id)
74
+
75
+ step_lines = opts.get("steps")
76
+ if isinstance(step_lines, list) and step_lines:
77
+ for s in step_lines:
78
+ step = str(s).strip()
79
+ if step:
80
+ await svc.add_step(task.id, step)
81
+ await svc.append_output(
82
+ task.id, "Plan:\n" + "\n".join(f"{i + 1}. {s}" for i, s in enumerate(step_lines))
83
+ )
84
+
85
+ msg = (
86
+ f"Created task: {task.name}\n"
87
+ f"Provider: {task.metadata.provider_label or task.metadata.provider_id}\n"
88
+ f"Starting execution…"
89
+ )
90
+
91
+ if callback:
92
+ await callback({"content": {"text": msg}})
93
+
94
+ svc.start_task_execution(task.id)
95
+
96
+ return {"success": True, "text": msg, "data": {"taskId": task.id}}
97
+
98
+
99
+ create_task_action["validate"] = validate_create_task
100
+ create_task_action["handler"] = handle_create_task
101
+
102
+
103
+ # ============================================================================
104
+ # LIST_TASKS
105
+ # ============================================================================
106
+
107
+ list_tasks_action = {
108
+ "name": "LIST_TASKS",
109
+ "similes": ["SHOW_TASKS", "GET_TASKS", "TASKS", "VIEW_TASKS"],
110
+ "description": "List tasks managed by the orchestrator.",
111
+ }
112
+
113
+
114
+ async def validate_list_tasks(runtime: Any, message: Any) -> bool:
115
+ """Validate LIST_TASKS action."""
116
+ text = (getattr(message.content, "text", "") or "").lower()
117
+ return "list task" in text or "show task" in text or text == "tasks" or "my task" in text
118
+
119
+
120
+ async def handle_list_tasks(
121
+ runtime: Any,
122
+ message: Any,
123
+ state: Any | None = None,
124
+ options: dict[str, Any] | None = None,
125
+ callback: Any | None = None,
126
+ ) -> dict[str, Any]:
127
+ """Handle LIST_TASKS action."""
128
+ svc = _get_service(runtime)
129
+ tasks = await svc.get_recent_tasks(20)
130
+
131
+ if not tasks:
132
+ msg = "No tasks."
133
+ if callback:
134
+ await callback({"content": {"text": msg}})
135
+ return {"success": True, "text": msg}
136
+
137
+ lines = ["Tasks:"]
138
+ current = svc.get_current_task_id()
139
+ for t in tasks:
140
+ marker = " (current)" if t.id == current else ""
141
+ lines.append(f"- {t.name} — {t.metadata.status.value} {t.metadata.progress}%{marker}")
142
+
143
+ msg = "\n".join(lines)
144
+ if callback:
145
+ await callback({"content": {"text": msg}})
146
+ return {"success": True, "text": msg}
147
+
148
+
149
+ list_tasks_action["validate"] = validate_list_tasks
150
+ list_tasks_action["handler"] = handle_list_tasks
151
+
152
+
153
+ # ============================================================================
154
+ # SWITCH_TASK
155
+ # ============================================================================
156
+
157
+ switch_task_action = {
158
+ "name": "SWITCH_TASK",
159
+ "similes": ["SELECT_TASK", "SET_TASK", "CHANGE_TASK", "GO_TO_TASK"],
160
+ "description": "Switch the current task context to a different task.",
161
+ }
162
+
163
+
164
+ async def validate_switch_task(runtime: Any, message: Any) -> bool:
165
+ """Validate SWITCH_TASK action."""
166
+ text = (getattr(message.content, "text", "") or "").lower()
167
+ return (
168
+ "switch to task" in text or "select task" in text or ("task" in text and "switch" in text)
169
+ )
170
+
171
+
172
+ async def handle_switch_task(
173
+ runtime: Any,
174
+ message: Any,
175
+ state: Any | None = None,
176
+ options: dict[str, Any] | None = None,
177
+ callback: Any | None = None,
178
+ ) -> dict[str, Any]:
179
+ """Handle SWITCH_TASK action."""
180
+ svc = _get_service(runtime)
181
+ query = _extract_query(getattr(message.content, "text", "") or "")
182
+
183
+ if not query:
184
+ msg = "Please specify which task to switch to (by name or id)."
185
+ if callback:
186
+ await callback({"content": {"text": msg}})
187
+ return {"success": False, "text": msg}
188
+
189
+ matches = await svc.search_tasks(query)
190
+ chosen = matches[0] if matches else None
191
+
192
+ if not chosen or not chosen.id:
193
+ msg = f'No task found matching: "{query}"'
194
+ if callback:
195
+ await callback({"content": {"text": msg}})
196
+ return {"success": False, "text": msg}
197
+
198
+ svc.set_current_task(chosen.id)
199
+ msg = f"Switched to task: {chosen.name}"
200
+ if callback:
201
+ await callback({"content": {"text": msg}})
202
+ return {"success": True, "text": msg, "data": {"taskId": chosen.id}}
203
+
204
+
205
+ switch_task_action["validate"] = validate_switch_task
206
+ switch_task_action["handler"] = handle_switch_task
207
+
208
+
209
+ # ============================================================================
210
+ # SEARCH_TASKS
211
+ # ============================================================================
212
+
213
+ search_tasks_action = {
214
+ "name": "SEARCH_TASKS",
215
+ "similes": ["FIND_TASK", "LOOKUP_TASK"],
216
+ "description": "Search tasks by query.",
217
+ }
218
+
219
+
220
+ async def validate_search_tasks(runtime: Any, message: Any) -> bool:
221
+ """Validate SEARCH_TASKS action."""
222
+ text = (getattr(message.content, "text", "") or "").lower()
223
+ return "search task" in text or "find task" in text or "look for task" in text
224
+
225
+
226
+ async def handle_search_tasks(
227
+ runtime: Any,
228
+ message: Any,
229
+ state: Any | None = None,
230
+ options: dict[str, Any] | None = None,
231
+ callback: Any | None = None,
232
+ ) -> dict[str, Any]:
233
+ """Handle SEARCH_TASKS action."""
234
+ svc = _get_service(runtime)
235
+ opts = options or {}
236
+ query = (
237
+ opts.get("query") or _extract_query(getattr(message.content, "text", "") or "")
238
+ ).strip()
239
+
240
+ if not query:
241
+ msg = "What would you like to search for?"
242
+ if callback:
243
+ await callback({"content": {"text": msg}})
244
+ return {"success": False, "text": msg}
245
+
246
+ matches = await svc.search_tasks(query)
247
+
248
+ if not matches:
249
+ msg = f'No tasks found matching: "{query}"'
250
+ if callback:
251
+ await callback({"content": {"text": msg}})
252
+ return {"success": True, "text": msg}
253
+
254
+ lines = [f'Found {len(matches)} task(s) matching "{query}":']
255
+ for t in matches[:10]:
256
+ lines.append(f"- {t.name} — {t.metadata.status.value} {t.metadata.progress}%")
257
+
258
+ msg = "\n".join(lines)
259
+ if callback:
260
+ await callback({"content": {"text": msg}})
261
+ return {"success": True, "text": msg}
262
+
263
+
264
+ search_tasks_action["validate"] = validate_search_tasks
265
+ search_tasks_action["handler"] = handle_search_tasks
266
+
267
+
268
+ # ============================================================================
269
+ # PAUSE_TASK
270
+ # ============================================================================
271
+
272
+ pause_task_action = {
273
+ "name": "PAUSE_TASK",
274
+ "similes": ["STOP_TASK", "HALT_TASK"],
275
+ "description": "Pause a running task.",
276
+ }
277
+
278
+
279
+ async def validate_pause_task(runtime: Any, message: Any) -> bool:
280
+ """Validate PAUSE_TASK action."""
281
+ text = (getattr(message.content, "text", "") or "").lower()
282
+ return any(word in text for word in ["pause", "stop", "halt"]) and "task" in text
283
+
284
+
285
+ async def handle_pause_task(
286
+ runtime: Any,
287
+ message: Any,
288
+ state: Any | None = None,
289
+ options: dict[str, Any] | None = None,
290
+ callback: Any | None = None,
291
+ ) -> dict[str, Any]:
292
+ """Handle PAUSE_TASK action."""
293
+ svc = _get_service(runtime)
294
+ query = _extract_query(getattr(message.content, "text", "") or "")
295
+ task = (await svc.search_tasks(query))[0] if query else await svc.get_current_task()
296
+
297
+ if not task or not task.id:
298
+ msg = "No task to pause."
299
+ if callback:
300
+ await callback({"content": {"text": msg}})
301
+ return {"success": False, "text": msg}
302
+
303
+ await svc.pause_task(task.id)
304
+ msg = f"Paused task: {task.name}"
305
+ if callback:
306
+ await callback({"content": {"text": msg}})
307
+ return {"success": True, "text": msg}
308
+
309
+
310
+ pause_task_action["validate"] = validate_pause_task
311
+ pause_task_action["handler"] = handle_pause_task
312
+
313
+
314
+ # ============================================================================
315
+ # RESUME_TASK
316
+ # ============================================================================
317
+
318
+ resume_task_action = {
319
+ "name": "RESUME_TASK",
320
+ "similes": ["CONTINUE_TASK", "RESTART_TASK", "RUN_TASK"],
321
+ "description": "Resume a paused task.",
322
+ }
323
+
324
+
325
+ async def validate_resume_task(runtime: Any, message: Any) -> bool:
326
+ """Validate RESUME_TASK action."""
327
+ text = (getattr(message.content, "text", "") or "").lower()
328
+ return "task" in text and any(word in text for word in ["resume", "restart", "continue"])
329
+
330
+
331
+ async def handle_resume_task(
332
+ runtime: Any,
333
+ message: Any,
334
+ state: Any | None = None,
335
+ options: dict[str, Any] | None = None,
336
+ callback: Any | None = None,
337
+ ) -> dict[str, Any]:
338
+ """Handle RESUME_TASK action."""
339
+ svc = _get_service(runtime)
340
+ query = _extract_query(getattr(message.content, "text", "") or "")
341
+ task = (await svc.search_tasks(query))[0] if query else await svc.get_current_task()
342
+
343
+ if not task or not task.id:
344
+ msg = "No task to resume."
345
+ if callback:
346
+ await callback({"content": {"text": msg}})
347
+ return {"success": False, "text": msg}
348
+
349
+ await svc.resume_task(task.id)
350
+ svc.start_task_execution(task.id)
351
+ msg = f"Resumed task: {task.name}"
352
+ if callback:
353
+ await callback({"content": {"text": msg}})
354
+ return {"success": True, "text": msg}
355
+
356
+
357
+ resume_task_action["validate"] = validate_resume_task
358
+ resume_task_action["handler"] = handle_resume_task
359
+
360
+
361
+ # ============================================================================
362
+ # CANCEL_TASK
363
+ # ============================================================================
364
+
365
+ cancel_task_action = {
366
+ "name": "CANCEL_TASK",
367
+ "similes": ["DELETE_TASK", "REMOVE_TASK", "ABORT_TASK"],
368
+ "description": "Cancel a task.",
369
+ }
370
+
371
+
372
+ async def validate_cancel_task(runtime: Any, message: Any) -> bool:
373
+ """Validate CANCEL_TASK action."""
374
+ text = (getattr(message.content, "text", "") or "").lower()
375
+ return any(word in text for word in ["cancel", "delete", "remove"]) and "task" in text
376
+
377
+
378
+ async def handle_cancel_task(
379
+ runtime: Any,
380
+ message: Any,
381
+ state: Any | None = None,
382
+ options: dict[str, Any] | None = None,
383
+ callback: Any | None = None,
384
+ ) -> dict[str, Any]:
385
+ """Handle CANCEL_TASK action."""
386
+ svc = _get_service(runtime)
387
+ query = _extract_query(getattr(message.content, "text", "") or "")
388
+ task = (await svc.search_tasks(query))[0] if query else await svc.get_current_task()
389
+
390
+ if not task or not task.id:
391
+ msg = "No task to cancel."
392
+ if callback:
393
+ await callback({"content": {"text": msg}})
394
+ return {"success": False, "text": msg}
395
+
396
+ await svc.cancel_task(task.id)
397
+ msg = f"Cancelled task: {task.name}"
398
+ if callback:
399
+ await callback({"content": {"text": msg}})
400
+ return {"success": True, "text": msg}
401
+
402
+
403
+ cancel_task_action["validate"] = validate_cancel_task
404
+ cancel_task_action["handler"] = handle_cancel_task
@@ -0,0 +1,28 @@
1
+ """
2
+ Configuration management for the Agent Orchestrator plugin.
3
+ """
4
+
5
+ from .types import AgentOrchestratorPluginOptions
6
+
7
+ _configured_options: AgentOrchestratorPluginOptions | None = None
8
+
9
+
10
+ def configure_agent_orchestrator_plugin(options: AgentOrchestratorPluginOptions) -> None:
11
+ """
12
+ Configure the agent orchestrator plugin with providers.
13
+
14
+ This must be called before the runtime is initialized.
15
+ """
16
+ global _configured_options
17
+ _configured_options = options
18
+
19
+
20
+ def get_configured_options() -> AgentOrchestratorPluginOptions | None:
21
+ """Get the configured options, or None if not configured."""
22
+ return _configured_options
23
+
24
+
25
+ def reset_configuration() -> None:
26
+ """Reset configuration (useful for testing)."""
27
+ global _configured_options
28
+ _configured_options = None
@@ -0,0 +1,7 @@
1
+ """
2
+ Providers for the Agent Orchestrator plugin.
3
+ """
4
+
5
+ from .task_context import task_context_provider
6
+
7
+ __all__ = ["task_context_provider"]
@@ -0,0 +1,58 @@
1
+ """
2
+ Task context provider for the Agent Orchestrator plugin.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Any
8
+
9
+ from ..service import AgentOrchestratorService
10
+
11
+
12
+ def _add_header(title: str, content: str) -> str:
13
+ """Add a markdown header to content."""
14
+ return f"{title}\n\n{content}"
15
+
16
+
17
+ async def get_task_context(
18
+ runtime: Any,
19
+ message: Any,
20
+ state: Any | None = None,
21
+ ) -> dict[str, Any]:
22
+ """Get task context for prompting."""
23
+ svc: AgentOrchestratorService | None = runtime.get_service("CODE_TASK")
24
+
25
+ if svc is None:
26
+ return {
27
+ "values": {
28
+ "taskContext": "Task orchestrator service is not available",
29
+ "currentTaskName": "N/A",
30
+ "currentTaskStatus": "N/A",
31
+ },
32
+ "text": _add_header("# Task Context", "Task orchestrator service is not available"),
33
+ "data": {"taskCount": 0},
34
+ }
35
+
36
+ context_text = await svc.get_task_context()
37
+ current = await svc.get_current_task()
38
+
39
+ return {
40
+ "values": {
41
+ "taskContext": context_text,
42
+ "currentTaskName": current.name if current else "None",
43
+ "currentTaskStatus": current.metadata.status.value if current else "N/A",
44
+ },
45
+ "text": _add_header("# Task Context", context_text),
46
+ "data": {
47
+ "taskCount": len(await svc.get_tasks()),
48
+ "currentTaskId": current.id if current else None,
49
+ },
50
+ }
51
+
52
+
53
+ task_context_provider = {
54
+ "name": "TASK_CONTEXT",
55
+ "description": "Provides context about active and recent orchestrated tasks",
56
+ "position": 90,
57
+ "get": get_task_context,
58
+ }