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.
Files changed (86) hide show
  1. flowly_code/__init__.py +30 -0
  2. flowly_code/__main__.py +8 -0
  3. flowly_code/activity/__init__.py +1 -0
  4. flowly_code/activity/bus.py +91 -0
  5. flowly_code/activity/events.py +40 -0
  6. flowly_code/agent/__init__.py +8 -0
  7. flowly_code/agent/context.py +485 -0
  8. flowly_code/agent/loop.py +1349 -0
  9. flowly_code/agent/memory.py +109 -0
  10. flowly_code/agent/skills.py +259 -0
  11. flowly_code/agent/subagent.py +249 -0
  12. flowly_code/agent/tools/__init__.py +6 -0
  13. flowly_code/agent/tools/base.py +55 -0
  14. flowly_code/agent/tools/delegate.py +194 -0
  15. flowly_code/agent/tools/dispatch.py +840 -0
  16. flowly_code/agent/tools/docker.py +609 -0
  17. flowly_code/agent/tools/filesystem.py +280 -0
  18. flowly_code/agent/tools/mcp.py +85 -0
  19. flowly_code/agent/tools/message.py +235 -0
  20. flowly_code/agent/tools/registry.py +257 -0
  21. flowly_code/agent/tools/screenshot.py +444 -0
  22. flowly_code/agent/tools/shell.py +166 -0
  23. flowly_code/agent/tools/spawn.py +65 -0
  24. flowly_code/agent/tools/system.py +917 -0
  25. flowly_code/agent/tools/trello.py +420 -0
  26. flowly_code/agent/tools/web.py +139 -0
  27. flowly_code/agent/tools/x.py +399 -0
  28. flowly_code/bus/__init__.py +6 -0
  29. flowly_code/bus/events.py +37 -0
  30. flowly_code/bus/queue.py +81 -0
  31. flowly_code/channels/__init__.py +6 -0
  32. flowly_code/channels/base.py +121 -0
  33. flowly_code/channels/manager.py +135 -0
  34. flowly_code/channels/telegram.py +1132 -0
  35. flowly_code/cli/__init__.py +1 -0
  36. flowly_code/cli/commands.py +1831 -0
  37. flowly_code/cli/setup.py +1356 -0
  38. flowly_code/compaction/__init__.py +39 -0
  39. flowly_code/compaction/estimator.py +88 -0
  40. flowly_code/compaction/pruning.py +223 -0
  41. flowly_code/compaction/service.py +297 -0
  42. flowly_code/compaction/summarizer.py +384 -0
  43. flowly_code/compaction/types.py +71 -0
  44. flowly_code/config/__init__.py +6 -0
  45. flowly_code/config/loader.py +102 -0
  46. flowly_code/config/schema.py +324 -0
  47. flowly_code/exec/__init__.py +39 -0
  48. flowly_code/exec/approvals.py +288 -0
  49. flowly_code/exec/executor.py +184 -0
  50. flowly_code/exec/safety.py +247 -0
  51. flowly_code/exec/types.py +88 -0
  52. flowly_code/gateway/__init__.py +5 -0
  53. flowly_code/gateway/server.py +103 -0
  54. flowly_code/heartbeat/__init__.py +5 -0
  55. flowly_code/heartbeat/service.py +130 -0
  56. flowly_code/multiagent/README.md +248 -0
  57. flowly_code/multiagent/__init__.py +1 -0
  58. flowly_code/multiagent/invoke.py +210 -0
  59. flowly_code/multiagent/orchestrator.py +156 -0
  60. flowly_code/multiagent/router.py +156 -0
  61. flowly_code/multiagent/setup.py +171 -0
  62. flowly_code/pairing/__init__.py +21 -0
  63. flowly_code/pairing/store.py +343 -0
  64. flowly_code/providers/__init__.py +6 -0
  65. flowly_code/providers/base.py +69 -0
  66. flowly_code/providers/litellm_provider.py +178 -0
  67. flowly_code/providers/transcription.py +64 -0
  68. flowly_code/session/__init__.py +5 -0
  69. flowly_code/session/manager.py +249 -0
  70. flowly_code/skills/README.md +24 -0
  71. flowly_code/skills/compact/SKILL.md +27 -0
  72. flowly_code/skills/github/SKILL.md +48 -0
  73. flowly_code/skills/skill-creator/SKILL.md +371 -0
  74. flowly_code/skills/summarize/SKILL.md +67 -0
  75. flowly_code/skills/tmux/SKILL.md +121 -0
  76. flowly_code/skills/tmux/scripts/find-sessions.sh +112 -0
  77. flowly_code/skills/tmux/scripts/wait-for-text.sh +83 -0
  78. flowly_code/skills/weather/SKILL.md +49 -0
  79. flowly_code/utils/__init__.py +5 -0
  80. flowly_code/utils/helpers.py +91 -0
  81. flowly_code-1.0.0.dist-info/METADATA +724 -0
  82. flowly_code-1.0.0.dist-info/RECORD +86 -0
  83. flowly_code-1.0.0.dist-info/WHEEL +4 -0
  84. flowly_code-1.0.0.dist-info/entry_points.txt +2 -0
  85. flowly_code-1.0.0.dist-info/licenses/LICENSE +191 -0
  86. 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
+ )