swarmly 0.9.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.
- swarmly/__init__.py +42 -0
- swarmly/_aliases.py +30 -0
- swarmly/adapters/__init__.py +17 -0
- swarmly/adapters/base.py +54 -0
- swarmly/adapters/deepseek.py +58 -0
- swarmly/adapters/gemini.py +60 -0
- swarmly/adapters/hermes.py +46 -0
- swarmly/adapters/openai_swarm.py +68 -0
- swarmly/bus.py +331 -0
- swarmly/client.py +216 -0
- swarmly/crypto.py +221 -0
- swarmly/github/__init__.py +5 -0
- swarmly/github/client.py +305 -0
- swarmly/github/watch.py +293 -0
- swarmly/keygen.py +247 -0
- swarmly/logsetup.py +121 -0
- swarmly/models.py +394 -0
- swarmly/server.py +301 -0
- swarmly/swarm/__init__.py +23 -0
- swarmly/swarm/cli.py +483 -0
- swarmly/swarm/doc.py +124 -0
- swarmly/swarm/doorbell.py +237 -0
- swarmly/swarm/envelope.py +195 -0
- swarmly/swarm/governor.py +344 -0
- swarmly/swarm/inbox.py +287 -0
- swarmly/swarm/keys.py +95 -0
- swarmly/swarm/lanes.py +376 -0
- swarmly/swarm/mcp.py +181 -0
- swarmly/swarm/mcp_setup.py +617 -0
- swarmly/swarm/projection.py +719 -0
- swarmly/swarm/protocol.py +288 -0
- swarmly/swarm/protocol.yaml +268 -0
- swarmly/telegram/__init__.py +82 -0
- swarmly/telegram/acks.py +140 -0
- swarmly/telegram/adapters/__init__.py +146 -0
- swarmly/telegram/adapters/antigravity.py +1304 -0
- swarmly/telegram/adapters/base.py +796 -0
- swarmly/telegram/adapters/claude.py +337 -0
- swarmly/telegram/adapters/codex.py +616 -0
- swarmly/telegram/adapters/handover.py +141 -0
- swarmly/telegram/adapters/hermes.py +74 -0
- swarmly/telegram/adapters/reasonix.py +297 -0
- swarmly/telegram/channel.py +3961 -0
- swarmly/telegram/cli.py +995 -0
- swarmly/telegram/commands.py +120 -0
- swarmly/telegram/config.py +1310 -0
- swarmly/telegram/endpoint.py +800 -0
- swarmly/telegram/git_sync.py +109 -0
- swarmly/telegram/guard.py +244 -0
- swarmly/telegram/init.py +403 -0
- swarmly/telegram/oslock.py +92 -0
- swarmly/telegram/poll.py +2134 -0
- swarmly/telegram/projects.py +141 -0
- swarmly/telegram/relay.py +131 -0
- swarmly/telegram/replies.py +330 -0
- swarmly/telegram/roles/__init__.py +140 -0
- swarmly/telegram/roles/coordinator.md +29 -0
- swarmly/telegram/roles/developer.md +21 -0
- swarmly/telegram/roles/qa-tester.md +20 -0
- swarmly/telegram/roles/researcher.md +17 -0
- swarmly/telegram/roles/reviewer.md +21 -0
- swarmly/telegram/roster.py +270 -0
- swarmly/telegram/selftest.py +405 -0
- swarmly/telegram/templates/a2a_config.json +8 -0
- swarmly/telegram/templates/config.example.yaml +66 -0
- swarmly/telegram/topic_memory.py +228 -0
- swarmly/telegram/topic_open.py +131 -0
- swarmly/telegram/topics.py +141 -0
- swarmly/telegram/wire.py +302 -0
- swarmly/transports/__init__.py +58 -0
- swarmly/transports/base.py +149 -0
- swarmly/transports/folder.py +62 -0
- swarmly/transports/gdrive.py +206 -0
- swarmly/transports/git.py +344 -0
- swarmly-0.9.0.dist-info/METADATA +402 -0
- swarmly-0.9.0.dist-info/RECORD +81 -0
- swarmly-0.9.0.dist-info/WHEEL +5 -0
- swarmly-0.9.0.dist-info/entry_points.txt +7 -0
- swarmly-0.9.0.dist-info/licenses/LICENSE +21 -0
- swarmly-0.9.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +70 -0
- swarmly-0.9.0.dist-info/top_level.txt +1 -0
swarmly/__init__.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Swarmly: coordinate coding-agent CLIs from different vendors, on different machines.
|
|
3
|
+
|
|
4
|
+
The import package was ``a2a`` before the rename; the wire identifiers (``a2a/v1``,
|
|
5
|
+
``a2a/2``) and the ``A2A_*`` environment variables keep their names.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__version__ = "0.9.0"
|
|
9
|
+
__protocol__ = "a2a/v1.0"
|
|
10
|
+
|
|
11
|
+
from swarmly.models import (
|
|
12
|
+
A2AMessage,
|
|
13
|
+
SenderInfo,
|
|
14
|
+
RecipientInfo,
|
|
15
|
+
MessageIntent,
|
|
16
|
+
MessagePriority,
|
|
17
|
+
TaskClaimPayload,
|
|
18
|
+
TaskHandoffPayload,
|
|
19
|
+
PRReviewRequestPayload,
|
|
20
|
+
PRReviewVerdictPayload,
|
|
21
|
+
FalsificationPayload,
|
|
22
|
+
)
|
|
23
|
+
from swarmly.crypto import CryptoAuth, KeyPair
|
|
24
|
+
from swarmly.bus import DurableBus
|
|
25
|
+
from swarmly.client import A2AClient
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"A2AMessage",
|
|
29
|
+
"SenderInfo",
|
|
30
|
+
"RecipientInfo",
|
|
31
|
+
"MessageIntent",
|
|
32
|
+
"MessagePriority",
|
|
33
|
+
"TaskClaimPayload",
|
|
34
|
+
"TaskHandoffPayload",
|
|
35
|
+
"PRReviewRequestPayload",
|
|
36
|
+
"PRReviewVerdictPayload",
|
|
37
|
+
"FalsificationPayload",
|
|
38
|
+
"CryptoAuth",
|
|
39
|
+
"KeyPair",
|
|
40
|
+
"DurableBus",
|
|
41
|
+
"A2AClient",
|
|
42
|
+
]
|
swarmly/_aliases.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""The command names from before the rename to Swarmly, kept for one release.
|
|
2
|
+
|
|
3
|
+
Each prints one line to stderr naming its replacement, then runs it unchanged, so a
|
|
4
|
+
launcher or scheduled task still using an old name keeps working and its log says so.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _note(old: str, new: str) -> None:
|
|
11
|
+
print(f"{old}: renamed to {new}; this alias goes away in the next release",
|
|
12
|
+
file=sys.stderr)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def a2a_telegram() -> None:
|
|
16
|
+
_note("a2a-telegram", "swarmly-telegram")
|
|
17
|
+
from swarmly.telegram.cli import main
|
|
18
|
+
sys.exit(main())
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def a2a() -> None:
|
|
22
|
+
_note("a2a", "swarmly")
|
|
23
|
+
from swarmly.swarm.cli import main
|
|
24
|
+
sys.exit(main())
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def a2a_keygen() -> None:
|
|
28
|
+
_note("a2a-keygen", "swarmly-keygen")
|
|
29
|
+
from swarmly.keygen import main
|
|
30
|
+
sys.exit(main())
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Multi-Agent Framework Adapters for Swarmly.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from swarmly.adapters.base import BaseAgentAdapter
|
|
6
|
+
from swarmly.adapters.openai_swarm import OpenAISwarmAdapter
|
|
7
|
+
from swarmly.adapters.deepseek import DeepSeekAdapter
|
|
8
|
+
from swarmly.adapters.gemini import GeminiAdapter
|
|
9
|
+
from swarmly.adapters.hermes import HermesAdapter
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"BaseAgentAdapter",
|
|
13
|
+
"OpenAISwarmAdapter",
|
|
14
|
+
"DeepSeekAdapter",
|
|
15
|
+
"GeminiAdapter",
|
|
16
|
+
"HermesAdapter",
|
|
17
|
+
]
|
swarmly/adapters/base.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base abstract adapter defining interface for heterogeneous agent frameworks.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
from abc import ABC, abstractmethod
|
|
7
|
+
from typing import Any, Dict, List, Optional
|
|
8
|
+
from swarmly.models import A2AMessage, MessageIntent, MessagePriority
|
|
9
|
+
from swarmly.client import A2AClient
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BaseAgentAdapter(ABC):
|
|
13
|
+
"""Abstract adapter connecting a specific agent framework to Swarmly."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, client: A2AClient):
|
|
16
|
+
self.client = client
|
|
17
|
+
self.agent_id = client.agent_id
|
|
18
|
+
|
|
19
|
+
@abstractmethod
|
|
20
|
+
def export_tools(self) -> List[Dict[str, Any]]:
|
|
21
|
+
"""Export tool definitions suitable for the framework's tool-calling API."""
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
async def execute_tool_call(self, tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
|
25
|
+
"""Dispatch a tool call triggered by the underlying LLM."""
|
|
26
|
+
if tool_name == "a2a_send_message":
|
|
27
|
+
msg = await self.client.send_message(
|
|
28
|
+
intent=arguments.get("intent", MessageIntent.QUERY_REQUEST),
|
|
29
|
+
payload=arguments.get("payload", {}),
|
|
30
|
+
recipient_id=arguments.get("recipient_id", "*"),
|
|
31
|
+
priority=arguments.get("priority", MessagePriority.ROUTINE),
|
|
32
|
+
correlation_id=arguments.get("correlation_id"),
|
|
33
|
+
)
|
|
34
|
+
return {"status": "sent", "message_id": msg.message_id}
|
|
35
|
+
|
|
36
|
+
elif tool_name == "a2a_read_inbox":
|
|
37
|
+
messages = self.client.bus.read_inbox(verify_signatures=True)
|
|
38
|
+
return {
|
|
39
|
+
"count": len(messages),
|
|
40
|
+
"messages": [
|
|
41
|
+
{
|
|
42
|
+
"message_id": m.message_id,
|
|
43
|
+
"sender": m.sender.agent_id,
|
|
44
|
+
"intent": m.intent.value,
|
|
45
|
+
"priority": m.priority.value,
|
|
46
|
+
"payload": m.payload,
|
|
47
|
+
"is_valid": valid,
|
|
48
|
+
"status": status,
|
|
49
|
+
}
|
|
50
|
+
for m, valid, status in messages
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {"error": f"Unknown tool: {tool_name}"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DeepSeek Adapter for Swarmly.
|
|
3
|
+
Supports both programmatic integration and CLI Aider/runner workflows.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
from typing import Any, Dict, List
|
|
8
|
+
from swarmly.adapters.base import BaseAgentAdapter
|
|
9
|
+
from swarmly.models import MessageIntent, MessagePriority
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DeepSeekAdapter(BaseAgentAdapter):
|
|
13
|
+
"""Adapter for DeepSeek-R1 and DeepSeek-V3 reasoning agents."""
|
|
14
|
+
|
|
15
|
+
def export_tools(self) -> List[Dict[str, Any]]:
|
|
16
|
+
return [
|
|
17
|
+
{
|
|
18
|
+
"name": "a2a_send_falsification_report",
|
|
19
|
+
"description": "Publish a formal falsification or statistical audit to the swarm with citations.",
|
|
20
|
+
"parameters": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"required": ["hypothesis", "predicted_metric", "measured_metric", "verdict"],
|
|
23
|
+
"properties": {
|
|
24
|
+
"hypothesis": {"type": "string"},
|
|
25
|
+
"predicted_metric": {"type": "string"},
|
|
26
|
+
"measured_metric": {"type": "string"},
|
|
27
|
+
"bounds_violated": {"type": "boolean"},
|
|
28
|
+
"verdict": {"type": "string"},
|
|
29
|
+
"citation_scripts": {"type": "array", "items": {"type": "string"}}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
async def publish_falsification(
|
|
36
|
+
self,
|
|
37
|
+
hypothesis: str,
|
|
38
|
+
predicted_metric: str,
|
|
39
|
+
measured_metric: str,
|
|
40
|
+
bounds_violated: bool,
|
|
41
|
+
verdict: str,
|
|
42
|
+
citation_scripts: List[str]
|
|
43
|
+
):
|
|
44
|
+
"""Helper to post a typed falsification report to all agents."""
|
|
45
|
+
payload = {
|
|
46
|
+
"hypothesis": hypothesis,
|
|
47
|
+
"predicted_metric": predicted_metric,
|
|
48
|
+
"measured_metric": measured_metric,
|
|
49
|
+
"bounds_violated": bounds_violated,
|
|
50
|
+
"verdict": verdict,
|
|
51
|
+
"citation_scripts": citation_scripts
|
|
52
|
+
}
|
|
53
|
+
return await self.client.send_message(
|
|
54
|
+
intent=MessageIntent.FALSIFICATION_REPORT,
|
|
55
|
+
payload=payload,
|
|
56
|
+
recipient_id="*",
|
|
57
|
+
priority=MessagePriority.HIGH
|
|
58
|
+
)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Google Gemini and Antigravity Adapter for Swarmly.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
from typing import Any, Dict, List
|
|
7
|
+
from swarmly.adapters.base import BaseAgentAdapter
|
|
8
|
+
from swarmly.models import MessageIntent, MessagePriority
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GeminiAdapter(BaseAgentAdapter):
|
|
12
|
+
"""Adapter for Google Gemini and Antigravity agents."""
|
|
13
|
+
|
|
14
|
+
def export_tools(self) -> List[Dict[str, Any]]:
|
|
15
|
+
return [
|
|
16
|
+
{
|
|
17
|
+
"name": "a2a_handoff_task",
|
|
18
|
+
"description": "Formally hand off an experimental task to a peer agent with pre-registered bounds.",
|
|
19
|
+
"parameters": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"required": ["task_id", "git_commit_sha", "description", "pre_registered_hypothesis", "expected_bounds"],
|
|
22
|
+
"properties": {
|
|
23
|
+
"task_id": {"type": "string"},
|
|
24
|
+
"git_commit_sha": {"type": "string"},
|
|
25
|
+
"description": {"type": "string"},
|
|
26
|
+
"pre_registered_hypothesis": {"type": "string"},
|
|
27
|
+
"expected_bounds": {"type": "array", "items": {"type": "number"}},
|
|
28
|
+
"verification_command": {"type": "string"},
|
|
29
|
+
"target_output": {"type": "string"}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
async def handoff_task(
|
|
36
|
+
self,
|
|
37
|
+
task_id: str,
|
|
38
|
+
git_commit_sha: str,
|
|
39
|
+
description: str,
|
|
40
|
+
hypothesis: str,
|
|
41
|
+
bounds: List[float],
|
|
42
|
+
cmd: str = "",
|
|
43
|
+
target: str = "",
|
|
44
|
+
recipient_id: str = "hermes"
|
|
45
|
+
):
|
|
46
|
+
payload = {
|
|
47
|
+
"task_id": task_id,
|
|
48
|
+
"git_commit_sha": git_commit_sha,
|
|
49
|
+
"description": description,
|
|
50
|
+
"pre_registered_hypothesis": hypothesis,
|
|
51
|
+
"expected_bounds": bounds,
|
|
52
|
+
"verification_command": cmd,
|
|
53
|
+
"target_output": target
|
|
54
|
+
}
|
|
55
|
+
return await self.client.send_message(
|
|
56
|
+
intent=MessageIntent.TASK_HANDOFF,
|
|
57
|
+
payload=payload,
|
|
58
|
+
recipient_id=recipient_id,
|
|
59
|
+
priority=MessagePriority.HIGH
|
|
60
|
+
)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Hermes and Local vLLM Agent Adapter for Swarmly.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
from typing import Any, Dict, List
|
|
7
|
+
from swarmly.adapters.base import BaseAgentAdapter
|
|
8
|
+
from swarmly.models import MessageIntent, MessagePriority
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class HermesAdapter(BaseAgentAdapter):
|
|
12
|
+
"""Adapter for Hermes agents running on Linux/vLLM compute nodes."""
|
|
13
|
+
|
|
14
|
+
def export_tools(self) -> List[Dict[str, Any]]:
|
|
15
|
+
return [
|
|
16
|
+
{
|
|
17
|
+
"name": "a2a_claim_task",
|
|
18
|
+
"description": "Claim a research or feature engineering task atomically.",
|
|
19
|
+
"parameters": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"required": ["task_id", "topic"],
|
|
22
|
+
"properties": {
|
|
23
|
+
"task_id": {"type": "string"},
|
|
24
|
+
"topic": {"type": "string"},
|
|
25
|
+
"branch": {"type": "string"},
|
|
26
|
+
"claimed_files": {"type": "array", "items": {"type": "string"}},
|
|
27
|
+
"rationale": {"type": "string"}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
async def claim_task(self, task_id: str, topic: str, branch: str, files: List[str], rationale: str):
|
|
34
|
+
payload = {
|
|
35
|
+
"task_id": task_id,
|
|
36
|
+
"topic": topic,
|
|
37
|
+
"branch": branch,
|
|
38
|
+
"claimed_files": files,
|
|
39
|
+
"rationale": rationale
|
|
40
|
+
}
|
|
41
|
+
return await self.client.send_message(
|
|
42
|
+
intent=MessageIntent.TASK_CLAIM,
|
|
43
|
+
payload=payload,
|
|
44
|
+
recipient_id="*",
|
|
45
|
+
priority=MessagePriority.HIGH
|
|
46
|
+
)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
OpenAI Swarm and Function Calling Adapter for Swarmly.
|
|
3
|
+
Exports tools conforming to standard OpenAI Chat Completion JSON schemas.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
from typing import Any, Dict, List
|
|
8
|
+
from swarmly.adapters.base import BaseAgentAdapter
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class OpenAISwarmAdapter(BaseAgentAdapter):
|
|
12
|
+
"""Adapter for ChatGPT, OpenAI Swarm, AutoGen, and CrewAI."""
|
|
13
|
+
|
|
14
|
+
def export_tools(self) -> List[Dict[str, Any]]:
|
|
15
|
+
"""Export tool specifications in standard OpenAI function format."""
|
|
16
|
+
return [
|
|
17
|
+
{
|
|
18
|
+
"type": "function",
|
|
19
|
+
"function": {
|
|
20
|
+
"name": "a2a_send_message",
|
|
21
|
+
"description": "Send a verified A2A message to the swarm (Hermes, DeepSeek, Antigravity, MiniMax) via WebSocket or Git.",
|
|
22
|
+
"parameters": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"required": ["intent", "payload"],
|
|
25
|
+
"properties": {
|
|
26
|
+
"intent": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"enum": [
|
|
29
|
+
"TASK_PROPOSE", "TASK_CLAIM", "TASK_APPROVE", "TASK_HANDOFF",
|
|
30
|
+
"PR_REVIEW_REQUEST", "PR_REVIEW_VERDICT", "FALSIFICATION_REPORT",
|
|
31
|
+
"EXPERIMENT_REGISTER", "QUERY_REQUEST", "QUERY_RESPONSE", "ESCALATE_BLOCKER"
|
|
32
|
+
],
|
|
33
|
+
"description": "The operational intent of this message"
|
|
34
|
+
},
|
|
35
|
+
"recipient_id": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"description": "Target agent ID (e.g. 'antigravity', 'hermes', 'deepseek') or '*' for broadcast",
|
|
38
|
+
"default": "*"
|
|
39
|
+
},
|
|
40
|
+
"priority": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"enum": ["ROUTINE", "HIGH", "BLOCKING"],
|
|
43
|
+
"default": "ROUTINE"
|
|
44
|
+
},
|
|
45
|
+
"correlation_id": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"description": "Optional correlation ID linking to a task or PR"
|
|
48
|
+
},
|
|
49
|
+
"payload": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"description": "Structured message payload"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"type": "function",
|
|
59
|
+
"function": {
|
|
60
|
+
"name": "a2a_read_inbox",
|
|
61
|
+
"description": "Read and cryptographically verify all pending incoming messages from the swarm inbox.",
|
|
62
|
+
"parameters": {
|
|
63
|
+
"type": "object",
|
|
64
|
+
"properties": {}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
]
|