Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added __init__.py
Empty file.
4 changes: 4 additions & 0 deletions __main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import runpy

if __name__ == "__main__":
runpy.run_module("seclab_taskflow_agent.main", run_name="__main__")
Copy link

Copilot AI Oct 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The module name 'seclab_taskflow_agent.main' is hardcoded and doesn't match the actual package structure. This should use a relative import or derive the module name dynamically to avoid breaking if the package is renamed.

Suggested change
runpy.run_module("seclab_taskflow_agent.main", run_name="__main__")
module_name = (__package__ + ".main") if __package__ else "main"
runpy.run_module(module_name, run_name="__main__")

Copilot uses AI. Check for mistakes.
2 changes: 1 addition & 1 deletion agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from agents.run import RunHooks
from agents import Agent, Runner, AgentHooks, RunHooks, result, function_tool, Tool, RunContextWrapper, TContext, OpenAIChatCompletionsModel, set_default_openai_client, set_default_openai_api, set_tracing_disabled

from capi import COPILOT_INTEGRATION_ID, COPILOT_API_ENDPOINT
from .capi import COPILOT_INTEGRATION_ID, COPILOT_API_ENDPOINT

# grab our secrets from .env, this must be in .gitignore
load_dotenv()
Expand Down
26 changes: 16 additions & 10 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import uuid
import pathlib

from agent import DEFAULT_MODEL, TaskRunHooks, TaskAgentHooks
from .agent import DEFAULT_MODEL, TaskRunHooks, TaskAgentHooks
#from agents.run import DEFAULT_MAX_TURNS # XXX: this is 10, we need more than that
from agents.exceptions import MaxTurnsExceeded, AgentsException
from agents.agent import ModelSettings
Expand All @@ -23,21 +23,27 @@
from openai.types.responses import ResponseTextDeltaEvent
from typing import Any

from shell_utils import shell_tool_call
from mcp_utils import DEFAULT_MCP_CLIENT_SESSION_TIMEOUT, ReconnectingMCPServerStdio, AsyncDebugMCPServerStdio, MCPNamespaceWrap, mcp_client_params, mcp_system_prompt, StreamableMCPThread, compress_name
from render_utils import render_model_output, flush_async_output
from env_utils import TmpEnv
from yaml_parser import YamlParser
from agent import TaskAgent
from capi import list_tool_call_models
from available_tools import AvailableTools
from .shell_utils import shell_tool_call
from .mcp_utils import DEFAULT_MCP_CLIENT_SESSION_TIMEOUT, ReconnectingMCPServerStdio, AsyncDebugMCPServerStdio, MCPNamespaceWrap, mcp_client_params, mcp_system_prompt, StreamableMCPThread, compress_name
from .render_utils import render_model_output, flush_async_output
from .env_utils import TmpEnv
from .yaml_parser import YamlParser
from .agent import TaskAgent
from .capi import list_tool_call_models
from .available_tools import AvailableTools

load_dotenv()

# only model output or help message should go to stdout, everything else goes to log
logging.getLogger('').setLevel(logging.NOTSET)

# Create logs directory if it doesn't exist
# This is needed when running the code as a package from any directory
log_dir = os.path.join(os.getcwd(), 'logs')
os.makedirs(log_dir, exist_ok=True)

log_file_handler = RotatingFileHandler(
'logs/task_agent.log',
os.path.join(log_dir, 'task_agent.log'),
maxBytes=1024*1024*10,
backupCount=10)
log_file_handler.setLevel(os.getenv('TASK_AGENT_LOGLEVEL', default='DEBUG'))
Expand Down
2 changes: 1 addition & 1 deletion mcp_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from mcp.types import CallToolResult, TextContent
from agents.mcp import MCPServerStdio

from env_utils import swap_env
from .env_utils import swap_env

DEFAULT_MCP_CLIENT_SESSION_TIMEOUT = 120

Expand Down
30 changes: 30 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[build-system]
requires = ["hatchling >= 1.26", "hatch-requirements-txt"]
build-backend = "hatchling.build"

