v0
13 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d3b5340583 |
fix: audit batch — MCP timeouts, entryPost, vip_audit 404s, and 6 more (#824)
* fix: 9 unaddressed log-audit / run-audit findings Co-authored-by: Cursor <cursoragent@cursor.com> #815 entryPost stdlib-only imports; #823 MCP timeoutMs on checkout_pr/shell; #816 FREE_FALLBACK → opencode/big-pickle; #822 chunk GraphQL nodes ≤100; #817/#821 vip_audit 404 skip paths; #813 longer serializable retries; #818 run-context handler-entered log; #805 audit severity template. * fix: update footer test for big-pickle fallback slug Co-authored-by: Cursor <cursoragent@cursor.com> * fix: anneal round 1 — ghaCore getState casing, post-hook timeout Match @actions/core STATE_ key semantics (no uppercasing), cap postApiFetch at 30s, trim serializable retries to stay under GitHub's 10s webhook window, log Clerk failures in getUserTokenByGithubLogin. Co-authored-by: Cursor <cursoragent@cursor.com> * revert: drop run-context handler log (#818 deferred) The #692 client-side fix is already on main; residual SyntaxError hits are old action pins. Per-request log added noise without fixing anything. Co-authored-by: Cursor <cursoragent@cursor.com> * document per-issue Closes syntax for audit PRs GitHub only auto-closes the first issue when numbers are comma-separated; /audits and AGENTS.md now require Closes before each issue number. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: anneal round 2 — outreach privacy, alert resilience, vertex cleanup Filter private repos from VIP authority output, harden console alert lines against DB failures, drop spoofable changesets body check, and unset GOOGLE_APPLICATION_CREDENTIALS after vertex credential cleanup. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor: drop codexHome re-export of detectCodexRefresh Import detectCodexRefresh directly from codexRefreshDetect.ts everywhere; rename the unit test file to match. codexHome.ts stays install-only. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: drop deprecated minimax-m2.5-free; add paid minimax-m2.5 Remove the deprecated free MiniMax promo from the catalog, docs, and tests. BYOK fallback and picker copy stay on opencode/big-pickle. Add opencode/minimax-m2.5 and openrouter/minimax-m2.5 for Zen BYOK and Router. Pin #816 regressions with freeFallbackCatalog and runErrorRenderer unit tests. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: hidden minimax-m2.5-free fallback for stored slugs Re-add opencode/minimax-m2.5-free as a hidden fallback alias to big-pickle so repos with the legacy slug still resolve as free. Drop live Zen API experiment tests in freeFallbackCatalog.test.ts. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
7c5ed7add0 |
Rename OSS_PROXY_MODEL to DEFAULT_PROXY_MODEL; default Router proxy to Kimi K2.6.
Derive the platform default from moonshotai/kimi-k2 openRouterResolve, add models.dev drift coverage, and promote repos on default-branch workflow pushes when still needs_setup. |
||
|
|
c43ed65c3b |
Add Vertex AI routing support (#753)
* add Vertex AI routing support * include Vertex smokes in action CI |
||
|
|
8f9208bd3f |
feat: Amazon Bedrock support via routing slug (#720)
* add Amazon Bedrock as a routing slug introduces a single `bedrock/byok` catalog entry that the harness translates to the appropriate Bedrock model ID at run time via `BEDROCK_MODEL_ID`. routes Anthropic IDs through claude-code (with `CLAUDE_CODE_USE_BEDROCK=1`) and everything else through opencode's `amazon-bedrock` provider — keeps the catalog flat for an audience that needs version pinning rather than aliasing. accepts either `AWS_BEARER_TOKEN_BEDROCK` or `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY` for auth; both validated alongside `AWS_REGION` and `BEDROCK_MODEL_ID` in `validateAgentApiKey`. catalog drift tests, the bumps cron, and per-alias smoke scripts all skip routing slugs since there's no fixed `resolve` to validate. docs/bedrock.mdx walks through setup; wiki/model-resolution.md has a section explaining why bedrock breaks the usual alias pattern. closes pullfrog/pullfrog#40 * ci: add bedrock env vars to test workflows mirrors the new bedrock provider's required env vars (AWS_BEARER_TOKEN_BEDROCK inherited from org secret + AWS_REGION + BEDROCK_MODEL_ID hardcoded) into both .github/workflows/test.yml files so the ci.test "env vars cover all provider API keys" assertion passes. * docs(bedrock): clearer setup flow + screenshot of model selector restructures the setup section into three concrete steps in execution order: select Bedrock from the dropdown, store the bearer token as a secret (Pullfrog or GitHub — links to keys.mdx for the trade-off), then add region + model id directly in pullfrog.yml since neither is sensitive. enable-model-access in the Bedrock console moved to step 4 (only required once per model and only when AWS rejects the call, not blocking on first run). adds a screenshot of the console model selector with Amazon Bedrock selected so readers can recognize the UI state they're aiming for. * fix(bedrock): tolerate raw Bedrock model IDs in validateAgentApiKey main.ts passes the resolved model into validateAgentApiKey (`payload.proxyModel ?? resolvedModel ?? payload.model`). For Bedrock, `resolveModel` translates `bedrock/byok` into the raw AWS model ID (e.g. `us.anthropic.claude-opus-4-6-v1`), which has no `/` and so trips parseModel inside getModelEnvVars. Detect the no-slash case and re-run the bedrock setup check (auth + region; BEDROCK_MODEL_ID is already enforced upstream by resolveModel). Caught by PR #720 e2e dispatch on pullfrog/preview-720-bedrock — "invalid model slug 'us.anthropic.claude-opus-4-6-v1' — expected 'provider/model'". Two regression tests cover the raw-ID path. * fix(bedrock): always prepend amazon-bedrock/ prefix when bedrock-routed opencode.ts was gating the prefix-injection on `!isBedrockAnthropicId(rawModel)`, on the theory that Anthropic Bedrock IDs always go through claude-code. But `PULLFROG_AGENT=opencode` is a documented escape hatch — when it forces opencode for an Anthropic Bedrock model, the prefix still has to be added or opencode fails with 'Model not found: <modelId>/.'. The Anthropic-vs-other discriminant only belongs in resolveAgent. Once an agent is selected, it should consistently honor the bedrock route. Caught by the PULLFROG_AGENT=opencode + Opus 4.6 e2e on pullfrog/preview-720-bedrock — run 25823437606. * ui+docs(bedrock): bespoke setup callout + clearer docs UI: - BedrockSetupCallout in components/AgentSettings.tsx covers both the Model costs section and the onboarding card. Detects bedrock via resolveDisplayAlias().routing === "bedrock", shows a dedicated message ("store AWS_BEARER_TOKEN_BEDROCK as a secret, then put AWS_REGION + BEDROCK_MODEL_ID directly in pullfrog.yml") + link to the setup guide. Replaces the generic "X, Y, or Z is required" prompt that misrepresented the three values as three separate secrets to add (and used the wrong "or" connector for what's actually an AND). - OnboardingCard re-uses the same callout with the gradient-card variant. Docs: - Drop the obsolete "Enable model access" step. AWS retired the manual enrollment page; foundation models auto-enable on first invocation. Anthropic models still need a one-time use-case form for first-time users — surfaced under the AccessDenied troubleshooting entry. - Drop the "Testing a different model in one run" PULLFROG_MODEL note. It introduced the secrets-vs-vars distinction we want to keep out of the bedrock setup story. - Step 3 already recommends hardcoding region + model id in pullfrog.yml. Workflow template: - The default pullfrog.yml customers receive (utils/github/pullfrog.yml.ts) now references AWS_BEARER_TOKEN_BEDROCK from secrets but inlines AWS_REGION and BEDROCK_MODEL_ID as plain values. Matches the docs. * fix(bedrock): three review-caught edges in routing + UI copy Addresses three real issues from PR #720 review: 1. agent.ts: PULLFROG_MODEL=bedrock/byok no longer leaks the literal sentinel "bedrock" downstream. resolveCliModel returns the alias's resolve field verbatim, which for routing entries IS the sentinel. Refactored both the env-override and slug-lookup paths through a shared resolveSlug() that recognizes routing aliases and defers to their backing env var (BEDROCK_MODEL_ID). 2. models.ts: isBedrockAnthropicId() now anchors on a discrete dot/slash/colon-segment match (case-insensitive) instead of a substring contains. The substring check was fragile in both directions for inference-profile ARNs (BEDROCK_MODEL_ID accepts ARNs per AWS docs) — a non-Anthropic profile whose user-chosen name contained "anthropic" would mis-route to claude-code, and an Anthropic profile whose name omitted it would miss CLAUDE_CODE_USE_BEDROCK=1. 3. AgentSettings.tsx: BedrockSetupCallout's configured-state copy showed "AWS_BEARER_TOKEN_BEDROCK configured" even when the user satisfied the gate via AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY, gaslighting access-key users about a secret they never set. Detect which auth method is actually present and name the right secret(s) in the success message. Regression tests in models.test.ts (5 new isBedrockAnthropicId cases including positive and negative ARN forms) and agent.test.ts (2 new PULLFROG_MODEL=bedrock/byok cases). 171/171 action tests pass. * yml template: add commented AWS access-key alternative for Bedrock auth Mirrors the IAM access-key path verified end-to-end on PR #720 e2e run 25830764987. Bearer token stays as the primary nudge; the access-key pair is the fallback for users who can't mint Bedrock API keys. * yml template: drop redundant 'or, alternatively' annotation * ui+docs(bedrock): rewrite callout copy + refresh screenshot Reframes the BedrockSetupCallout away from generic BYOK language to a Bedrock-specific message: leads with "Amazon Bedrock is configured entirely via environment variables", lists all four (auth, region, model id), and ends with the requested CTA sentence ("click below to learn more about Bedrock support in Pullfrog"). Promotes the "Bedrock setup guide" docs link from an inline anchor to a prominent button (always visible, regardless of auth state). The "Add AWS_BEARER_TOKEN_BEDROCK" affordance is now a secondary chip shown only when no auth secret is configured. Refreshes docs/images/model-selector-bedrock.png to capture the new callout — the prior screenshot still showed the old generic "BYOK / X, Y, or Z required" wording. |
||
|
|
d5d8a0d7ac |
fix(#691): drop opencode/gpt-5-nano + opencode/mimo-v2-pro-free (not actually keyless on Zen) (#695)
* remove opencode/gpt-5-nano and opencode/mimo-v2-pro-free from catalog #7 delete aliases. both were listed as `isFree: true, envVars: []` but neither is keyless on opencode zen, producing a hard-fail `UnknownError: Model not found: opencode/<id>` on every run without an opencode_api_key. fixes pullfrog/app#691 (5 runs across 3 repos, 100% failure rate in the last 24h). root cause: opencode's provider gate (`packages/opencode/src/provider/provider.ts` `opencode:` loader) keeps a zen model only when models.dev reports `cost.input === 0` for it, then signs requests with `apiKey: "public"`. paid zen models get deleted from the autoloaded set and opencode surfaces the deletion as "model not found". - `opencode/gpt-5-nano`: models.dev reports `cost: {input: 0.05, output: 0.4, cache_read: 0.005}`. paid → requires `OPENCODE_API_KEY`. - `opencode/mimo-v2-pro-free`: free on models.dev but not in `https://opencode.ai/zen/v1/models` — zen never served it, so even the public-key path fails. remaining free aliases (`opencode/big-pickle`, `opencode/minimax-m2.5-free`) both pass both checks (cost.input === 0 in models.dev AND present in zen's served list) and continue to work without a key — verified against the opencode source. callers swept: `action/utils/apiKeys.test.ts`, `action/models.test.ts`, `action/test/list-aliases.ts`, `action/test/model-smoke.ts`, `components/ModelSelector.tsx` (`modelIdToUpstream`), `wiki/model-resolution.md`, `wiki/models-catalog.md`. wrote up the free-zen verification rule in models-catalog so the next maintainer can sanity-check both conditions before adding any `isFree` alias. users with a stored `opencode/gpt-5-nano` or `opencode/mimo-v2-pro-free` will now fall through `resolveCliModel → undefined` into the auto-select path — a strict improvement over today's hard fail. no DB migration needed; the slugs are simply unknown and treated like any other unrecognized stored value. * rework: keep mimo deprecated, demote gpt-5-nano to paid, add free-zen invariants revised approach after the first commit over-corrected. mimo was never broken at runtime — `fallback: "opencode/big-pickle"` already routes stored values through to a real free model before any zen call. the literal `opencode/mimo-v2-pro-free` being absent from zen's served list is irrelevant because `resolveCliModel` walks the chain first. restoring it as-is. the actual bug was `opencode/gpt-5-nano`: marked `isFree: true, envVars: []` but `models.dev` reports `cost: {input: 0.05, output: 0.4}` on the opencode provider, so opencode's keyless gate (`packages/opencode/src/provider/provider.ts` `opencode:`) deletes it when `OPENCODE_API_KEY` is missing and the run hard-fails with `UnknownError: Model not found: opencode/gpt-5-nano`. demoting it to a regular paid zen alias (drop `isFree`/`envVars: []`, add `openRouterResolve: "openrouter/openai/gpt-5-nano"` — verified to exist on openrouter at the same price). users without `OPENCODE_API_KEY` now get our explicit "no API key found" error pointing at the secrets page instead of opencode's cryptic upstream error. confirmed via `https://opencode.ai/zen/v1/models` that zen serves no free GPT variants, so there's no cheaper-than-`gpt-mini` free option to suggest in its place. CI gap analysis (why this slipped through): - `models-catalog.main.test.ts` only checked existence + `status !== "deprecated"` on models.dev. paid-model-marked-free regressions and zen-served-list drift both passed. - `models-live` (`model-smoke.ts`) runs with `OPENCODE_API_KEY` in env, so the keyless deletion gate never fires. `gpt-5-nano` returned "OK" in CI even though end users hit a hard fail. - `model-smoke.ts` walks the fallback chain, so mimo would have been smoked as big-pickle anyway — the dead resolve target was never exercised directly. (this is the right design; the gap is at the catalog layer, not the smoke layer.) new tests: - PR-blocking, static (`action/test/models.test.ts`, `isFree invariants`): every `isFree` alias must live under `opencode`, have `envVars: []`, omit `openRouterResolve`, AND have a fallback chain whose terminal alias is also `isFree` (catches "deprecate a free alias to a paid target" — the worst silent-charge regression). - main-only, network (`action/test/models-catalog.main.test.ts`, `opencode Zen served list`): every alias whose terminal-fallback resolve is `opencode/*` must appear in `https://opencode.ai/zen/v1/models`. catches zen dropping a model from its served list. - main-only, network (same file, `isFree models.dev cost`): every `isFree` alias's terminal-fallback resolve must have `cost.input === 0` in the `opencode` provider block on `models.dev`. would have caught `gpt-5-nano` at the next models-bump run. both network tests dedupe on terminal resolve, so deprecated aliases sharing a target aren't double-counted. `pnpm vitest run`: 113 static tests pass. `pnpm test:catalog`: 142 network tests pass against the live `models.dev`, `openrouter.ai`, and `opencode.ai/zen/v1/models` endpoints. wiki/models-catalog.md: rewrote the new "Free-Zen aliases need Zen-side verification" section to (a) describe the two conditions, (b) note that a fallback to an isFree alias is the legitimate escape hatch (mimo's pattern), and (c) point at the three tests by name so the next maintainer can find the enforcement surface. wiki/model-resolution.md points at the new section. * make gpt-5-nano a deprecated free alias falling back to big-pickle revising the previous "demote to paid" approach. the user-facing ergonomics are cleaner: anyone who picked gpt-5-nano under the "Free" badge gets transparent-upgraded to a real free model (big-pickle) instead of suddenly being asked to set OPENCODE_API_KEY. matches the existing mimo pattern exactly. the dropdown already filters `!a.fallback`, so the slug disappears from the picker on its own and the trigger renders it as "Big Pickle" via `resolveDisplayAlias`. no other catalog or test surface changes — the isFree invariants and the main-only zen/cost checks still pass (gpt-5-nano's terminal is now big-pickle, which is both isFree and zero-cost on models.dev, deduping with big-pickle's own row in both network tests). * revise: keep gpt-5-nano as paid alias, backfill affected DB rows instead dropping the deprecated-alias approach. `opencode/gpt-5-nano` is a legitimate cheap paid model people may want with BYOK (`OPENCODE_API_KEY`) — giving it `fallback: "opencode/big-pickle"` would foreclose that for everyone going forward. correct fix is two parts: (a) reclassify in the catalog as a regular paid OpenCode alias: - drop `isFree: true` and `envVars: []` so the local validator demands `OPENCODE_API_KEY` - add `openRouterResolve: "openrouter/openai/gpt-5-nano"` to satisfy the completeness test and route BYOK-via-OpenRouter users - no `fallback` — slug stays visible in the picker as a paid option (b) one-shot DB backfill of provably-affected repos (`scripts/backfill-gpt5-nano-affected.ts`). scope: - `Repo.model = "opencode/gpt-5-nano"` - AND at least one `WorkflowRun` with `inputTokens IS NULL` (evidence of an attempted run that didn't get past the model-init gate) skipped intentionally: - repos whose runs have `inputTokens > 0` — they have a key, gpt-5- nano works for them - repos with zero WorkflowRun rows — never dispatched; touching them would be presumptuous - `LearningsRevision.model` — audit trail of which model authored a revision, rewriting it would falsify history ran against .env.prod: 2 repos stored the slug; 1 was provably affected (sodown4thecause/seobot, 5/5 zero-token runs — matches #691's 3 failed runs from this repo plus 2 outside the 24h audit window). 1 was an internal test account that never dispatched (left as-is). applied: 1 row updated. confirmed idempotent on re-run. the other two repos in #691 (Nantiee/ALTA-breast-pump-tool, keksiqc/ansible-setup-linux) don't store the slug in `Repo.model`; their failed dispatches passed the model inline in the `workflow_dispatch` `prompt` payload, so the catalog fix alone (no longer offering it as free) is what helps them. tests: - models.test.ts: `getModelEnvVars("opencode/gpt-5-nano")` now returns `["OPENCODE_API_KEY"]`, moved into the keyed-model group - apiKeys.test.ts: added "throws without OPENCODE_API_KEY" case - isFree invariants from the previous commit still pass — gpt-5-nano no longer triggers them since it's no longer isFree - main-only catalog tests still pass (gpt-5-nano served by Zen, just paid; no isFree cost check applies) * docs: drop stale GPT Nano + MiMo V2 Pro from free-tier lists addressing pullfrog auto-review feedback on #695. three mintlify pages still advertised both as keyless after the catalog pivot, which now makes the docs affirmatively wrong rather than merely stale: - gpt nano is paid in the catalog (no `isFree`, inherits `OPENCODE_API_KEY`); a user following the docs would hit the same "missing API key" failure that's described 4 lines below in `docs/keys.mdx`. - mimo v2 pro is hidden from the picker (`fallback` triggers `ModelSelector`'s `!a.fallback` filter); the alias only exists for legacy stored-value resolution. a user reading the docs cannot actually pick it. surviving picker-visible free set: Big Pickle and MiniMax M2.5. - `docs/keys.mdx`: drop both bullets from the "Free models" list - `docs/billing.mdx`: drop both bullets from the "Free models" list - `docs/getting-started.mdx`: collapse the inline mention from a 4-model list to "Big Pickle and MiniMax M2.5" * address third review: picker grouping + backfill classifier honesty i had not pulled the third pullfrog review (`02:17:28Z`) when i declared reviews triaged after the docs sweep — the fourth review flagged that three findings remained pending. addressing them now. 1. picker grouping for now-selectable paid gpt-5-nano. when i removed `"gpt-5-nano": "OpenAI"` from `modelIdToUpstream` in the previous pivot-to-paid commit, i mistook it for dead code. it's not — the map IS consulted for paid opencode aliases via `groupByUpstream → getUpstreamLabel` inside the OpenCode submenu's `renderSubContent`. without the entry, `gpt-5-nano` falls back to `getProviderDisplayName("opencode")` = "OpenCode" and gets dropped into its own sub-header instead of joining opencode/gpt, opencode/gpt-pro, opencode/gpt-mini under the "OpenAI" upstream group. re-added with an explanatory comment so the next refactor doesn't make the same mistake. 2. JSDoc / code mismatch in `scripts/backfill-gpt5-nano-affected.ts`. the JSDoc said "at least one `WorkflowRun` with `inputTokens IS NULL`" but the code is `no WorkflowRun has inputTokens > 0` — a strictly broader filter (catches `null` AND `0`). rewrote the scope block to describe what the code actually does, with the operative classifier spelled out: "a billable run with `inputTokens > 0` is proof the agent successfully reached and called the model". 3. classifier breadth (raised in the same review). honest answer: the "no positive-token run" filter IS a heuristic — a repo whose only dispatches happened to fail or cancel for unrelated reasons would get false-positive-classified A. for THIS one-shot population (2 repos, 1 with 5/5 zero-token runs — strong systematic-failure signal) the heuristic was good enough and the dry-run inspection confirmed before APPLY. for any larger reuse of this pattern, you need to cross-reference the runtime error string (`UnknownError: Model not found: opencode/gpt-5-nano`) from GitHub Actions logs or Better Stack — that error doesn't live on `WorkflowRun` rows. added a "Classifier limitations" section to the JSDoc making this explicit. nothing about the actual applied backfill changes — the prod write (1 repo: sodown4thecause/seobot → opencode/big-pickle) is unchanged and re-running the script remains idempotent. |
||
|
|
b835d53d83 |
add /anneal + pullfrog-reviewer named subagent + Build self-review polish (#550)
* cherry-pick updated /anneal command from billing branch + add as Claude Code slash command mirrors origin/billing:.cursor/commands/anneal.md (commit 4f389a8f) into both .cursor/commands/ and .claude/commands/ so the parallel-lens annealing prompt is available in both editors. content is identical between the two files. * anneal: drop REVIEW.md pointer, surface-agnostic dispatch wording, fix modes.ts self-review contradictions Anneal pass over the /anneal slash command and the Build-mode self-review step: - Drop REVIEW.md references in both anneal.md copies. The file does not exist on the Claude Code surface (only .cursor/commands/), and its contents (correctness/security/impact framing) directly contradict the prescribed single-lens, no-pre-shaping discipline. - Replace "Task tool calls" with surface-agnostic "parallel subagent calls" so the meta-prompt does not couple to either CLI's tool naming. - Hedge the "verify via web search" instruction to acknowledge subagents may not have web search available. - modes.ts: drop "and the changed files" — the same step's don't-list forbids handing subagents a curated reading list (in-file contradiction). - modes.ts: restore the "skim only, don't pre-review" warning that the long-form treats as load-bearing. - modes.ts: drop "NO MCP tools" — overbroad; the actual safety property is captured by "no writes, no shell commands, no side effects". Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * anneal: two-round self-anneal of /anneal + modes.ts self-review Expand the multi-lens parallel-review protocol with fixes surfaced by running /anneal on this branch twice. Material additions: /anneal canonical (.claude/commands/anneal.md + .cursor mirror): - promote orientation-vs-defect-hunting distinction to a load-bearing framing in the opening paragraphs - add an empty-target early exit ("nothing to anneal" stop) at §1 - spell out the read-only constraint with the no-op-if-reverted test, and forbid recursive subagent dispatch (incl. agentic MCP tools) - add cleanup-and-debt sub-categories (env vars, feature flags, dangling symbols), supply-chain, test-integrity lenses to the catalog - §1 lens-count rule: explicit trivial/typical/high-risk tiers; "treat as typical" tiebreaker for the unsure case - §2 example uses bare `git diff <primary-branch>` to capture uncommitted edits (three-dot syntax is committed-only) - §5 targeted-follow-up cross-references the fresh-eyes carve-out in Delegation discipline - final-message format spells out coverage shape, findings-table shape, dry-run fix-plan branch, and plan/doc summary branch - stopping criteria distinguish "trivial" from "small / low-risk" action/modes.ts Build mode step 4 (self-review one-pass anneal): - empty-diff early exit; "step 4 mandatory whenever there is a diff" resolves the prior contradiction with the always-runs assertion - lens count by risk (2-3 typical / 4 high-risk single-round-cap / exactly 1 trivial) with separate Tiebreaker - expand swap-in lens menu (research-validated assumptions, security, user-journey, ops, integration, test integrity, supply chain, performance, holistic) so the catalog is a starting menu, not a closed set - rename `cleanup & scope` to `diff hygiene` to avoid colliding with the canonical's broader `cleanup & debt` - delegation discipline bulletized (don't lens-review yourself, don't summarize, don't curate, don't pre-shape, don't mention other lenses); independence rationale stated inline - explicit research-discipline reminder for any lens that touches external contracts (web search, quote URLs) - comment block enumerates deliberate omissions vs the canonical (dry-run, severity categorization, read-only shell) and the deliberate scope decision (sibling diff-producing modes stay solo) action/modes.ts Review + IncrementalReview subagent-dispatch wording: - propagate the no-recursive-dispatch rule (was missing) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * add set_plan/get_plan + restructure Review/IncrementalReview as parallel-subagent orchestrators Build mode's self-review and Review/IncrementalReview now follow the multi-lens parallel-subagent fan-out pattern from the canonical /anneal protocol. New set_plan/get_plan MCP tools (orchestrator-only) persist the implementation plan in tool state so the self-review's plan-adherence lens can verify the diff against the original intent rather than reconstructing it post-hoc. Subagent "read-only / no further dispatch" is currently enforced via prompt prose only — neither claude-code's --disallowedTools nor opencode's per-agent tools allowlist is configured to scope subagent MCP access. Documented as a deferred ~30-50 LOC follow-up in the modes.ts header comment. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * revert Review/IncrementalReview mode prompts to main; keep Build self-review changes E2e testing on this branch only exercised the trivial-1-lens path for Review (preview repo had only docs PRs). Multi-lens Review fan-out was never directly validated against a real code PR. Splitting the Review/IncrementalReview restructure to its own branch (review-mode-orchestrator, draft PR #555) pending focused validation. Keep on this branch: - set_plan/get_plan MCP tools - Build mode multi-lens self-review (Test 3 directly validated 2-subagent parallel fan-out on a 2-file diff) - /anneal command updates (.claude/ and .cursor/ mirrors) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * require plan parameter when selecting Build mode Adds an arktype .narrow on SelectModeParams that rejects select_mode({mode:"Build"}) unless a non-empty 'plan' string is also provided. When valid, the plan is stored into ctx.toolState.plan at mode-selection time, so step 4's plan-adherence lens always has a comparison target. This closes the e2e finding that agents never reached for set_plan on their own (5 of 6 runs in production). Build mode prompt updated to reflect that plan is already populated at mode selection; set_plan remains as the mid-task replan tool. Other modes are unaffected. Validation surfaces the error to the agent with a descriptive message including the path ('plan') and recovery instructions, so a failing call is recoverable on the next turn rather than a hard fail. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * move Build-mode plan-required check from arktype .narrow to execute() arktype .narrow predicates aren't JSON-Schema serializable — FastMCP's toJsonSchema() emitted a {code: "predicate", predicate: Function} object instead of a serialized schema. Effect: agents couldn't see select_mode in their tool list (verified by 5 consecutive runs across two models silently bypassing select_mode entirely after the prior commit). Fix: keep the param schema clean (.narrow removed) and check selectedMode.name === "Build" && !params.plan in the execute() body, returning a structured error response. The agent now sees select_mode normally, gets a clear actionable error if it forgets the plan, and can recover on the next turn by retrying with the plan included. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * flip lens architecture: Build = single fresh-eyes subagent, Review/IncrementalReview = multi-lens Build mode self-review previously fanned out 1-4 lenses on the agent's own diff. The bias-mitigation argument for fan-out is weaker for self-review than for reviewing someone else's PR — the orchestrator just wrote the code, so what matters is one fresh-eyes subagent that doesn't share the implementation context, not breadth across parallel angles. Build now dispatches exactly one subagent that gets the original user request and the diff and evaluates whether the diff fulfills the request. Review and IncrementalReview now use the multi-lens orchestrator pattern (triage → parallel read-only fan-out → aggregate → draft comments → submit). For someone else's PR, parallel lenses (correctness, security, research-validated, user-journey, etc.) provide breadth that a single subagent can't carry coherently. Was previously parked on the review-mode-orchestrator branch (PR #555). Removes set_plan/get_plan MCP tools, ToolState.plan field, and the plan parameter on select_mode. Validated end-to-end that those didn't cause agents to actually use plan tracking (5 of 6 e2e runs skipped them); the original user request from the prompt body is the source of truth and the orchestrator already has it. Drops timeout test plan-param workaround that was added for the prior validation. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * split Review/IncrementalReview multi-lens back out to review-mode-orchestrator branch The multi-lens orchestrator restructure for Review/IncrementalReview was bundled into this branch in commit e964ae0c, but it hasn't been validated against a real code-heavy PR (the e2e exercised it only on docs PRs). Splitting it back out keeps this branch focused on the validated half — Build → single fresh-eyes subagent — and lets the Review changes ship in a focused PR (#555 reopened). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * anneal: fix Build prompt contract bugs found by 3-lens review Major fixes: - checkout_pr returns the field as `base`, not `baseRef` (per checkout.ts:611-616). The prompt was telling agents to read `result.baseRef` which would be undefined. - The base-ref fallback "after fetching" is unreachable via the `git` MCP tool (it blocks `fetch` per AUTH_REQUIRED_REDIRECT). Now names `git_fetch` explicitly. - Boundary-tag wrapping for the user request had no escape rule for input that contains the literal close marker, and no fallback for an empty request. Both are now documented with a nonce-suffix mitigation. - PR reference updated #555 → #557 (the active PR for the multi-lens review-mode-orchestrator branch; #555 was closed after the rebase). Minor fixes: - Retry predicate tightened: "errors out (tool error) or returns an empty body", not "returns nothing usable" (which is unfalsifiable and lets an orchestrator declare any output not-usable to skip review). - Subagent read-only constraints rephrased as prescriptive ("MUST NOT call") rather than descriptive ("you have only"), since on inheriting runtimes the subagent does in fact have access to write tools and the constraint is prompt-only. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * anneal round 2: tighten Build prompt edge cases (workflow_dispatch, base-ref, footer-strip, skip marker) Cross-lens findings from holistic + user-journey + research-validated lenses: - workflow_dispatch + empty diff: report_progress silently no-ops when there's no parent issue/PR. Now also call set_output with a "no-op" summary so the user gets surfacable feedback. - base-ref resolution: clarified `base` from checkout_pr is a bare ref name, added explicit `git remote show origin` path for repos whose primary is not `main` (master, trunk, etc.). - bare `git diff` description: tightened from "shows working tree" to "shows unstaged working-tree changes" — bare diff misses staged changes too, not just committed ones. - prompt-body stripping: explicitly call out the leading `> ` blockquote prefix (added by the *YOUR TASK* section formatting) and the entire Pullfrog footer block, not just one example link. - boundary-tag nonce: always-on now, not conditional on detecting a close marker. Cost is one random short string; failure mode (prompt injection if input contains literal close marker) is silent. - subagent-skip marker: structured `Self-review: SKIPPED (subagent error: ...)` on its own commit-message line, so the gap is greppable. Header comment also documents: - AddressReviews/Fix/Task asymmetry (deliberately deferred) - Subagent-runtime-fence deferred fix must explicitly deny Skill / agentic MCP tools, not just destructive tools (claude-code blocks recursive Task spawn but not alternative dispatch paths). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * anneal round 3: targeted re-review of round-2 changes catches real regressions Round 2's "fixes" introduced two real bugs that round 3's targeted correctness re-review caught: CRITICAL (fixed): tier-3 base-ref resolution used `git remote show origin`, which requires network auth — the MCP `git` tool runs commands through plain spawn() without auth, so this hangs on private repos. Replaced with `git symbolic-ref refs/remotes/origin/HEAD` (local symref, no network), which actions/checkout populates. MAJOR (fixed): the eventInstructions fallback was incoherent — the agent has no separately-addressable eventInstructions field; whatever it received in *YOUR TASK* is its only input. Removed the misleading reference. MAJOR (fixed): per-line `> ` strip was ambiguous, could destructively flatten user-pasted markdown blockquotes. Now: "strip exactly one leading `> ` per line". MAJOR (fixed): tier-1 base-ref preferred bare `<base>` over `origin/<base>`, which fails on the rare alreadyOnBranch path in checkout_pr where the local ref isn't re-created. Now prefers `origin/<base>` (always populated post-fetch). MINOR (fixed): footer-strip anchor was `<sup>`/`<picture>`, both of which appear in legitimate user content (footnotes, etc.). Switched to the PULLFROG_DIVIDER sentinel which is purpose-built for this. MAJOR (acknowledged, partial fix): 4-hex nonce is theatrical security; bumped to 8 hex and explicitly noted it's a typo-guard, not a security boundary, and that the structural fix (separate task() argument) is the real solution. REJECTED (verified false positive): subagent claimed `set_output` is not registered for workflow_dispatch. Verified at action/utils/payload.ts:118 — workflow_dispatch from `gh workflow run` resolves to trigger:"unknown", which IS standalone, which IS registered with set_output. E2e logs from prior tests confirm agents successfully call pullfrog_set_output on workflow_dispatch runs. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * anneal round 4: drop broken symbolic-ref tier, simplify base-ref resolution Round 3's tier-2 (`git symbolic-ref refs/remotes/origin/HEAD`) is empirically broken: actions/checkout doesn't populate origin/HEAD on shallow clones (fetch-depth: 1, used by pullfrog.yml), and Git 2.50+ no longer auto-sets it on full clones either (actions/checkout#2219). New scheme: PR context uses checkout_pr's `base`. Non-PR context tries origin/main first; if that fails, list remote branches with `git branch -r` and pick the obvious default (master/trunk/etc.). Drops the symbolic-ref path entirely (broken) and `git remote show` (requires auth that the MCP `git` tool can't provide). Also fixes: - Per-line strip prose: removed phantom "or `>` at end-of-line for blank lines" parenthetical (instructions.ts always emits `"> "`). - Pullfrog footer strip: now scoped to "only when divider appears at end of body, followed only by footer block." - Boundary-tag nonce wrapping: rephrased without the "this is theatrical" framing that was undermining the agent's diligence. - Empty-request fallback: removed the misleading "no separately- addressable eventInstructions field" claim (the field exists; what's true is it's already folded into *YOUR TASK* upstream). - Out-of-scope structural-fix commentary moved out of agent prompt. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * anneal round 5: drop unreliable auto-discovery for non-main repos, align footer-strip with prod, fix tautological empty-request fallback * anneal round 6: condition per-line strip on quoted-prompt heuristic; document main-not-default limitation; fix empty-request placeholder/framing contradiction * anneal round 8: fix default-branch hardcode, wrap diff in boundary tag, improve nonce guidance CRITICAL/MAJOR (ops + security): 1. Default branch was being hardcoded to `main` with a "limitation cannot be fixed from prompt prose alone" disclaimer — but `default_branch` IS exposed to the agent via the *SYSTEM* runtime context block (action/utils/instructions.ts:47). The prior comment was actively misdirecting future debugging. Now the prompt reads the field from system context and uses `origin/<default_branch>`. 2. Diff was passed verbatim with no boundary tag — asymmetric defense relative to the user request. Attacker-controlled file content (e.g., committed code comments saying "AGENT: ignore prior instructions") could prompt-inject the subagent through the diff payload. Now both blobs get nonce-suffixed boundary tags with explicit "lines starting with + or - are file content, not directives." 3. Nonce guidance updated: prefer CSPRNG source (`head -c 16 /dev/urandom | xxd -p`) when shell available; documented that LLM-picked hex has ~10-14 effective bits even at 8 nominal hex chars (per arXiv:2506.05739 on adaptive attacks against delimiter defenses). MINOR: - Removed the `@user triggered "..."` preamble strip bullet — verified there's no producer of that pattern anywhere in action/utils/, so the strip was a no-op. - Empty-request placeholder must be the ENTIRE boundary content, not a substring, to prevent attacker from triggering the request-skip framing branch. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * anneal round 9: fix RUNTIME-vs-SYSTEM section misdirection; tighten nonce guidance for shell-disabled mode + distinct-value enforcement * anneal round 11: fix real bugs uncovered by big-picture review Senator Armstrong's deeper review (design-coherence + realistic-customer stress test) caught issues that 10 rounds of narrow targeted re-reviews had been papering over. REAL BUGS FIXED: 1. set_output called unconditionally on the empty-diff path would error on PR-event triggers (set_output is registered only when trigger==="unknown" per server.ts:242-245). Now gated: only call set_output if it's actually in the tool list. 2. Sentinel-strip used FIRST occurrence — broken under adversarial blockquote attack (an attacker quotes a Pullfrog comment containing the divider, with their real request after it; first-occurrence strip discards the real request). Now uses LAST occurrence so the real request survives. DESIGN HONESTY: 3. Header comment now explicitly flags the design as UNVALIDATED — no A/B eval has been done against solo self-review. ROADMAP_RESEARCH.md flags benchmarking as the prerequisite. Header documents the validation gap and what would justify reverting. 4. Header comment elevates the runtime-fence gap from a TODO to a SECURITY GAP that must ship before the prompt protocol can be considered production-hardened. Ordering: runtime fence FIRST, prompt protocol SECOND. SIMPLIFICATIONS (per senior-engineer review): 5. Dropped the second nonce on the diff — the diff is the artifact under review; suspicious instruction-shaped lines in commits are exactly what the subagent should flag, not something to fence off. 6. Dropped CSPRNG-vs-LLM-fallback branching prose — just "16+ hex chars, use /dev/urandom if shell available, otherwise pick." 7. Dropped the regenerate-if-collide rule (vanishingly unlikely with 16 hex chars, costs tokens to enforce). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * anneal round 12: revert round-11 regressions (sentinel-strip, set_output gate, diff nonce) Round 12's sharper review caught three regressions round 11 introduced: 1. Sentinel-strip last-occurrence was strictly worse than first-occurrence for the common "user references a prior Pullfrog comment" case. The adversarial-quote scenario it was defending against is contrived (an attacker can put hostile payload anywhere; strip discipline doesn't change attack surface). Reverted to first-occurrence to align with canonical stripExistingFooter() and avoid silently swallowing user reference context. 2. set_output "gate" via "if it's in your tool list" relied on tool introspection that LLMs cannot reliably perform. Replaced with: just call report_progress; document the workflow_dispatch limitation as acceptable (job log is feedback-of-last-resort) rather than asking the agent to conditional-call a tool that may not exist. 3. Diff was de-nonced in round 11 on the assumption runtime fence ships first, but until that runtime fence lands the plain label is forgeable (committed file content can include "--- END DIFF ---" + injection). Restored nonce wrapping. The cost is one extra hex string; the benefit is real until runtime fence ships. Also added explicit caveat on the self-attested skip marker: the proper fix is MCP-layer dispatch-counting, not commit-message annotation. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ruthless cut: revert Build self-review elaboration to compact form main already had subagent dispatch (4 compact lines). This branch added 70+ lines of elaboration — header warnings, base-ref dance, footer-strip rules, nonce- suffixed boundary tags, retry-once skip markers, delegation-discipline list — all predicated on a runtime fence that doesn't exist and validation that never ran. Senior-engineer review (round 11) explicitly recommended cutting; ROADMAP_RESEARCH flags A/B benchmarking as the prerequisite for this design. Net change vs main now matches what the user actually asked for: - drop the optional plan step (and its "follow the plan" / Notes references) - subagent receives the original user request alongside the diff, evaluated against base ref, with explicit no-further-dispatch constraint Everything else reverts to main's prose. ~10 lines net change instead of 70+. * anneal round 13: tighten self-review prompt inputs to runtime-resolvable values Two underspecified inputs flagged by parallel holistic + mechanics review: 1. "the original user request" is empty for non-@pullfrog-tagged auto-triggers (sync, check_suite, opened, etc.); only YOUR TASK is reliably present in the assembled prompt across all event types. Replace. 2. "base ref (PR base or repo default branch)" requires the agent to resolve and fetch the default branch on non-PR runs (origin/<default> typically not fetched). Drop the elaboration — bare git diff captures all changes at step-3 time since step 2 doesn't commit. Aligns with 3ed2c55a's ruthless-cut philosophy: less elaboration, not more. Verified in round 14: YOUR TASK is the literal section header in instructions.ts (buildTaskSection); bare git diff scope is correct. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * restore plan step to Build mode prompt The plan step was removed alongside the MCP-contract plan-required work, but the user only wanted it gone from the MCP contract, not from the prompt itself. Restores step 1 (plan), the "follow the plan" build sub-bullet, the trailing Notes section, and renumbers learningsStep back to 6. Made-with: Cursor * add pullfrog-reviewer named subagent; standardize review fence to non-mutative+non-recursive Defines a constrained `pullfrog-reviewer` named subagent for the Build mode self-review and /anneal lens dispatch, with a single source of truth in action/agents/reviewer.ts (allowed tools, denied mutating MCP tools, system prompt). Enforcement: - opencode: real fence via agent.pullfrog-reviewer block in buildSecurityConfig — denies edit/bash/task and globs each mutating pullfrog_* MCP tool to false. - claude-code: forward-looking only. Per-agent disallowedTools is upstream-broken (anthropics/claude-agent-sdk-typescript#172, open as of latest update Mar 2026 — subagent child processes still see and can call disallowed tools, including Task). The --agents JSON is defined anyway so the fence becomes real when upstream fixes #172; until then the prompt prose constraint is the actual fence. The PreToolUse hook workaround that does enforce is out of scope. Read-only MCP tools (get_*, list_*) intentionally remain enabled so the reviewer can pull PR/issue/check context without dispatching state changes. Both modes.ts Build self-review and the two anneal.md files now share the same "non-mutative + non-recursive" framing — file reads, grep, search, web search/fetch, read-only shell, and read-only MCP queries allowed; writes, state-changing MCP, and nested subagent dispatch denied. Resolves the previous inconsistency where /anneal allowed read-only shell and Build self-review banned all shell. Made-with: Cursor * Build self-review: pass build-phase failure summary to reviewer subagent Adds an instruction in step 4's dispatch: along with YOUR TASK and git diff, pass a tight plain-text summary of any lint/typecheck/test failures fixed during build (what broke, root cause, the fix) — or "no build-phase failures" if clean. Goal: let the reviewer check that fixes addressed root causes rather than suppressed symptoms (e.g., editing a test to make it pass instead of fixing the bug). Implemented as agent self-summarization rather than piping raw build output to avoid context flooding — typecheck/test output can be hundreds to thousands of lines per failure. The agent has the failure trail in its own conversation history and summarizes from memory; the reviewer sees a few lines per failure, not raw stderr. Caveat: this is a plausible-but-unvalidated quality improvement. The mechanical justification (signal already produced, currently not passed on) is real; "this catches more bugs" is a hypothesis that will need actual run data to confirm. Downside is bounded (reviewer gets slightly more context, no behavior change if the summary is empty or ignored). Made-with: Cursor * Build self-review: distill /anneal delegation + research discipline into dispatch instructions Lifts the codified learnings from /anneal's "Delegation discipline" and "Research discipline" sections into Build mode step 4. These rules are about how-to-prompt the reviewer (not about parallelism), so they transfer losslessly to single-agent dispatch and address bias modes the prior prompt was silent on: - Don't summarize what you implemented (biases toward shape-validation) - Don't curate a reading list (your curation is itself a lens) - Don't pre-shape output with severity/category (leaks hypotheses) - Don't defect-hunt in parallel (reintroduces the implementation bias the subagent is meant to mitigate) - For diffs touching third-party API contracts / SDK semantics / framework directives / DB engine specifics, instruct the reviewer to verify load-bearing claims via web search and quote URLs rather than trust training data Restructures step 4 from one paragraph into three (constraints, inputs, discipline) plus a final review-and-commit paragraph for readability. These are validated learnings from many anneal rounds, not theoretical best practices — they're the single substantive piece this branch was missing. Made-with: Cursor * pullfrog-reviewer: drop MCP deny-list, rely on prose constraint Per-PR-review feedback: hand-maintaining MUTATING_MCP_TOOLS against action/mcp/server.ts was fragile — a future mutating tool added to the MCP server without updating this list would silently grant write access to the reviewer. Inverting to an allowlist or adding a structural test both keep the drift problem. Drop the list and all per-agent runtime denies (claude disallowedTools, opencode tools/permission map). Strengthen REVIEWER_SYSTEM_PROMPT to spell out the categories of state-changing MCP tools by example and explicitly tell the model to apply the no-op-if-reverted invariant to tools added after the prompt was written — the rule is the invariant, not the enumeration. Keep the named subagent so the prompt is reliably injected. Update modes.ts and both anneal.md copies to drop the runtime-enforces-where-supported claim. Co-authored-by: Cursor <cursoragent@cursor.com> * pullfrog-reviewer: fix description to allow read-only shell The description field was overstating the constraint as 'must not shell', but the system prompt explicitly allows read-only commands like git diff, git log, cat, ls. Align description with the actual contract. Co-authored-by: Cursor <cursoragent@cursor.com> * restructure Review/IncrementalReview as multi-lens parallel-subagent orchestrators For someone else's PR, parallel lenses (correctness, security, research-validated claims, user-journey, etc.) provide breadth across angles that a single subagent can't carry coherently. The orchestrator does triage → parallel read-only subagent fan-out → aggregate → draft comments → submit. Lens count by risk: 1 lens for trivial PRs, 2-3 for typical, 4 for high-risk surfaces (billing, auth, migrations). This branch contains ONLY the Review/IncrementalReview multi-lens prompts. Build mode keeps its single-fresh-eyes-subagent shape (different problem — orchestrator just wrote the code; bias-mitigation comes from one subagent that doesn't share the implementation context). The Build changes ship in a separate PR (self-review-subagents → main). Pending validation against a real code-heavy PR before merge — e2e on a docs-only preview repo only exercised the trivial-1-lens path. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Review/IncrementalReview: dispatch fan-out via reviewfrog named subagent The fan-out steps previously said "launch one read-only subagent per lens" without naming the subagent. That bypassed the only enforcement layer the named subagent provides: a baked-in system prompt that restates the non-mutative + non-recursive contract regardless of what the orchestrator sends. Both modes now dispatch via REVIEWER_AGENT_NAME (matching Build mode's self-review wiring) and restate the constraint inline so the rule is present twice. * rename pullfrog-reviewer → reviewfrog Mechanical rename of the named subagent. Constant names (REVIEWER_AGENT_NAME, REVIEWER_SYSTEM_PROMPT) and file paths (action/agents/reviewer.ts) stay as-is — only the agent identifier string and prose references in anneal.md and code comments change. * modes/anneal: trivial PRs skip review entirely; lens count is judgment, not table; allow subsystem lenses Three coupled changes to Review/IncrementalReview/Build self-review and the canonical /anneal command: 1. Trivial-skip: trivial diffs (single-line, formatting/comment-only, doc typo, low-risk dep bump, no behavior change) skip the fan-out / self-review entirely. Build mode skips its self-review subagent; Review submits a bare "Reviewed — no issues found." without dispatching lenses; IncrementalReview takes the existing non-substantive submit path. Tiebreaker on uncertainty: treat as non-trivial. 2. Drop prescriptive lens counts. Replaces "2-3 typical / 4 high-risk cap / 1 trivial" with judgment-based guidance: pick as many lenses as the target has distinct surfaces of risk worth investigating independently; one is sometimes enough; bias toward more (and toward follow-up rounds in /anneal) for high-stakes subsystems; 5+ is a smell that lenses are overlapping rather than covering distinct ground. 3. Subsystem lenses. Adds an explicit second flavor of lens — domain-scoped frames like "the auth lens", "the billing lens", "the schema-migration lens" — alongside the existing themed lenses (correctness, security, user-journey, etc.). Stack themed + subsystem freely. modes.ts and anneal.md (.cursor/ + .claude/, kept byte-identical) move together so the canonical pattern doc and the orchestrator prompt agree on the protocol. * add SessionLabeler so parallel subagent log lines are differentiable When the orchestrator dispatches multiple `reviewfrog` subagents in a single assistant turn (the parallel fan-out the multi-lens prompt now requires), their tool_use / tool_result / text events arrive on opencode's NDJSON stream tagged with distinct `sessionID`s but go through a single `[Pullfrog]` log prefix. Result: log readers can't attribute which lens issued which tool call, making CI logs unreadable for any review with 2+ lenses. SessionLabeler: - Binds the first-seen sessionID to "orchestrator" and subsequent new sessionIDs to FIFO-popped lens labels seeded from task tool_use inputs. - Derives labels from `lens: <name>` markers in the dispatch prompt, the Task `description` field, the `subagent_type`, or `subagent#N` fallback. - Keeps state local to a single runOpenCode invocation. Wiring: - opencode.ts: every event handler (init, message, text, tool_use, tool_result) now looks up the per-event label and prefixes log output via formatWithLabel(). Subagent finalOutput/token-reset paths gated on ORCHESTRATOR_LABEL so child sessions can't clobber parent state. - claude.ts: claude rolls subagent activity into a single tool_result block (no per-event session_id), so it gets a minimal "» dispatching subagent: <label>" log line on Task tool_use as the only attribution. - modes.ts (Review + IncrementalReview): orchestrator instructed to set the Task `description` to the lens name, since that's what the labeler reads when no explicit `lens:` marker is in the prompt. Tests: 18 unit tests covering label derivation, FIFO binding, interleaved sessions, fallback paths, and a realistic four-lens parallel fan-out simulation. Full action test suite stays green (400 passing). This is the pre-flight instrumentation that the multi-lens validation runs depend on — without it, post-hoc log analysis can't tell two subagents apart. * log subagent dispatch + finish at info level for per-lens visibility OpenCode's runtime currently encapsulates subagent execution inside the `task` tool — subagent-internal tool_use/tool_result events do not surface on the parent's NDJSON stream. The SessionLabeler I added in 0c4647f4 therefore can't actually differentiate concurrent subagent log lines (there are no concurrent log lines on the parent stream to differentiate). What CAN be observed on the parent stream is the dispatch and the result of each `task` tool call. This patch surfaces both at info level: » dispatching subagent: lens:security (subagent_type=reviewfrog) ... » subagent finished: lens:security (15.3s, status=completed) — ... Without this, a 4-lens parallel fan-out looks like 4 dispatches in close succession followed by a long quiet gap and then an aggregation turn — you can't see when each lens finished or how the durations overlapped. With it, parallel execution is visible from the timestamps on the "finished" lines. The dispatched label comes from SessionLabeler.recordTaskDispatch (so both lines share the same lens identity). taskDispatchInfo maps callID to {label, startedAt} so the matching tool_result can compute duration and emit the finished line. Also added a defensive comment on the SessionLabeler instantiation documenting that the per-event session-prefix path is currently dormant in the opencode runtime, but kept in place so attribution flips on automatically if/when opencode begins streaming subagent sessions. * fix subagent-finished log: hybrid exact+FIFO callID matching opencode does not consistently surface a tool_result callID matching the originating tool_use callID for the `task` tool, so the previous exact-match-only finish line never fired. Now we: - Dual-index task dispatches by callID AND in a FIFO queue. - Track non-task callIDs so we can identify "unrecognised callID" results as likely-task-with-mismatched-id. - On tool_result, exact-match first; fall back to FIFO when the output looks like a subagent reply (>300 chars) and the callID is unknown. - Flush leftover dispatches at run end with an "(inferred at run-end)" suffix so the gap is visible if subagent results arrive entirely off the tool_result event path (e.g. inlined into the next assistant message). * fix subagent-finished log: move run-end flush to post-subprocess block Investigation on T3 + finish-log-validation runs revealed two real issues with my prior attempt: 1. The `result` event handler is dead — opencode never emits a `result`-typed event over its NDJSON stream, so the inferred-at-run-end flush I had placed there never fired. Move the flush to right after `runSubprocess` returns where it actually executes. 2. The FIFO heuristic was too strict — the >300-char output check excluded short or empty outputs that opencode's `task` tool_result appears to carry (the subagent's full reply seems to arrive via a separate channel, not the result event itself). Drop the size check; rely solely on `knownNonTaskCallIDs` to keep genuinely-non-task tool_results from popping a pending task. Net effect: every `task` tool dispatch gets a matching `» subagent finished` line in the logs, either from the FIFO fallback during the run or from the run-end flush as a backstop. * modes/anneal: anchor lens calibration in worked examples The prior trivial-skip definition ("single-line fix, formatting-only, …") was anchored on diff size, but real-world risk is anchored on diff *shape*: a 5000-line lockfile regen IS trivial, and a 1-line SQL operator flip in a billing path is NOT. The prior lens-count guidance ("there's no fixed count, bias toward more for high-stakes subsystems") gave the agent no concrete shapes to anchor against, so runs varied between under-pick (4 generic lenses on a billing PR) and over-pick (5 overlapping themed lenses on a refactor). This commit hardens both: - Trivial definition gets explicit "looks trivial but isn't" anti-patterns: SQL operator flips, money/tax/timeout constants, feature-flag defaults, comparison operator changes, semantic 1-liners buried in whitespace, public-API renames, new direct deps. Skip lists get explicit "size doesn't matter" calibration for lockfile regens and mechanical renames. - Lens count gets a worked-example ladder: 1 lens (refactor / new test file / isolated fix), 2-3 lenses (typical features), 4-5 lenses (high-stakes subsystem touches), 6+ is a smell. - Subsystem lenses get an explicit recommendation to lead over generic themed equivalents for high-stakes domains, with the reasoning: domain framing primes the subagent for domain-specific failure modes (double-charges, refund races, dispute flows) the generic lens misses. Mirrored byte-identical into both anneal.md copies; modes.ts updates all three review surfaces (Build self-review, Review triage, IncrementalReview triage). * fix harness false-failure when Review submits without todowrite Review and IncrementalReview prompts explicitly forbid calling report_progress (the review IS the durable record). The post-run harness in action/utils/run.ts errors with "agent completed without reporting progress" when toolState.wasUpdated is false at exit. Until now, the only path that set wasUpdated for these modes was the todoTracker's debounced publish — which only fires if the agent happens to call todowrite during the run. Adversarial run on PR #16 (misleading-trivial billing tweak) hit exactly this case: agent went straight from triage → fan-out → review submission with no todowrite calls, and the harness reported failure even though the substantive review was successfully submitted with two inline comments. Fix: create_pull_request_review now marks wasUpdated=true (and finalSummaryWritten=true) on every terminal path — successful submit, empty-content skip, and all-comments-dropped skip. Submitting a review is unambiguously a "done" signal in these modes. Found via adversarial testing of the multi-lens orchestrator on a 1-line tax constant change. Logged in /tmp/pullfrog-validation/v3/. * fix harness false-failure when Review submits without todowrite (correctly) Replaces the prior fix (acc2bd65) which set wasUpdated=true inside create_pull_request_review. That approach worked for the harness check but broke the orphan-comment cleanup: with wasUpdated=true and finalSummaryWritten=true, the (!wasUpdated || trackerWasLastWriter) condition in main.ts evaluated false and the "Leaping into action" progress comment was left behind on every Review run — the exact behavior the cleanup logic was designed to prevent (see plans/review_progress_comment_cleanup_b0120f6c.plan.md). Correct fix: change the harness check in action/utils/run.ts to recognize a submitted PR review as an alternate completion signal alongside wasUpdated. wasUpdated stays false on purpose so cleanup deletes the orphan, but the run no longer false-fails when the agent followed the Review-mode contract (submit a review, never call report_progress). The bug was discovered during adversarial testing of PR #16 (misleading-trivial billing tweak) where the agent went straight from triage → fan-out → review submission without using todowrite, causing the harness to error even though the substantive review (a CAUTION blocking review with two inline comments catching a 10x tax cut) was successfully posted. * fix harness false-failure for Review modes (mode-based carve-out) Replaces the prior carve-out (4c0f69aa) which gated on toolState.review.id. That worked for runs where the review tool actually populated the toolState (validation-2 succeeded), but failed for runs that took a slightly different path where the assignment didn't propagate visibly to handleAgentResult — even when the review verifiably posted to GitHub. Found this empirically: PR #19 (pure mechanical rename across 20 files) opened with the prior fix in place, the agent picked exactly one impact lens (correct calibration!), confirmed no stale references, submitted "Reviewed — no issues found." successfully (visible in GitHub API), and the harness STILL errored with "agent completed without reporting progress." Same SHA, same branch, same code as validation-2 which passed. The toolState.review.id check turns out not to be reliably visible from the run.ts handler in all paths. Better fix: gate on toolState.selectedMode. Review and IncrementalReview modes are designed to never call report_progress (the review is the durable record, and IncrementalReview's non-substantive path produces no artifact at all by design). The harness completion check makes no sense for these modes — skip it entirely. The agent's clean subprocess exit is the completion signal. This also handles edge cases the previous fix missed: IncrementalReview's non-substantive path (no review submitted by design) and any future Review-flow shape that doesn't end at create_pull_request_review. * ci: trigger Test run to validate models-live timeout/concurrency changes * ci: prune passthrough models from live smoke matrix openrouter/* aliases and keyed opencode/* aliases are routing-layer wrappers around models we already smoke-test directly. running every passthrough burns CI minutes (~30 min/run) without catching anything the direct smoke doesn't — slug drift is already covered by the models-catalog job. keep one canary per routing layer (openrouter/claude-sonnet, opencode/claude-sonnet) to validate auth + tool-call translation. free opencode models stay in the matrix since they're unique to the provider. INCLUDE_ALL_PASSTHROUGHS=1 bypasses the prune for full validation. matrix size: 37 → 20 jobs. * fix isRateLimited false-positive on UUIDs/timestamps containing 429 The bare "429" substring pattern was matching MCP session IDs (e.g. `...-4429-...`) and microsecond timestamps in agent stdout, sending transient failures down the 60s rate-limit retry path. With the new 4-minute per-step CI timeout, that backoff plus a slow retry pushed the step past its budget and timed out. Switch to regex patterns and gate the numeric code on `\b429\b` so word boundaries prevent the substring false-match. Verified locally that the UUID `97287d2f-ae1d-4429-8627-73e2454e80ca` and timestamp `02:04:50.9429654` no longer match while real `HTTP 429` / `"status":429` strings still do. --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com> |
||
|
|
3bacf01e48 |
bump model registry for deepseek v4, kimi k2.6, claude opus 4.7 (#554)
* bump model registry for deepseek v4, kimi k2.6, claude opus 4.7
deepseek released v4 (pro/flash) on 2026-04-24 as a generational replacement
for v3-era reasoner/chat. deepseek will fully retire deepseek-chat and
deepseek-reasoner on 2026-07-24 — both already route server-side to v4-flash.
introduce deepseek-pro (preferred) and deepseek-flash slugs and mark the
legacy aliases deprecated via fallback so existing users transparently
upgrade. mirror on the openrouter side.
also bump moonshotai/kimi to k2.6 (from k2.5, 2026-04-21 release) and bump
the anthropic claude-opus openrouter resolves to 4.7 (we'd already moved the
native side to claude-opus-4-7 but openrouter resolves still pointed at 4.6).
update OSS_PROXY_MODEL fallback and stale doc reference accordingly.
snapshot regenerated; all 111 catalog tests + 66 unit tests pass.
* walk fallback chain when resolving the OSS proxy model
the OSS proxy path in run-context/route.ts read alias.openRouterResolve
directly, bypassing the fallback chain. so an OSS repo configured with
deepseek/deepseek-reasoner kept proxying to openrouter/deepseek/deepseek-v3.2
instead of resolving through the new fallback to openrouter/deepseek-v4-pro.
that worked today (v3.2 routes server-side to V4-Flash) but breaks when
deepseek and openrouter retire v3.2 alongside the 2026-07-24 deprecation.
extract the chain walk into a private resolveTerminalAlias helper and add
resolveOpenRouterModel that mirrors resolveCliModel but returns
openRouterResolve. fallback semantics now apply uniformly across both
runtime resolution paths.
* hide deprecated aliases from model selector dropdowns
aliases with a fallback (currently deepseek-reasoner / deepseek-chat /
openrouter/deepseek-chat) should not be selectable from the model dropdown
or the interactive cli model picker — they're a transition path, not a
choice. but if a repo already has a deprecated slug stored in the db, the
selector trigger still resolves it against the full alias registry so the
display name renders correctly until the user opens the menu and picks a
new model.
verified manually: deepseek submenu shows pro+flash only, openrouter submenu
shows pro+flash but no chat, and a deprecated stored value still renders
its full display name in the trigger.
* ci: run models-live on PRs that touch resolution files
Previously the per-alias smoke matrix only fired on push-to-main, so
resolution-affecting PRs (this one included) shipped without ever
exercising the agent harness against the real provider for each alias.
Loosen the gate on the `aliases` step in the `changes` job to fire
whenever the `models` paths-filter matches (action/models.ts,
action/package.json, action/agents/**) — same set that already drives
the comment about "resolution-affecting files". `models-live` itself
is unchanged: it still keys on a non-empty matrix.
`models-catalog` stays gated to main-push intentionally — its existing
comment justifies that (transient upstream catalog drift shouldn't
block PRs).
* relabel codex aliases as GPT, bump to 5.5 family, add gpt-pro
OpenAI retired the "-codex" model suffix on 2026-07-23 (gpt-5.3-codex,
gpt-5.1-codex-mini, gpt-5.2-codex et al all shut down) and unified the
codex+gpt lines into a single family at gpt-5.4. Per OpenAI's own
deprecation table, every "-codex" substitute is plain gpt-5.x — no
future Codex-suffixed frontier models are coming.
Keep the existing slugs for DB stability (no migration needed) but roll
displayName + resolve forward across openai, opencode, and openrouter:
- openai/gpt-codex → "GPT" → openai/gpt-5.5
- openai/gpt-codex-mini → "GPT Mini" → openai/gpt-5.4-mini
- openai/gpt-pro (new) → "GPT Pro" → openai/gpt-5.5-pro
Same relabel + new gpt-pro slug for opencode/* and openrouter/*.
gpt-5.5 (and gpt-5.5-pro) hit the OpenAI public API on 2026-04-24,
day after launch — both are live on OpenRouter as well.
There's no gpt-5.5-mini yet (analysts speculate late June – mid August
based on the gpt-5.4-mini cycle), so "GPT Mini" stays at gpt-5.4-mini
for now; one-line bump when the smaller variant ships.
Also pick up unrelated upstream catalog drift in the snapshot
(xai/grok-4.3 released 2026-05-01, openrouter/poolside laguna).
* deprecate gpt-codex aliases, mint gpt/gpt-pro/gpt-mini, render terminal alias in UI
The previous commit relabeled gpt-codex/gpt-codex-mini in place ("GPT" /
"GPT Mini") so a single slug carried two different identities. That worked
but was self-contradictory: the slug name no longer described the model.
Switch to the same shape we use for the deepseek V3→V4 transition:
- Mint new live slugs: openai/gpt, openai/gpt-pro, openai/gpt-mini
(mirrored on opencode/* and openrouter/*)
- Restore honest deprecated state on gpt-codex/gpt-codex-mini —
displayName "GPT Codex" / "GPT Codex Mini", original 5.3-codex /
5.1-codex-mini resolves, fallback set to the new gpt / gpt-mini slugs
- resolveCliModel + resolveOpenRouterModel walk the chain (existing
machinery), so DB rows holding "openai/gpt-codex" transparently route
to gpt-5.5 with no migration
UI render contract: display sites resolve to the *terminal* alias so a
deprecated stored slug shows the model the user is actually running, not
the historical name. Three call sites updated:
- components/ModelSelector.tsx (dropdown trigger label + provider label)
- action/utils/buildPullfrogFooter.ts (PR-comment "Using `X`" footer)
- action/commands/init.ts ("using model X" startup line)
Promoted internal resolveTerminalAlias → exported resolveDisplayAlias so
all three sites use the same primitive (also re-exported from external.ts
+ internal/index.ts so the Next.js app can import it).
Selectable lists (dropdown options, init picker) still filter on
!a.fallback so deprecated slugs never appear as fresh choices — only
deprecated stored values render.
wiki/model-resolution.md: replaced the muddled "slug names outlive
product names" bullet with a clear decision table for in-place bump
(generational, e.g. Opus 4.6 → 4.7) vs. deprecate+replace (vendor
restructures, e.g. codex → unified GPT, deepseek V3 → V4). Documents
the UI render contract too.
models-live CI matrix will smoke-test all 6 new slugs (gpt, gpt-pro,
gpt-mini × openai/opencode/openrouter) plus the 6 deprecated codex slugs
(which resolve through fallback to the same terminal targets) — 12 jobs
total against real provider APIs.
* wiki: slugs are evergreen, resolves are versioned
Document the slug-naming rule explicitly so future entries don't repeat
the deepseek-chat/deepseek-reasoner mistake (mirroring an upstream's
versioned/product-line-specific ID into the slug). Slugs should track
brand-style tier names that survive major version bumps; embedding
versions is the resolve string's job.
|
||
|
|
74b313e612 |
bump claude-opus alias to 4-7
anthropic shipped claude-opus-4-7 today; opencode also republished it. point the "claude-opus" alias at the new version for both providers so existing users get the upgrade automatically. openrouter hasn't published 4.7 yet, so leave openRouterResolve at 4.6 as the BYOR fallback. also clarify the latest-model snapshot comment: new model drops usually just mean bumping the `resolve` on an existing alias, not adding a new one. Made-with: Cursor |
||
|
|
0055aef618 |
feat: add Claude Code agent for Anthropic model users (#502)
* feat: add Claude Code agent for Anthropic model users Re-adds Claude Code support (removed in #478) so users with Anthropic API keys or Claude Code OAuth tokens can use their Claude subscriptions directly. When an Anthropic model is selected and Claude Code credentials are available, the system auto-selects the Claude agent instead of OpenCode. The harness mirrors opentoad's security model: native Bash blocked via --disallowedTools, MCP ShellTool for restricted shell, ASKPASS for git auth. Includes NDJSON streaming, provider error detection, cache/cost tracking, browser skill, and todo progress tracking. Key changes: - action/agents/claude.ts: full Claude Code harness - action/utils/agent.ts: auto-select Claude for anthropic/* models - action/utils/providerErrors.ts: extracted shared provider error detection - action/utils/skills.ts: extracted shared skill installation (agent-aware) - action/models.ts: add CLAUDE_CODE_OAUTH_TOKEN to anthropic envVars - action/utils/docker.ts: add CLAUDE_CODE_OAUTH_TOKEN to test env allowlist - CI: add claude to test matrix, pass CLAUDE_CODE_OAUTH_TOKEN secret Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove unused toolId variable, fix apiKeys test env cleanup The apiKeys test cleanup stripped *_API_KEY vars but missed CLAUDE_CODE_OAUTH_TOKEN which doesn't match that pattern. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: strip provider prefix from PULLFROG_MODEL in Claude agent the env override path was returning the raw value (e.g. "anthropic/claude-sonnet-4-5") without stripping the provider prefix, causing the Claude CLI to receive an invalid model ID. Made-with: Cursor * fix: remove dead cliPath field, add CLAUDE_CODE_OAUTH_TOKEN to workflows remove unused cliPath from Claude agent RunParams, and pass CLAUDE_CODE_OAUTH_TOKEN through all pullfrog.yml workflow templates so users with Claude Pro/Team subscriptions can use their membership. Made-with: Cursor * fix: block Bash subagent in Claude Code disallowedTools Made-with: Cursor * chore: update model snapshot (opencode/openrouter latest → qwen3.6-plus-free) Made-with: Cursor --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
e6d34ee01b |
add OpenRouter proxy for managed model routing and OSS program (#488)
* add OpenRouter proxy for managed model routing and OSS program proxy layer that mints ephemeral OpenRouter keys for users without BYOK API keys. two paths: pro plan users get their selected model proxied via OpenRouter; OSS program repos (hard-coded allowlist in config.ts) get free Claude Opus. BYOK env vars (PULLFROG_MODEL/OPENCODE_MODEL) always take precedence. frontend: OSS repos see a static "Opus (Free)" badge with the model dropdown disabled and no API key requirement. all models now carry openRouterResolve metadata for proxy target resolution. Made-with: Cursor * implement OSS program: proxy infrastructure for free model credits server-side OSS allowlist determines eligible repos. action mints ephemeral OpenRouter keys via OIDC-authenticated /api/proxy-token endpoint (idempotent on runId, $10 per-key safety limit). keys are disabled on workflow completion when no running refs remain. HWM-based usage sync tracks cumulative spend per account. schema: ProxyKey model, Account.usageUsd/activeKeyId, WorkflowRun.proxyKeyId action: OIDC credential stashing, resolveProxyModel uses server oss flag frontend: isOss flows from server page to components (no client allowlist) Made-with: Cursor * address PR review: repo cross-check, key retirement lifecycle, dead code removal - proxy-token: verify runId belongs to OIDC-authenticated repo via repo relation - add retireKey() shared primitive: disable in OpenRouter first, then mark disabled in DB - rotateKey: retire old active key after swap to prevent orphans - webhook: replace inline cleanupProxyKey with retireKey calls - syncAccountUsage: skip disabled keys - remove vestigial AccountPlan/plan field from action types - add disabled field to ProxyKey schema + migration Made-with: Cursor * replace deprecated opencode/mimo-v2-flash-free with mimo-v2-pro-free Made-with: Cursor * fix migration ordering: rename disabled migration to sort after table creation Made-with: Cursor * squash proxy key migrations into single migration Made-with: Cursor * add preview repo to OSS allowlist for testing Made-with: Cursor * populate OSS allowlist from oss-program-invitees.json Made-with: Cursor * format oss-program-invitees.json Made-with: Cursor * add installed public repos to OSS allowlist split ossRepos into three provenance-tracked lists: - internalRepos (pullfrog, colinhacks, RobinTail) - installedPublicRepos (external public non-fork repos with active installs) - invitees (from oss-program-invitees.json) also adds scripts/list-oss-candidates.ts to regenerate the installed list Made-with: Cursor * fix: resolve tokens before clearing OIDC env vars resolveTokens → acquireNewToken → isOIDCAvailable() checks ACTIONS_ID_TOKEN_REQUEST_URL/TOKEN env vars. The new OIDC stashing code was deleting them in restricted shell mode before resolveTokens ran, causing it to fall through to the GitHub App path which requires GITHUB_APP_ID/GITHUB_PRIVATE_KEY. Made-with: Cursor * derive proxy-token auth from OIDC claims, add ensureWorkflowRun upsert - proxy-token no longer requires body.runId; uses claims.run_id + claims.repository - shared ensureWorkflowRun upsert called from both webhook and proxy-token - workflow_run_requested handler now eagerly creates WorkflowRun records - eliminates race condition between webhook and action proxy-token call Made-with: Cursor * hardcode PULLFROG_ACCOUNT_ID, document preview debugging lessons GitHub node IDs are constant — no reason for this to be an env var. Removes the trailing-newline bug that caused P2025 errors. Adds wiki docs on workflow testing, Vercel env gotchas, and Neon preview branch discovery. Made-with: Cursor * fix: parse OpenRouter create-key response correctly the API returns `key` at the top level, not inside `data` Made-with: Cursor * onboarding cards, unlock OSS model selection, simplify console - add OnboardingCard component with two states: workflow install and model+test (dispatches "Tell me a joke" for test run) - replace PromptBox overlay gates with dedicated onboarding cards; PromptBox is now just the form, always enabled - use hasWorkflowRuns DB check to decide onboarding vs promptbox - unlock ModelSelector for OSS repos (was locked to Opus badge); resolve proxyModel from repo's selected model alias in run-context - rename "API key" row to "Model costs" with pure client-side states: OSS covered, auto-resolve, free model, BYOK with env var names - add "(Recommended)" badge to model aliases with recommended: true - remove OSS_MODEL_DISPLAY_NAME constant and secrets-fetching logic Made-with: Cursor * update stale xai model snapshot Made-with: Cursor * rename Permissions to Security, hide git push toggle, add shell isolation toggle with disabled state for public repos, remove opentoad agent name from logs Made-with: Cursor * chevron hover states, sidebar hooks/security entries Made-with: Cursor * address PR review feedback: rename recommended→preferred, fix dispatch orphan, update wiki docs - rename `recommended` to `preferred` in model alias registry to distinguish from the UI "Recommended" badge (which is hardcoded for opus + codex only) - cancel precreated WorkflowRun when workflow lookup fails in dispatch-workflow - replace run_sql/vercel env pull in wiki docs with neonctl + Prisma pattern - extend scripts/neon-branch.ts to output DATABASE_URL via neonctl - add Recommended badge to GPT Codex alongside Claude Opus in ModelSelector Made-with: Cursor |
||
|
|
3ff11f97eb |
replace deprecated opencode/mimo-v2-flash-free with mimo-v2-pro-free, update model snapshot
Made-with: Cursor |
||
|
|
df9598ea5f |
add free opencode model metadata and improve model picker UX
Made-with: Cursor |
||
|
|
6d25adfd1a |
Agent & model refactor (#478)
* agent & model refactor with ASKPASS git auth, UI restructure, clerk v7 Made-with: Cursor * fix stale agent/effort refs, add tests for askpass + model resolution - reviewCleanup.ts: payload.agent -> payload.model, remove effort - selectMode.ts PlanEdit: remove delegation/subagent/effort references - pullfrog.yml.ts: update env vars (drop GOOGLE_API_KEY/CURSOR_API_KEY, add GOOGLE_GENERATIVE_AI_API_KEY/XAI_API_KEY/MOONSHOT_API_KEY/OPENCODE_API_KEY) - FlagsSettings/RepoInstructionsSection: remove stale effort/timeout copy - new: gitAuthServer.test.ts (10 tests — lifecycle, token delivery, tamper detection, script gen) - new: agent.test.ts (4 tests — default opentoad, AGENT_OVERRIDE, invalid override) - new: models.test.ts (19 tests — parseModel, resolution, registry invariants) - update models.dev snapshot Made-with: Cursor * fix changed-agents.sh to filter legacy agent files from CI matrix legacy agent files (claude.ts, codex.ts, etc.) are @ts-nocheck and not exported from index.ts. changed-agents.sh now reads index.ts imports to build the active agent set and treats changes to inactive files as non-agent changes (opentoad canary only). Made-with: Cursor * remove MCP file tools, old agent harnesses, and obsolete security tests ASKPASS-based git auth makes the old MCP file tool security layer unnecessary: - token never in subprocess env, so symlink/gitattributes/hook attacks can't exfiltrate it - agents now use native file tools (OpenCode builtin read/edit) deleted: - action/mcp/file.ts (file_read, file_write, file_edit, file_delete, list_directory) - action/mcp/index.ts (dead re-export) - agent harnesses: claude.ts, codex.ts, cursor.ts, gemini.ts, opencode.ts - opencode-runner.ts (inlined into opentoad.ts) - security tests that validated MCP file tool restrictions - commented-out three-step review flow (~300 lines) - sanitizeSchema/wrapSchema dead code from mcp/shared.ts - OPENCODE_MODEL_MINI/MAX env vars (effort-level model overrides removed) updated test prompts to use generic file ops instead of MCP tool names. restored pkg-json-scripts + requirements-txt-attack (test --ignore-scripts defense). Made-with: Cursor * bump actions/checkout v4 → v6 (node 24) node 20 actions deprecated june 2, 2026. Made-with: Cursor * temporarily disable fail-fast on agnostic tests to debug checkout@v6 Made-with: Cursor * re-enable fail-fast on agnostic tests Made-with: Cursor * fix test token mismatch: mint OIDC tokens scoped to target repo CI tests override GITHUB_REPOSITORY to pullfrog/test-repo but inherit the runner's GITHUB_TOKEN (scoped to pullfrog/app), causing 401s on every run-context fetch. Clear GITHUB_TOKEN in the test subprocess so ensureGitHubToken() mints a properly scoped token via OIDC. Also centralizes the default GITHUB_REPOSITORY in runAgentStreaming instead of repeating it in every test file, and fixes preview-cleanup to remove workers from all queues (not just name-matching ones). Made-with: Cursor * fix ensureGitHubToken to try OIDC when app credentials are absent ensureGitHubToken only attempted token minting when GITHUB_APP_ID and GITHUB_PRIVATE_KEY were set. In CI, OIDC is available but app creds aren't exposed — so the guard prevented minting entirely. Made-with: Cursor * dead code cleanup: remove remnants of deleted agents, file tools, effort system remove unused @anthropic-ai/claude-agent-sdk and @openai/codex-sdk deps, orphaned file-tool security tests, dead GEMINI_MODEL passthrough, stale opencode-runner wiki refs, deleted test file references, and MCP file tool docs. rename docs/effort → docs/models. fix vitest setup: move dotenv to globalSetup (runs once before forks instead of per-file, 19s → 200ms). Made-with: Cursor * address review feedback: remove dead code, update stale references - remove AGENT_OVERRIDE (only opentoad exists) - remove shellToolName plumbing (always restricted shell) - bump action version to 0.0.179 - remove CURSOR_API_KEY from all workflows/configs - remove OPENCODE_MODEL_MINI/MAX from workflows/docs - delete wiki/effort.md, rewrite docs/effort.mdx as "Models" - rewrite wiki/modes.md: orchestrator/subagent → single agent - simplify flag system: drop builtin flag extraction (debug, effort, timeout, agent), keep custom flag replacement only - reserve all legacy flag names to prevent custom flag conflicts Made-with: Cursor * regenerate lockfile after removing claude-agent-sdk and codex-sdk Made-with: Cursor * fix import ordering, add lockfile check to pre-push hook Made-with: Cursor * remove dead debug payload field, stale packageExtensions Made-with: Cursor * merge proc-sandbox and token-exfil into a single test proc-sandbox and token-exfil were duplicative — both tested that SANDBOX_TEST_TOKEN couldn't be exfiltrated. consolidated into token-exfil with shell:restricted (which actually exercises filterEnv) and the /proc attack vector hints from proc-sandbox. Made-with: Cursor * fix wiki adversarial.md to match actual tokenExfil validator Made-with: Cursor |