Skip to content

Commit d554f29

Browse files
authored
Merge pull request #60 from Blazity/dev
Sync dev
2 parents 98a8479 + b66d517 commit d554f29

54 files changed

Lines changed: 8063 additions & 1326 deletions

Some content is hidden

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

.claude/learnings.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@
88
- `@vercel/sandbox` git clones can be shallow by default, causing "no history in common with main" on PR creation when force-pushing from the sandbox. Always unshallow before pushing (`git fetch --unshallow origin`).
99
- `GitHubAdapter.createBranch` must force-reset existing branches to the base SHA on 422, not silently return. Stale branches from previous failed runs can retain orphan history.
1010

11+
## Jira adapter
12+
- Jira REST v3 comments require ADF, and **ADF text nodes cannot contain `\n`**. Multi-line content must be modeled as multiple paragraph nodes (or use `hardBreak` inline nodes between text nodes). Stuffing newline-joined text into a single text node returns 400 Bad Request on `/rest/api/3/issue/{id}/comment`. Adapter helper `toAdfParagraphs` splits on `\n` and emits one paragraph per line.
13+
14+
## Codex agent in Vercel Sandbox
15+
- The `using-git-worktrees` superpowers skill is the dominant root cause of empty-PR / `.gitignore`-only commits, NOT `.codex/` pollution. The skill's contract: "If the worktree directory is not in .gitignore, add it and commit before proceeding." The `executing-plans` skill REQUIRES `using-git-worktrees`, so any prompt that tells the agent to use `executing-plans` chains into "modify .gitignore + commit" before any real implementation work. Confirmed on AWT-641 / AWT-642. Mitigation: prompts (research + implement) explicitly forbid invoking `using-git-worktrees`/`executing-plans` and forbid any `git worktree` command or `.gitignore` change. The block is in the prompt body and must override conflicting skill text.
16+
- Codex CLI also creates `.codex/` in cwd at runtime (per-session state). Without intervention, the agent sees it as untracked pollution in `git status`, "fixes" it by adding `.codex/` to `.gitignore`, commits only that. Mitigation: `CodexAgentAdapter.configure` writes `.codex/` to `.git/info/exclude` so it's hidden from the agent's git status. The post-phase cleanup `rm -rf .codex/` exists for the same reason but only runs after the agent exits.
17+
- `extractUsage` cannot derive duration from Codex NDJSON event timestamps (the events do not carry `timestamp`/`ts`/`time` keys). The wrapper script appends a synthetic `{"type":"phase.duration","duration_ms":N}` line to stdout as a fallback so Slack reports show real wall-clock minutes instead of `0m`.
18+
- Codex Stop-hook protocol accepts BOTH `{"decision":"block","reason":"..."}` (legacy) and `{"continue":true,"stopReason":"..."}` (new) on stdout with exit 0 to force the agent to take another turn — confirmed against developers.openai.com/codex/hooks. Either format works; the codebase uses the legacy one for parity with the Claude commit-guard.
19+
- `fixAndRetryPush` must dispatch the configured agent's CLI, not hardcode `claude`. When AGENT_KIND=codex the claude binary isn't installed and `|| true` swallows the failure, leaving the same broken HEAD to be force-pushed.
20+
1121
## E2E in GitHub Actions
1222
- `@vercel/sandbox` reads credentials from `process.env` — a GH secret is not enough; it must be mapped via the job's `env:` block (e.g. `VERCEL_OIDC_TOKEN: ${{ secrets.VERCEL_OIDC_TOKEN }}`). Prefer long-lived `VERCEL_TOKEN` + `VERCEL_TEAM_ID` + `VERCEL_PROJECT_ID` over OIDC — OIDC tokens expire in ~12h and the SDK's refresh path requires `.vercel/project.json`, which CI doesn't have.
1323
- Reconcile (`src/lib/reconcile.ts`) has a 30s `ORPHAN_GRACE_MS` window that skips entries younger than 30s. Any e2e test seeding a registry entry via `setEntry` and expecting reconcile to cancel it on the next cron tick must backdate the timestamp past the grace window (`setEntry(key, runId, { ageMs: 60_000 })`). Without backdating the test is racy — it only passes if Vercel's 1-min scheduled cron happens to fire at T>30s during the test's wait window.

