Skip to content

Commit 998de26

Browse files
Merge branch 'main' into test
2 parents bfdce95 + 0b0ec0f commit 998de26

83 files changed

Lines changed: 2702 additions & 1251 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.devcontainer/post-attach.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ if [ -v CODESPACES ]; then
77
if [ ! -v AI_API_TOKEN ]; then
88
echo "⚠️ Running in Codespaces - please add AI_API_TOKEN to your Codespaces secrets"
99
fi
10-
if [ ! -v GITHUB_PERSONAL_ACCESS_TOKEN ]; then
11-
echo "⚠️ Running in Codespaces - please add GITHUB_PERSONAL_ACCESS_TOKEN to your Codespaces secrets"
10+
if [ ! -v GH_TOKEN ]; then
11+
echo "⚠️ Running in Codespaces - please add GH_TOKEN to your Codespaces secrets"
1212
fi
1313
fi
1414

.github/workflows/ci.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ jobs:
3535

3636
- name: Run static analysis
3737
run: |
38-
# hatch fmt --check
39-
echo linter errors will be fixed in a separate PR
38+
hatch fmt --linter --check
4039
4140
- name: Run tests
4241
run: hatch test --python ${{ matrix.python-version }} --cover --randomize --parallel --retries 2 --retry-delay 1

.github/workflows/smoketest.yaml

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,12 @@ permissions:
1414
statuses: read # Required for checking if all commit statuses are "success" in order to deploy the PR
1515

1616
jobs:
17-
permission-check:
17+
Linux:
1818
runs-on: ubuntu-latest
1919
environment: smoketest
2020
if: github.event.issue.pull_request # Make sure the comment is on a PR
21-
outputs:
22-
allowed: ${{ steps.branch-deploy.outputs.continue }}
2321
steps:
24-
- name: branch-deploy
22+
- name: Branch Deploy
2523
id: branch-deploy
2624
uses: github/branch-deploy@48285b12b35e47e2dde0c27d2abb33daa846d98b # v11.0.0
2725
with:
@@ -31,35 +29,30 @@ jobs:
3129
stable_branch: "main"
3230
update_branch: "disabled"
3331

34-
run-tests:
35-
runs-on: ubuntu-latest
36-
environment: smoketest
37-
needs: permission-check
38-
if: needs.permission-check.outputs.allowed == 'true'
39-
steps:
4032
- name: Setup Python
33+
if: steps.branch-deploy.outputs.continue == "true"
4134
uses: actions/setup-python@v5
4235
with:
4336
python-version: '3.11'
4437

4538
- name: Checkout the repo
46-
uses: actions/checkout@v5
47-
48-
- name: Checkout the PR
49-
env:
50-
PR_NUMBER: ${{ github.event.issue.number }}
51-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
52-
run: |
53-
gh pr checkout $PR_NUMBER
39+
if: steps.branch-deploy.outputs.continue == "true"
40+
uses: actions/checkout@v6
41+
with:
42+
ref: ${{ steps.branch-deploy.outputs.sha }}
5443

5544
- name: Setup Python venv
45+
if: steps.branch-deploy.outputs.continue == "true"
5646
run: |
5747
python -m venv .venv
5848
source .venv/bin/activate
5949
python -m pip install hatch
6050
6151
- name: Run tests
52+
if: steps.branch-deploy.outputs.continue == "true"
6253
env:
54+
MODEL_TEMP: ${{ vars.SMOKETEST_TEMPERATURE }}
55+
AI_API_ENDPOINT: ${{ vars.SMOKETEST_ENDPOINT }}
6356
AI_API_TOKEN: ${{ secrets.AI_API_TOKEN }}
6457
GITHUB_AUTH_HEADER: "Bearer ${{ secrets.GITHUB_TOKEN }}"
6558

CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# This repository is maintained by:
2-
* @m-y-mo @p- @jarlob @kevinbackhouse @sylwia-budzynska @kwstubbs
2+
* @m-y-mo @p- @jarlob @kevinbackhouse @sylwia-budzynska @kwstubbs @anticomputer

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2025 GitHub
3+
Copyright GitHub, Inc.
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

NOTICE

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
Seclab Taskflow Agent
2-
Copyright 2025 GitHub
2+
Copyright GitHub, Inc.
33

