flowly-code 1.0.0__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.
- flowly_code/__init__.py +30 -0
- flowly_code/__main__.py +8 -0
- flowly_code/activity/__init__.py +1 -0
- flowly_code/activity/bus.py +91 -0
- flowly_code/activity/events.py +40 -0
- flowly_code/agent/__init__.py +8 -0
- flowly_code/agent/context.py +485 -0
- flowly_code/agent/loop.py +1349 -0
- flowly_code/agent/memory.py +109 -0
- flowly_code/agent/skills.py +259 -0
- flowly_code/agent/subagent.py +249 -0
- flowly_code/agent/tools/__init__.py +6 -0
- flowly_code/agent/tools/base.py +55 -0
- flowly_code/agent/tools/delegate.py +194 -0
- flowly_code/agent/tools/dispatch.py +840 -0
- flowly_code/agent/tools/docker.py +609 -0
- flowly_code/agent/tools/filesystem.py +280 -0
- flowly_code/agent/tools/mcp.py +85 -0
- flowly_code/agent/tools/message.py +235 -0
- flowly_code/agent/tools/registry.py +257 -0
- flowly_code/agent/tools/screenshot.py +444 -0
- flowly_code/agent/tools/shell.py +166 -0
- flowly_code/agent/tools/spawn.py +65 -0
- flowly_code/agent/tools/system.py +917 -0
- flowly_code/agent/tools/trello.py +420 -0
- flowly_code/agent/tools/web.py +139 -0
- flowly_code/agent/tools/x.py +399 -0
- flowly_code/bus/__init__.py +6 -0
- flowly_code/bus/events.py +37 -0
- flowly_code/bus/queue.py +81 -0
- flowly_code/channels/__init__.py +6 -0
- flowly_code/channels/base.py +121 -0
- flowly_code/channels/manager.py +135 -0
- flowly_code/channels/telegram.py +1132 -0
- flowly_code/cli/__init__.py +1 -0
- flowly_code/cli/commands.py +1831 -0
- flowly_code/cli/setup.py +1356 -0
- flowly_code/compaction/__init__.py +39 -0
- flowly_code/compaction/estimator.py +88 -0
- flowly_code/compaction/pruning.py +223 -0
- flowly_code/compaction/service.py +297 -0
- flowly_code/compaction/summarizer.py +384 -0
- flowly_code/compaction/types.py +71 -0
- flowly_code/config/__init__.py +6 -0
- flowly_code/config/loader.py +102 -0
- flowly_code/config/schema.py +324 -0
- flowly_code/exec/__init__.py +39 -0
- flowly_code/exec/approvals.py +288 -0
- flowly_code/exec/executor.py +184 -0
- flowly_code/exec/safety.py +247 -0
- flowly_code/exec/types.py +88 -0
- flowly_code/gateway/__init__.py +5 -0
- flowly_code/gateway/server.py +103 -0
- flowly_code/heartbeat/__init__.py +5 -0
- flowly_code/heartbeat/service.py +130 -0
- flowly_code/multiagent/README.md +248 -0
- flowly_code/multiagent/__init__.py +1 -0
- flowly_code/multiagent/invoke.py +210 -0
- flowly_code/multiagent/orchestrator.py +156 -0
- flowly_code/multiagent/router.py +156 -0
- flowly_code/multiagent/setup.py +171 -0
- flowly_code/pairing/__init__.py +21 -0
- flowly_code/pairing/store.py +343 -0
- flowly_code/providers/__init__.py +6 -0
- flowly_code/providers/base.py +69 -0
- flowly_code/providers/litellm_provider.py +178 -0
- flowly_code/providers/transcription.py +64 -0
- flowly_code/session/__init__.py +5 -0
- flowly_code/session/manager.py +249 -0
- flowly_code/skills/README.md +24 -0
- flowly_code/skills/compact/SKILL.md +27 -0
- flowly_code/skills/github/SKILL.md +48 -0
- flowly_code/skills/skill-creator/SKILL.md +371 -0
- flowly_code/skills/summarize/SKILL.md +67 -0
- flowly_code/skills/tmux/SKILL.md +121 -0
- flowly_code/skills/tmux/scripts/find-sessions.sh +112 -0
- flowly_code/skills/tmux/scripts/wait-for-text.sh +83 -0
- flowly_code/skills/weather/SKILL.md +49 -0
- flowly_code/utils/__init__.py +5 -0
- flowly_code/utils/helpers.py +91 -0
- flowly_code-1.0.0.dist-info/METADATA +724 -0
- flowly_code-1.0.0.dist-info/RECORD +86 -0
- flowly_code-1.0.0.dist-info/WHEEL +4 -0
- flowly_code-1.0.0.dist-info/entry_points.txt +2 -0
- flowly_code-1.0.0.dist-info/licenses/LICENSE +191 -0
- flowly_code-1.0.0.dist-info/licenses/NOTICE +74 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""Delegate tool — allows main agent to delegate tasks to other agents.
|
|
2
|
+
|
|
3
|
+
Runs agent subprocess in the background (fire-and-forget). The tool returns
|
|
4
|
+
immediately so the main agent can respond to the user. When the subprocess
|
|
5
|
+
completes, the result is automatically sent back to the user via the bus.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from loguru import logger
|
|
13
|
+
|
|
14
|
+
from flowly_code.agent.tools.base import Tool
|
|
15
|
+
from flowly_code.bus.events import InboundMessage, OutboundMessage
|
|
16
|
+
from flowly_code.bus.queue import MessageBus
|
|
17
|
+
from flowly_code.config.schema import MultiAgentConfig, MultiAgentTeamConfig
|
|
18
|
+
from flowly_code.multiagent.invoke import invoke_agent, resolve_claude_model, resolve_codex_model
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DelegateTool(Tool):
|
|
22
|
+
"""Delegate a task to another configured agent.
|
|
23
|
+
|
|
24
|
+
This tool allows the main Flowly agent to invoke specialized agents
|
|
25
|
+
(Claude Code, Codex, etc.) for specific tasks. The agent subprocess
|
|
26
|
+
runs in the background — the tool returns immediately with an
|
|
27
|
+
acknowledgment, and the result is delivered asynchronously via the bus.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
agents: dict[str, MultiAgentConfig],
|
|
33
|
+
teams: dict[str, MultiAgentTeamConfig],
|
|
34
|
+
workspace: Path,
|
|
35
|
+
bus: MessageBus,
|
|
36
|
+
activity_bus: "ActivityBus | None" = None,
|
|
37
|
+
):
|
|
38
|
+
self._agents = agents
|
|
39
|
+
self._teams = teams
|
|
40
|
+
self._workspace = workspace
|
|
41
|
+
self._bus = bus
|
|
42
|
+
self._activity_bus = activity_bus
|
|
43
|
+
# Current message context — set before each execution by the routing layer
|
|
44
|
+
self._channel: str = ""
|
|
45
|
+
self._chat_id: str = ""
|
|
46
|
+
|
|
47
|
+
def _emit_activity(self, event_type: str, **kwargs) -> None:
|
|
48
|
+
if not self._activity_bus or not self._activity_bus.has_subscribers:
|
|
49
|
+
return
|
|
50
|
+
try:
|
|
51
|
+
from flowly_code.activity.events import ActivityEvent
|
|
52
|
+
self._activity_bus.emit(ActivityEvent(type=event_type, **kwargs))
|
|
53
|
+
except Exception:
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
def set_context(self, channel: str, chat_id: str) -> None:
|
|
57
|
+
"""Set the current message context for async result delivery."""
|
|
58
|
+
self._channel = channel
|
|
59
|
+
self._chat_id = chat_id
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def name(self) -> str:
|
|
63
|
+
return "delegate_to"
|
|
64
|
+
|
|
65
|
+
def _resolve_model(self, cfg: MultiAgentConfig) -> str:
|
|
66
|
+
"""Resolve short model name to full model ID for display."""
|
|
67
|
+
if cfg.provider == "anthropic":
|
|
68
|
+
return resolve_claude_model(cfg.model)
|
|
69
|
+
if cfg.provider == "openai":
|
|
70
|
+
return resolve_codex_model(cfg.model)
|
|
71
|
+
return cfg.model
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def description(self) -> str:
|
|
75
|
+
agent_list = ", ".join(
|
|
76
|
+
f"@{aid} ({cfg.name or aid}, {self._resolve_model(cfg)})"
|
|
77
|
+
for aid, cfg in self._agents.items()
|
|
78
|
+
)
|
|
79
|
+
return (
|
|
80
|
+
"Delegate a task to another specialized agent. "
|
|
81
|
+
"The task runs in the background — you will NOT receive the result in this turn. "
|
|
82
|
+
"Tell the user that the task has been delegated and they will receive the result shortly. "
|
|
83
|
+
f"Available agents: {agent_list}"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def parameters(self) -> dict[str, Any]:
|
|
88
|
+
agent_ids = list(self._agents.keys())
|
|
89
|
+
return {
|
|
90
|
+
"type": "object",
|
|
91
|
+
"properties": {
|
|
92
|
+
"agent_id": {
|
|
93
|
+
"type": "string",
|
|
94
|
+
"description": f"Target agent ID. Available: {agent_ids}",
|
|
95
|
+
},
|
|
96
|
+
"message": {
|
|
97
|
+
"type": "string",
|
|
98
|
+
"description": "The task or message to send to the agent.",
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
"required": ["agent_id", "message"],
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async def execute(self, agent_id: str, message: str, **kwargs: Any) -> str:
|
|
105
|
+
"""Delegate a task to the specified agent (fire-and-forget).
|
|
106
|
+
|
|
107
|
+
Starts the agent subprocess in the background and returns immediately.
|
|
108
|
+
When the subprocess completes, the result is sent via the bus.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
agent_id: Target agent identifier.
|
|
112
|
+
message: Task/message for the agent.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Immediate acknowledgment string (result comes async via bus).
|
|
116
|
+
"""
|
|
117
|
+
if agent_id not in self._agents:
|
|
118
|
+
available = list(self._agents.keys())
|
|
119
|
+
return f"Error: Agent '{agent_id}' not found. Available agents: {available}"
|
|
120
|
+
|
|
121
|
+
agent = self._agents[agent_id]
|
|
122
|
+
model_display = self._resolve_model(agent)
|
|
123
|
+
logger.info(f"Delegating to @{agent_id}: {message[:80]}...")
|
|
124
|
+
|
|
125
|
+
# Emit activity: agent is starting work
|
|
126
|
+
self._emit_activity(
|
|
127
|
+
"subagent_spawn",
|
|
128
|
+
agent_name=agent_id,
|
|
129
|
+
subagent_id=agent_id,
|
|
130
|
+
subagent_label=agent.name or agent_id,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Capture context for the background task
|
|
134
|
+
channel = self._channel
|
|
135
|
+
chat_id = self._chat_id
|
|
136
|
+
|
|
137
|
+
async def _run_in_background() -> None:
|
|
138
|
+
try:
|
|
139
|
+
result = await invoke_agent(
|
|
140
|
+
agent, agent_id, message, self._workspace, timeout=1800,
|
|
141
|
+
)
|
|
142
|
+
self._emit_activity(
|
|
143
|
+
"subagent_end",
|
|
144
|
+
agent_name=agent_id,
|
|
145
|
+
subagent_id=agent_id,
|
|
146
|
+
subagent_label=agent.name or agent_id,
|
|
147
|
+
success=True,
|
|
148
|
+
)
|
|
149
|
+
content = (
|
|
150
|
+
f"[DELEGATE_RESULT:{agent_id}] "
|
|
151
|
+
f"@{agent_id} has completed the task. "
|
|
152
|
+
f"Summarize the result for the user in your own words.\n\n"
|
|
153
|
+
f"Result:\n{result}"
|
|
154
|
+
)
|
|
155
|
+
except Exception as e:
|
|
156
|
+
logger.error(f"Background delegation to @{agent_id} failed: {e}")
|
|
157
|
+
self._emit_activity(
|
|
158
|
+
"subagent_end",
|
|
159
|
+
agent_name=agent_id,
|
|
160
|
+
subagent_id=agent_id,
|
|
161
|
+
subagent_label=agent.name or agent_id,
|
|
162
|
+
success=False,
|
|
163
|
+
error_message=str(e),
|
|
164
|
+
)
|
|
165
|
+
content = (
|
|
166
|
+
f"[DELEGATE_RESULT:{agent_id}] "
|
|
167
|
+
f"@{agent_id} failed with an error. "
|
|
168
|
+
f"Tell the user what happened.\n\n"
|
|
169
|
+
f"Error: {e}"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# Send back through agent loop so the model summarizes it.
|
|
173
|
+
# The DELEGATE_RESULT marker tells the routing layer to temporarily
|
|
174
|
+
# remove the delegate_to tool, preventing re-delegation loops.
|
|
175
|
+
if channel and chat_id:
|
|
176
|
+
await self._bus.publish_inbound(
|
|
177
|
+
InboundMessage(
|
|
178
|
+
channel=channel,
|
|
179
|
+
sender_id="delegate_result",
|
|
180
|
+
chat_id=chat_id,
|
|
181
|
+
content=content,
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
else:
|
|
185
|
+
logger.warning(f"No message context for @{agent_id} result delivery")
|
|
186
|
+
|
|
187
|
+
# Fire-and-forget
|
|
188
|
+
asyncio.create_task(_run_in_background())
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
f"Task delegated to @{agent_id} ({agent.name or agent_id}, {model_display}). "
|
|
192
|
+
f"The agent is now working in the background. "
|
|
193
|
+
f"The result will be delivered automatically when the agent finishes."
|
|
194
|
+
)
|