.env.e2e.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,10 @@ AI_WORKFLOW_KV_REST_API_TOKEN=
2525

2626
# Vercel Deployment Protection bypass (optional, needed for preview URLs)
2727
# VERCEL_AUTOMATION_BYPASS_SECRET=
28+
29+
# Vercel Sandbox credentials for the e2e runner (Sandbox.list / Sandbox.create
30+
# in e2e/helpers/sandbox.ts and us02). Set all three to use a long-lived PAT
31+
# instead of re-pasting a rotating OIDC token from `vercel env pull`.
32+
# VERCEL_TOKEN=
33+
# VERCEL_TEAM_ID=
34+
# VERCEL_PROJECT_ID=

.env.example

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,23 @@ CLAUDE_MODEL=claude-opus-4-6
3636
COMMIT_AUTHOR=ai-workflow-blazity
3737
COMMIT_EMAIL=ai-workflow@blazity.com
3838

39+
# Agent — choose runtime (claude | codex). Defaults to claude.
40+
AGENT_KIND=claude
41+
42+
# Codex (only when AGENT_KIND=codex)
43+
# CODEX_API_KEY=
44+
# CODEX_CHATGPT_OAUTH_TOKEN= # alternative to CODEX_API_KEY
45+
# CODEX_MODEL=gpt-5-codex
46+
# CODEX_PRICING_URL=https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json
47+
# CODEX_PRICING_TTL_MS=3600000
48+
49+
# Arthur AI Engine (optional — tracing + hosted prompts)
50+
# Set both API_KEY and TRACE_ENDPOINT to install the tracer into every sandbox.
51+
# Set PROMPT_TASK_ID after running `npx tsx scripts/setup-arthur-prompts.ts`.
52+
# GENAI_ENGINE_API_KEY=
53+
# GENAI_ENGINE_TRACE_ENDPOINT=https://your-arthur-host/api/v1/traces
54+
# GENAI_ENGINE_PROMPT_TASK_ID=
55+
3956
# Sandbox
4057
MAX_CONCURRENT_AGENTS=3
4158
JOB_TIMEOUT_MS=1800000

