aury-agent 0.0.4__py3-none-any.whl

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 (149) hide show
  1. aury/__init__.py +2 -0
  2. aury/agents/__init__.py +55 -0
  3. aury/agents/a2a/__init__.py +168 -0
  4. aury/agents/backends/__init__.py +196 -0
  5. aury/agents/backends/artifact/__init__.py +9 -0
  6. aury/agents/backends/artifact/memory.py +130 -0
  7. aury/agents/backends/artifact/types.py +133 -0
  8. aury/agents/backends/code/__init__.py +65 -0
  9. aury/agents/backends/file/__init__.py +11 -0
  10. aury/agents/backends/file/local.py +66 -0
  11. aury/agents/backends/file/types.py +40 -0
  12. aury/agents/backends/invocation/__init__.py +8 -0
  13. aury/agents/backends/invocation/memory.py +81 -0
  14. aury/agents/backends/invocation/types.py +110 -0
  15. aury/agents/backends/memory/__init__.py +8 -0
  16. aury/agents/backends/memory/memory.py +179 -0
  17. aury/agents/backends/memory/types.py +136 -0
  18. aury/agents/backends/message/__init__.py +9 -0
  19. aury/agents/backends/message/memory.py +122 -0
  20. aury/agents/backends/message/types.py +124 -0
  21. aury/agents/backends/sandbox.py +275 -0
  22. aury/agents/backends/session/__init__.py +8 -0
  23. aury/agents/backends/session/memory.py +93 -0
  24. aury/agents/backends/session/types.py +124 -0
  25. aury/agents/backends/shell/__init__.py +11 -0
  26. aury/agents/backends/shell/local.py +110 -0
  27. aury/agents/backends/shell/types.py +55 -0
  28. aury/agents/backends/shell.py +209 -0
  29. aury/agents/backends/snapshot/__init__.py +19 -0
  30. aury/agents/backends/snapshot/git.py +95 -0
  31. aury/agents/backends/snapshot/hybrid.py +125 -0
  32. aury/agents/backends/snapshot/memory.py +86 -0
  33. aury/agents/backends/snapshot/types.py +59 -0
  34. aury/agents/backends/state/__init__.py +29 -0
  35. aury/agents/backends/state/composite.py +49 -0
  36. aury/agents/backends/state/file.py +57 -0
  37. aury/agents/backends/state/memory.py +52 -0
  38. aury/agents/backends/state/sqlite.py +262 -0
  39. aury/agents/backends/state/types.py +178 -0
  40. aury/agents/backends/subagent/__init__.py +165 -0
  41. aury/agents/cli/__init__.py +41 -0
  42. aury/agents/cli/chat.py +239 -0
  43. aury/agents/cli/config.py +236 -0
  44. aury/agents/cli/extensions.py +460 -0
  45. aury/agents/cli/main.py +189 -0
  46. aury/agents/cli/session.py +337 -0
  47. aury/agents/cli/workflow.py +276 -0
  48. aury/agents/context_providers/__init__.py +66 -0
  49. aury/agents/context_providers/artifact.py +299 -0
  50. aury/agents/context_providers/base.py +177 -0
  51. aury/agents/context_providers/memory.py +70 -0
  52. aury/agents/context_providers/message.py +130 -0
  53. aury/agents/context_providers/skill.py +50 -0
  54. aury/agents/context_providers/subagent.py +46 -0
  55. aury/agents/context_providers/tool.py +68 -0
  56. aury/agents/core/__init__.py +83 -0
  57. aury/agents/core/base.py +573 -0
  58. aury/agents/core/context.py +797 -0
  59. aury/agents/core/context_builder.py +303 -0
  60. aury/agents/core/event_bus/__init__.py +15 -0
  61. aury/agents/core/event_bus/bus.py +203 -0
  62. aury/agents/core/factory.py +169 -0
  63. aury/agents/core/isolator.py +97 -0
  64. aury/agents/core/logging.py +95 -0
  65. aury/agents/core/parallel.py +194 -0
  66. aury/agents/core/runner.py +139 -0
  67. aury/agents/core/services/__init__.py +5 -0
  68. aury/agents/core/services/file_session.py +144 -0
  69. aury/agents/core/services/message.py +53 -0
  70. aury/agents/core/services/session.py +53 -0
  71. aury/agents/core/signals.py +109 -0
  72. aury/agents/core/state.py +363 -0
  73. aury/agents/core/types/__init__.py +107 -0
  74. aury/agents/core/types/action.py +176 -0
  75. aury/agents/core/types/artifact.py +135 -0
  76. aury/agents/core/types/block.py +736 -0
  77. aury/agents/core/types/message.py +350 -0
  78. aury/agents/core/types/recall.py +144 -0
  79. aury/agents/core/types/session.py +257 -0
  80. aury/agents/core/types/subagent.py +154 -0
  81. aury/agents/core/types/tool.py +205 -0
  82. aury/agents/eval/__init__.py +331 -0
  83. aury/agents/hitl/__init__.py +57 -0
  84. aury/agents/hitl/ask_user.py +242 -0
  85. aury/agents/hitl/compaction.py +230 -0
  86. aury/agents/hitl/exceptions.py +87 -0
  87. aury/agents/hitl/permission.py +617 -0
  88. aury/agents/hitl/revert.py +216 -0
  89. aury/agents/llm/__init__.py +31 -0
  90. aury/agents/llm/adapter.py +367 -0
  91. aury/agents/llm/openai.py +294 -0
  92. aury/agents/llm/provider.py +476 -0
  93. aury/agents/mcp/__init__.py +153 -0
  94. aury/agents/memory/__init__.py +46 -0
  95. aury/agents/memory/compaction.py +394 -0
  96. aury/agents/memory/manager.py +465 -0
  97. aury/agents/memory/processor.py +177 -0
  98. aury/agents/memory/store.py +187 -0
  99. aury/agents/memory/types.py +137 -0
  100. aury/agents/messages/__init__.py +40 -0
  101. aury/agents/messages/config.py +47 -0
  102. aury/agents/messages/raw_store.py +224 -0
  103. aury/agents/messages/store.py +118 -0
  104. aury/agents/messages/types.py +88 -0
  105. aury/agents/middleware/__init__.py +31 -0
  106. aury/agents/middleware/base.py +341 -0
  107. aury/agents/middleware/chain.py +342 -0
  108. aury/agents/middleware/message.py +129 -0
  109. aury/agents/middleware/message_container.py +126 -0
  110. aury/agents/middleware/raw_message.py +153 -0
  111. aury/agents/middleware/truncation.py +139 -0
  112. aury/agents/middleware/types.py +81 -0
  113. aury/agents/plugin.py +162 -0
  114. aury/agents/react/__init__.py +4 -0
  115. aury/agents/react/agent.py +1923 -0
  116. aury/agents/sandbox/__init__.py +23 -0
  117. aury/agents/sandbox/local.py +239 -0
  118. aury/agents/sandbox/remote.py +200 -0
  119. aury/agents/sandbox/types.py +115 -0
  120. aury/agents/skill/__init__.py +16 -0
  121. aury/agents/skill/loader.py +180 -0
  122. aury/agents/skill/types.py +83 -0
  123. aury/agents/tool/__init__.py +39 -0
  124. aury/agents/tool/builtin/__init__.py +23 -0
  125. aury/agents/tool/builtin/ask_user.py +155 -0
  126. aury/agents/tool/builtin/bash.py +107 -0
  127. aury/agents/tool/builtin/delegate.py +726 -0
  128. aury/agents/tool/builtin/edit.py +121 -0
  129. aury/agents/tool/builtin/plan.py +277 -0
  130. aury/agents/tool/builtin/read.py +91 -0
  131. aury/agents/tool/builtin/thinking.py +111 -0
  132. aury/agents/tool/builtin/yield_result.py +130 -0
  133. aury/agents/tool/decorator.py +252 -0
  134. aury/agents/tool/set.py +204 -0
  135. aury/agents/usage/__init__.py +12 -0
  136. aury/agents/usage/tracker.py +236 -0
  137. aury/agents/workflow/__init__.py +85 -0
  138. aury/agents/workflow/adapter.py +268 -0
  139. aury/agents/workflow/dag.py +116 -0
  140. aury/agents/workflow/dsl.py +575 -0
  141. aury/agents/workflow/executor.py +659 -0
  142. aury/agents/workflow/expression.py +136 -0
  143. aury/agents/workflow/parser.py +182 -0
  144. aury/agents/workflow/state.py +145 -0
  145. aury/agents/workflow/types.py +86 -0
  146. aury_agent-0.0.4.dist-info/METADATA +90 -0
  147. aury_agent-0.0.4.dist-info/RECORD +149 -0
  148. aury_agent-0.0.4.dist-info/WHEEL +4 -0
  149. aury_agent-0.0.4.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,153 @@
