Skip to content

Commit 2c1c751

Browse files
Merge branch 'main' into feat/deploy-yml
2 parents be509ab + 4364855 commit 2c1c751

187 files changed

Lines changed: 30033 additions & 3141 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.

.dockerignore

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Build context is repo root (see cdk/src/stacks/agent.ts) so the
2+
# Dockerfile can COPY contracts/ alongside agent/. Exclusions below
3+
# keep the context lean — without them the entire monorepo (CDK
4+
# cdk.out/, node_modules/, docs/dist/, etc.) gets uploaded on every
5+
# AgentCore deploy.
6+
7+
# CDK output (recursive include if not excluded)
8+
cdk/cdk.out/
9+
cdk/lib/
10+
cdk/node_modules/
11+
12+
# CLI and docs build artifacts
13+
cli/lib/
14+
cli/node_modules/
15+
docs/dist/
16+
docs/node_modules/
17+
docs/.astro/
18+
19+
# Shared node_modules
20+
node_modules/
21+
22+
# Agent venv and cache (rebuilt inside image via uv)
23+
agent/.venv/
24+
agent/__pycache__/
25+
agent/**/__pycache__/
26+
agent/**/*.pyc
27+
28+
# Git and tooling
29+
.git/
30+
.prek/
31+
.claude/
32+
**/.DS_Store
33+
34+
# Docs and assets not needed in image
35+
*.md
36+
*.png
37+
*.drawio
38+
*.html
39+
*.gif
40+
*.tape
41+
42+
# Worktrees + scratch
43+
abca-worktrees/
44+
.next-session-prompt.md
45+
.e2e-test-plan.md
46+
47+
# Test/coverage output
48+
coverage/
49+
**/coverage/
50+
.pytest_cache/
51+
**/.pytest_cache/
52+
53+
# IDE / OS
54+
.idea/
55+
.vscode/
56+
yarn-error.log
57+
yarn-debug.log
58+
npm-debug.log*

.github/dependabot.yml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
version: 2
2+
3+
updates:
4+
- package-ecosystem: "github-actions"
5+
directory: "/"
6+
schedule:
7+
interval: "weekly"
8+
day: "saturday"
9+
cooldown:
10+
default-days: 7
11+
commit-message:
12+
prefix: "chore(deps): actions"
13+
open-pull-requests-limit: 1
14+
groups:
15+
all-actions:
16+
patterns: ["*"]
17+
18+
- package-ecosystem: "docker"
19+
directories:
20+
- "**/*"
21+
schedule:
22+
interval: "weekly"
23+
day: "saturday"
24+
cooldown:
25+
default-days: 7
26+
commit-message:
27+
prefix: "chore(deps): docker"
28+
open-pull-requests-limit: 1
29+
groups:
30+
all-docker:
31+
patterns: ["*"]
32+
33+
- package-ecosystem: "uv"
34+
directories:
35+
- "**/*"
36+
schedule:
37+
interval: "weekly"
38+
day: "saturday"
39+
cooldown:
40+
default-days: 7
41+
commit-message:
42+
prefix: "chore(deps): uv"
43+
open-pull-requests-limit: 1
44+
groups:
45+
all-python:
46+
patterns: ["*"]
47+
48+
- package-ecosystem: "npm"
49+
directories:
50+
- "**/*"
51+
schedule:
52+
interval: "weekly"
53+
day: "saturday"
54+
cooldown:
55+
default-days: 7
56+
commit-message:
57+
prefix: "chore(deps): npm"
58+
open-pull-requests-limit: 1
59+
groups:
60+
all-npm:
61+
patterns: ["*"]

.pre-commit-config.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,22 @@ repos:
7171
exclude: ^docs/node_modules/
7272
stages: [pre-commit]
7373