.github/workflows/ci.yml

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ jobs:
4747
AI_WORKFLOW_KV_REST_API_URL: ${{ secrets.AI_WORKFLOW_KV_REST_API_URL }}
4848
AI_WORKFLOW_KV_REST_API_TOKEN: ${{ secrets.AI_WORKFLOW_KV_REST_API_TOKEN }}
4949
VERCEL_AUTOMATION_BYPASS_SECRET: ${{ secrets.VERCEL_AUTOMATION_BYPASS_SECRET }}
50-
VERCEL_OIDC_TOKEN: ${{ secrets.VERCEL_OIDC_TOKEN }}
50+
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
51+
VERCEL_TEAM_ID: ${{ secrets.VERCEL_TEAM_ID }}
52+
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
5153
MAX_CONCURRENT_AGENTS: ${{ secrets.MAX_CONCURRENT_AGENTS }}
5254
steps:
5355
- uses: actions/checkout@v4
@@ -59,6 +61,11 @@ jobs:
5961
node-version: "20.12"
6062
cache: pnpm
6163
- run: pnpm install --frozen-lockfile
64+
- name: Verify Vercel Sandbox credentials present
65+
run: |
66+
: "${VERCEL_TOKEN:?VERCEL_TOKEN missing in e2e environment}"
67+
: "${VERCEL_TEAM_ID:?VERCEL_TEAM_ID missing in e2e environment}"
68+
: "${VERCEL_PROJECT_ID:?VERCEL_PROJECT_ID missing in e2e environment}"
6269
- run: pnpm run test:e2e:orchestration
6370
- if: failure()
6471
uses: actions/upload-artifact@v4
@@ -90,7 +97,9 @@ jobs:
9097
AI_WORKFLOW_KV_REST_API_URL: ${{ secrets.AI_WORKFLOW_KV_REST_API_URL }}
9198
AI_WORKFLOW_KV_REST_API_TOKEN: ${{ secrets.AI_WORKFLOW_KV_REST_API_TOKEN }}
9299
VERCEL_AUTOMATION_BYPASS_SECRET: ${{ secrets.VERCEL_AUTOMATION_BYPASS_SECRET }}
93-
VERCEL_OIDC_TOKEN: ${{ secrets.VERCEL_OIDC_TOKEN }}
100+
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
101+
VERCEL_TEAM_ID: ${{ secrets.VERCEL_TEAM_ID }}
102+
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
94103
MAX_CONCURRENT_AGENTS: ${{ secrets.MAX_CONCURRENT_AGENTS }}
95104
steps:
96105
- uses: actions/checkout@v4
@@ -102,6 +111,11 @@ jobs:
102111
node-version: "20.12"
103112
cache: pnpm
104113
- run: pnpm install --frozen-lockfile
114+
- name: Verify Vercel Sandbox credentials present
115+
run: |
116+
: "${VERCEL_TOKEN:?VERCEL_TOKEN missing in e2e environment}"
117+
: "${VERCEL_TEAM_ID:?VERCEL_TEAM_ID missing in e2e environment}"
118+
: "${VERCEL_PROJECT_ID:?VERCEL_PROJECT_ID missing in e2e environment}"
105119
- run: pnpm run test:e2e:capacity
106120
- if: failure()
107121
uses: actions/upload-artifact@v4
@@ -133,7 +147,9 @@ jobs:
133147
AI_WORKFLOW_KV_REST_API_URL: ${{ secrets.AI_WORKFLOW_KV_REST_API_URL }}
134148
AI_WORKFLOW_KV_REST_API_TOKEN: ${{ secrets.AI_WORKFLOW_KV_REST_API_TOKEN }}
135149
VERCEL_AUTOMATION_BYPASS_SECRET: ${{ secrets.VERCEL_AUTOMATION_BYPASS_SECRET }}
136-
VERCEL_OIDC_TOKEN: ${{ secrets.VERCEL_OIDC_TOKEN }}
150+
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
151+
VERCEL_TEAM_ID: ${{ secrets.VERCEL_TEAM_ID }}
152+
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
137153
MAX_CONCURRENT_AGENTS: ${{ secrets.MAX_CONCURRENT_AGENTS }}
138154
steps:
139155
- uses: actions/checkout@v4
@@ -145,6 +161,11 @@ jobs:
145161
node-version: "20.12"
146162
cache: pnpm
147163
- run: pnpm install --frozen-lockfile
164+
- name: Verify Vercel Sandbox credentials present
165+
run: |
166+
: "${VERCEL_TOKEN:?VERCEL_TOKEN missing in e2e environment}"
167+
: "${VERCEL_TEAM_ID:?VERCEL_TEAM_ID missing in e2e environment}"
168+
: "${VERCEL_PROJECT_ID:?VERCEL_PROJECT_ID missing in e2e environment}"
148169
- run: pnpm run test:e2e:agent
149170
- if: failure()
150171
uses: actions/upload-artifact@v4

.github/workflows/e2e.yml

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ on:
1212
- agent
1313
- all
1414
default: all
15+
agent:
16+
description: "Which agent the agent-tier should run under (per-ticket label override)"
17+
type: choice
18+
options:
19+
- claude
20+
- codex
21+
default: claude
1522