[project]
name = "seclab-taskflow-agent"
version = "0.0.1"
dynamic = ["dependencies"]
authors = [
{ name="GitHub Security Lab", email="[email protected]" },
]
description = "A taskflow agent for the SecLab project, enabling secure and automated workflow execution."
readme = "seclab_taskflow_agent/README.md"
requires-python = ">=3.9"
classifiers = [
"Programming Language :: Python :: 3",
"Operating System :: OS Independent",
]
license = "MIT"
license-files = ["seclab_taskflow_agent/LICEN[CS]E*"]

[tool.hatch.metadata.hooks.requirements_txt]
files = ["seclab_taskflow_agent/requirements.txt"]

[tool.hatch.build.targets.wheel]
packages = ["seclab_taskflow_agent"]

[project.urls]
Homepage = "https://github.com/GitHubSecurityLab/seclab-taskflow-agent"
Issues = "https://github.com/GitHubSecurityLab/seclab-taskflow-agent/issues"
65 changes: 65 additions & 0 deletions release_tools/HOWTO.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,68 @@ server_params:
command: docker
args: ["run", "--entrypoint", "python" "-i", "--rm", "ghcr.io/githubsecuritylab/seclab-taskflow-agent", "toolboxes/mcp_servers/echo/echo.py"]
```

# How to test and release new PyPI package

See [the packaging tutorial](https://packaging.python.org/en/latest/tutorials/packaging-projects/#namespace-packages).

We need all the code to be in a separate directory to build it into a package, so we create a new dir and copy what is need for the build.

For packages, only underscores are allowed as legal python identifiers and not dashes, so we need to rename the folder.
```bash
mkdir taskflow-package
cd taskflow-package
git clone https://github.com/GitHubSecurityLab/seclab-taskflow-agent.git
mv seclab-taskflow-agent seclab_taskflow_agent
```

Build instructions are in pyproject.toml, and we also need .gitignore to ignore the `venv`.
```bash
cp seclab_taskflow_agent/pyproject.toml pyproject.toml
cp seclab_taskflow_agent/.gitignore .gitignore

# Create a new python virtual env, activate it, and install the tools for the build.
python -m venv venv
source venv/bin/activate

pip install --upgrade build
pip install hatch-requirements-txt

python -m build
pip install --upgrade twine

```

This will create a `dist` directory with a tar.gz source distribution and whl built distribution.

You can test if the package works without uploading it to PyPI by installing it with the whl. Use ` --force-reinstall` if you made a new version of the package. We use `pydantic_core` in the deps, which doesn't seem to work on some versions of macos due to Rust bindings.
```bash
pip install dist/seclab_taskflow_agent-0.0.1-py3-none-any.whl
```

Create an .env file with `COPILOT_TOKEN`, and run the package with:
```bash
python -m seclab_taskflow_agent -p assistant 'how do modems work'
```

To upload it to TestPyPI (you'll need [an account on testpypi and an API token](https://packaging.python.org/en/latest/tutorials/packaging-projects/#uploading-the-distribution-archives)). Note if you then try to download the package from TestPyPI and run it, it won't work, because TestPyPi does not have the dependencies that are required for seclab-taskflow-agent. New packages on TestPyPI are regularly cleared. Test it instead using the wheel, or by using PyPI.
```bash
python -m twine upload --repository testpypi dist/*
```

To upload it on PyPI (you'll need [an account on PyPI and an API token](https://packaging.python.org/en/latest/tutorials/packaging-projects/#uploading-the-distribution-archives)). Note you need to update pyproject.toml to a new (higher) version.
```bash
python -m twine upload dist/*
```

Create a fresh venv, and download the package:
```bash
python -m venv .venv
source .venv/bin/activate
pip install seclab-taskflow-agent
```

Create an .env file with `COPILOT_TOKEN`, and run the package with:
```bash
python -m seclab_taskflow_agent -p assistant 'how do modems work'
```