74+
- id: types-sync-cdk-cli
75+
name: type sync drift (CDK ↔ CLI)
76+
entry: bash -lc 'cd "$(git rev-parse --show-toplevel)" && mise run check:types-sync'
77+
language: system
78+
pass_filenames: false
79+
files: ^(cdk/src/handlers/shared/types\.ts$|cli/src/types\.ts$|scripts/check-types-sync\.ts$)
80+
stages: [pre-commit]
81+
82+
- id: constants-sync
83+
name: cross-language constants drift (contracts/constants.json)
84+
entry: bash -lc 'cd "$(git rev-parse --show-toplevel)" && mise run check:constants-sync'
85+
language: system
86+
pass_filenames: false
87+
files: ^(contracts/constants\.json$|agent/src/policy\.py$|cdk/src/handlers/shared/types\.ts$|cdk/src/constructs/blueprint\.ts$|scripts/check-constants-sync\.ts$)
88+
stages: [pre-commit]
89+
7490
- id: monorepo-security-pre-push
7591
name: security scans (pre-push)
7692
entry: bash -lc 'cd "$(git rev-parse --show-toplevel)" && mise run hooks:pre-push:security'

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Use this routing before editing so the right package and tests get updated:
2222
| Agent runtime (clone, tools, prompts, container) | `agent/src/` (`pipeline.py`, `runner.py`, `config.py`, `hooks.py`, `policy.py`, `prompts/`, Dockerfile, etc.) | `agent/tests/`, `agent/README.md` for env/PAT |
2323
| Agent progress events (written to `TaskEventsTable` from the MicroVM; read by `bgagent watch`) | `agent/src/progress_writer.py`, `agent/src/pipeline.py` and `agent/src/runner.py` (integration points) | `agent/tests/test_progress_writer.py`; `cli/src/commands/watch.ts` for the consumer side |
2424
| User-facing or design prose | `docs/guides/`, `docs/design/` | Run **`mise //docs:sync`** or **`mise //docs:build`** (do not edit `docs/src/content/docs/` by hand) |
25+
| Architecture decisions (ADRs) | `docs/decisions/` | Run **`mise //docs:sync`** after adding or editing an ADR |
2526
| Monorepo tasks, CI glue | Root `mise.toml`, `scripts/`, `.github/workflows/` ||
2627

2728
### CDK handler tests (quick map)
@@ -47,6 +48,7 @@ Handler entry tests: `cdk/test/handlers/orchestrate-task.test.ts`, `create-task.
4748
- **`prek install`** fails if Git **`core.hooksPath`** is set — another hook manager owns hooks; see [CONTRIBUTING.md](./CONTRIBUTING.md).
4849
- **Editing on `main` directly** — ALWAYS create a worktree with a feature branch for changes, even trivial ones. Main should stay clean; all work flows through worktree → branch → PR → merge.
4950
- **Git worktrees** — Always **`git fetch origin main`** before creating a new worktree to ensure you branch from the latest remote state. `node_modules/` and `agent/.venv/` are per-tree (not shared). Run **`mise run install`** in each new worktree before building. All CDK path references (`__dirname`-relative) and mise `config_roots` resolve correctly without extra setup.
51+
- **Bumping Cedar engines in isolation**`cedarpy` (Python, `agent/pyproject.toml`) and `@cedar-policy/cedar-wasm` (TypeScript, `cdk/package.json`) are two language bindings over the same Cedar Rust core. They MUST move together; even patch-version drift between bindings can yield divergent `(decision, matching_rule_ids)` on the same `(policy, input)` — invisible to per-side unit tests, caught (only) by `contracts/cedar-parity/` golden fixtures in CI. If you bump one engine you MUST bump the other to a tested-compatible version AND refresh the parity fixtures in the same commit. Both pins are EXACT (no `^`/`~`). See `docs/design/CEDAR_HITL_GATES.md` §15.6 (decision #23) and the parity-contract banner in `mise.toml`. **DO NOT** accept upstream's "Update branch" or auto-merge suggestions on cedarpy without verifying parity with cedar-wasm.
5052

5153
### Tech stack
5254

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Guidelines:
2828
- If you change API types in `cdk/src/handlers/shared/types.ts`, update `cli/src/types.ts` to match.
2929
- If you change docs sources (`docs/guides/`, `docs/design/`), run `mise //docs:sync` so generated content stays in sync.
3030
- For significant features, add a design document to `docs/design/`.
31+
- For cross-cutting or hard-to-reverse decisions, add an ADR to `docs/decisions/` (see [ADR README](./docs/decisions/README.md)).
3132

3233
### 4. Commit
3334

agent/.dockerignore

Lines changed: 0 additions & 6 deletions
This file was deleted.

agent/Dockerfile

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,37 @@ FROM --platform=$TARGETPLATFORM golang:1.26.3-bookworm AS gh-builder
88
ARG GH_VERSION
99
RUN GOPROXY=direct GOBIN=/out go install "github.com/cli/cli/v2/cmd/gh@v${GH_VERSION}"
1010

11-
FROM --platform=$TARGETPLATFORM python:3.13-slim
11+
FROM --platform=$TARGETPLATFORM python:3.13-slim@sha256:dc1546eefcbe8caaa1f004f16ab76b204b5e1dbd58ff81b899f21cd40541232f
1212

