@openhands/extensions 0.29.0 → 0.30.0
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.
- package/.agents/skills/custom-codereview-guide.md +44 -1
- package/.claude-plugin/marketplace.json +7 -5
- package/.codex-plugin/marketplace.json +7 -5
- package/.github/workflows/build-vscode-app.yml +3 -3
- package/.github/workflows/check-extensions.yml +1 -1
- package/.github/workflows/tests.yml +1 -1
- package/.plugin/marketplace.json +7 -5
- package/.release-please-manifest.json +1 -1
- package/README.md +3 -3
- package/apps/vscode/README.md +8 -6
- package/apps/vscode/canvas-extension.json +11 -3
- package/apps/vscode/extension.js +60 -12
- package/apps/vscode/scripts/package.mjs +20 -74
- package/automations/bundle-index.js +9 -7
- package/automations/catalog/github-issue-to-pr/manifest.json +1 -1
- package/automations/catalog/github-issue-triage/manifest.json +4 -3
- package/automations/catalog/github-pr-reviewer/manifest.json +5 -4
- package/automations/catalog/gitlab-issue-to-mr/manifest.json +3 -3
- package/automations/catalog/jira-issue-to-bitbucket-pr/manifest.json +4 -4
- package/automations/catalog/jira-issue-to-gitlab-mr/manifest.json +5 -5
- package/automations/catalog/jira-issue-to-pr/manifest.json +1 -1
- package/integrations/catalog/atlassian-rovo.json +4 -3
- package/integrations/catalog/bitbucket.json +16 -1
- package/integrations/catalog/github.json +1 -0
- package/integrations/catalog/gitlab.json +1 -1
- package/marketplaces/openhands-extensions.json +7 -5
- package/package.json +1 -1
- package/plugins/pr-review/README.md +1 -0
- package/plugins/pr-review/scripts/prompt.py +44 -11
- package/plugins/runtime-api-configs/SKILL.md +1 -1
- package/plugins/vulnerability-remediation/skills/github/scripts/agent_conversation.py +224 -8
- package/pyproject.toml +1 -1
- package/python/openhands_extensions/_version.py +1 -1
- package/skills/canvas-extension-api/references/backend-safety.md +3 -3
- package/skills/canvas-extension-api/references/v1-contract.md +7 -4
- package/skills/canvas-extension-api/scripts/validate-extension.mjs +17 -1
- package/skills/canvas-extension-api/scripts/validate-extension.test.mjs +49 -0
- package/skills/github/scripts/agent_conversation.py +224 -8
- package/skills/github-issue-to-pr/scripts/agent_conversation.py +224 -8
- package/skills/github-issue-triage/README.md +73 -14
- package/skills/github-issue-triage/SKILL.md +26 -46
- package/skills/github-issue-triage/scripts/agent_conversation.py +224 -8
- package/skills/github-issue-triage/scripts/triage_publication.py +293 -0
- package/skills/github-issue-triage/scripts/worker.py +153 -103
- package/skills/github-pr-reviewer/README.md +103 -1
- package/skills/github-pr-reviewer/SKILL.md +35 -8
- package/skills/github-pr-reviewer/references/state-schema.md +8 -0
- package/skills/github-pr-reviewer/scripts/agent_conversation.py +224 -8
- package/skills/github-pr-reviewer/scripts/main.py +163 -39
- package/skills/github-pr-reviewer/scripts/publication.py +193 -0
- package/skills/github-pr-reviewer/scripts/worker.py +297 -8
- package/skills/github-pr-reviewer/tests/test_main.py +73 -5
- package/skills/gitlab-issue-to-mr/README.md +10 -3
- package/skills/gitlab-issue-to-mr/SKILL.md +43 -9
- package/skills/gitlab-issue-to-mr/references/state-schema.md +4 -0
- package/skills/gitlab-issue-to-mr/scripts/main.py +359 -25
- package/skills/index.js +8 -8
- package/skills/jira-issue-to-pr/.claude-plugin/plugin.json +4 -1
- package/skills/jira-issue-to-pr/.codex-plugin/plugin.json +4 -1
- package/skills/jira-issue-to-pr/.plugin/plugin.json +4 -1
- package/skills/jira-issue-to-pr/README.md +18 -11
- package/skills/jira-issue-to-pr/SKILL.md +72 -23
- package/skills/jira-issue-to-pr/references/setup.md +43 -12
- package/skills/jira-issue-to-pr/scripts/main.py +136 -26
- package/skills/slack-channel-monitor/README.md +6 -1
- package/skills/slack-channel-monitor/SKILL.md +6 -3
- package/skills/slack-channel-monitor/scripts/main.py +36 -4
- package/skills/ticket-to-code-change/.claude-plugin/plugin.json +1 -2
- package/skills/ticket-to-code-change/.codex-plugin/plugin.json +1 -2
- package/skills/ticket-to-code-change/.plugin/plugin.json +1 -2
- package/skills/ticket-to-code-change/SKILL.md +5 -3
- package/skills/ticket-to-code-change/commands/ticket-to-code-change-setup.md +1 -1
- package/tests/fixtures/automations/github-pr-reviewer.json +9 -9
- package/tests/test_agent_conversation_dispatch.py +173 -6
- package/tests/test_automation_llm_provenance.py +1209 -0
- package/tests/test_github_review_publication.py +422 -0
- package/tests/test_github_reviewer_delivery.py +174 -21
- package/tests/test_github_triage_delivery.py +411 -238
- package/tests/test_gitlab_issue_to_mr.py +284 -0
- package/tests/test_jira_issue_to_pr.py +200 -6
- package/tests/test_pr_review_prompt_injection.py +353 -0
- package/tests/test_slack_channel_monitor.py +32 -1
- package/tests/test_vscode_app.py +8 -2
- package/tests/vscode-app.test.mjs +8 -2
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
# Jira Issue to GitHub
|
|
1
|
+
# Jira Issue to Pull Request (GitHub, GitLab, Bitbucket)
|
|
2
2
|
|
|
3
3
|
Deploy a cron-based OpenHands automation that polls a Jira Cloud project for
|
|
4
4
|
open issues carrying a configurable label (default: `create-pr`) and, for each
|
|
5
|
-
new issue, starts an OpenHands agent conversation that clones the
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
new issue, starts an OpenHands agent conversation that clones the repository
|
|
6
|
+
named in the ticket body on the configured git provider (GitHub by default,
|
|
7
|
+
GitLab or Bitbucket with `git_provider`), creates a branch, implements the
|
|
8
|
+
requested change, and opens a pull request (a merge request on GitLab). Once the
|
|
9
|
+
conversation starts, it posts a
|
|
8
10
|
comment on the Jira ticket: "I'm on it: `<conversation URL>`".
|
|
9
11
|
|
|
10
12
|
## Triggers
|
|
@@ -16,12 +18,16 @@ This skill is activated by keywords:
|
|
|
16
18
|
- `automatically create GitHub PRs from Jira tickets`
|
|
17
19
|
- `deploy a Jira issue-to-PR automation`
|
|
18
20
|
- `create a Jira to GitHub PR workflow`
|
|
21
|
+
- `set up the Jira issue to GitLab MR automation`
|
|
22
|
+
- `set up the Jira issue to Bitbucket PR automation`
|
|
19
23
|
- `automate GitHub PR creation from a Jira label`
|
|
20
24
|
|
|
21
25
|
## How It Works
|
|
22
26
|
|
|
23
|
-
1. **Poll** - every N minutes, the script
|
|
24
|
-
|
|
27
|
+
1. **Poll** - every N minutes, the script searches the Jira Cloud instance for
|
|
28
|
+
open issues with the configured label: through the user's connected
|
|
29
|
+
Atlassian Rovo MCP server on OpenHands Cloud and Enterprise, or with
|
|
30
|
+
`POST /rest/api/3/search/jql` and an API token.
|
|
25
31
|
2. **Deduplicate** - on the first run the script records a `first_run_at`
|
|
26
32
|
baseline timestamp in the KV store; issues whose `updated` timestamp
|
|
27
33
|
predates that baseline are skipped (no backfill blast on first deploy).
|
|
@@ -32,7 +38,8 @@ This skill is activated by keywords:
|
|
|
32
38
|
conversation with a PR-creation prompt: `POST /api/conversations` on the
|
|
33
39
|
agent server when running locally, `POST /api/v1/app-conversations` on
|
|
34
40
|
OpenHands Cloud and Enterprise. The prompt instructs the agent to extract the
|
|
35
|
-
target
|
|
41
|
+
target repository (`owner/repo` on GitHub; `group/project` or a URL on
|
|
42
|
+
GitLab; `workspace/repo` on Bitbucket) from the ticket body. On Cloud the
|
|
36
43
|
next poll confirms the conversation came up and starts it again when it did
|
|
37
44
|
not.
|
|
38
45
|
4. **Comment** - immediately after the conversation is created, the script
|
|
@@ -52,13 +59,13 @@ OpenHands and Jira. See "Event-Based Alternative" in `SKILL.md`.
|
|
|
52
59
|
|
|
53
60
|
| Requirement | Details |
|
|
54
61
|
|---|---|
|
|
55
|
-
| **Jira API token
|
|
56
|
-
| **
|
|
62
|
+
| **Jira access** | OpenHands Cloud and Enterprise: the user's connected Atlassian Rovo MCP server, with no API token. Local, or without that connection: a Jira API token stored as an OpenHands secret (see `references/setup.md`) |
|
|
63
|
+
| **Git provider access** | OpenHands Cloud and Enterprise: the connected GitHub, GitLab or Bitbucket integration named by `git_provider`, or a connected MCP server (GitHub's, GitLab's, or Atlassian Rovo for Bitbucket). Local: that provider's token stored as an OpenHands secret so the spawned conversation can push branches and open the request |
|
|
57
64
|
| **Jira label** | The label to watch for (default: `create-pr`) must exist in the Jira project |
|
|
58
|
-
| **
|
|
65
|
+
| **Target repository** | Must exist on the configured provider; the connected integration or token must have write access |
|
|
59
66
|
|
|
60
67
|
## Quick Start
|
|
61
68
|
|
|
62
69
|
See `SKILL.md` for the full step-by-step deployment guide, and
|
|
63
|
-
`references/setup.md` for Jira API token creation,
|
|
70
|
+
`references/setup.md` for Jira API token creation, git provider tokens, cron
|
|
64
71
|
schedule reference, and troubleshooting.
|
|
@@ -4,25 +4,31 @@ description: >
|
|
|
4
4
|
This skill should be used when the user asks to "set up a Jira automation to create pull requests",
|
|
5
5
|
"poll Jira for create-pr issues", "automatically create GitHub PRs from Jira tickets",
|
|
6
6
|
"deploy a Jira issue-to-PR automation", "create a Jira to GitHub PR workflow",
|
|
7
|
-
|
|
7
|
+
"set up the Jira issue to GitLab MR automation", "set up the Jira issue to Bitbucket PR automation",
|
|
8
|
+
"open GitLab merge requests from Jira tickets", "open Bitbucket pull requests from Jira tickets",
|
|
9
|
+
or mentions automating pull request or merge request creation from a Jira label.
|
|
8
10
|
Deploys a cron-based OpenHands automation that watches a Jira Cloud project for issues
|
|
9
11
|
labeled with a configurable label (default: "create-pr") and spawns an agent conversation
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
that opens a pull request on GitHub (the default), a merge request on GitLab, or a pull
|
|
13
|
+
request on Bitbucket for each new issue found, chosen with git_provider. The target
|
|
14
|
+
repository is read from the body of the Jira ticket - no repo parameter is required at deploy time.
|
|
12
15
|
---
|
|
13
16
|
|
|
14
|
-
# Jira → GitHub
|
|
17
|
+
# Jira → Pull Request Automation (GitHub, GitLab, Bitbucket)
|
|
15
18
|
|
|
16
19
|
Deploys a cron automation that polls a Jira Cloud instance for open issues carrying a
|
|
17
20
|
configurable label and, for each new issue, starts an OpenHands agent conversation that
|
|
18
|
-
clones the
|
|
19
|
-
or
|
|
21
|
+
clones the repository specified in the ticket body on the configured git provider (GitHub
|
|
22
|
+
by default, GitLab or Bitbucket with `git_provider`), creates a branch, implements or
|
|
23
|
+
placeholders the requested change, and opens a pull request (a merge request on GitLab).
|
|
24
|
+
Once the conversation
|
|
20
25
|
starts, it also posts a comment on the Jira ticket: "I'm on it: <conversation URL>".
|
|
21
26
|
|
|
22
27
|
## How It Works
|
|
23
28
|
|
|
24
|
-
1. **Poll** - every N minutes,
|
|
25
|
-
|
|
29
|
+
1. **Poll** - every N minutes, search the Jira Cloud instance for open issues with the
|
|
30
|
+
configured label: through the user's connected Atlassian Rovo MCP server on OpenHands
|
|
31
|
+
Cloud and Enterprise, or with `POST /rest/api/3/search/jql` and an API token.
|
|
26
32
|
2. **Deduplicate** - on the very first run the script records a `first_run_at` baseline
|
|
27
33
|
timestamp in the KV store; any issue whose `updated` timestamp predates that baseline
|
|
28
34
|
is skipped (no backfill blast on first deploy). Using `updated` rather than `created`
|
|
@@ -33,7 +39,8 @@ starts, it also posts a comment on the Jira ticket: "I'm on it: <conversation
|
|
|
33
39
|
3. **Dispatch** - for each new issue, start an independent agent conversation with a
|
|
34
40
|
PR-creation prompt: `POST /api/conversations` on the agent server when running locally,
|
|
35
41
|
or `POST /api/v1/app-conversations` on OpenHands Cloud. The prompt instructs the agent
|
|
36
|
-
to extract the target
|
|
42
|
+
to extract the target repository (`owner/repo` on GitHub; `group/project` or a URL on GitLab;
|
|
43
|
+
`workspace/repo` on Bitbucket) from the ticket body.
|
|
37
44
|
4. **Comment** - immediately after the conversation is created, post a Jira comment on the
|
|
38
45
|
issue: `I'm on it: <conversation URL>`.
|
|
39
46
|
5. **Persist** - record the processed issue key so re-runs never duplicate work.
|
|
@@ -47,11 +54,11 @@ Before deploying, ensure the following are in place:
|
|
|
47
54
|
|
|
48
55
|
| Requirement | Details |
|
|
49
56
|
|---|---|
|
|
50
|
-
| **Jira API token
|
|
51
|
-
| **
|
|
57
|
+
| **Jira access** | OpenHands Cloud and Enterprise: the user's connected Atlassian Rovo MCP server, with no API token. Local, or without that connection: a Jira API token stored as an OpenHands secret (see [Jira API token setup](#jira-api-token)) |
|
|
58
|
+
| **Git provider access** | OpenHands Cloud and Enterprise: the user's connected GitHub, GitLab or Bitbucket integration, whichever `git_provider` names. A connected MCP server also works - GitHub's (with the saved access token), GitLab's, or Atlassian Rovo for Bitbucket - with a private repository read through the tools rather than cloned. Local: a token for that provider stored as an OpenHands secret (`GITHUB_TOKEN` with `repo` + `workflow`, `GITLAB_TOKEN` with `api`, `BITBUCKET_TOKEN`) so the spawned conversation can push branches and open the request |
|
|
52
59
|
| **KV store** (OpenHands Cloud only) | The automation service must have its KV store enabled (`kvStore` in `GET /api/automation/v1/capabilities`); each cloud run starts in a fresh sandbox, so processed issues are remembered there |
|
|
53
60
|
| **Jira label** | The label to watch for (default: `create-pr`) must exist in the Jira project |
|
|
54
|
-
| **
|
|
61
|
+
| **Target repository** | Must exist on the configured provider; the connected integration or token must have write access |
|
|
55
62
|
|
|
56
63
|
## Deploying the Automation
|
|
57
64
|
|
|
@@ -72,24 +79,53 @@ Set `OPENHANDS_HOST` and `AUTH_HEADER` for the curl commands below:
|
|
|
72
79
|
|
|
73
80
|
### Step 1 - Collect parameters
|
|
74
81
|
|
|
75
|
-
|
|
82
|
+
First decide how the poller reaches Jira:
|
|
83
|
+
|
|
84
|
+
- **Connected Atlassian Rovo MCP server** - on OpenHands Cloud and Enterprise, when the
|
|
85
|
+
user has connected it. OpenHands calls Jira for the poller through that connection, so
|
|
86
|
+
do not ask for a Jira email or an API token and do not create a secret.
|
|
87
|
+
- **Jira API token** - on a local Agent Canvas, or when the Rovo server is not connected.
|
|
88
|
+
|
|
89
|
+
Then pick `git_provider` from the automation the user asked for: `github` for *Jira issue to
|
|
90
|
+
GitHub PR* (the default, so it can be left out), `gitlab` for *Jira issue to GitLab MR*,
|
|
91
|
+
`bitbucket` for *Jira issue to Bitbucket PR*. It changes only what the conversation is told;
|
|
92
|
+
the conversation pushes with the user's connected integration for that provider, or delivers
|
|
93
|
+
through the connected MCP server's tools when it has no token.
|
|
94
|
+
|
|
95
|
+
Then gather the parameters for that route, plus the shared ones, before proceeding:
|
|
76
96
|
|
|
77
97
|
| Parameter | Example | Notes |
|
|
78
98
|
|---|---|---|
|
|
79
|
-
| `
|
|
80
|
-
| `
|
|
81
|
-
| `
|
|
99
|
+
| `jira_mcp_server` | `atlassian-rovo` | Rovo connection only. The name the server is stored under: the key in `agent_settings.mcp_config` of `GET ${OPENHANDS_HOST}/api/v1/settings` whose `url` is on `mcp.atlassian.com`. Look it up; do not ask the user |
|
|
100
|
+
| `jira_cloud_id` | `1324a887-45db-1bf4-1e99-ef0ff456d421` | Rovo connection only. The `id` of the Jira site, from the Rovo tool `getAccessibleAtlassianResources`. Ask which site only when it lists several |
|
|
101
|
+
| `jira_base_url` | `https://acme.atlassian.net` | API token only. No trailing slash |
|
|
102
|
+
| `jira_email` | `alice@acme.com` | API token only. Atlassian account email for Basic auth |
|
|
103
|
+
| `jira_token_secret` | `JIRA_CLOUD_KEY` | API token only. Name of the OpenHands secret holding the API token |
|
|
82
104
|
| `jira_label` | `create-pr` | Label to watch for (optional, defaults to `create-pr`) |
|
|
105
|
+
| `git_provider` | `gitlab` | `github` (default), `gitlab` or `bitbucket` - the provider the ticket names its repository on (optional) |
|
|
83
106
|
| `max_new_per_run` | `5` | Max conversations dispatched per cron firing (optional, defaults to `5`) |
|
|
84
107
|
| `cron_schedule` | `*/5 * * * *` | Polling frequency in cron syntax |
|
|
85
108
|
|
|
86
|
-
> **Note**: The
|
|
87
|
-
>
|
|
88
|
-
> The spawned agent extracts it from
|
|
109
|
+
> **Note**: The repository is not configured here. Each Jira ticket body must name it:
|
|
110
|
+
> `owner/repo` on GitHub, `group/subgroup/project` or a full URL on GitLab (self-managed
|
|
111
|
+
> hosts included), `workspace/repo` on bitbucket.org. The spawned agent extracts it from
|
|
112
|
+
> the ticket text.
|
|
89
113
|
|
|
90
114
|
### Step 2 - Create config.json
|
|
91
115
|
|
|
92
|
-
Create `config.json` next to `scripts/main.py` when packaging
|
|
116
|
+
Create `config.json` next to `scripts/main.py` when packaging. With the connected
|
|
117
|
+
Atlassian Rovo MCP server:
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"jira_mcp_server": "atlassian-rovo",
|
|
122
|
+
"jira_cloud_id": "1324a887-45db-1bf4-1e99-ef0ff456d421",
|
|
123
|
+
"jira_label": "create-pr",
|
|
124
|
+
"max_new_per_run": 5
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
With a Jira API token:
|
|
93
129
|
|
|
94
130
|
```json
|
|
95
131
|
{
|
|
@@ -101,6 +137,9 @@ Create `config.json` next to `scripts/main.py` when packaging:
|
|
|
101
137
|
}
|
|
102
138
|
```
|
|
103
139
|
|
|
140
|
+
For the GitLab and Bitbucket templates add `"git_provider": "gitlab"` or
|
|
141
|
+
`"git_provider": "bitbucket"` to either example; it is `github` when omitted.
|
|
142
|
+
|
|
104
143
|
### Step 3 - Package the tarball
|
|
105
144
|
|
|
106
145
|
Copy `scripts/main.py` from this skill and package it with the `config.json`:
|
|
@@ -143,7 +182,8 @@ curl -s -X POST "${OPENHANDS_HOST}/api/automation/v1" \
|
|
|
143
182
|
}" | python3 -m json.tool
|
|
144
183
|
```
|
|
145
184
|
|
|
146
|
-
Save the returned `id` - use it for updates and monitoring.
|
|
185
|
+
Save the returned `id` - use it for updates and monitoring. Name the automation after the
|
|
186
|
+
template the user picked, for example `Jira issue-to-MR Poller` for GitLab.
|
|
147
187
|
|
|
148
188
|
### Step 6 - Verify with a test dispatch
|
|
149
189
|
|
|
@@ -158,6 +198,14 @@ curl -s "${OPENHANDS_HOST}/api/automation/v1/<AUTOMATION_ID>/runs?limit=1" \
|
|
|
158
198
|
| python3 -c "import sys,json; r=json.load(sys.stdin)['runs'][0]; print(r['status'], r.get('error_detail'))"
|
|
159
199
|
```
|
|
160
200
|
|
|
201
|
+
With the Rovo connection, a run that fails on `/api/v1/mcp/servers/<name>/call-tool` says why:
|
|
202
|
+
|
|
203
|
+
- `404` with `No connected MCP server is named ...` - `jira_mcp_server` is not the name the
|
|
204
|
+
server is stored under, or the user switched the server off.
|
|
205
|
+
- `404` with `Not Found` - this deployment cannot call a connected MCP server for a run yet.
|
|
206
|
+
Set the automation up with a Jira API token instead.
|
|
207
|
+
- `502` - the server could not be reached, or the user has to reconnect it in the settings.
|
|
208
|
+
|
|
161
209
|
## Event-Based Alternative
|
|
162
210
|
|
|
163
211
|
Where Jira can reach the deployment over the internet (OpenHands Cloud and Enterprise),
|
|
@@ -287,7 +335,8 @@ The automation script lives at `scripts/main.py`. Key behaviors:
|
|
|
287
335
|
- **First-run baseline** - on the very first execution the script writes `first_run_at` (UTC timestamp) into the KV store and exits without dispatching; issues whose `updated` timestamp predates that baseline are skipped on all subsequent runs. Using `updated` (not `created`) means an old issue that has its label applied after deployment is correctly treated as new.
|
|
288
336
|
- **Per-run cap** - `max_new_per_run` (default 5) limits how many conversations are started per cron firing; any remaining new issues are dispatched on the next run.
|
|
289
337
|
- **KV store** - persists `{"processed_keys": [...], "first_run_at": "..."}` between runs; falls back to a local file in local dev environments where `AUTOMATION_KV_TOKEN` is absent (on OpenHands Cloud the run fails instead, since its sandbox does not persist).
|
|
290
|
-
- **Jira
|
|
338
|
+
- **Jira access** - with `jira_mcp_server` set, searches and comments through the user's connected Atlassian Rovo MCP server (`POST /api/v1/mcp/servers/<name>/call-tool` on OpenHands Cloud, tools `searchJiraIssuesUsingJql` and `addOrEditJiraIssueComment`, or `addCommentToJiraIssue` on a server that still offers the v1 tools), so the run reads no Jira credential. Otherwise uses the Jira REST API with the token secret: `POST /rest/api/3/search/jql` (the current non-deprecated endpoint).
|
|
339
|
+
- **Git provider** - `git_provider` selects the prompt's wording (GitHub pull request, GitLab merge request, Bitbucket pull request), the repository formats the agent looks for, and how it delivers without a token (GitLab's MCP tools, Rovo's Bitbucket tools); nothing else differs between the three templates.
|
|
291
340
|
- **Conversation dispatch** - locally, calls `POST /api/conversations` on the agent server with the current user's LLM/agent settings forwarded to the new conversation; on OpenHands Cloud, calls `POST /api/v1/app-conversations`, which runs each conversation in its own sandbox with the user's settings, secrets and connected git provider.
|
|
292
341
|
- **Start confirmation** (OpenHands Cloud only) - the API answers a start request before the conversation exists, so the next run checks each one. A start that failed is made again, up to three times, and after that the failure is posted on the Jira issue.
|
|
293
342
|
- **Error transparency** - captures Jira HTTP response bodies in error messages for fast diagnosis.
|
|
@@ -324,4 +373,4 @@ that issue again.
|
|
|
324
373
|
|
|
325
374
|
## Additional Resources
|
|
326
375
|
|
|
327
|
-
- **`references/setup.md`** - Jira API token creation,
|
|
376
|
+
- **`references/setup.md`** - Jira API token creation, git provider tokens, cron schedule reference, and troubleshooting guide.
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
## Jira API Token
|
|
4
4
|
|
|
5
|
+
The poller needs this token only when it does not reach Jira through a connected
|
|
6
|
+
Atlassian Rovo MCP server (see Step 1 in `SKILL.md`).
|
|
7
|
+
|
|
5
8
|
### Create a token
|
|
6
9
|
|
|
7
10
|
1. Log in to [id.atlassian.com](https://id.atlassian.com/manage-profile/security/api-tokens).
|
|
@@ -38,11 +41,19 @@ The Jira Cloud REST API v3 requires this for all authenticated requests.
|
|
|
38
41
|
|
|
39
42
|
---
|
|
40
43
|
|
|
41
|
-
##
|
|
44
|
+
## Git Provider Access
|
|
42
45
|
|
|
43
46
|
The spawned agent conversation clones the target repository, pushes a new branch, and
|
|
44
|
-
opens
|
|
45
|
-
|
|
47
|
+
opens the pull or merge request. On OpenHands Cloud and Enterprise it uses the connected
|
|
48
|
+
integration for the provider named by `git_provider` (GitHub, GitLab or Bitbucket). With
|
|
49
|
+
only an MCP connection (GitHub's with its saved token, GitLab's, or Atlassian Rovo for
|
|
50
|
+
Bitbucket) it commits through the tools instead, and reads a private repository through
|
|
51
|
+
them rather than cloning it. On a local Agent Canvas a token for the provider must be
|
|
52
|
+
stored as an OpenHands secret.
|
|
53
|
+
|
|
54
|
+
### GitHub
|
|
55
|
+
|
|
56
|
+
The GitHub token must have the following scopes:
|
|
46
57
|
|
|
47
58
|
| Scope | Required for |
|
|
48
59
|
|---|---|
|
|
@@ -57,20 +68,39 @@ inherits the user's configured secrets automatically through the agent server.
|
|
|
57
68
|
Navigate to **Settings → Secrets** and store the token under the name `GITHUB_TOKEN`
|
|
58
69
|
(or whatever name is expected by the agent's GitHub skill).
|
|
59
70
|
|
|
71
|
+
### GitLab
|
|
72
|
+
|
|
73
|
+
Store a personal access token with the `api` scope and at least the Developer role on the
|
|
74
|
+
target projects under the name `GITLAB_TOKEN`.
|
|
75
|
+
|
|
76
|
+
Calling GitLab MCP directly, to commit without a token, needs GitLab 19.4 or later, where its
|
|
77
|
+
branch, commit and repository tools first appear. On OpenHands Cloud and Enterprise the token
|
|
78
|
+
of the user's GitLab sign-in or connected integration reaches the conversation as
|
|
79
|
+
`GITLAB_TOKEN`, so on an older GitLab sign in with GitLab or connect it under Settings ->
|
|
80
|
+
Integrations, and store this secret only if neither is possible.
|
|
81
|
+
|
|
82
|
+
### Bitbucket
|
|
83
|
+
|
|
84
|
+
Store the token under the name `BITBUCKET_TOKEN`; the `bitbucket` skill describes the token
|
|
85
|
+
type and permissions.
|
|
86
|
+
|
|
60
87
|
---
|
|
61
88
|
|
|
62
89
|
## config.json Field Reference
|
|
63
90
|
|
|
64
91
|
| Field | Required | Default | Description |
|
|
65
92
|
|---|---|---|---|
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
93
|
+
| `jira_mcp_server` | ❌ | - | Name the connected Atlassian Rovo MCP server is stored under. When set, the poller reaches Jira through it (OpenHands Cloud and Enterprise only) and the three fields after `jira_cloud_id` are not used |
|
|
94
|
+
| `jira_cloud_id` | with `jira_mcp_server` | - | Atlassian cloud id of the Jira site |
|
|
95
|
+
| `jira_base_url` | without `jira_mcp_server` | - | Full URL of your Atlassian instance, e.g. `https://acme.atlassian.net` |
|
|
96
|
+
| `jira_email` | without `jira_mcp_server` | - | Email address of the Atlassian account that owns the API token |
|
|
68
97
|
| `jira_token_secret` | ❌ | `"JIRA_CLOUD_KEY"` | Name of the OpenHands secret holding the Jira API token |
|
|
69
98
|
| `jira_label` | ❌ | `"create-pr"` | Jira issue label to watch for |
|
|
99
|
+
| `git_provider` | ❌ | `"github"` | `github`, `gitlab` or `bitbucket` - the provider the ticket names its repository on; changes only the prompt |
|
|
70
100
|
|
|
71
|
-
> **
|
|
72
|
-
> Jira ticket
|
|
73
|
-
>
|
|
101
|
+
> **Repository**: not a config field. The target repository is read from the body of each
|
|
102
|
+
> Jira ticket: `owner/repo` or a GitHub URL on GitHub, `group/subgroup/project` or a full
|
|
103
|
+
> URL on GitLab, `workspace/repo` on bitbucket.org.
|
|
74
104
|
|
|
75
105
|
---
|
|
76
106
|
|
|
@@ -142,11 +172,12 @@ The response is `SearchAndReconcileResults`; the script reads the `issues` array
|
|
|
142
172
|
- The Jira project has issues with the label but all are in a Done status category.
|
|
143
173
|
The JQL filter is `statusCategory != Done` - reopen or create a fresh issue.
|
|
144
174
|
|
|
145
|
-
### Conversation started but no PR appears
|
|
175
|
+
### Conversation started but no PR or MR appears
|
|
146
176
|
|
|
147
177
|
- The spawned conversation inherits the user's agent/LLM settings. Check the conversation
|
|
148
178
|
in the OpenHands UI (it appears in the conversations list under the automation run).
|
|
149
|
-
- The
|
|
179
|
+
- The connected integration or token may lack the scope to push or open the request
|
|
180
|
+
(GitHub `repo`, GitLab `api` with the Developer role).
|
|
150
181
|
- The target repo's default branch may be protected; the token must have bypass permission,
|
|
151
182
|
or the PR should be created against a different base branch.
|
|
152
183
|
|
|
@@ -166,13 +197,13 @@ The response is `SearchAndReconcileResults`; the script reads the `issues` array
|
|
|
166
197
|
|
|
167
198
|
## Multiple Deployments
|
|
168
199
|
|
|
169
|
-
To watch multiple Jira projects or multiple
|
|
200
|
+
To watch multiple Jira projects or multiple repositories, deploy separate automations —
|
|
170
201
|
one per `config.json`. Each automation has its own KV-store namespace (keyed by
|
|
171
202
|
automation ID), so processed-key sets do not cross-contaminate.
|
|
172
203
|
|
|
173
204
|
Example: two automations, one per project:
|
|
174
205
|
|
|
175
|
-
| Automation name | `jira_base_url` | `jira_label` |
|
|
206
|
+
| Automation name | `jira_base_url` | `jira_label` | Repository (in ticket body) |
|
|
176
207
|
|---|---|---|---|
|
|
177
208
|
| `Jira issue-to-PR - frontend` | `https://acme.atlassian.net` | `create-pr` | `acme/frontend` (written by ticket author) |
|
|
178
209
|
| `Jira issue-to-PR - backend` | `https://acme.atlassian.net` | `create-pr-backend` | `acme/backend` (written by ticket author) |
|
|
@@ -2,19 +2,26 @@
|
|
|
2
2
|
Jira issue-to-PR poller — reads all configuration from config.json in the same directory.
|
|
3
3
|
|
|
4
4
|
config.json fields:
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
jira_mcp_server Settings name of the connected Atlassian Rovo MCP server. When set,
|
|
6
|
+
Jira is reached through it (OpenHands Cloud and Enterprise only)
|
|
7
|
+
and no Jira credential is configured here
|
|
8
|
+
jira_cloud_id Atlassian cloud id of the Jira site; required with jira_mcp_server
|
|
9
|
+
jira_base_url e.g. "https://yourcompany.atlassian.net"; without jira_mcp_server
|
|
10
|
+
jira_email Atlassian account email used for Basic auth; without jira_mcp_server
|
|
7
11
|
jira_token_secret Name of the OpenHands secret holding the Jira API token
|
|
8
12
|
jira_label Label to watch for (default: "create-pr")
|
|
9
13
|
max_new_per_run Max conversations dispatched per run (default: 5)
|
|
14
|
+
git_provider Where the ticket's repository lives: "github" (default), "gitlab"
|
|
15
|
+
or "bitbucket". It only changes what the conversation is asked
|
|
10
16
|
|
|
11
|
-
The target
|
|
12
|
-
|
|
17
|
+
The target repository is NOT configured here. Each Jira ticket body must name it
|
|
18
|
+
(owner/repo on GitHub, group/project or a URL on GitLab, workspace/repo on Bitbucket);
|
|
19
|
+
the spawned agent extracts it from the ticket text.
|
|
13
20
|
|
|
14
21
|
Runs against a local agent server (AGENT_SERVER_URL is injected) or on OpenHands Cloud
|
|
15
22
|
(OPENHANDS_CLOUD_API_URL / OPENHANDS_API_KEY / SANDBOX_ID are injected instead).
|
|
16
23
|
"""
|
|
17
|
-
import base64, json, os, re, sys, tempfile, urllib.error, urllib.request, uuid
|
|
24
|
+
import base64, json, os, re, sys, tempfile, urllib.error, urllib.parse, urllib.request, uuid
|
|
18
25
|
from datetime import datetime, timezone
|
|
19
26
|
from pathlib import Path
|
|
20
27
|
|
|
@@ -23,11 +30,67 @@ _HERE = Path(__file__).parent
|
|
|
23
30
|
with open(_HERE / "config.json") as _f:
|
|
24
31
|
_cfg = json.load(_f)
|
|
25
32
|
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
# Jira is reached through this connected MCP server when it is named, and through
|
|
34
|
+
# Jira's REST API with an API token otherwise.
|
|
35
|
+
JIRA_MCP_SERVER = _cfg.get("jira_mcp_server")
|
|
36
|
+
JIRA_CLOUD_ID = _cfg["jira_cloud_id"] if JIRA_MCP_SERVER else None
|
|
37
|
+
JIRA_BASE_URL = None if JIRA_MCP_SERVER else _cfg["jira_base_url"].rstrip("/")
|
|
38
|
+
JIRA_EMAIL = None if JIRA_MCP_SERVER else _cfg["jira_email"]
|
|
28
39
|
JIRA_TOKEN_SECRET = _cfg.get("jira_token_secret", "JIRA_CLOUD_KEY")
|
|
29
40
|
JIRA_LABEL = _cfg.get("jira_label", "create-pr")
|
|
41
|
+
# Atlassian renamed the comment tool in v2 of its MCP server and moves v1 connections
|
|
42
|
+
# to the v2 tools on its own schedule, so a deployment can meet either name.
|
|
43
|
+
JIRA_COMMENT_TOOLS = ("addOrEditJiraIssueComment", "addCommentToJiraIssue")
|
|
30
44
|
MAX_NEW_PER_RUN = int(_cfg.get("max_new_per_run", 5))
|
|
45
|
+
GIT_PROVIDER = _cfg.get("git_provider", "github")
|
|
46
|
+
# What the conversation is told about the provider the ticket names its repository on.
|
|
47
|
+
# Only the prompt differs: the conversation pushes with the user's connected integration,
|
|
48
|
+
# and without one it delivers through the tools of the connected MCP server instead.
|
|
49
|
+
GIT_PROVIDERS = {
|
|
50
|
+
"github": {
|
|
51
|
+
"name": "GitHub",
|
|
52
|
+
"request": "Pull Request",
|
|
53
|
+
"short": "PR",
|
|
54
|
+
"reference": '"owner/repo" format (e.g. "acme-org/backend") or a full GitHub URL\n'
|
|
55
|
+
' (e.g. "https://github.com/acme-org/backend")',
|
|
56
|
+
"clone": "https://github.com/<owner>/<repo>",
|
|
57
|
+
"delivery": "",
|
|
58
|
+
},
|
|
59
|
+
"gitlab": {
|
|
60
|
+
"name": "GitLab",
|
|
61
|
+
"request": "Merge Request",
|
|
62
|
+
"short": "MR",
|
|
63
|
+
"reference": '"group/project" format, subgroups included (e.g. "acme-org/platform/backend"),\n'
|
|
64
|
+
' or a full GitLab URL, which may be on a self-managed host\n'
|
|
65
|
+
' (e.g. "https://gitlab.example.com/acme-org/platform/backend")',
|
|
66
|
+
"clone": "https://gitlab.com/<group>/<project>, or the host the ticket's URL names",
|
|
67
|
+
"delivery": """
|
|
68
|
+
Delivering through GitLab:
|
|
69
|
+
- If GitLab tools from a connected MCP server are available to you, prefer them for reading
|
|
70
|
+
the issue and for opening the merge request.
|
|
71
|
+
- Without a `GITLAB_TOKEN` you cannot push with git: create the branch with `add_branch`,
|
|
72
|
+
commit your changes with `add_commit` (one call, a file action per changed file), and open
|
|
73
|
+
the merge request with `save_merge_request`. A private project cannot be cloned without a
|
|
74
|
+
credential; read it with `list_repository_tree` and `get_repository_file` instead.
|
|
75
|
+
""",
|
|
76
|
+
},
|
|
77
|
+
"bitbucket": {
|
|
78
|
+
"name": "Bitbucket",
|
|
79
|
+
"request": "Pull Request",
|
|
80
|
+
"short": "PR",
|
|
81
|
+
"reference": '"workspace/repo" format (e.g. "acme-org/backend") or a full Bitbucket URL\n'
|
|
82
|
+
' (e.g. "https://bitbucket.org/acme-org/backend")',
|
|
83
|
+
"clone": "https://bitbucket.org/<workspace>/<repo>",
|
|
84
|
+
"delivery": """
|
|
85
|
+
Delivering through Bitbucket:
|
|
86
|
+
- Without a `BITBUCKET_TOKEN`, use the Bitbucket tools of the connected Atlassian Rovo MCP
|
|
87
|
+
server: read the repository with `getBitbucketRepoFileContent`, create the branch with
|
|
88
|
+
`createBitbucketRepoBranch`, commit your changes with `createBitbucketRepoCommit`, and open
|
|
89
|
+
the pull request with `createBitbucketRepoPullRequest`. Tools the server does not list
|
|
90
|
+
directly are reached through `discover` and `executeRead` / `executeWrite`.
|
|
91
|
+
""",
|
|
92
|
+
},
|
|
93
|
+
}
|
|
31
94
|
# How often a conversation that OpenHands Cloud failed to start is started again.
|
|
32
95
|
MAX_START_ATTEMPTS = 3
|
|
33
96
|
|
|
@@ -163,14 +226,39 @@ def extract_adf_text(adf):
|
|
|
163
226
|
return " ".join(p for p in parts if p).strip()
|
|
164
227
|
|
|
165
228
|
|
|
229
|
+
def call_mcp_tool(name, arguments):
|
|
230
|
+
"""Call a tool on the connected MCP server and return its text output.
|
|
231
|
+
|
|
232
|
+
OpenHands makes the call with the credentials the user stored when connecting
|
|
233
|
+
the server, so this run holds no Jira credential of its own.
|
|
234
|
+
"""
|
|
235
|
+
server = urllib.parse.quote(JIRA_MCP_SERVER, safe="")
|
|
236
|
+
result = _cloud_request("POST", f"/api/v1/mcp/servers/{server}/call-tool", {
|
|
237
|
+
"name": name, "arguments": arguments,
|
|
238
|
+
})
|
|
239
|
+
if result.get("is_error"):
|
|
240
|
+
raise RuntimeError(f"{name} failed: {str(result.get('text'))[:500]}")
|
|
241
|
+
return result.get("text") or ""
|
|
242
|
+
|
|
243
|
+
|
|
166
244
|
def fetch_labeled_issues(auth_header):
|
|
167
|
-
"""Return open Jira issues with JIRA_LABEL
|
|
168
|
-
|
|
169
|
-
|
|
245
|
+
"""Return open Jira issues with JIRA_LABEL.
|
|
246
|
+
|
|
247
|
+
Uses the connected MCP server's JQL search, or the current v3 search endpoint.
|
|
248
|
+
"""
|
|
249
|
+
search = {
|
|
170
250
|
"jql": f'labels = "{JIRA_LABEL}" AND statusCategory != Done',
|
|
171
251
|
"fields": ["key", "summary", "description", "status", "updated"],
|
|
172
252
|
"maxResults": 50,
|
|
173
|
-
}
|
|
253
|
+
}
|
|
254
|
+
if JIRA_MCP_SERVER:
|
|
255
|
+
text = call_mcp_tool("searchJiraIssuesUsingJql", {"cloudId": JIRA_CLOUD_ID, **search})
|
|
256
|
+
try:
|
|
257
|
+
return json.loads(text).get("issues", [])
|
|
258
|
+
except (ValueError, AttributeError) as exc:
|
|
259
|
+
raise RuntimeError(f"Unexpected Jira search result: {text[:500]}") from exc
|
|
260
|
+
url = f"{JIRA_BASE_URL}/rest/api/3/search/jql"
|
|
261
|
+
body = json.dumps(search).encode()
|
|
174
262
|
req = urllib.request.Request(
|
|
175
263
|
url, data=body,
|
|
176
264
|
headers={
|
|
@@ -188,7 +276,19 @@ def fetch_labeled_issues(auth_header):
|
|
|
188
276
|
|
|
189
277
|
|
|
190
278
|
def post_jira_comment(issue_key, auth_header, text):
|
|
191
|
-
"""Post a plain-text comment on a Jira issue
|
|
279
|
+
"""Post a plain-text comment on a Jira issue."""
|
|
280
|
+
if JIRA_MCP_SERVER:
|
|
281
|
+
errors = []
|
|
282
|
+
for tool in JIRA_COMMENT_TOOLS:
|
|
283
|
+
try:
|
|
284
|
+
call_mcp_tool(tool, {
|
|
285
|
+
"cloudId": JIRA_CLOUD_ID, "issueIdOrKey": issue_key, "commentBody": text,
|
|
286
|
+
})
|
|
287
|
+
return
|
|
288
|
+
except RuntimeError as exc:
|
|
289
|
+
errors.append(str(exc))
|
|
290
|
+
print(f"Warning: failed to post Jira comment on {issue_key}: {'; '.join(errors)}")
|
|
291
|
+
return
|
|
192
292
|
url = f"{JIRA_BASE_URL}/rest/api/3/issue/{issue_key}/comment"
|
|
193
293
|
body = json.dumps({
|
|
194
294
|
"body": {
|
|
@@ -321,10 +421,21 @@ try:
|
|
|
321
421
|
raise RuntimeError("The automation KV store is required on OpenHands Cloud, "
|
|
322
422
|
"but AUTOMATION_KV_TOKEN is not set for this run.")
|
|
323
423
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
424
|
+
if GIT_PROVIDER not in GIT_PROVIDERS:
|
|
425
|
+
raise RuntimeError(f"Unknown git_provider {GIT_PROVIDER!r}; "
|
|
426
|
+
f"expected one of: {', '.join(GIT_PROVIDERS)}")
|
|
427
|
+
git = GIT_PROVIDERS[GIT_PROVIDER]
|
|
428
|
+
|
|
429
|
+
auth_header = None
|
|
430
|
+
if not JIRA_MCP_SERVER:
|
|
431
|
+
jira_token = get_secret(JIRA_TOKEN_SECRET)
|
|
432
|
+
auth_header = "Basic " + base64.b64encode(
|
|
433
|
+
f"{JIRA_EMAIL}:{jira_token}".encode()
|
|
434
|
+
).decode()
|
|
435
|
+
elif IS_LOCAL:
|
|
436
|
+
# Only OpenHands Cloud and Enterprise call a connected MCP server for a run.
|
|
437
|
+
raise RuntimeError("jira_mcp_server needs OpenHands Cloud or Enterprise; "
|
|
438
|
+
"a local run reaches Jira with jira_token_secret.")
|
|
328
439
|
|
|
329
440
|
state = load_state()
|
|
330
441
|
processed_keys = set(state.get("processed_keys", []))
|
|
@@ -343,7 +454,7 @@ try:
|
|
|
343
454
|
if not IS_LOCAL:
|
|
344
455
|
settle_cloud_starts(state, processed_keys, auth_header)
|
|
345
456
|
|
|
346
|
-
print(f"Polling {JIRA_BASE_URL} for issues labeled '{JIRA_LABEL}'…")
|
|
457
|
+
print(f"Polling {JIRA_BASE_URL or JIRA_MCP_SERVER} for issues labeled '{JIRA_LABEL}'…")
|
|
347
458
|
issues = fetch_labeled_issues(auth_header)
|
|
348
459
|
print(f"Total matching issues : {len(issues)}")
|
|
349
460
|
|
|
@@ -393,28 +504,27 @@ try:
|
|
|
393
504
|
description = extract_adf_text(issue["fields"].get("description"))
|
|
394
505
|
branch = f"jira/{key.lower()}"
|
|
395
506
|
|
|
396
|
-
prompt = f"""Create a
|
|
507
|
+
prompt = f"""Create a {git['name']} {git['request']} for the following Jira issue.
|
|
397
508
|
|
|
398
509
|
Jira Issue : {key}
|
|
399
510
|
Summary : {summary}
|
|
400
511
|
Description: {description or "No description provided."}
|
|
401
512
|
|
|
402
513
|
Steps:
|
|
403
|
-
1. Find the target
|
|
404
|
-
|
|
405
|
-
(e.g. "https://github.com/acme-org/backend"). Use that repository.
|
|
514
|
+
1. Find the target {git['name']} repository in the Description above. Look for a reference in
|
|
515
|
+
{git['reference']}. Use that repository.
|
|
406
516
|
If no repository is mentioned, create a file `jira/{key}/notes.md` with the issue
|
|
407
|
-
details and print a message explaining that no
|
|
408
|
-
2. Clone the repository (e.g.
|
|
517
|
+
details and print a message explaining that no {git['name']} repo was found in the ticket.
|
|
518
|
+
2. Clone the repository (e.g. {git['clone']}).
|
|
409
519
|
3. Create branch `{branch}` from the default branch.
|
|
410
520
|
4. Implement the changes described in the issue.
|
|
411
521
|
If the description is vague or missing, create `jira/{key}/notes.md`
|
|
412
522
|
with the issue key, summary, and description as a placeholder.
|
|
413
|
-
5. Commit, push the branch, and open a
|
|
523
|
+
5. Commit, push the branch, and open a {git['request']}:
|
|
414
524
|
- Title : [{key}] {summary}
|
|
415
525
|
- Body : Reference the Jira issue key and describe the changes made.
|
|
416
|
-
6. Print the
|
|
417
|
-
"""
|
|
526
|
+
6. Print the {git['short']} URL when done.
|
|
527
|
+
{git['delivery']}"""
|
|
418
528
|
if IS_LOCAL:
|
|
419
529
|
workdir = tempfile.mkdtemp(prefix=f"jira-{key.lower()}-")
|
|
420
530
|
payload = {
|
|
@@ -37,12 +37,17 @@ This skill is activated by keywords:
|
|
|
37
37
|
thread immediately on trigger detection
|
|
38
38
|
- **Automatic summaries**: when the conversation reaches a terminal state the
|
|
39
39
|
agent's final response is posted back to the thread; error/stuck states
|
|
40
|
-
receive a clear error notice
|
|
40
|
+
receive a clear error notice, and every result shows the LLM settings source
|
|
41
|
+
and concrete model
|
|
41
42
|
- **Persistent state**: conversation tracking and poll timestamps are stored
|
|
42
43
|
in `automation-state/slack_poller_{automation_id}.json` across runs
|
|
43
44
|
|
|
44
45
|
## Prerequisites
|
|
45
46
|
|
|
47
|
+
The footer reports `default` and the concrete model from the server settings
|
|
48
|
+
already used to start the conversation. Follow-up replies keep those recorded
|
|
49
|
+
values because they reuse the same conversation.
|
|
50
|
+
|
|
46
51
|
Set at least one of the following in **OpenHands Settings - Secrets**:
|
|
47
52
|
|
|
48
53
|
| Secret | Token type | Minimum scopes |
|
|
@@ -251,14 +251,17 @@ Each cron run executes `main.py`, which runs **10 polling iterations** (every
|
|
|
251
251
|
ignored.
|
|
252
252
|
- Contains trigger phrase outside a tracked conversation → 👀 reaction, create
|
|
253
253
|
a new conversation, post link.
|
|
254
|
+
- Records `default` and the concrete model from the server settings already
|
|
255
|
+
used to start the conversation, for the response footer.
|
|
254
256
|
- Thread replies: agent receives full thread history for context.
|
|
255
257
|
- Root messages: agent receives the trigger text only.
|
|
256
258
|
6. **Checks conversation statuses** - for each active conversation where
|
|
257
259
|
`time.time() - last_activity > 15 s`:
|
|
258
260
|
- If status is `idle`, `finished`, `error`, or `stuck` → fetch the agent's
|
|
259
|
-
final response via `/api/conversations/{id}/agent_final_response
|
|
260
|
-
it to the Slack thread using Slack's
|
|
261
|
-
formatting renders correctly. Mark the
|
|
261
|
+
final response via `/api/conversations/{id}/agent_final_response`, append
|
|
262
|
+
the LLM profile and model, and post it to the Slack thread using Slack's
|
|
263
|
+
`markdown_text` field so Markdown formatting renders correctly. Mark the
|
|
264
|
+
record `watching` for five minutes
|
|
262
265
|
so triggered follow-up replies can continue the same conversation.
|
|
263
266
|
7. **Advances `last_poll`** to `now - 10 s` (overlap window prevents boundary
|
|
264
267
|
races). If a conversation creation failed, pins `last_poll` further back to
|