Commit f136d89
* docs(guides): note supported Node version range in Quick Start prerequisites
* feat(types): add 'jira' to ChannelSource union
Phase 1 of Jira Cloud integration (#288). Extends the ChannelSource
discriminant on both sides of the wire and updates the agent-side
comment so the runtime knows 'jira' is a recognized channel value;
no behavior changes yet.
* feat(jira): add DDB table constructs for projects, users, workspaces
Phase 2 of Jira Cloud integration (#288). Mirrors the Linear constructs
file-for-file. Composite PKs use cloudId as the tenant prefix
(`{cloudId}#{projectKey}`, `{cloudId}#{accountId}`) so the same project
key or account id stays unambiguous across distinct Atlassian tenants.
Tables are unwired until Phase 4 — JiraIntegration instantiates and
grants them.
* feat(jira): add webhook, processor, link Lambdas + shared helpers
Phase 3 of Jira Cloud integration (#288). Mirrors Linear's adapter
shape: per-tenant OAuth resolver (auth.atlassian.com), X-Hub-Signature
HMAC verify with per-tenant + stack-wide fallback, REST-based feedback
poster (ADF-wrapped, no reaction primitive — marker folded into text),
and three Lambdas (webhook, processor, link).
Non-trivial bit: the processor diffs `changelog.items[]` where
`field === 'labels'` and tokenizes the space-separated `fromString` /
`toString` to detect a label add — Atlassian's diff format differs
from Linear's `updatedFrom.labelIds`. Includes a minimal ADF→markdown
walker for issue descriptions.
Handlers reference JIRA_* env vars set by the JiraIntegration construct
in Phase 4; they don't deploy yet.
* feat(jira): add JiraIntegration construct + stack wiring
Phase 4 of Jira Cloud integration (#288). Mirrors LinearIntegration:
3 DDB tables, dedup table (8h TTL), 3 Lambdas (webhook/processor/link),
API routes under /jira/*, per-tenant `bgagent-jira-oauth-*` IAM grants,
cdk-nag suppressions.
Stack wiring grants the agent runtime GetSecretValue on the per-tenant
prefix and pipes the workspace registry table + Get/Put grant into the
orchestrator (matches Linear's path for pre-container failure feedback).
Synth confirms clean CloudFormation + no nag findings.
* feat(jira): wire agent-side MCP + OAuth resolver for jira channel
Phase 5 of Jira Cloud integration (#288). Refactors channel_mcp.py from
a single-channel gate to a CHANNEL_MCP_BUILDERS dispatch dict so adding
future channels stays one-entry. Adds resolve_jira_oauth_token() to
config.py mirroring the Linear resolver — same race-handling, same
fail-closed semantics; only differences are the endpoint
(auth.atlassian.com, JSON body) and the env-var name (JIRA_API_TOKEN).
Pipeline now dispatches to the right resolver based on channel_source.
JIRA_MCP_URL is flagged in-source as needs-verification — Atlassian's
Remote MCP may still be preview-gated; if so, fall back to a REST shim
in a future jira_reactions.py module (Plan B).
Tests: 6 new Jira test cases in test_channel_mcp.py; full agent suite
remains green (825 passed).
* feat(cli): add bgagent jira commands (app-template, setup, link, map)
Phase 6 of Jira Cloud integration (#288). Minimal v1 surface (4 of 10
Linear subcommands), per scoping decision. Mirrors the Linear CLI shape
where the contracts are similar:
- jira-oauth.ts ports linear-oauth.ts. Atlassian's token endpoint takes
JSON (Linear takes form-encoded). offline_access scope is required
for a refresh_token. fetchAccessibleResources() resolves cloudId +
siteUrl post-consent.
- commands/jira.ts: app-template prints dev-console values; setup
drives the OAuth dance + writes the per-tenant secret + registry row
+ webhook signing secret; link does dry-run preview UX; map writes
the project → repo row.
Deferred to follow-ups: add-workspace, update-webhook-secret,
invite-user (with self-link picker), list-projects.
* test(jira): add webhook, processor, and link handler tests
Covers signature verify pass/fail, dedup, event filtering, label-add
detection (create vs update changelog), and Cognito-authenticated linking.
56 tests, mirrors the Linear handler test surface.
* docs(jira): add setup guide, ADR-014, and integration listings
- docs/guides/JIRA_SETUP_GUIDE.md — OAuth 3LO app, scopes, webhook
registration, label trigger, project mapping, troubleshooting
- docs/decisions/ADR-014-jira-integration.md — Jira Cloud only, OAuth 3LO,
label trigger, MCP outbound; documents the Jira-vs-Linear divergences
- README, USER_GUIDE, ROADMAP — add Jira to channel listings
- sync-starlight.mjs + astro.config.mjs — register the Jira guide mirror;
regenerate Starlight content under docs/src/content/docs/
Completes the docs phase of #288.
* test(jira): close build/coverage gaps for jira integration
Bring `mise run build` green on the jira integration branch:
- check-types-sync: allowlist JiraLinkResponse as CLI-only, matching
SlackLinkResponse/LinearLinkResponse (link responses are inlined
server-side; no CDK source-of-truth type)
- channel_mcp.py: move Callable into a TYPE_CHECKING block (ruff TC003;
safe under `from __future__ import annotations`)
- agent.test.ts: bump expected DynamoDB table count 13 -> 17 for the
four new Jira tables (project/user/workspace-registry/webhook-dedup)
- test_config.py: cover resolve_jira_oauth_token (cache, fallback,
refresh, concurrent-refresh, malformed/expiry paths); agent coverage
70.41% -> 72.91%
- jira-oauth-resolver.test.ts: new suite (32 tests) mirroring the Linear
resolver tests; clears the CDK statement/line/function/branch gates
- jira.ts / jira-oauth.ts: ESLint --fix cosmetic edits (quote-props,
redundant template literals)
Tests: 294 CLI + 837 agent + 1896 CDK, all passing.
* fix(jira): resolve cloudId from sole tenant when webhook omits it
Jira webhooks created via the Settings → System → Webhooks UI do not
include a top-level `cloudId` in their payload (only app/OAuth-registered
dynamic webhooks do). Without it the processor can't resolve the tenant,
so it dropped the event and never created a task — the inbound trigger
silently failed for the common single-tenant, UI-webhook setup.
Add a safe fallback: when `payload.cloudId` is absent, scan the workspace
registry and use the sole `active` tenant. Deliberately refuses to guess
when zero or multiple active tenants exist (returns undefined → event
dropped), so the multi-tenant design is preserved — a multi-tenant
operator must use a webhook that carries its own cloudId.
`grantReadData` on the registry table already covers the Scan, so no IAM
change is needed.
Adds tests for: sole-tenant recovery (task created), empty registry
(drop), and multiple active tenants (ambiguous → drop).
* feat(jira): post issue progress comments via REST shim
Jira-origin tasks now comment on the originating issue at start
("🤖 picked up…") and on completion ("✅ finished — PR: <url>" / "❌ …"),
matching the Linear integration's progress UX.
Why a REST shim instead of the Atlassian Remote MCP: the hosted MCP
(mcp.atlassian.com) requires an interactive, browser-based OAuth 2.1 flow
with dynamic client registration — it does NOT accept the stored Jira REST
OAuth token as a Bearer header, so it fails to connect from a headless
agent ("claude mcp list" → Failed to connect; no mcp__jira-server__* tools
load). The Jira REST API accepts the same stored token (it carries
write:jira-work), so comments go via POST /rest/api/3/issue/{key}/comment
on the cross-region api.atlassian.com/ex/jira/{cloudId} base.
- New `jira_reactions.py`: gated by channel_source=='jira' + required
metadata; swallows all network/auth errors (comments are advisory, never
gate the pipeline); auth circuit-breaker mirrors linear_reactions.
- Wired into pipeline.py at task start, normal finish (with PR url), and
the crash path — parallel to the existing Linear reaction hooks.
- prompt_builder: Jira tasks now get NO MCP-comment addendum (the earlier
Linear-only gate already skipped them); instructing the agent to use the
non-loading MCP tools would just waste turns. Comments are out-of-band.
Adds test_jira_reactions.py (gate, ADF body, success/failure/PR variants,
error-swallowing, auth circuit breaker) and channel-addendum tests.
* fix(jira): repair botched merge in test_config.py imports
The 'Merge branch main' commit (c84cc66) left an invalid import block:
a missing comma after PR_WORKFLOW_IDS (syntax error) plus a stale
PR_TASK_TYPES import that main's #248 removed from config.py. ruff
rejected the file, aborting the agentcore build.
Drop the orphaned PR_TASK_TYPES import and fix the comma.
* fix(jira): type _config base dict so ty accepts TaskConfig(**base)
test_prompts.py:_config built base from a homogeneous str literal, so ty
inferred dict[str, str] and rejected the spread into TaskConfig's
bool/int/list fields (17 invalid-argument-type errors). Annotate base as
dict[str, Any], matching the existing helper in test_runner.py. This
failure was previously masked by the lint syntax error that aborted the
build before typecheck ran.
* fix(jira): apply ruff format and resync stale docs mirrors
Three more issues that were masked behind the earlier lint/typecheck
failures, all surfaced once the build progressed to its 'fail on
mutation' gate:
- ruff format reflowed two long boolean/string lines in jira_reactions.py
and test_jira_reactions.py that were committed unformatted.
- USER_GUIDE.md still referenced the retired `pr_review` task_type on the
intro 'For example' line (a #248 merge leftover); the rest of the guide
uses `coding/pr-review-v1`. Fixed the source and regenerated the
Starlight mirror (using/Overview.md).
- Quick-start mirror was missing the Node.js prerequisite line present in
the QUICK_START.md source; docs-sync adds it.
Full `mise run build` now completes with no working-tree mutation.
* fix(jira): address PR #302 review — security binding, token refresh, ADR/docs
Blocking (krokoko):
- Multi-tenant signature binding: webhook receiver flags stack-wide
verification to the processor, which then ignores the body cloudId and
binds to the sole active tenant (drops when ambiguous). CLI no longer
mirrors the stack-wide secret into new per-tenant bundles; stack-wide is
seeded once from the first tenant. Missing-timestamp replay skip is logged.
- Renumber ADR-014 -> ADR-015 (collides with workflow-driven-tasks); rewrite
to the implemented REST-outbound reality (status accepted), correct dedup
key, binding + refresh-ownership sections. Reconcile JIRA_SETUP_GUIDE,
USER_GUIDE ("six ways"), ROADMAP, channel_mcp.py (placeholder + in-band
log), jira-webhook-processor.ts, jira-integration.ts, agent.ts.
- Fix non-existent CLI command in feedback: onboard-project -> map
<cloud-id> <project-key> --repo (processor + CLI next-steps hint).
- Implement notifyJiraOnConcurrencyCap (Linear parity) so the orchestrator
IAM grant is used and Jira users aren't silently dropped on the cap.
Significant (ayushtr):
- Agent never refreshes the Jira token (Atlassian rotates refresh_tokens;
agent has GetSecretValue only). Use the Lambda-written token verbatim and
fail closed when expiring; Lambda path owns all refreshes.
- ADF media nodes (external images) now render to markdown so attachment
extraction works; ADF->markdown computed once and reused.
Minor: one-sided clock-skew-tolerant timestamp freshness, base64-body guard,
replay window 24h->1h, resolveSoleTenantCloudId Scan comment.
Tests: agent no-refresh/fail-closed suite, multi-tenant binding tests,
base64/missing-timestamp/stack-wide-flag webhook tests, ADF media-node test,
notifyJiraOnConcurrencyCap parity suite. Docs mirrors regenerated.
Relates to #288
* fix(docs): repair botched main-merge in sync-starlight.mjs
The 'Merge branch main' commit (0f47343) dropped the closing ');' on the
Jira mirrorMarkdownFile() call where it interleaved with main's new
'Deploy preview screenshots' mirror block, producing a SyntaxError that
broke the //docs:sync build step (and thus the whole build job).
* fix(cdk): repair botched main-merge in agent.ts
The 'Merge branch main' (0f47343) dropped the closing '});' on the
JiraWorkspaceRegistryTableName CfnOutput where main's new
GitHubScreenshotIntegration block was spliced in, cascading into ~40
TS1005 errors and breaking //cdk:compile.
* fix(cdk): correct DynamoDB table-count assertion + import ordering
Collapse the duplicated 17/14 table-count assertions left by an earlier
botched main-merge into a single correct count of 18 (17 enumerated
tables incl. the 4 Jira tables, plus github-webhook-dedup from
GitHubScreenshotIntegration). Also fix import ordering (github before
jira) flagged by eslint import/order.
* fix(jira): post Lambda-side feedback to api.atlassian.com gateway base
The Lambda-side Jira feedback helper built its REST URL from the tenant
site host (`*.atlassian.net`), but the per-tenant 3LO token is minted
with `audience=api.atlassian.com` and is only valid against the gateway
base `https://api.atlassian.com/ex/jira/{cloudId}/rest/...`. Every
pre-container feedback comment (unmapped project, unlinked user,
concurrency-cap rejection, createTaskCore non-201) therefore 401'd
silently, since these comments are best-effort and swallow errors.
Build the URL from `cloudId` (already on `JiraFeedbackContext`) against
the gateway base — matching the agent-side path in jira_reactions.py —
and drop the unused `siteUrl`. Correct the misleading
jira-workspace-registry-table comment that called site_url a "REST base".
Adds jira-feedback.test.ts asserting the request host is
api.atlassian.com (never atlassian.net), plus encoding and
never-throws coverage.
* fix(jira): extract magic numbers into named constants
Resolves @typescript-eslint/no-magic-numbers eslint errors that were
failing the cdk:eslint build step. Mirrors the named-constant convention
already used by the Linear integration siblings.
* fix(jira): address review — HMAC empty-secret guard, verify tests, doc fixes
Resolves the blocking items and nits from the PR #302 review.
Blocking:
- B1: route the Jira webhook signing secret through isUsableHmacSecret in
both getJiraSecret (on fetch) and verifyJiraSignature (defense-in-depth),
matching the Linear/Slack/GitHub invariant. A whitespace-only per-tenant
secret was previously truthy and made HMAC(' ', body) forgeable.
- B2: add cdk/test/handlers/shared/jira-verify.test.ts (30 cases) covering
verifyJiraRequestForTenant's four outcomes (verified/mismatch/revoked/
no-per-tenant-secret), the strict-lookup rethrow, verifyJiraRequest
rotation-refetch, empty/whitespace-secret rejection, and the one-sided
timestamp freshness window (stale / far-future / within-skew). Mirrors
linear-verify.test.ts; the multi-tenant trust boundary had no coverage.
- B3: rewrite the JiraWorkspaceRegistryTable docstring (and the matching
jira-integration.ts comments) to describe the real oauth_secret_arn /
Secrets Manager resolution path instead of the never-implemented
provider_name / AgentCore Identity model.
Nits:
- Reword the stale "MCP token" comment in jira-webhook-processor.ts to the
REST-outbound reality.
- Add ts-/py-silent-success-masking nosemgrep justifications to the
best-effort swallows in jira-verify.ts, jira-oauth-resolver.ts (3), and
jira_reactions.py, matching the Linear annotations.
- On a refresh PutSecretValue failure, no longer cache the rotated token
(SM holds a stale refresh_token); invalidate the cache and escalate the
log so the breakage surfaces promptly.
- URL-encode the cloudId path segment in jira-feedback.ts and both cloud_id
and issue_key in jira_reactions.py (defense-in-depth; issueKey was already
encoded on the TS side).
- Re-extract the inlined 512 into WEBHOOK_PROCESSOR_MEMORY_MB (AI007).
mise run build green: 2177 CDK + 355 CLI + 1090 agent tests passing.
Relates to #288
---------
Co-authored-by: bgagent <bgagent@noreply.github.com>
Co-authored-by: Alain Krok <alkrok@amazon.com>
Co-authored-by: Sphia Sadek <isadeks@gmail.com>
1 parent 6844256 commit f136d89
52 files changed
Lines changed: 7718 additions & 48 deletions
File tree
- agent
- src
- tests
- cdk
- src
- constructs
- handlers
- stacks
- test
- handlers
- stacks
- cli/src
- bin
- commands
- docs
- decisions
- guides
- scripts
- src/content/docs
- decisions
- getting-started
- roadmap
- using
- scripts
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
| 22 | + | |
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| |||
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
34 | | - | |
| 34 | + | |
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
4 | | - | |
5 | | - | |
6 | | - | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
7 | 14 | | |
8 | 15 | | |
9 | | - | |
10 | | - | |
| 16 | + | |
11 | 17 | | |
12 | | - | |
13 | | - | |
| 18 | + | |
| 19 | + | |
14 | 20 | | |
15 | 21 | | |
16 | 22 | | |
17 | 23 | | |
18 | 24 | | |
19 | 25 | | |
20 | | - | |
| 26 | + | |
21 | 27 | | |
22 | 28 | | |
23 | 29 | | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
24 | 35 | | |
25 | 36 | | |
26 | 37 | | |
27 | 38 | | |
28 | | - | |
| 39 | + | |
29 | 40 | | |
30 | 41 | | |
31 | 42 | | |
32 | | - | |
| 43 | + | |
33 | 44 | | |
34 | 45 | | |
35 | 46 | | |
| |||
44 | 55 | | |
45 | 56 | | |
46 | 57 | | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
47 | 109 | | |
48 | 110 | | |
49 | 111 | | |
50 | 112 | | |
51 | | - | |
| 113 | + | |
52 | 114 | | |
53 | 115 | | |
54 | 116 | | |
| |||
67 | 129 | | |
68 | 130 | | |
69 | 131 | | |
70 | | - | |
71 | | - | |
| 132 | + | |
| 133 | + | |
72 | 134 | | |
73 | 135 | | |
74 | | - | |
| 136 | + | |
75 | 137 | | |
76 | 138 | | |
77 | 139 | | |
78 | 140 | | |
79 | 141 | | |
80 | 142 | | |
81 | | - | |
82 | | - | |
| 143 | + | |
| 144 | + | |
83 | 145 | | |
84 | | - | |
| 146 | + | |
| 147 | + | |
85 | 148 | | |
86 | 149 | | |
| 150 | + | |
| 151 | + | |
87 | 152 | | |
88 | 153 | | |
89 | 154 | | |
| |||
94 | 159 | | |
95 | 160 | | |
96 | 161 | | |
97 | | - | |
| 162 | + | |
98 | 163 | | |
99 | 164 | | |
100 | 165 | | |
101 | 166 | | |
102 | 167 | | |
103 | 168 | | |
104 | 169 | | |
105 | | - | |
| 170 | + | |
106 | 171 | | |
107 | 172 | | |
108 | 173 | | |
109 | 174 | | |
110 | | - | |
| 175 | + | |
111 | 176 | | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
112 | 187 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
330 | 330 | | |
331 | 331 | | |
332 | 332 | | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
333 | 450 | | |
334 | 451 | | |
335 | 452 | | |
| |||
0 commit comments