1313
# Install mise (polyglot dev tool manager)
1414
COPY --from=mise /usr/local/bin/mise /usr/local/bin/mise
1515
COPY --from=gh-builder /out/gh /usr/local/bin/gh
1616

1717
# Install system dependencies in multiple layers:
18-
# - Node.js 20 (required by Claude Code CLI)
18+
# - Node.js 24 LTS (required by Claude Code CLI)
1919
# - git (repo operations)
2020
# - build-essential (native compilation for some repos)
2121
# - curl (downloads)
2222
RUN apt-get update && \
23+
# Patch any base-image CVEs that have a fix available in the
24+
# current Debian point release. Without this, transitive system-
25+
# library CVEs (e.g. libnghttp2 CVE-2026-27135) ride the base
26+
# ``python:3.13-slim`` tag until upstream rebuilds, which can be
27+
# weeks. ``--no-install-recommends`` keeps the upgrade narrow and
28+
# reproducible — only already-installed packages get bumped.
29+
apt-get upgrade -y --no-install-recommends && \
2330
apt-get install -y --no-install-recommends \
2431
curl \
2532
git \
2633
build-essential \
2734
ca-certificates \
2835
gnupg && \
29-
# Upgrade base image's CVE-2026-27135 vulnerability
30-
apt-get upgrade -y --no-install-recommends libnghttp2-14 && \
3136
# Cleanup early to keep peak disk usage low during builds.
3237
apt-get clean && \
3338
rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
3439

35-
# Node.js 20
36-
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
40+
# Node.js 24 LTS (https://nodejs.org/en/about/previous-releases)
41+
RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - && \
3742
apt-get update && \
3843
apt-get install -y --no-install-recommends nodejs && \
3944
apt-get clean && \
@@ -42,22 +47,35 @@ RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
4247
# Install Claude Code CLI (the Python SDK requires this binary)
4348
# Then update known vulnerable transitive packages where fixed versions exist.
4449
RUN npm install -g npm@latest && \
45-
npm install -g @anthropic-ai/claude-code@2.1.139 && \
50+
npm install -g @anthropic-ai/claude-code@2.1.142 && \
4651
CLAUDE_NPM_ROOT="$(npm root -g)/@anthropic-ai/claude-code" && \
4752
npm --prefix "${CLAUDE_NPM_ROOT}" update tar minimatch glob cross-spawn picomatch
4853

49-
# Install uv (fast Python package manager)
50-
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
54+
# Install uv (fast Python package manager) — pinned for reproducibility
55+
COPY --from=ghcr.io/astral-sh/uv:0.11.14 /uv /usr/local/bin/uv
5156

52-
# Install Python dependencies via uv
53-
COPY pyproject.toml uv.lock /app/
57+
# Install Python dependencies via uv. Build context is repo root (set in
58+
# ``cdk/src/stacks/agent.ts``) so source paths are prefixed with ``agent/``.
59+
COPY agent/pyproject.toml agent/uv.lock /app/
5460
RUN uv sync --frozen --no-dev --directory /app
5561

5662
# Copy agent code (ARG busts cache so file edits are always picked up)
5763
ARG CACHE_BUST=0
58-
COPY src/ /app/src/
59-
COPY prepare-commit-msg.sh /app/
60-
COPY test_sdk_smoke.py test_subprocess_threading.py /app/
64+
COPY agent/src/ /app/src/
65+
# Cedar HITL built-in policy files (hard_deny.cedar + soft_deny.cedar).
66+
# ``agent/src/policy.py::_POLICIES_DIR`` resolves to ``/app/policies``
67+
# at import time; without these files the PolicyEngine init raises
68+
# ``missing built-in hard-deny policies`` and every task fails at 0
69+
# turns before the agent even connects to the CLI. Discovered during
70+
# Chunk 10 E2E T2.2 — the Dockerfile previously only copied ``src/``.
71+
COPY agent/policies/ /app/policies/
72+
# Cross-language constants (S9). ``agent/src/policy.py`` reads
73+
# ``/app/contracts/constants.json`` at import; the same file is consumed
74+
# by ``cdk/src/handlers/shared/types.ts`` at synth time. See
75+
# ``contracts/README.md`` for the contract.
76+
COPY contracts/ /app/contracts/
77+
COPY agent/prepare-commit-msg.sh /app/
78+
COPY agent/test_sdk_smoke.py agent/test_subprocess_threading.py /app/
6179

6280
# Create non-root user (Claude Code CLI refuses bypassPermissions as root)
6381
RUN useradd -m -s /bin/bash agent && \

agent/mise.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ run = "uvx ruff format --check"
3838