1
+ """RawMessageMiddleware - stores complete messages for HITL recovery.
2
+
3
+ This middleware stores complete, untruncated messages to RawMessageStore.
4
+ Works alongside MessageBackendMiddleware which stores truncated messages.
5
+
6
+ Usage:
7
+ raw_store = InMemoryRawMessageStore()
8
+ raw_middleware = RawMessageMiddleware(raw_store, persist_raw=False)
9
+
10
+ agent = ReactAgent.create(
11
+ llm=llm,
12
+ middlewares=[raw_middleware, MessageBackendMiddleware()],
13
+ )
14
+ """
15
+ from __future__ import annotations
16
+
17
+ from typing import TYPE_CHECKING, Any
18
+
19
+ from .base import BaseMiddleware
20
+ from .types import HookResult
21
+
22
+ if TYPE_CHECKING:
23
+ from ..messages.raw_store import RawMessageStore
24
+ from ..core.state import State
25
+
26
+
27
+ class RawMessageMiddleware(BaseMiddleware):
28
+ """Middleware that stores complete messages to RawMessageStore.
29
+
30
+ Stores untruncated messages for:
31
+ - HITL recovery (restore exact conversation state)
32
+ - Full-context recall (when truncated history is insufficient)
33
+
34
+ Messages are stored per invocation and can be cleaned up when
35
+ the invocation completes (controlled by persist_raw).
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ raw_store: "RawMessageStore",
41
+ persist_raw: bool = False,
42
+ state: "State | None" = None,
43
+ ):
44
+ """Initialize with RawMessageStore.
45
+
46
+ Args:
47
+ raw_store: RawMessageStore for storing complete messages
48
+ persist_raw: Whether to keep messages after invocation completes.
49
+ False = clean up after invocation (default)
50
+ True = keep forever (for audit/recall)
51
+ state: State instance for storing message_ids in execution namespace.
52
+ If provided, message IDs are automatically added to
53
+ state.execution["message_ids"].
54
+ """
55
+ self.raw_store = raw_store
56
+ self.persist_raw = persist_raw
57
+ self.state = state
58
+
59
+ # Track message IDs per invocation (for cleanup)
60
+ self._invocation_msg_ids: dict[str, list[str]] = {}
61
+
62
+ def set_state(self, state: "State") -> None:
63
+ """Set state instance (can be set after construction).
64
+
65
+ Args:
66
+ state: State instance for storing message_ids
67
+ """
68
+ self.state = state
69
+
70
+ async def on_message_save(
71
+ self,
72
+ message: dict[str, Any],
73
+ context: dict[str, Any],
74
+ ) -> dict[str, Any] | None:
75
+ """Store complete message to RawMessageStore.
76
+
77
+ Args:
78
+ message: Complete message dict with 'role', 'content', etc.
79
+ context: Execution context with 'invocation_id', etc.
80
+
81
+ Returns:
82
+ The message with added 'raw_msg_id' field
83
+ """
84
+ invocation_id = context.get("invocation_id", "")
85
+ if not invocation_id:
86
+ return message
87
+
88
+ # Store to raw store
89
+ msg_id = await self.raw_store.add(invocation_id, message)
90
+
91
+ # Track for cleanup
92
+ if invocation_id not in self._invocation_msg_ids:
93
+ self._invocation_msg_ids[invocation_id] = []
94
+ self._invocation_msg_ids[invocation_id].append(msg_id)
95
+
96
+ # Add to state.execution["message_ids"] if state is available
97
+ if self.state:
98
+ message_ids = self.state.execution.get("message_ids", [])
99
+ message_ids.append(msg_id)
100
+ self.state.execution["message_ids"] = message_ids
101
+
102
+ # Add msg_id to message for downstream middlewares
103
+ message["raw_msg_id"] = msg_id
104
+
105
+ return message
106
+
107
+ async def on_agent_end(
108
+ self,
109
+ agent_id: str,
110
+ result: Any,
111
+ context: dict[str, Any],
112
+ ) -> HookResult:
113
+ """Clean up raw messages when invocation completes.
114
+
115
+ Only cleans up if persist_raw is False.
116
+ """
117
+ if self.persist_raw:
118
+ return HookResult.proceed()
119
+
120
+ invocation_id = context.get("invocation_id", "")
121
+ if invocation_id:
122
+ await self._cleanup_invocation(invocation_id)
123
+
124
+ return HookResult.proceed()
125
+
126
+ async def _cleanup_invocation(self, invocation_id: str) -> int:
127
+ """Clean up raw messages for an invocation.
128
+
129
+ Args:
130
+ invocation_id: Invocation ID to clean up
131
+
132
+ Returns:
133
+ Number of messages deleted
134
+ """
135
+ # Remove from tracking
136
+ self._invocation_msg_ids.pop(invocation_id, None)
137
+
138
+ # Delete from store
139
+ return await self.raw_store.delete_by_invocation(invocation_id)
140
+
141
+ def get_message_ids(self, invocation_id: str) -> list[str]:
142
+ """Get tracked message IDs for an invocation.
143
+
144
+ Args:
145
+ invocation_id: Invocation ID
146
+
147
+ Returns:
148
+ List of message IDs
149
+ """
150
+ return self._invocation_msg_ids.get(invocation_id, []).copy()
151
+
152
+
153
+ __all__ = ["RawMessageMiddleware"]
@@ -0,0 +1,139 @@
1
+ """MessageTruncationMiddleware - truncates large message content before persistence.
2
+
3
+ This middleware intercepts on_message_save and truncates content
4
+ that exceeds configured limits, ensuring historical messages don't
5
+ consume excessive storage.
6
+
7
+ Current invocation messages remain complete in State (for recovery),
8
+ only persisted messages get truncated.
9
+ """
10
+ from __future__ import annotations
11
+
12
+ from typing import Any
13
+
14
+ from .base import BaseMiddleware
15
+
16
+
17
+ class MessageTruncationMiddleware(BaseMiddleware):
18
+ """Truncates large message content before persistence.
19
+
20
+ Use this middleware BEFORE MessageBackendMiddleware in the chain:
21
+
22
+ middleware = MiddlewareChain([
23
+ MessageTruncationMiddleware(max_content_length=2000),
24
+ MessageBackendMiddleware(),
25
+ ])
26
+
27
+ Features:
28
+ - Truncates string content exceeding max_content_length
29
+ - Truncates individual items in list content (tool_use, tool_result)
30
+ - Adds "[truncated]" marker to truncated content
31
+ - Configurable truncation threshold
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ max_content_length: int = 2000,
37
+ truncate_marker: str = "... [truncated]",
38
+ ):
39
+ """Initialize MessageTruncationMiddleware.
40
+
41
+ Args:
42
+ max_content_length: Maximum content length before truncation
43
+ truncate_marker: Marker appended to truncated content
44
+ """
45
+ self.max_content_length = max_content_length
46
+ self.truncate_marker = truncate_marker
47
+
48
+ async def on_message_save(
49
+ self,
50
+ message: dict[str, Any],
51
+ context: dict[str, Any],
52
+ ) -> dict[str, Any] | None:
53
+ """Truncate message content before saving.
54
+
55
+ Args:
56
+ message: Message dict with 'role', 'content', etc.
57
+ context: Execution context
58
+
59
+ Returns:
60
+ Modified message with truncated content
61
+ """
62
+ content = message.get("content")
63
+ if content is None:
64
+ return message
65
+
66
+ # Create a copy to avoid mutating original
67
+ truncated_message = message.copy()
68
+ truncated_message["content"] = self._truncate_content(content)
69
+
70
+ return truncated_message
71
+
72
+ def _truncate_content(self, content: Any) -> Any:
73
+ """Truncate content based on type.
74
+
75
+ Args:
76
+ content: String or list content
77
+
78
+ Returns:
79
+ Truncated content
80
+ """
81
+ if isinstance(content, str):
82
+ return self._truncate_string(content)
83
+
84
+ if isinstance(content, list):
85
+ return self._truncate_list(content)
86
+
87
+ # Unknown type, return as-is
88
+ return content
89
+
90
+ def _truncate_string(self, text: str) -> str:
91
+ """Truncate string content."""
92
+ if len(text) <= self.max_content_length:
93
+ return text
94
+
95
+ # Keep first part, add marker
96
+ return text[:self.max_content_length] + self.truncate_marker
97
+
98
+ def _truncate_list(self, items: list[Any]) -> list[Any]:
99
+ """Truncate list content (tool_use, tool_result, etc.)."""
100
+ truncated_items = []
101
+
102
+ for item in items:
103
+ if isinstance(item, dict):
104
+ truncated_item = item.copy()
105
+
106
+ # Truncate text content
107
+ if "text" in truncated_item and isinstance(truncated_item["text"], str):
108
+ truncated_item["text"] = self._truncate_string(truncated_item["text"])
109
+
110
+ # Truncate tool_result content
111
+ if "content" in truncated_item and isinstance(truncated_item["content"], str):
112
+ truncated_item["content"] = self._truncate_string(truncated_item["content"])
113
+
114
+ # Truncate tool_use input (arguments)
115
+ if "input" in truncated_item and isinstance(truncated_item["input"], dict):
116
+ truncated_item["input"] = self._truncate_dict(truncated_item["input"])
117
+
118
+ truncated_items.append(truncated_item)
119
+ else:
120
+ truncated_items.append(item)
121
+
122
+ return truncated_items
123
+
124
+ def _truncate_dict(self, data: dict[str, Any]) -> dict[str, Any]:
125
+ """Truncate string values in a dict (for tool arguments)."""
126
+ truncated = {}
127
+ for key, value in data.items():
128
+ if isinstance(value, str):
129
+ truncated[key] = self._truncate_string(value)
130
+ elif isinstance(value, dict):
131
+ truncated[key] = self._truncate_dict(value)
132
+ elif isinstance(value, list):
133
+ truncated[key] = self._truncate_list(value)
134
+ else:
135
+ truncated[key] = value
136
+ return truncated
137
+
138
+
139
+ __all__ = ["MessageTruncationMiddleware"]
@@ -0,0 +1,81 @@
1
+ """Middleware types and data classes."""
2
+ from __future__ import annotations
3
+
4
+ from dataclasses import dataclass, field
5
+ from enum import Enum
6
+ from typing import Any
7
+
8
+
9
+ class TriggerMode(Enum):
10
+ """Middleware trigger mode for streaming."""
11
+ EVERY_TOKEN = "every_token" # Trigger on every token
12
+ EVERY_N_TOKENS = "every_n_tokens" # Trigger every N tokens
13
+ ON_BOUNDARY = "on_boundary" # Trigger on sentence/paragraph boundaries
14
+
15
+
16
+ class HookAction(Enum):
17
+ """Control flow action returned by lifecycle hooks.
18
+
19
+ CONTINUE: Proceed with normal execution
20
+ SKIP: Skip the current operation (tool call, etc.)
21
+ RETRY: Retry the current operation (with modified params)
22
+ STOP: Stop the agent execution entirely
23
+ """
24
+ CONTINUE = "continue"
25
+ SKIP = "skip"
26
+ RETRY = "retry"
27
+ STOP = "stop"
28
+
29
+
30
+ @dataclass
31
+ class HookResult:
32
+ """Result from a lifecycle hook.
33
+
34
+ Attributes:
35
+ action: Control flow action
36
+ modified_data: Modified data (for RETRY action)
37
+ message: Optional message explaining the action
38
+ metadata: Additional metadata
39
+ """
40
+ action: HookAction = HookAction.CONTINUE
41
+ modified_data: Any = None
42
+ message: str | None = None
43
+ metadata: dict[str, Any] = field(default_factory=dict)
44
+
45
+ @classmethod
46
+ def proceed(cls) -> "HookResult":
47
+ """Continue with normal execution."""
48
+ return cls(action=HookAction.CONTINUE)
49
+
50
+ @classmethod
51
+ def skip(cls, message: str | None = None) -> "HookResult":
52
+ """Skip the current operation."""
53
+ return cls(action=HookAction.SKIP, message=message)
54
+
55
+ @classmethod
56
+ def retry(cls, modified_data: Any = None, message: str | None = None) -> "HookResult":
57
+ """Retry with optional modified data."""
58
+ return cls(action=HookAction.RETRY, modified_data=modified_data, message=message)
59
+
60
+ @classmethod
61
+ def stop(cls, message: str | None = None) -> "HookResult":
62
+ """Stop agent execution."""
63
+ return cls(action=HookAction.STOP, message=message)
64
+
65
+
66
+ @dataclass
67
+ class MiddlewareConfig:
68
+ """Middleware configuration."""
69
+ trigger_mode: TriggerMode = TriggerMode.EVERY_TOKEN
70
+ trigger_n: int = 10 # For EVERY_N_TOKENS mode
71
+ async_mode: bool = True # Execute asynchronously
72
+ priority: int = 0 # Lower priority runs first
73
+ inherit: bool = True # Whether to pass to sub-agents (can be overridden at use() time)
74
+
75
+
76
+ __all__ = [
77
+ "TriggerMode",
78
+ "HookAction",
79
+ "HookResult",
80
+ "MiddlewareConfig",
81
+ ]
aury/agents/plugin.py ADDED
@@ -0,0 +1,162 @@
1
+ """Plugin system for packaging providers, middlewares and tools.
2
+
3
+ The Plugin class provides an optional assembly layer that packages:
4
+ - ContextProviders (context sources)
5
+ - Middlewares (execution hooks)
6
+ - Tools, Skills, SubAgents (direct contributions)
7
+
8
+ Design Goals:
9
+ 1. Simple case: Use plugins for packaged functionality
10
+ 2. Flexible case: Use providers/middlewares directly
11
+ 3. Mixed case: Combine plugins with custom providers/middlewares
12
+
13
+ Example Usage:
14
+ # Simple: use plugins
15
+ agent = ReactAgent.create(
16
+ llm=llm,
17
+ plugins=[CodePlugin()],
18
+ )
19
+
20
+ # Flexible: use providers/middlewares directly
21
+ agent = ReactAgent.create(
22
+ llm=llm,
23
+ context_providers=[MyCustomProvider()],
24
+ middlewares=[MyMiddleware()],
25
+ )
26
+
27
+ # Mixed + custom
28
+ agent = ReactAgent.create(
29
+ llm=llm,
30
+ plugins=[CodePlugin()],
31
+ context_providers=[MyRAGProvider()],
32
+ subagents=[researcher_config],
33
+ delegate_tool_class=MyDelegateTool,
34
+ )
35
+
36
+ Inheritance Rules:
37
+ - Plugin.inherit: Whether this plugin should be inherited by sub-agents
38
+ - AgentConfig.inherit_plugins: Whether to inherit plugins from parent
39
+ - Both must be True for inheritance to occur
40
+ """
41
+
42
+ from __future__ import annotations
43
+
44
+ from dataclasses import dataclass, field
45
+ from typing import TYPE_CHECKING
46
+
47
+ if TYPE_CHECKING:
48
+ from .core.types.tool import BaseTool
49
+ from .skill import Skill
50
+ from .backends.subagent import AgentConfig as SubAgentConfig
51
+ from .context_providers.base import ContextProvider
52
+ from .middleware.base import Middleware
53
+
54
+
55
+ # =============================================================================
56
+ # Plugin class
57
+ # =============================================================================
58
+
59
+ # @dataclass
60
+ # class Plugin:
61
+ # """Plugin packages providers, middlewares, tools, skills, and subagents.
62
+ #
63
+ # Attributes:
64
+ # name: Plugin identifier
65
+ # priority: Execution order (lower runs first, default 100)
66
+ # inherit: Whether this plugin should be inherited by sub-agents
67
+ # tools: Tools provided by this plugin
68
+ # skills: Skills provided by this plugin
69
+ # subagents: SubAgent configs provided by this plugin
70
+ # providers: ContextProviders provided by this plugin
71
+ # middlewares: Middlewares provided by this plugin
72
+ #
73
+ # Priority Guidelines:
74
+ # 0-50: Core/framework plugins (run first)
75
+ # 50-100: Standard plugins
76
+ # 100+: User plugins (run last)
77
+ #
78
+ # Example:
79
+ # @dataclass
80
+ # class CodePlugin(Plugin):
81
+ # name: str = "code"
82
+ # priority: int = 50
83
+ # inherit: bool = True
84
+ #
85
+ # def __post_init__(self):
86
+ # self.tools = [
87
+ # FileReadTool(),
88
+ # FileWriteTool(),
89
+ # ShellTool(),
90
+ # ]
91
+ # self.providers = [
92
+ # ProjectContextProvider(),
93
+ # ]
94
+ # self.middlewares = [
95
+ # CodeReviewMiddleware(),
96
+ # ]
97
+ # """
98
+ #
99
+ # name: str
100
+ # priority: int = 100
101
+ # inherit: bool = False
102
+ #
103
+ # tools: list["BaseTool"] = field(default_factory=list)
104
+ # skills: list["Skill"] = field(default_factory=list)
105
+ # subagents: list["SubAgentConfig"] = field(default_factory=list)
106
+ # providers: list["ContextProvider"] = field(default_factory=list)
107
+ # middlewares: list["Middleware"] = field(default_factory=list)
108
+
109
+
110
+ # =============================================================================
111
+ # PluginChain for managing multiple plugins
112
+ # =============================================================================
113
+
114
+ # class PluginChain:
115
+ # """Chain of plugins sorted by priority.
116
+ #
117
+ # Provides methods to:
118
+ # - Collect all tools from plugins
119
+ # - Collect all providers from plugins
120
+ # - Collect all middlewares from plugins
121
+ # - Get inheritable plugins for sub-agents
122
+ # """
123
+ #
124
+ # def __init__(self, plugins: list[Plugin] | None = None):
125
+ # self._plugins: list[Plugin] = []
126
+ # if plugins:
127
+ # for p in plugins:
128
+ # self.add(p)
129
+ #
130
+ # def add(self, plugin: Plugin) -> "PluginChain":
131
+ # """Add plugin and maintain sorted order by priority."""
132
+ # self._plugins.append(plugin)
133
+ # self._plugins.sort(key=lambda p: p.priority)
134
+ # return self
135
+ #
136
+ # def collect_tools(self) -> list["BaseTool"]:
137
+ # """Collect all tools from all plugins."""
138
+ # tools = []
139
+ # for p in self._plugins:
140
+ # tools.extend(p.tools)
141
+ # return tools
142
+ #
143
+ # def collect_providers(self) -> list["ContextProvider"]:
144
+ # """Collect all providers from all plugins."""
145
+ # providers = []
146
+ # for p in self._plugins:
147
+ # providers.extend(p.providers)
148
+ # return providers
149
+ #
150
+ # def collect_middlewares(self) -> list["Middleware"]:
151
+ # """Collect all middlewares from all plugins."""
152
+ # middlewares = []
153
+ # for p in self._plugins:
154
+ # middlewares.extend(p.middlewares)
155
+ # return middlewares
156
+ #
157
+ # def get_inheritable(self) -> list[Plugin]:
158
+ # """Get plugins that should be inherited by sub-agents."""
159
+ # return [p for p in self._plugins if p.inherit]
160
+
161
+
162
+ __all__: list[str] = [] # Classes are commented out
@@ -0,0 +1,4 @@
1
+ """React Agent implementation."""
2
+ from .agent import ReactAgent
3
+
4
+ __all__ = ["ReactAgent"]