1623
jobs:
1724
e2e-orchestration:
@@ -36,7 +43,9 @@ jobs:
3643
AI_WORKFLOW_KV_REST_API_URL: ${{ secrets.AI_WORKFLOW_KV_REST_API_URL }}
3744
AI_WORKFLOW_KV_REST_API_TOKEN: ${{ secrets.AI_WORKFLOW_KV_REST_API_TOKEN }}
3845
VERCEL_AUTOMATION_BYPASS_SECRET: ${{ secrets.VERCEL_AUTOMATION_BYPASS_SECRET }}
39-
VERCEL_OIDC_TOKEN: ${{ secrets.VERCEL_OIDC_TOKEN }}
46+
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
47+
VERCEL_TEAM_ID: ${{ secrets.VERCEL_TEAM_ID }}
48+
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
4049
MAX_CONCURRENT_AGENTS: ${{ secrets.MAX_CONCURRENT_AGENTS }}
4150
steps:
4251
- uses: actions/checkout@v4
@@ -48,6 +57,11 @@ jobs:
4857
node-version: "20.12"
4958
cache: pnpm
5059
- run: pnpm install --frozen-lockfile
60+
- name: Verify Vercel Sandbox credentials present
61+
run: |
62+
: "${VERCEL_TOKEN:?VERCEL_TOKEN missing in e2e environment}"
63+
: "${VERCEL_TEAM_ID:?VERCEL_TEAM_ID missing in e2e environment}"
64+
: "${VERCEL_PROJECT_ID:?VERCEL_PROJECT_ID missing in e2e environment}"
5165
- run: pnpm run test:e2e:orchestration
5266
- if: failure()
5367
uses: actions/upload-artifact@v4
@@ -83,7 +97,9 @@ jobs:
8397
AI_WORKFLOW_KV_REST_API_URL: ${{ secrets.AI_WORKFLOW_KV_REST_API_URL }}
8498
AI_WORKFLOW_KV_REST_API_TOKEN: ${{ secrets.AI_WORKFLOW_KV_REST_API_TOKEN }}
8599
VERCEL_AUTOMATION_BYPASS_SECRET: ${{ secrets.VERCEL_AUTOMATION_BYPASS_SECRET }}
86-
VERCEL_OIDC_TOKEN: ${{ secrets.VERCEL_OIDC_TOKEN }}
100+
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
101+
VERCEL_TEAM_ID: ${{ secrets.VERCEL_TEAM_ID }}
102+
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
87103
MAX_CONCURRENT_AGENTS: ${{ secrets.MAX_CONCURRENT_AGENTS }}
88104
steps:
89105
- uses: actions/checkout@v4
@@ -95,6 +111,11 @@ jobs:
95111
node-version: "20.12"
96112
cache: pnpm
97113
- run: pnpm install --frozen-lockfile
114+
- name: Verify Vercel Sandbox credentials present
115+
run: |
116+
: "${VERCEL_TOKEN:?VERCEL_TOKEN missing in e2e environment}"
117+
: "${VERCEL_TEAM_ID:?VERCEL_TEAM_ID missing in e2e environment}"
118+
: "${VERCEL_PROJECT_ID:?VERCEL_PROJECT_ID missing in e2e environment}"
98119
- run: pnpm run test:e2e:capacity
99120
- if: failure()
100121
uses: actions/upload-artifact@v4
@@ -130,8 +151,11 @@ jobs:
130151
AI_WORKFLOW_KV_REST_API_URL: ${{ secrets.AI_WORKFLOW_KV_REST_API_URL }}
131152
AI_WORKFLOW_KV_REST_API_TOKEN: ${{ secrets.AI_WORKFLOW_KV_REST_API_TOKEN }}
132153
VERCEL_AUTOMATION_BYPASS_SECRET: ${{ secrets.VERCEL_AUTOMATION_BYPASS_SECRET }}
133-
VERCEL_OIDC_TOKEN: ${{ secrets.VERCEL_OIDC_TOKEN }}
154+
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
155+
VERCEL_TEAM_ID: ${{ secrets.VERCEL_TEAM_ID }}
156+
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
134157
MAX_CONCURRENT_AGENTS: ${{ secrets.MAX_CONCURRENT_AGENTS }}
158+
E2E_AGENT_KIND: ${{ inputs.agent }}
135159
steps:
136160
- uses: actions/checkout@v4
137161
- uses: pnpm/action-setup@v4
@@ -142,6 +166,11 @@ jobs:
142166
node-version: "20.12"
143167
cache: pnpm
144168
- run: pnpm install --frozen-lockfile
169+
- name: Verify Vercel Sandbox credentials present
170+
run: |
171+
: "${VERCEL_TOKEN:?VERCEL_TOKEN missing in e2e environment}"
172+
: "${VERCEL_TEAM_ID:?VERCEL_TEAM_ID missing in e2e environment}"
173+
: "${VERCEL_PROJECT_ID:?VERCEL_PROJECT_ID missing in e2e environment}"
145174
- run: pnpm run test:e2e:agent
146175
- if: failure()
147176
uses: actions/upload-artifact@v4