3939
[tasks.typecheck]
4040
description = "Type check with ty"
41-
run = "uvx ty check"
41+
run = "uv run ty check"
4242

4343
[tasks.test]
4444
description = "Run tests with pytest"
@@ -52,8 +52,12 @@ run = "uvx bandit[toml] -c pyproject.toml -r . --severity-level=high"
5252

5353
[tasks."security:image"]
5454
description = "Scan container image with trivy"
55+
# Build context is repo root (..) so Dockerfile can COPY contracts/
56+
# alongside agent/ — matches cdk/src/stacks/agent.ts. Without -f and
57+
# the .. context, the build fails because COPY agent/... can't find
58+
# agent/ inside the agent/ directory.
5559
run = [
56-
"docker image inspect bgagent-local:latest >/dev/null 2>&1 || (ARCH=\"$(uname -m)\"; PLATFORM=\"linux/arm64\"; if [ \"$ARCH\" = \"x86_64\" ]; then PLATFORM=\"linux/amd64\"; fi; docker build --build-arg TARGETPLATFORM=\"$PLATFORM\" --build-arg CACHE_BUST=\"$(date +%s)\" -t bgagent-local:latest .)",
60+
"ARCH=\"$(uname -m)\"; PLATFORM=\"linux/arm64\"; if [ \"$ARCH\" = \"x86_64\" ]; then PLATFORM=\"linux/amd64\"; fi; docker build --build-arg TARGETPLATFORM=\"$PLATFORM\" --build-arg CACHE_BUST=\"$(date +%s)\" -f Dockerfile -t bgagent-local:latest ..",
5761
"trivy image --scanners vuln --ignore-unfixed --ignorefile .trivyignore --severity HIGH,CRITICAL --exit-code 1 bgagent-local:latest",
5862
]
5963

agent/policies/hard_deny.cedar

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Built-in hard-deny policy set for Cedar HITL engine.
2+
//
3+
// Hard-deny is ABSOLUTE: no --pre-approve scope and no blueprint `disable:`
4+
// directive can bypass these rules. See docs/design/CEDAR_HITL_GATES.md
5+
// §12.5 and decision #8.
6+
//
7+
// Every rule in this file MUST carry @tier("hard") + @rule_id annotations.
8+
// Adding a rule here expands the set of categorically-forbidden agent
9+
// actions; removing a rule requires a security review.
10+
11+
// Base catch-all permit. Specific forbid rules below override.
12+
@rule_id("base_permit")
13+
permit (principal, action, resource);
14+
15+
// pr_review tasks may never invoke Write. Absolute; cannot be overridden
16+
// by per-blueprint customization or --pre-approve.
17+
@tier("hard")
18+
@rule_id("pr_review_forbid_write")
19+
forbid (
20+
principal == Agent::TaskAgent::"pr_review",
21+
action == Agent::Action::"invoke_tool",
22+
resource == Agent::Tool::"Write"
23+
);
24+
25+
// pr_review tasks may never invoke Edit.
26+
@tier("hard")
27+
@rule_id("pr_review_forbid_edit")
28+
forbid (
29+
principal == Agent::TaskAgent::"pr_review",
30+
action == Agent::Action::"invoke_tool",
31+
resource == Agent::Tool::"Edit"
32+
);
33+
34+
// Reject `rm -rf /` and similar absolute-root destructive commands.
35+
@tier("hard")
36+
@rule_id("rm_slash")
37+
forbid (principal, action == Agent::Action::"execute_bash", resource)
38+
when { context.command like "*rm -rf /*" };
39+
40+
// Reject writes into `.git/` at the repo root (breaks local git state).
41+
@tier("hard")
42+
@rule_id("write_git_internals")
43+
forbid (principal, action == Agent::Action::"write_file", resource)
44+
when { context.file_path like ".git/*" };
45+
46+
// Reject writes into nested `.git/` directories (submodules, worktrees).
47+
@tier("hard")
48+
@rule_id("write_git_internals_nested")
49+
forbid (principal, action == Agent::Action::"write_file", resource)
50+
when { context.file_path like "*/.git/*" };
51+
52+
// Reject any SQL DROP TABLE through Bash — agents should not be running
53+
// destructive DDL against production or dev databases without a human
54+
// in the loop. Hard-deny because even "just testing locally" is a common
55+
// vector for data loss (wrong DB connected via saved credentials).
56+
@tier("hard")
57+
@rule_id("drop_table")
58+
forbid (principal, action == Agent::Action::"execute_bash", resource)
59+
when { context.command like "*DROP TABLE*" };

0 commit comments

Comments
 (0)