44
This product includes software developed at GitHub (https://github.com) released under the terms
55
of the MIT license (https://github.com/GitHubSecurityLab/seclab-taskflow-agent/blob/main/LICENSE)
6-
Copyright (c) 2025 GitHub
6+
Copyright GitHub, Inc.
77

88
The Initial Developer of some parts of the framework, which are copied from, derived from, or
99
inspired by jsonrpyc (https://github.com/riga/jsonrpyc) under the terms of the BSD 3-clause

README.md

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
Bogus change to test the updated smoketest.yml
22

3-
# Seclab Taskflow Agent
3+
# GitHub Security Lab Taskflow Agent
44

55
The Security Lab Taskflow Agent is an MCP enabled multi-Agent framework.
66

77
The Taskflow Agent is built on top of the [OpenAI Agents SDK](https://openai.github.io/openai-agents-python/).
88

9-
While the Taskflow Agent does not integrate into the GitHub Dotcom Copilot UX, it does operate using the Copilot API (CAPI) as its backend, similar to Copilot IDE extensions.
10-
119
## Core Concepts
1210

1311
The Taskflow Agent leverages a GitHub Workflow-esque YAML based grammar to perform a series of tasks using a set of Agents.
@@ -18,38 +16,48 @@ Agents are defined through [personalities](examples/personalities/), that receiv
1816

1917
Agents can cooperate to complete sequences of tasks through so-called [taskflows](doc/GRAMMAR.md).
2018

21-
You can find a detailed overview of the taskflow grammar [here](taskflows/GRAMMAR.md) and example taskflows [here](examples/taskflows/).
19+
You can find a detailed overview of the taskflow grammar [here](doc/GRAMMAR.md) and example taskflows [here](examples/taskflows/).
2220

2321
## Use Cases and Examples
2422

2523
The Seclab Taskflow Agent framework was primarily designed to fit the iterative feedback loop driven work involved in Agentic security research workflows and vulnerability triage tasks.
2624

2725
Its design philosophy is centered around the belief that a prompt level focus of capturing vulnerability patterns will greatly improve and scale security research results as frontier model capabilities evolve over time.
2826

29-
While the maintainer himself primarily uses this framework as a code auditing tool it also serves as a more generic swiss army knife for exploring Agentic workflows. For example, the GitHub Security Lab also uses this framework for automated code scanning alert triage.
27+
At GitHub Security Lab, we primarily use this framework as a code auditing tool, but it can also serve as a more generic swiss army knife for exploring Agentic workflows. For example, we also use this framework for automated code scanning alert triage.
3028

3129
The framework includes a [CodeQL](https://codeql.github.com/) MCP server that can be used for Agentic code review, see the [CVE-2023-2283](examples/taskflows/CVE-2023-2283.yaml) taskflow for an example of how to have an Agent review C code using a CodeQL database ([demo video](https://www.youtube.com/watch?v=eRSPSVW8RMo)).
3230

3331
Instead of generating CodeQL queries itself, the CodeQL MCP Server is used to provide CodeQL-query based MCP tools that allow an Agent to navigate and explore code. It leverages templated CodeQL queries to provide targeted context for model driven code analysis.
3432

3533
## Requirements
3634

37-
Python >= 3.9 or Docker
35+
Python >= 3.10 or Docker
3836

3937
## Configuration
4038

41-
Provide a GitHub token for an account that is entitled to use [GitHub Models](https://models.github.ai) via the `AI_API_TOKEN` environment variable. Further configuration is use case dependent, i.e. pending which MCP servers you'd like to use in your taskflows.
39+
Provide a GitHub token for an account that is entitled to use [GitHub Models](https://models.github.ai) via the `AI_API_TOKEN` environment variable. Further configuration is use case dependent, i.e. pending which MCP servers you'd like to use in your taskflows. In a terminal, you can add `AI_API_TOKEN` to the environment like this:
40+
41+
```sh
42+
export AI_API_TOKEN=<your_github_token>
43+
```
44+
45+
Or, if you are using GitHub Codespaces, then you can [add a Codespace secret](https://github.com/settings/codespaces/secrets/new) so that `AI_API_TOKEN` is automatically available when working in a Codespace.
4246

43-
You can set persisting environment variables via an `.env` file in the project root.
47+
Many of the MCP servers in the [seclab-taskflow](https://github.com/GitHubSecurityLab/seclab-taskflows) repo also need an environment variable named `GH_TOKEN` for accessing the GitHub API. You can use two separate PATs if you want, or you can use one PAT for both purposes, like this:
48+
49+
```sh
50+
export GH_TOKEN=$AI_API_TOKEN
51+
```
52+
53+
We do not recommend storing secrets on disk, but you can persist non-sensitive environment variables by adding a `.env` file in the project root.
4454

4555
Example:
4656

4757
```sh
48-
# Tokens
49-
AI_API_TOKEN=<your_github_token>
5058
# MCP configs
51-
GITHUB_PERSONAL_ACCESS_TOKEN=<your_github_token>
5259
CODEQL_DBS_BASE_PATH="/app/my_data/codeql_databases"
60+
AI_API_ENDPOINT="https://models.github.ai/inference"
5361
```
5462

5563
## Deploying from Source
@@ -168,15 +176,15 @@ Every YAML files used by the Seclab Taskflow Agent must include a header like th
168176

169177
```yaml
170178
seclab-taskflow-agent:
171-
version: 1
179+
version: "1.0"
172180
filetype: taskflow
173181
```
174182
175-
The `version` number in the header should always be 1. It means that the
183+
The `version` number in the header is currently 1. It means that the
176184
file uses version 1 of the seclab-taskflow-agent syntax. If we ever need
177185
to make a major change to the syntax, then we'll update the version number.
178186
This will hopefully enable us to make changes without breaking backwards
179-
compatibility.
187+
compatibility. Version can be specified as an integer, float, or string.
180188

181189
The `filetype` determines whether the file defines a personality, toolbox, etc.
182190
This means that different types of files can be stored in the same directory.
@@ -288,10 +296,10 @@ server_params:
288296
url: https://api.githubcopilot.com/mcp/
289297
#See https://github.com/github/github-mcp-server/blob/main/docs/remote-server.md
290298
headers:
291-
Authorization: "{{ env GITHUB_AUTH_HEADER }}"
299+
Authorization: "{{ env('GITHUB_AUTH_HEADER') }}"
292300
optional_headers:
293-
X-MCP-Toolsets: "{{ env GITHUB_MCP_TOOLSETS }}"
294-
X-MCP-Readonly: "{{ env GITHUB_MCP_READONLY }}"
301+
X-MCP-Toolsets: "{{ env('GITHUB_MCP_TOOLSETS') }}"
302+
X-MCP-Readonly: "{{ env('GITHUB_MCP_READONLY') }}"
295303
```
296304

297305
You can force certain tools within a `toolbox` to require user confirmation to run. This can be helpful if a tool may perform irreversible actions and should require user approval prior to its use. This is done by including the name of the tool (function) in the MCP server in the `confirm` section:
@@ -320,7 +328,7 @@ seclab-taskflow-agent:
320328
321329
taskflow:
322330
- task:
323-
# taskflows can optionally choose any of the support CAPI models for a task
331+
# taskflows can optionally choose any of the models supported by your API for a task
324332
model: gpt-4.1
325333
# taskflows can optionally limit the max allowed number of Agent task loop
326334
# iterations to complete a task, this defaults to 50 when not provided
@@ -339,7 +347,7 @@ taskflow:
339347
Finally, why are apples and oranges healthy to eat?
340348

341349
# taskflows can set temporary environment variables, these support the general
342-
# "{{ env FROM_EXISTING_ENVIRONMENT }" pattern we use elsewhere as well
350+
# "{{ env('FROM_EXISTING_ENVIRONMENT') }}" pattern we use elsewhere as well
343351
# these environment variables can then be made available to any stdio mcp server
344352
# through its respective yaml configuration, see memcache.yaml for an example
345353
# you can use these to override top-level environment variables on a per-task basis
@@ -386,7 +394,7 @@ taskflow:
386394
387395
Taskflows support [Agent handoffs](https://openai.github.io/openai-agents-python/handoffs/). Handoffs are useful for implementing triage patterns where the primary Agent can decide to handoff a task to any subsequent Agents in the `Agents` list.
388396

389-
See the [taskflow examples](taskflows/examples) for other useful Taskflow patterns such as repeatable and asynchronous templated prompts.
397+
See the [taskflow examples](examples/taskflows) for other useful Taskflow patterns such as repeatable and asynchronous templated prompts.
390398

391399

392400
You can run a taskflow from the command line like this:
@@ -486,12 +494,12 @@ Files of types `taskflow` and `toolbox` allow environment variables to be passed
486494
server_params:
487495
...
488496
env:
489-
CODEQL_DBS_BASE_PATH: "{{ env CODEQL_DBS_BASE_PATH }}"
497+
CODEQL_DBS_BASE_PATH: "{{ env('CODEQL_DBS_BASE_PATH') }}"
490498
# prevent git repo operations on gh codeql executions
491499
GH_NO_UPDATE_NOTIFIER: "disable"
492500
```
493501

494-
For `toolbox`, `env` can be used inside `server_params`. A template of the form `{{ env ENV_VARIABLE_NAME }}` can be used to pass values of the environment variable from the current process to the MCP server. So in the above, the MCP server is run with `GH_NO_UPDATE_NOTIFIER=disable` and passes the value of `CODEQL_DBS_BASE_PATH` from the current process to the MCP server. The templated paramater `{{ env CODEQL_DBS_BASE_PATH }}` is replaced by the value of the environment variable `CODEQL_DBS_BASE_PATH` in the current process.
502+
For `toolbox`, `env` can be used inside `server_params`. A template of the form `{{ env('ENV_VARIABLE_NAME') }}` can be used to pass values of the environment variable from the current process to the MCP server. So in the above, the MCP server is run with `GH_NO_UPDATE_NOTIFIER=disable` and passes the value of `CODEQL_DBS_BASE_PATH` from the current process to the MCP server. The templated parameter `{{ env('CODEQL_DBS_BASE_PATH') }}` is replaced by the value of the environment variable `CODEQL_DBS_BASE_PATH` in the current process.
495503

496504
Similarly, environment variables can be passed to a `task` in a `taskflow`:
497505

@@ -508,9 +516,9 @@ taskflow:
508516
MEMCACHE_BACKEND: "dictionary_file"
509517
```
510518
511-
This overwrites the environment variables `MEMCACHE_STATE_DIR` and `MEMCACHE_BACKEND` for the task only. A template `{{ env ENV_VARIABLE_NAME }}` can also be used.
519+
This overwrites the environment variables `MEMCACHE_STATE_DIR` and `MEMCACHE_BACKEND` for the task only. A template `{{ env('ENV_VARIABLE_NAME') }}` can also be used.
512520

513-
Note that when using the template `{{ env ENV_VARIABLE_NAME }}`, `ENV_VARIABLE_NAME` must be the name of an environment variable in the current process.
521+
Note that when using the template `{{ env('ENV_VARIABLE_NAME') }}`, `ENV_VARIABLE_NAME` must be the name of an environment variable in the current process.
514522

515523
## Import paths
516524

doc/GRAMMAR.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,10 @@ Often we may want to iterate through the same tasks with different inputs. For e
133133
agents:
134134
- seclab_taskflow_agent.personalities.c_auditer
135135
user_prompt: |
136-
The function has name {{ RESULT_name }} and body {{ RESULT_body }} analyze the function.
136+
The function has name {{ result.name }} and body {{ result.body }} analyze the function.
137137
```
138138

139-
In the above, the first task fetches functions in the code base and creates a json list object, with each entry having a `name` and `body` field. In the next task, `repeat_prompt` is set to true, meaning that a task is created for each individual object in the list and the object fields are referenced in the templated prompt using `{{ RESULT_<fieldname> }}`. In other words, `{{ RESULT_name }}` in the prompt is replaced with the value of the `name` field of the object etc. For example, if the list of functions fetched from the first task is:
139+
In the above, the first task fetches functions in the code base and creates a json list object, with each entry having a `name` and `body` field. In the next task, `repeat_prompt` is set to true, meaning that a task is created for each individual object in the list and the object fields are referenced in the templated prompt using `{{ result.fieldname }}`. In other words, `{{ result.name }}` in the prompt is replaced with the value of the `name` field of the object etc. For example, if the list of functions fetched from the first task is:
140140

141141
```javascript
142142
[{'name' : foo, 'body' : foo(){return 1;}}, {'name' : bar, 'body' : bar(a) {return a + 1;}}]
@@ -152,7 +152,7 @@ etc.
152152

153153
Note that when using `repeat_prompt`, the last tool call result of the previous task is used as the iterable. It is recommended to keep the task that creates the iterable short and simple (e.g. just make one tool call to fetch a list of results) to avoid wrong results being passed to the repeat prompt.
154154

155-
The iterable can also contain a list of primitives like string or number, in which case, the template `{{ RESULT }}` can be used in the `repeat_prompt` prompt to parse the results instead:
155+
The iterable can also contain a list of primitives like string or number, in which case, the template `{{ result }}` can be used in the `repeat_prompt` prompt to parse the results instead:
156156

157157
```yaml
158158
- task:
@@ -173,7 +173,7 @@ The iterable can also contain a list of primitives like string or number, in whi
173173
agents:
174174
- seclab_taskflow_agent.personalities.assistant
175175
user_prompt: |
176-
What is the integer value of {{ RESULT }}?
176+
What is the integer value of {{ result }}?
177177
```
178178
179179
Repeat prompt can be run in parallel by setting the `async` field to `true`:
@@ -185,7 +185,7 @@ Repeat prompt can be run in parallel by setting the `async` field to `true`:
185185
agents:
186186
- seclab_taskflow_agent.personalities.c_auditer
187187
user_prompt: |
188-
The function has name {{ RESULT_name }} and body {{ RESULT_body }} analyze the function.
188+
The function has name {{ result.name }} and body {{ result.body }} analyze the function.
189189
```
190190

191191
An optional limit can be set to limit the number of asynchronous tasks via `async_limit`. If not set, the default value (5) is used.
@@ -198,7 +198,7 @@ An optional limit can be set to limit the number of asynchronous tasks via `asyn
198198
agents:
199199
- seclab_taskflow_agent.personalities.c_auditer
200200
user_prompt: |
201-
The function has name {{ RESULT_name }} and body {{ RESULT_body }} analyze the function.
201+
The function has name {{ result.name }} and body {{ result.body }} analyze the function.
202202
```
203203

204204
Both `async` and `async_limit` have no effect when used outside of a `repeat_prompt`.
@@ -211,7 +211,7 @@ At the moment, we do not support nested `repeat_prompt`. So the following is not
211211
agents:
212212
- seclab_taskflow_agent.personalities.c_auditer
213213
user_prompt: |
214-
The function has name {{ RESULT_name }} and body {{ RESULT_body }} analyze the function.
214+
The function has name {{ result.name }} and body {{ result.body }} analyze the function.
215215
- task:
216216
repeat_prompt: true
217217
...
@@ -233,7 +233,7 @@ For example:
233233
agents:
234234
- seclab_taskflow_agent.personalities.assistant
235235
user_prompt: |
236-
What kind of fruit is {{ RESULT }}?
236+
What kind of fruit is {{ result }}?
237237
```
238238

239239
The string `["apple", "banana", "orange"]` is then passed directly to the next task.
@@ -349,7 +349,7 @@ taskflow:
349349
agents:
350350
- examples.personalities.fruit_expert
351351
user_prompt: |
352-
Tell me more about {{ GLOBALS_fruit }}.
352+
Tell me more about {{ globals.fruit }}.
353353
```
354354
355355
Global variables can also be set or overridden from the command line using the `-g` or `--global` flag:
@@ -422,10 +422,10 @@ A reusable taskflow can also have a templated prompt that takes inputs from its
422422
agents:
423423
- examples.personalities.fruit_expert
424424
user_prompt: |
425-
Tell me more about {{ INPUTS_fruit }}.
425+
Tell me more about {{ inputs.fruit }}.
426426
```
427427

428-
In this case, the template parameter `{{ INPUTS_fruit }}` is replaced by the value of `fruit` from the `inputs` of the user, which is apples in this case:
428+
In this case, the template parameter `{{ inputs.fruit }}` is replaced by the value of `fruit` from the `inputs` of the user, which is apples in this case:
429429

430430
```yaml
431431
- task:
@@ -437,9 +437,9 @@ In this case, the template parameter `{{ INPUTS_fruit }}` is replaced by the val
437437

438438
### Reusable Prompts
439439

440-
Reusable prompts are defined in files of `filetype` `prompts`. These are like macros that get replaced when a templated parameter of the form `{{ PROMPTS_<import-path> }}` is encountered.
440+
Reusable prompts are defined in files of `filetype` `prompts`. These are like macros that get included using Jinja2's `{% include %}` directive.
441441

442-
Tasks can incorporate templated prompts which are then replaced by the actual prompt. For example:
442+
Tasks can incorporate reusable prompts using the include directive. For example:
443443

444444
Example:
445445

@@ -449,8 +449,8 @@ Example:
449449
- examples.personalities.fruit_expert
450450
user_prompt: |
451451
Tell me more about apples.
452-
453-
{{ PROMPTS_examples.prompts.example_prompt }}
452+
453+
{% include 'examples.prompts.example_prompt' %}
454454
```
455455
and `examples.prompts.example_prompt` is the following:
456456

0 commit comments

Comments
 (0)