README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,23 @@ COMMIT_AUTHOR=ai-workflow-blazity # Git commit author name
142142
COMMIT_EMAIL=ai-workflow@blazity.com # Git commit author email
143143
```
144144

145+
**Switching agents** — Blazebot supports two CLI runtimes. Set `AGENT_KIND` once per deployment:
146+
147+
```bash
148+
AGENT_KIND=claude # default — Anthropic Claude Code
149+
# or
150+
AGENT_KIND=codex # OpenAI Codex CLI
151+
```
152+
153+
When `AGENT_KIND=codex`:
154+
155+
```bash
156+
CODEX_API_KEY=sk-codex-xxxxxxxxxxxx # or CODEX_CHATGPT_OAUTH_TOKEN
157+
CODEX_MODEL=gpt-5-codex # default
158+
```
159+
160+
Pricing is fetched from [LiteLLM's community-maintained JSON](https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json) on each cold start (1h TTL by default). Override `CODEX_PRICING_URL` in air-gapped environments. When pricing is unavailable, Slack reports show tokens-only with `cost unknown`.
161+
145162
**Sandbox** — Concurrency and timeout limits:
146163
```bash
147164
MAX_CONCURRENT_AGENTS=3 # Max parallel sandboxes (default: 3)
@@ -222,8 +239,15 @@ curl -H "Authorization: Bearer $CRON_SECRET" http://localhost:3000/cron/poll
222239
| `CHAT_SDK_CHANNEL_ID` | Yes || Notification channel ID |
223240
| `CHAT_SDK_BOT_NAME` | No | `blazebot` | Bot display name |
224241
| **Agent** | | | |
225-
| `ANTHROPIC_API_KEY` | Yes || Anthropic API key |
242+
| `AGENT_KIND` | No | `claude` | Runtime: `claude` or `codex` |
243+
| `ANTHROPIC_API_KEY` | Yes* || Anthropic API key (required when `AGENT_KIND=claude`) |
244+
| `CLAUDE_CODE_OAUTH_TOKEN` | No || Alternative to `ANTHROPIC_API_KEY` |
226245
| `CLAUDE_MODEL` | No | `claude-opus-4-6` | Claude model ID |
246+
| `CODEX_API_KEY` | Yes* || OpenAI Codex API key (required when `AGENT_KIND=codex`) |
247+
| `CODEX_CHATGPT_OAUTH_TOKEN` | No || Alternative to `CODEX_API_KEY` |
248+
| `CODEX_MODEL` | No | `gpt-5-codex` | Codex model ID |
249+
| `CODEX_PRICING_URL` | No | LiteLLM JSON | Pricing source for Codex cost reporting |
250+
| `CODEX_PRICING_TTL_MS` | No | `3600000` | Pricing cache TTL (ms) |
227251
| `COMMIT_AUTHOR` | No | `ai-workflow-blazity` | Git author name |
228252
| `COMMIT_EMAIL` | No | `ai-workflow@blazity.com` | Git author email |
229253
| **Sandbox** | | | |

0 commit comments

Comments
 (0)