v0.0.201
166 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4b3c5ca905 |
rename agent key to opencode and add skill invocation coverage (#541)
* rename agent key to opencode and add skill invocation coverage. add skill-invoke tests for claude and opencode with local play-based validation signals, update CI matrices, and include the current tracked refactors in this branch for review. Made-with: Cursor * exclude agent-specific skill-invoke tests from wrong agent in CI matrix * address review follow-up and preserve workflow run UI tweak. switch changed-agents ci coverage to exercise the opentoad implementation path while keeping the opencode expectation, and include the local workflow run client interaction updates requested on this branch. Made-with: Cursor * remove opentoad agent filename from runtime. rename the opencode harness implementation file from opentoad.ts to opencode.ts and update ci coverage input accordingly so action code no longer carries the old filename. Made-with: Cursor * ensure security prompt bypass is set on every test fixture. this keeps adversarial and permissions harnesses from being blocked by the default prompt-injection refusal path during CI security tests. Made-with: Cursor --------- Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com> |
||
|
|
2799cce4bf |
homepage redesign + docs cleanup + agent prompting (#540)
* homepage redesign + docs cleanup + agent prompting improvements - rewrote hero section: new tagline, responsive font sizing with clamp(), extracted shared constants for copy management - added feature screenshots (shell isolation, github permissions, mcp tools, agent browser) and ensured consistent image sizing - reworked feature section mobile layout: caption-style descriptions, bigger h3s, image padding - made CTA buttons visible on all breakpoints (stacked on mobile, row on md+) - reorganized docs/tools.mdx into single table with category dividers, simplified tool descriptions - added markdown image syntax instruction to agent system prompt - fixed InfoPopover overflow on small screens - misc: InlineCode proportional sizing, OnboardingCard updates, shell/security doc improvements Made-with: Cursor * update pnpm-lock.yaml for agent-browser 0.25.4 Made-with: Cursor |
||
|
|
b748355cbe |
homepage copy refresh + fix skills CLI installation (#539)
* add wiki/betterstack.md documenting log querying, request-ID grouping, and MCP usage
Made-with: Cursor
* fix webhook race conditions: separate runId assignment from data updates
the workflow_run webhook handler had a race where concurrent handlers assigned
the same runId to different pending records. the loser's P2002 silently dropped
data updates (jobId, status, completedAt). fix by splitting into two steps:
assignRunId() handles the race-safe unique assignment, then data updates always
target where: { runId } so they hit the correct record regardless of who won.
also downgrade R2 ObjectLockedByBucketPolicy errors from error to warn level
since duplicate webhook deliveries writing the same key is expected under load.
Made-with: Cursor
* homepage copy refresh + fix skills CLI installation
- update hero to "Agent x GitHub" with new subtagline
- rewrite intro paragraphs: workflow, harness capabilities, billing
- add feature sections: bash isolation, headless browser, MCP tools
- update FAQ answers, footer attribution, free-for-oss copy
- update APP_DESCRIPTION for SEO
- fix skills install: use npx from tmpdir instead of local binary
(the bundled action has no node_modules; running npx from tmpdir
avoids project .npmrc with pnpm settings breaking binary resolution)
- instruct agents to use markdown image syntax in upload_file tool
- start dependency installation eagerly from main.ts
- include event title in task instructions
Made-with: Cursor
|
||
|
|
426ef8c0d8 |
review: append todo list to review body, always delete progress comment
- in Review mode, stop the todo tracker and append the completed task list as a collapsible section to the review body before submitting - always delete the progress comment after a review is submitted, regardless of whether the agent called report_progress Made-with: Cursor |
||
|
|
b9b6503315 |
reorder prompt sections: task-first with dynamic TOC (#513)
* reorder prompt sections: task-first with dynamic TOC put the actual task at the top of the prompt for primacy, add a dynamic table of contents, and push system/runtime metadata to the end. new section order: TOC → YOUR TASK → PROCEDURE → EVENT CONTEXT → SYSTEM → LEARNINGS → RUNTIME Made-with: Cursor * enforce clean working tree: continue session if agent leaves uncommitted changes after each agent run, check `git status --porcelain`. if dirty, resume the same session with instructions to commit on a new branch, push, and open a PR. retries up to 3 times before giving up. - claude code: capture session_id from result event, use --resume <id> - opencode: use --continue to resume the last session - remove --no-session-persistence from claude (needed for --resume) - update Task mode to clarify branch/push/PR is the default finalize step Made-with: Cursor * log full prompt in collapsible group for debugging Made-with: Cursor * fix: format tool refs in buildCommitPrompt via formatMcpToolRef * enforce clean git status: general instructions, stop hook, and Task mode Made-with: Cursor * fix: rename stale titleBody references after body leak fix Made-with: Cursor --------- Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com> |
||
|
|
d525fc21be |
show fallback indicator in model dropdown, move agent logs to main, bump to 0.0.191
Made-with: Cursor |
||
|
|
b1f9878877 |
extract resolveModel() to run before agent selection
model resolution was duplicated inside each agent (opentoad, claude) and PULLFROG_MODEL override was not considered when choosing the agent. now resolveModel() runs first in main.ts, its result feeds into resolveAgent() for agent selection, and the resolved model is passed to the agent via ctx.resolvedModel. agents only handle their own fallback (opentoad: auto-select via opencode models, claude: strip provider prefix). also removes the hardcoded anthropic/claude-sonnet test runner default since ANTHROPIC_API_KEY is no longer in CI. Made-with: Cursor |
||
|
|
ba1966f17c |
feat: encrypted account-level secrets (#501)
* feat: add encrypted account-level secrets with UI for adding API keys Adds AccountSecret model with AES-256-GCM encryption, API routes for CRUD, "Add secret" button in model costs section, and injects decrypted secrets into action env (YAML secrets take precedence). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: repo secrets, sidebar icons, lazy learnings history - add repo-level secrets with inheritance from org secrets - add icons to console sidebar sections - fix learnings history modal: lazy fetch with hover prefetch, strip content from list response, load content per-expansion Made-with: Cursor * add input validation bounds for secrets and fix client-side name filter Made-with: Cursor * refactor: migrate all client-side data fetching to TanStack Query Replace manual useState/useEffect/fetch patterns and the custom usePolling hook with useQuery, useInfiniteQuery, and useMutation across the entire frontend for consistent caching, background refetching, and reactive invalidation. - ActiveWorkflowRunsSection: useQuery + refetchInterval - WorkflowRunHistory: useInfiniteQuery + polling query - LearningsSection: useQuery per revision (lazy) - FlagsSettings: self-contained useQuery + useMutation - SecretsCard: useMutation for delete - AddWorkflowButton, VerifyWorkflowButton: useMutation - EmailSignupForm, email-waitlist: useMutation - providers.tsx: enable refetchOnWindowFocus - Delete usePolling.ts (no remaining consumers) Made-with: Cursor * address PR review: squash migrations, rename accountSecrets → dbSecrets Squash the two separate secrets migrations into a single migration. Rename the wire format field from accountSecrets to dbSecrets since it now carries merged account + repo secrets. Made-with: Cursor * fix: update proxyKeys.ts imports after cache.ts -> yes package migration Made-with: Cursor --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
a7b8dcbced |
Rework incremental diffing (#499)
* Improve our deepening logic * Use consistent SHA for PR-related operations in CheckoutPrTool * compute `deepenDepth` at more appropriate time * fix stale comment * add comments for `alreadyOnBranch` * ensure before sha is available * small cleanup * computeIncrementalDiff * move the util * improve algorithm * improve algorithm further * get rid of temp result array * add comment * compute incremental diff and updte instructions * add comment * update stale comment * get rid of redundant rev-parse call * improve comment * strenghten the instructions * make diff paths unique |
||
|
|
cb8e33360c |
feat: add prepush lifecycle hook (#498)
* feat: add prepush lifecycle hook Add `prepushScript` configuration — an optional shell script that runs automatically before pushing code to the remote repository. Reuses the existing `executeLifecycleHook` infrastructure (bash execution, 2-min timeout, error propagation on non-zero exit). When unconfigured the hook is a no-op. Made-with: Cursor * fix: add prepushScript to run-context API response, fix UI separator Include prepushScript in the settings returned by the run-context endpoint so the hook actually fires in production. Also fix the separator pattern in AgentSettings to match the existing convention (spacer + hr + spacer instead of margin). Made-with: Cursor |
||
|
|
6b18b6730b |
live todo tracking, collapsible task list in final progress, hide set_output outside standalone (#492)
* fix false "without reporting progress" error + live todo tracking clean up orphaned progress comments when review is skipped or only set_output is used, preventing the false positive in handleAgentResult. parse todowrite events from OpenCode's NDJSON stream and render a live markdown checklist in the PR progress comment (2s debounce). agent's explicit report_progress always takes priority. Made-with: Cursor * fix contradictory review/progress prompting align Review and IncrementalReview mode prompts with their guidance — mode prompts said "always submit" while guidance said "skip if clean." remove the empty-approval submission that was silently dropped by the tool. make progress comment lifecycle explicit: created on first call, updated in place, removed after review submission. Made-with: Cursor * centralize todo tracking into shared TodoTracker module extract inline todo tracking logic (~95 lines) from opentoad.ts into action/utils/todoTracking.ts. the tracker is created once in main.ts and passed to agents via AgentRunContext.todoTracker, making it agent-agnostic and reusable for future agent implementations. Made-with: Cursor * fix todoTracker optional type to match file convention add | undefined to todoTracker in AgentRunContext, matching every other optional property in the same interface. Made-with: Cursor * instruct agents to always maintain a task list for live progress system prompt now tells agents to create an internal task list at the start of every run. the tracker renders it to the progress comment automatically. report_progress is reserved for final results only — no more intermediate "Checking..." messages that cancel the tracker and leave stale text on the comment. Made-with: Cursor * require report_progress summary at end of every run agents must always call report_progress with a final summary — the completed task list should never be the end state of the progress comment. updated all review mode prompts to call report_progress after submitting (or not submitting) a review. Made-with: Cursor * keep progress comment after review with final summary stop deleting the progress comment after review submission — the agent now always calls report_progress with a summary at the end, and that summary should persist as a record of what was done. Made-with: Cursor * harden stranded progress comment cleanup - main.ts: detect when tracker was last writer (agent never called report_progress) and delete the stranded checklist instead of leaving it as the final comment state - postCleanup.ts: expand stuck-comment detection to also catch stranded todo checklists (regex match for checklist patterns) when the process is killed before normal cleanup runs - modes.ts + selectMode.ts: add report_progress step to Summarize and SummaryUpdate modes (only modes that were missing it) Made-with: Cursor * fix stale comments, typo, and build mode redundancy - comment.ts: update deleteProgressComment docstring and inline comment to reflect current usage (stranded-comment cleanup, not post-review) - modes.ts: merge duplicate report_progress steps (8 + 10) into single step 9, fix "optimizatfixons" typo - wiki/post-cleanup.md: document checklist detection regex Made-with: Cursor * collapsible completed todos in final progress, hide set_output outside standalone mode - add renderCollapsible() to TodoTracker, append completed task list as <details> section when agent calls report_progress - cancel tracker after agent's final report_progress so it doesn't overwrite with raw checklist - conditionally register SetOutputTool only in standalone mode or when output_schema is provided - remove unconditional set_output instruction from orchestrator task section - update Summarize/SummaryUpdate mode guidance to not reference set_output Made-with: Cursor * show completion count in collapsible task list summary Made-with: Cursor * only count completed (not cancelled) in collapsible task list summary Made-with: Cursor * reinforce concise summary prompting across system prompt, modes, and tool description Made-with: Cursor * address review feedback: wasUpdated bypass, tracker false-positive, race condition - remove wasUpdated=true from cleanup paths so handleAgentResult correctly detects genuinely silent runs - add hadProgressComment to ToolState as immutable snapshot for the safety check - use todoTracker.hasPublished instead of enabled for stranded-comment cleanup - serialize onUpdate calls via inflightPromise chain with post-cancel guard - add settled() to wait for in-flight updates before writing final summary Made-with: Cursor * address round-2 review: hasPublished after success, finalSummaryWritten flag - set hasPublished only after onUpdate resolves (not before) so failed writes are not counted as published - add finalSummaryWritten flag to ToolState, set after successful non-plan reportProgress; decouple cleanup detection from todoTracker.enabled so it survives API failures where cancel() ran but the write didn't succeed Made-with: Cursor |
||
|
|
64f2238316 |
Repo Intelligence: agent-managed per-repo learnings with revision history (#487)
* add repo learnings feature with edit history introduces a new "Learnings" section in the repo console where agents can persist operational knowledge (setup steps, test commands, conventions) at the end of runs via an MCP tool. users can also edit learnings manually. - add `learnings` field to Repo model and `LearningsRevision` audit table - add `update_learnings` MCP tool for agents to persist repo knowledge - integrate learnings into prompt assembly as REPO LEARNINGS section - add learnings step to mode guidance (Build, AddressReviews, Plan, Fix, Task) - add PATCH /api/repo/[owner]/[repo]/learnings endpoint (JWT auth) - add GET /api/repo/[owner]/[repo]/learnings/history endpoint (Clerk auth) - add LearningsSection component with textarea, save-on-blur, and history modal - record revision history with actor tracking (agent vs user) and pruning (50 max) - gate UI behind owner === "pullfrog" for internal dogfooding Made-with: Cursor * fix prisma enum import path for LearningsActor Made-with: Cursor * simplify learnings schema: remove LearningsActor enum, store model name directly the actor/actorName split was unnecessary — learnings are only written by agents so the revision table just needs a model column. removes all user editing concepts from schema, API, and frontend. Made-with: Cursor * fix migration: add separate migration instead of rewriting existing one restores original learnings_revisions migration and adds a new migration that drops actor/actorName columns, backfills model from actorName, and drops the LearningsActor enum. Made-with: Cursor * polish learnings feature: rename to Repo Intelligence, fix atomicity, fix review skip - rename user-facing "learnings" to "Repo Intelligence" (UI, prompt section, wiki, sidebar) - simplify description to "Automatically discovered by the agent across runs." - wrap repo.update + revision create in $transaction for atomicity - refactor recordLearningsRevision to pruneLearningsRevisions (prune-only) - fix empty review skip: don't block APPROVE reviews with no body - fix broken docs anchor: #free-options → #free-models - update agent guidance to require flat bullet list format with pruning - add accessibility: aria-expanded, sr-only loading, output element - add chevron rotation, stale data clear on modal close, max-h scroll - trim + length-limit model field, remove type cast, restore pre-existing comment - update wiki prompt examples with actual bullet-formatted content - update model test snapshot Made-with: Cursor * fix stale free model name in docs, rename utility file to match export - docs/keys.mdx: MiMo V2 Flash → MiMo V2 Pro (matches model code change) - rename recordLearningsRevision.ts → pruneLearningsRevisions.ts Made-with: Cursor * Add skill, .neon * polish learnings UI and remove verbose log - learnings code block: read-only appearance with muted text, copy button, rounded corners - history modal: full-width rows with cursor-pointer, chevron moved to right, no preview text - drop noisy update_learnings log line Made-with: Cursor * inject learningsStep into all modes, drop seed script, soften revision styling Made-with: Cursor * Drop seed --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.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 |
||
|
|
c6a3ee0e9a |
show model name in footer, drop pullfrog.com link (#484)
* show model name in footer, drop pullfrog.com link add model slug to buildPullfrogFooter so every Pullfrog comment displays the active model (e.g. "Using `Big Pickle` (free)" or "Using `Claude Opus`"). remove the pullfrog.com link from all footers. Made-with: Cursor * reject <br/> tags in comment bodies, add prompt guidance add runtime validation in addFooter that throws if <br/> is followed by a non-blank line (breaks GitHub heading rendering). the agent sees the error and retries with clean markdown. also update Summarize mode prompt to explicitly forbid <br/> tags. Made-with: Cursor * fix <br/> guidance: move to event instructions, clarify blank line rule the formatting rule belongs in DEFAULT_PR_SUMMARY_INSTRUCTIONS (event instructions), not the Summarize mode prompt. clarify that <br/> must always be followed by a blank line before headings. Made-with: Cursor * generalize block-level HTML rule in summary instructions add a prominent top-level rule about requiring blank lines between ALL block-level HTML elements and markdown syntax, not just <br/>. Made-with: Cursor * move model to toolState instead of threading through params model is set once at startup and read everywhere — it belongs on toolState, not threaded as a separate param through 8 call sites. postCleanup runs without toolState so it just omits the model label. Made-with: Cursor * update models.dev snapshot (openai latest changed) Made-with: Cursor * add comment to models snapshot test explaining its purpose Made-with: Cursor |
||
|
|
30d68e53a7 |
fix: skip API key validation for free opencode models
free models (big-pickle, gpt-5-nano, etc.) define envVars: [] and isFree: true but validateAgentApiKey always required at least one provider key. now the validation is model-aware: free models bypass the check, keyed models validate their specific vars, and auto-select still requires at least one known key. closes #483 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 |
||
|
|
5bcfae990a |
restructure dashboard UI, add mode instructions, post-review follow-up dispatch (#453)
* add mode instructions and restructure dashboard sidebar
- add modeInstructions JSONB field to Repo model for per-mode user instructions
- thread modeInstructions through settings API, run-context API, RepoSettings, ToolContext, and selectMode runtime
- merge user-defined mode instructions with hardcoded orchestrator guidance, with IncrementalReview inheriting from Review
- reduce visible built-in modes from 7 to 4 (Build, Review, Plan, Fix) with editable Instructions textareas
- add TRIGGERS group header to sidebar above Mentions, Pull requests, Issues
- add wiki/modes.md documenting triggers and modes conceptual model
Made-with: Cursor
* fix leaping comment deletion and address review feedback
- wrap post-createReview operations in try/finally so deleteProgressComment
runs even when updateReview or reportReviewNodeId throws
- add parseModeInstructions runtime guard to filter non-string values
from the JSONB field before passing to buildOrchestratorGuidance
- add useEffect sync for localInstructions when props change
- guard onBlur to skip save when instructions haven't changed
- update wiki/modes.md to reflect V2 is implemented (no longer "proposed")
Made-with: Cursor
* harden review cleanup, fix type cast, stabilize mode instructions state
- wrap deleteProgressComment in try/catch inside finally to prevent masking original errors
- replace `as Record<string,string>` cast with runtime parseModeInstructions + useMemo
- fix wiki dual-prompt table to reflect mode.prompt fallback status
Made-with: Cursor
* fix wiki tense and heading ambiguity from PR review
Made-with: Cursor
* fix review "edited" badge by using pending review + submit flow
create review as PENDING first (no event/body), build the footer with
the now-known review ID, then submitReview with the full body. single
atomic publish — no updateReview edit needed.
Made-with: Cursor
* add post-agent follow-up re-review dispatch
After the agent exits, check if PR HEAD moved past the reviewed commit
and dispatch a follow-up re-review. This closes the gap where push
webhooks are suppressed during in-flight reviews.
Made-with: Cursor
* add silent flag to follow-up re-review dispatch
Made-with: Cursor
* restructure dashboard for consistency and clarity
- consolidate tools into single grouped card (was 4 separate cards)
- merge coding + autofix CI into one section
- remove redundant trigger section descriptions
- add bidirectional crosslinks between modes and triggers
- inline instruction links (review/plan/build) into descriptions
- add save status indicators to all sections
- restructure flags with grouped built-in/custom cards
- flatten sidebar (remove dividers and group headers)
- tighten all descriptions
Made-with: Cursor
* update PR screenshots for new dashboard layout
Made-with: Cursor
* extend review context inline instead of dispatching new workflow
when commits are pushed during a review, the agent now handles them
inline: create_pull_request_review detects HEAD movement, returns
instructions to pull and review the incremental diff, and the agent
submits a second review covering only the new changes. this avoids
the cost of spinning up a full new workflow run.
also fixes a bug where reviewedSha was set to the submission HEAD
(current) rather than the checkout HEAD (what was actually reviewed),
which caused commits pushed between checkout and submission to be
silently missed by postReviewCleanup.
the workflow dispatch is kept as a safety net for agent timeout/error.
Made-with: Cursor
* polish dashboard UI: fix debug markers, crosslinks, title consistency, descriptions
- remove all red debug borders/labels and CM component
- remove all inline style={{}} debug outlines from crosslinks
- fix ambiguous crosslinks: Build→"Coding ↓", Plan→"Enrich issues ↓"
- add missing "Edit build instructions ↑" backlink on Auto-address reviews
- normalize card title weight to text-sm font-semibold across all cards
- rename "Default" subcard to "Setup" with broader description
- fix Mentions description to imperative tone
- broaden Flags section description to cover built-in and custom
- remove useless fragments in ModesSection and ToolsSettings
- restructure Agent section: remove ConsoleSection wrappers, add sidebar indent support
Made-with: Cursor
* extract PR quick links as standalone card, consistent with issues
- PR quick links is now its own card under Reviews (was a sub-toggle inside Review PRs disabled state)
- Review PRs OFF sets prCreated="none" instead of auto-falling back to "links"
- Review PRs card hides sub-toggles when disabled (re-review/approve don't apply)
- Both PRs and Issues now have identical Quick links card structure
Made-with: Cursor
* update reviews screenshot with standalone quick links card
Made-with: Cursor
* polish dashboard UI: revert quick links to inline toggles, fix fonts and spacing
- revert standalone PR/issue Quick Links cards back to inline toggles inside
Review PRs and Enrich Issues cards (fixes prCreated state coupling bug)
- restore original font-medium card titles across all trigger/settings cards
- fix sidebar: add CONSOLE heading, remove nested indentation, remove truncation
- right-justify Enrich Issues mode dropdown, group description with label
- move instructions links inline with behavior descriptions
- replace text save indicators with icon spinner/checkmark
- standardize section title spacing, move footer below danger zone
Made-with: Cursor
* fix formatting for biome lint
Made-with: Cursor
* address PR review feedback: cleanup guard, shared util, wiki update
- clear ctx.toolState.review after read to prevent double-execution of postReviewCleanup
- forward authorPermission in safety-net re-review dispatch
- extract parseModeInstructions to utils/schemas/modeInstructions.ts
- update wiki/modes.md: remove stale v1/v2 language, fix dashboard layout
- add typecheck to pre-push hook
Made-with: Cursor
* add action typecheck to pre-push, fix exactOptionalPropertyTypes errors
Made-with: Cursor
* fix duplicate actuallyReviewedSha from rebase
Made-with: Cursor
* remove PR screenshots
Made-with: Cursor
* add label/textarea association for mode instruction accessibility
Made-with: Cursor
---------
Co-authored-by: Colin McDonnell <colinmcd94@gmail.com>
|
||
|
|
8bac460177 |
fix: add concurrency protection to action sync workflows (#451)
* fix: add concurrency protection to action sync workflows * style: fix formatting in action/modes.ts --------- Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com> |
||
|
|
5684cbef77 |
Implement support for output schemas (#411)
* tweak examples * tweak prompt * Implement support for output schemas * fix: add example for structured output with zod schema * tweak * remove redundant cast * fix input name * strip $schema * hack around vendor requirement * clarify required result output --------- Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com> |
||
|
|
cc46af0d47 |
Share GitHub rate limit tracking between the action and the worker (#326)
* share GitHub rate limit tracking between the action and the worker The action now counts all GitHub API requests and captures the latest `x-ratelimit-remaining`/`x-ratelimit-reset` headers via a global request hook on every Octokit instance. On exit, the usage summary is written atomically to a path specified by `PULLFROG_USAGE_SUMMARY_PATH`. The worker sets this env var before sandbox execution, reads the file afterward, and feeds the data into the Durable Object's rate limit state. This closes the visibility gap where the worker had no insight into API calls made by the sandboxed action process. * address review: refactor rate limit state, randomize usage summary path * track actual rate limit cost using x-ratelimit-remaining delta * refactor usage summary writing to use onExitSignal API Replace the monolithic registerUsageSummaryHandler with direct use of onExitSignal in main.ts and a writeGitHubUsageSummaryToFile utility in github.ts. This keeps exitHandler.ts as a pure signal handler registry (from #299) and also writes the summary on normal exit. * tweak * unify * deduplicate stuff * improve error handling --------- Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com> Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com> |
||
|
|
53970308ee |
Add incremental re-review on new PR commits (#388)
* add incremental re-review on new PR commits When new commits are pushed to a PR that Pullfrog has previously reviewed, automatically perform a focused re-review on only the new changes. Includes a supersede mechanism to abort stale in-flight reviews on rapid pushes, a new IncrementalReview mode with incremental diff + prior-feedback awareness, and a PRReview tracking model so re-review fires for both auto-reviewed and manually-triggered PRs. Reviews now always submit (APPROVE when clean). Co-authored-by: Cursor <cursoragent@cursor.com> * simplify re-review eligibility and add summary to incremental reviews Remove the path1/path2 distinction for re-review eligibility — now simply requires prReReview=enabled and a prior Pullfrog review on the PR. Show the re-review toggle regardless of prCreated setting. Add a top-level summary body to incremental reviews for consistency with full reviews. Co-authored-by: Cursor <cursoragent@cursor.com> * replace superseded polling with server-side in-flight dedup and add prApproveEnabled setting Made-with: Cursor * add armstrong cursor command Made-with: Cursor * update armstrong * feat: Pullfrogger game v1 (#378) * Frogger game basis code (CC0 1.0 Unversal license). * Initial React port. * fix props for Sprite. * fixed context issue in FroggerGame. * Fixed format. * Fixed lint issue in FroggerGame. * Restoring LeapingLoader, using FroggerGame as a new fallback in Suspense. * feat: Display toast when URL ready. * Add props constraints on Sprite. * feat: frog sprite. * fix: zoom and alignment. * fix: extract const. * fix: mv types. * fix: mv game into index.tsx file. * fix: replacing deprecated event prop which with key. * feat: Log sprite. * feat: turtle sprite. * Adjusting game colors. * feat: sprites for the cars. * rm primitive sprites. * fix: bulldozer sprite position. * Adjusting colors. * Shape constraints. * rm original. * minor: naming, cleanup. * fix: renderers dict. * fix: adjusting and renaming racer. * fix renderer binding for scored frogs. * feat: responsive layout with gap below and maintained aspect ratio. * grammar fix. * feat: road lane dividers. * feat: using AbortController to cleanup events. * fix: cleanup and shortening. * feat: extracting drawGameBackground. * feat: initObstacleRows and updateAndDrawObstacles helpers. * feat: initFroggers helper. * feat: drawFroggers helper. * feat: checkForCollision helper. * feat: makeKeydownHandler helper. * feat: listenToKeyboardEvents helper. * mv cleanup into drawGameBackground. * fix: cleanup. * feat: better adjustBrightness helper. * Polling on the page.tsx side, restoring timeout and fallbacks, dynamic link msg. * FEAT: wait for workflow to complete and notify additionally with a big link (incl.db migration). * Fix: larger title, shorter link. * fix(hook): Writing completedAt from hook.workflow_run.updated_at according to suggestion. * fix(loader): rm unused props from WorkflowRunClientProps as suggested. * fix(loader): mv id=dev case into the page. * fix(DNRY): mv PollStartedResult and PollCompletedResult types. * fix(DNRY): reusing drawEllipse() helper in Sprite. * fix(style): reordering methods by priority in Sprite. * fix(frogger): rm empty rows from canvas, square game. * feat: game canvas rounded corners. * fix(DNRY): extracting SpriteShape type for faster reference. * fix: rm target from links, use same window. * add incremental re-review on new PR commits When new commits are pushed to a PR that Pullfrog has previously reviewed, automatically perform a focused re-review on only the new changes. Includes a supersede mechanism to abort stale in-flight reviews on rapid pushes, a new IncrementalReview mode with incremental diff + prior-feedback awareness, and a PRReview tracking model so re-review fires for both auto-reviewed and manually-triggered PRs. Reviews now always submit (APPROVE when clean). Co-authored-by: Cursor <cursoragent@cursor.com> * replace superseded polling with server-side in-flight dedup and add prApproveEnabled setting Made-with: Cursor * consolidate workflow_run completed handling and track completedAt Removes the duplicate exported handleWorkflowRunCompleted in favor of the private one, merges status + orphan resolution logic into a single path, and sets completedAt on both normal completion and orphan cancel. Made-with: Cursor * fix rebase conflict resolution: restore eligibility logic, incremental review summaries, and exhaustiveness check - replace deleted hasPullfrogReviewedPR call with WorkflowRun.findFirst (the utility file was removed by the dedup improvements commit) - restore IncrementalReview summary body in modes.ts and selectMode.ts (lost during ca0168b conflict resolution; origin had re-added them via f98f902) - use switch + satisfies never for workflow_run event dispatch - lowercase comments per project conventions Made-with: Cursor * fix workflow-run polling architecture and improve incremental review prompts move polling loops from server actions to client to avoid serverless timeouts (pollForCompleted ran up to 600s in a single invocation). each server action is now a single DB check; client drives retries. also fix misleading prompt text about incremental diff scope and remove dead code in handleWebhook. Made-with: Cursor * await reportReviewNodeId to eliminate race condition and webhook sleep - refactor reportReviewNodeId from fire-and-forget to async/awaited, guaranteeing the dedup signal lands before the tool returns - remove the 5-second grace period sleep in the synchronize webhook handler (no longer needed with the awaited PATCH) - update IncrementalReview guidance to use get_review_comments for detailed prior line-level feedback instead of just review summaries - remove dead fallbackUrl field from CheckStartedResult type Made-with: Cursor * fix rebase artifacts: broken triggeringIssue reference, review formatting, and prompt wording Made-with: Cursor --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Anna Bocharova <robin_tail@me.com> |
||
|
|
6311138132 |
fix: report errors to progress comment when agent fails without throwing (#376)
* fix: report errors to progress comment when agent fails without throwing when an agent returns success: false without throwing (e.g., opencode exits with code 0 despite provider errors), the catch block in main.ts is never reached, leaving the progress comment stuck on "leaping into action." two fixes: - opencode: return success: false when 0 events processed and a provider error was detected (converts silent failures to explicit failures) - main.ts: after handleAgentResult, check if the progress comment was ever updated — if not, report the error to the comment as a safety net Co-authored-by: Cursor <cursoragent@cursor.com> * address review: use result directly and force failure on unreported progress * refactor: move safety-net logic into handleAgentResult --------- Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Anna Bocharova <robin_tail@me.com> |
||
|
|
2017922780 |
Improve delegate (#377)
* Improve delegate * fix stale log regexes in delegate tests and add test-coupling comments Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
a7bd746f21 |
Restructure dash (#372)
* Restructure dash * WIP * WIP * refactor trigger UI: extract PR summary card, add mentions section, rename labels Co-authored-by: Cursor <cursoragent@cursor.com> * clean up console UI: remove info icons from section descriptions, rename mentions trigger Co-authored-by: Cursor <cursoragent@cursor.com> * fix review feedback: layout, terminology, form scope - extract console sidebar sections to module-level constant - align three-column layout breakpoints to xl (match sidebar visibility) - fix mixed shell/bash terminology in beta page - scope FormProvider to trigger sections only, restore autoComplete="off" Co-authored-by: Cursor <cursoragent@cursor.com> * Bump --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
cfd38d82fc |
refactor delegation system, add PR summary comments, and improve code quality (#334)
* refactor delegation system and add PR summary comments Delegation system: - replace mode-based delegation with select_mode → delegate two-step flow - orchestrator crafts self-contained subagent prompts (clean context — no system/repo/event instructions leak) - add role-based tool filtering via FastMCP authenticate hook (?role=subagent hides orchestrator-only tools) - add select_mode tool for orchestrator guidance per mode - add ask_question tool for lightweight research subagents - extract shared subagent lifecycle into subagent.ts (create, complete, stdout, instructions) - route set_output to per-subagent state when activeSubagentId is set - track per-subagent state (SubagentState Map) replacing boolean delegationActive flag - capture and aggregate AgentUsage across all agents (claude, codex, gemini, opencode) - write usage summary table to GitHub job summary - block built-in subagent spawning (Task for Claude, Task(*) for Cursor) - increase activity timeout from 60s to 300s (subagent thinking phases) - fix gh CLI misguidance in system prompt — explicitly forbid usage PR summary comments: - add prSummaryComment trigger (DB schema + migrations + Zod + UI toggle) - dispatch mini-effort summary job alongside PR review on pr.created - add update_pull_request_body MCP tool - add defaultEffort option to webhook dispatch Hardening: - rewrite delegate/selectMode tests with simulated state management - add toolFiltering.test.ts for role extraction, canAccess, set_output routing - remove non-null assertions for PULLFROG_TEMP_DIR (proper error throws) - use fetchWithRetry for direct tarball downloads - DRY fix for rate limit check in test runner Co-authored-by: Cursor <cursoragent@cursor.com> * fix: add type keyword to Effort import in handleWebhook.ts Co-authored-by: Cursor <cursoragent@cursor.com> * clean up delegation system, improve code quality across the codebase - simplify delegate tool to instructions + effort params with subagent lifecycle in subagent.ts - add select_mode and ask_question orchestrator-only tools with canAccess filtering - replace delegate.test.ts/selectMode.test.ts with toolFiltering.test.ts (live MCP integration) - add set_output routing for subagent context and AgentUsage tracking across all agents - add PR summary comment trigger (schema, UI, webhook dispatch with silent flag) - add update_pull_request_body MCP tool - fix changed-agents.sh to always include claude canary for non-agent action changes - fix cursor pagination bug in getSelectedInstallationReposPage - remove destructuring patterns, inline type definitions, and unsafe type casts - replace non-null assertions with explicit checks in install.ts - convert multi-param functions to single param objects (postCleanup, runActionLocal, etc.) - use isHttpError helper in API routes instead of catch-any patterns - add adhoc test fixtures for delegation scenarios (context isolation, error handling, synthesis, etc.) Co-authored-by: Cursor <cursoragent@cursor.com> * no subagent mutation, one mcp per subagent * address review feedback: parallel-safe usage tracking, subagent isolation, minor improvements * fix subagent state isolation: replace Object.freeze with shallow copy Object.freeze throws TypeErrors when subagent tools (checkout_pr, report_progress) write scalar properties to toolState. A shallow copy achieves the same isolation for scalar fields while allowing tools to work normally. Shared references (subagents Map, usageEntries array) remain shared for coordination. --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com> |
||
|
|
ee100354da |
fix: replace domain-specific exit handler with generic signal handler registry (#299)
* fix: replace domain-specific exit handler with generic signal handler registry Rewrite exitHandler.ts as a generic, domain-agnostic exit signal module that exports onExitSignal(handler) returning a dispose function. - subprocess.ts now registers via onExitSignal instead of direct process.on(SIGINT/SIGTERM) calls - resolveTokens registers a signal handler that captures tokens by closure, fixing the race condition where the exit handler would read the wrong token after disposal - Remove setupExitHandler and runCleanup — domain cleanup is handled by post.ts + await using Co-authored-by: Cursor <cursoragent@cursor.com> * tweaks * simplify handler installation * extract to util * fix race in dispose * wrap dispose body in try/finally to ensure disposingRef always settles --------- Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com> |
||
|
|
57537d1a95 |
move instructions logging earlier and clarify built-in tool logs
Log the instructions box immediately after instruction resolution in main, and standardize agent permission summaries to debug-level "disallowed built-ins" output to reduce confusion with pullfrog MCP tools. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
9071c0ae6c | refactor mode selection into delegate tool that spawns subagents (#265) | ||
|
|
bc28c658f2 |
harden sandbox escape vectors for bash disabled/restricted modes (#257)
* harden sandbox escape vectors for bash disabled/restricted modes block git config injection (-c flag as subcommand), dangerous subcommands (config, submodule, rebase, bisect), code-executing arg flags (--exec, --extcmd), .gitattributes/.gitmodules writes, and package lifecycle scripts. add retry logic to test runner for transient failures. add security unit tests and adhoc attack tests. Co-authored-by: Cursor <cursoragent@cursor.com> * only filter subcommands in nobash, remove nobash from ui * use regex matching * iterate on tests * simplify githooks --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
f67cc25f74 | migrate to flags (#249) | ||
|
|
1d59fd3d21 |
feat: Lifecycle hooks (#219)
* flatten lifecycle hooks into RepoSettings string fields replace the separate LifecycleHook model with setupScript and postCheckoutScript string fields directly on RepoSettings. move the UI into the Agent settings section alongside environment variables and custom instructions. delete the standalone lifecycle-hooks API route, component, and schema since the existing settings PATCH endpoint handles the new fields automatically. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: pass env to lifecycle hook spawn so scripts can use package managers Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
3a7145db1a |
Scope installation token permissions in restricted mode (#226)
* Scope installation token permissions in restricted mode
In restricted/disabled bash mode, the installation token is now scoped
to match the workflow's permissions block. This preserves fork push
capability while limiting what the agent can do with the token.
- Read workflow permissions from pullfrog.yml at runtime
- Pass permissions to API when acquiring installation token
- Clear OIDC env vars in restricted mode to prevent token minting
- Simplify setupGit by moving token resolution to main.ts
* Address review feedback: fail closed with default permissions
- Add restrictive default permissions (contents:read, pull_requests:read,
issues:read) as fallback when workflow permissions can't be read
- Add support for job-level permissions via GITHUB_JOB env var
- Fix misleading comment about token resolution in restricted mode
- Add documentation about fork PR checkout behavior
* Simplify to separate git/MCP tokens without workflow permission scoping
- gitToken: minimal contents:write only (assumed exfiltratable)
- mcpToken: full installation token (not exfiltratable via MCP tools)
- Remove workflowPermissions.ts - security-conscious users can pass
their own token via GH_TOKEN or inputs.token
- Add type-safe InstallationTokenPermissions to github.ts and API route
* Rename `write` permission to `push` and remove vestigial tool blocking
The `write` permission was previously used to block local file write tools
in agents. This was security theater since bash can write files anyway.
Now `push` only controls the git token scope:
- push: enabled → contents:write (can push commits)
- push: disabled → contents:read (read-only, can't push)
Changes:
- Rename `write` to `push` in action.yml, Prisma schema, and all TS types
- Remove vestigial write tool blocking from all agents (claude, cursor,
gemini, opencode, codex)
- Add data-preserving Prisma migration using RENAME COLUMN
- Update UI: "Write files" → "Git push" with updated description
* add PID namespace isolation for bash sandbox
when running in CI, attempts to use unshare --pid to create a new PID
namespace for bash subprocesses. this prevents the /proc/$PPID/environ
attack where a malicious command could read secrets from the parent
process's environment.
the protection works by:
1. creating a new PID namespace (subprocess becomes PID 1)
2. mounting fresh /proc showing only sandbox PIDs
3. parent PIDs become invisible (PPID = 0, /proc/0 doesn't exist)
combined with filterEnv(), this provides complete protection against
/proc-based secret theft. falls back gracefully if namespaces aren't
available.
includes test script to verify the protection works.
* add PID namespace test to CI workflow
tests whether unshare --pid works on GHA runners out of the box,
and if not, whether enabling via sysctl helps. also runs the
pidNamespace.ts test to verify the full protection.
* fix pnpm setup and add procIsolation agent test
- fix pnpm/action-setup by specifying package_json_file path
- add procIsolation crossagent test that has agent attempt to
read secrets via /proc/$PPID/environ
- add procIsolation to CI test matrix
* add pid-namespace test job to main workflow
this job tests unshare --pid capabilities on GHA runners and runs
the pidNamespace.ts adhoc test to verify /proc isolation works
* test bubblewrap's sysctl approach for enabling namespaces
- write to /etc/sysctl.d/99-userns.conf and run sysctl --system
- try aa-complain on unshare binary
- more detailed diagnostics
* fix pidNamespace test and add sudo-unshare fallback for GHA
- fix reference error in pidNamespace.ts (renamed function but didn't update calls)
- add sudo-unshare as fallback method for GHA runners where unprivileged
namespaces are blocked but sudo is available
- update bash.ts to detect and use sudo unshare when unprivileged fails
* consolidate security docs and document PID namespace isolation
- update security.md with current implementation details
- document sudo unshare fallback for GHA runners
- add testing instructions for local Docker and CI
- add "Further Exploration" section with Landlock and path validation ideas
- delete bash-sandbox.md and landlock.md (consolidated into security.md)
* move procIsolation test to adhoc folder
the procIsolation test requires PID namespace capabilities that aren't
available in the Docker test environment. moved to adhoc/ so it's excluded
from default test runs and can be run explicitly when needed (e.g. via
the pid-namespace CI job or locally with --privileged docker).
* fix Docker test environment for PID namespace isolation
- add CI and GITHUB_ACTIONS to testEnvAllowList so sandbox detection runs
- add --privileged to Docker run for PID namespace support (unshare)
this fixes the test environment to properly test the sandbox. in production,
the action runs directly on GHA runner where sudo unshare works.
* fix getJobToken() to work in test environment
add fallback to GH_TOKEN and GITHUB_TOKEN when INPUT_TOKEN is not set.
this allows tests to run without requiring workflow-level token input.
the token resolution order is:
1. INPUT_TOKEN (from workflow `with: token:`)
2. GH_TOKEN (external token override)
3. GITHUB_TOKEN (pre-acquired in tests or from GHA env)
* security: filter secrets from all subprocess environments
- extract filterEnv() to shared utils/secrets.ts
- make $() utility filter secrets by default (git, npm, etc. don't need them)
- disable git hooks via core.hooksPath to prevent hook-based exfiltration
- git auth uses token embedded in URL, not env vars
this prevents malicious git hooks, npm postinstall scripts, and other
code execution vectors from exfiltrating GITHUB_TOKEN and API keys.
* docs: clarify defense-in-depth security model
update security.md to explain why BOTH layers are required:
- filterEnv(): cleans child's own /proc/self/environ
- PID namespace: hides parent's /proc entries
PID namespace alone isn't sufficient - with --mount-proc, the child
becomes PID 1, so /proc/1/environ is the child's OWN environment.
without filterEnv(), secrets would still be accessible there.
* add procSandbox crossagent test for PID namespace security
- add crossagent/procSandbox.ts: security test that instructs agent to try
various /proc attack vectors and validates sandbox blocks them
- update wiki/security.md: document PID namespace isolation details, add
verification commands, explain why sudo inside sandbox doesn't break security
- update docker.ts: use node:24 with sudo for GHA-like test environment
- update instructions.ts: allow disabling security messaging for pentests
- clean up adhoc test files (procIsolation.ts, securityAudit.ts)
the procSandbox test sets SANDBOX_TEST_TOKEN (matches *_TOKEN filter), gives
the agent explicit hints about attack vectors (/proc, sudo, nsenter, etc.),
disables prompt-based mitigations, and validates the secret is never leaked.
* move procSandbox test to agnostic/ (runs with one agent)
* WIP
* docs: add agent testing guide (pnpm play, Docker, pentesting)
* docs: add CI details to agent testing guide
* docs: add interesting findings and gotchas from pentesting
* improve test fidelity: auto-set CI=true, verify sandbox active
- docker.ts: always set CI=true in container so sandbox activates
- docker.ts: skip sudo/user setup if already done (faster reruns)
- bash.ts: export getSandboxMethod() for diagnostics
- bash.ts: add debug log when sandbox disabled
- procSandbox.ts: add sandbox_was_active check to catch vacuous passes
the CI=true change is critical: without it, PID namespace isolation
is skipped and security tests pass without actually testing anything.
* docs: update agent-testing.md with CI=true auto-set note
* docs: clarify log format is agent-specific
* fix git auth, simplify MCP tools, add adversarial tests
- fix gitWithAuth to use Basic auth format (Bearer doesn't work with git's http.extraheader)
- fix token.ts: push:restricted now correctly gets contents:write
- fix github.ts: pass permissions when acquiring installation tokens locally
- remove commit_files and create_branch MCP tools (redundant, don't require credentials)
- remove containsSecrets() - trivially bypassable, not sound security
- add agnostic adversarial tests: pushDisabled, pushRestricted, tokenExfil
- update instructions.ts to clearly list available git MCP tools
- add wiki/git.md documenting credential isolation and push permission tiers
- update wiki/docker.md with custom image considerations
- update wiki/agent-testing.md with adversarial testing patterns
* fix type errors after rebase
- change ResolveTokensParams.push from ToolPermission to PushPermission
- use tags: ["agnostic"] instead of agnostic: true in test files
* fix cleanup permission error in sandbox tests
when sandbox isolation is enabled (CI=true), files created by the unshare
subprocess may have different ownership, causing rmSync to fail with EACCES.
this error in the finally block was overriding the test's success result.
fix: wrap cleanup in try-catch and fall back to sudo rm if rmSync fails.
* Add adhoc
* Handle git config/remote bypasses
* add git hooks protection and simplify ToolState
- disable git hooks in restricted mode via -c core.hooksPath=/dev/null
- add gitHooks adversarial test to verify hook protection works
- unify prNumber/issueNumber into single issueNumber field
- add pushUrl to ToolState for push validation
- add generateTestMarker() for simpler single-agent test markers
- export SENSITIVE_PATTERNS and isSensitiveEnvName from secrets.ts
- remove redundant pidNamespace.ts (duplicated by procSandbox.ts)
- update documentation
* harden $git() auth: subcommand whitelist, binary tamper detection
- rename gitWithAuth() to $git() with explicit subcommand first arg
- restrict to "fetch" | "push" at type level (filters don't run for these)
- resolve git binary path at startup via resolveGit(), sha256 fingerprint
- verify hash before each $git() call to detect binary replacement
- rename disableHooks to restricted for cleaner semantics
- document filter exfiltration attack and empirical verification in wiki
* remove redundant pid-namespace CI job
the PID namespace isolation testing is now handled by
action/test/agnostic/procSandbox.ts via pnpm runtest agnostic
* fix push_branch for new branches and improve token leak detection
- getPushDestination now falls back to origin/<branch> when @{push}
is not configured (happens for new branches created locally)
- gitPerms validator now checks for actual token patterns instead
of matching "x-access-token" string in test instructions
* use kebab-case for test names
* simplify shell env API: "restricted" | "inherit" | object
replace passFullEnv boolean with cleaner env option that accepts:
- "restricted" (default): filterEnv() to prevent secret leakage
- "inherit": full process.env
- object: custom env merged with restricted base
* share EnvMode and resolveEnv between shell.ts and bash.ts
move shared env resolution logic to secrets.ts
* add env option to bash tool (default: restricted)
* delete agent-testing.md (renamed to adversarial.md)
* Add checkout tests
* reframe githooks test prompt to avoid claude safety refusal
claude was refusing to execute the test because the prompt used words
like "malicious" and "security testing". reframed as a debugging task
with innocuous env var name (TESTING_DEBUG_TAG) per adversarial.md guidance.
Co-authored-by: Cursor <cursoragent@cursor.com>
* clean up verbose token acquisition logs
move logging responsibility to call sites which have better context
(git token vs MCP token). remove redundant intermediate OIDC logs
and unused "(permission-scoped)" suffix.
Co-authored-by: Cursor <cursoragent@cursor.com>
* isolate agnostic tests with matrix strategy, fix .pullfrog-env secret leak
- split action-agnostic into per-test matrix jobs for isolated logs and filesystems
- only write explicitly opted-in env vars to .pullfrog-env via fileAgentEnv
(fixes token-exfil test where claude found SANDBOX_TEST_TOKEN on disk)
- mcpmerge test opts in via fileAgentEnv for cursor's repo-level MCP fallback
Co-authored-by: Cursor <cursoragent@cursor.com>
* remove env parameter from bash tool to prevent agents bypassing filterEnv
the bash tool exposed an `env` parameter accepting "restricted" | "inherit"
which allowed agents to pass env: "inherit" and see all secrets including
SANDBOX_TEST_TOKEN, bypassing the restricted environment filtering entirely.
env mode is now determined internally (always restricted).
Co-authored-by: Cursor <cursoragent@cursor.com>
* use pullfrog/test-repo for push tests to stop polluting main repo
push tests were creating branches and tags on pullfrog/app directly.
now all push tests (push-restricted, push-disabled, push-enabled,
git-permissions) target pullfrog/test-repo instead.
Co-authored-by: Cursor <cursoragent@cursor.com>
* use pullfrog/test-repo for all tests, not just push tests
no test should clone or operate on pullfrog/app directly.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix token scoping for test-repo and bash timeout defaults
- acquireTokenViaOIDC now includes GITHUB_REPOSITORY repo in token
scope so push tests work against pullfrog/test-repo
- bash tool default timeout: 120s -> 30s, cap: 600s -> 120s
- activity timeout: 30s -> 60s
- prevents hung bash commands (e.g. find /) from killing the agent
via activity timeout
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
|
||
|
|
6fbff21fca | add agent and debug macros, improve activity timeouts, migrate claude and codex to cli (#224) | ||
|
|
bfe72ac2cf | Fixed how payload-as-prompt is handled and progress comment updates (#212) | ||
|
|
18ba8e5fd0 | improve runtest, optimize CI batching (#210) | ||
|
|
2b3bd97b86 |
Forward API calls from the preview repos (#211)
* Forward API calls from the preview repos * tweak doc * tweak * fix workflow-run forwarding |
||
|
|
c1f8247077 |
Add set_output tool (#205)
|
||
|
|
2daab6fc78 | Obtain job-level token by default for less privileged runs (#198) | ||
|
|
943409c417 | add #timeout, macro errors, refactor tests (#191) | ||
|
|
f77fecc2a0 | Update | ||
|
|
071e885d63 |
Add upload tool and related APIs (#187)
* Add utils for r2 upload * Add the tool and new routes * fix auth issue * sign headers * add comment * use our own API key to auth signed uploads * Restructure things slightly * tweak * tweak * add comments * tweak * revert a thing * twaek * drop mime type filtering * new incarnation of mime type filtering * jsut allow all octet-streams * simplify further * tweak * update lockfile --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> |
||
|
|
102417f442 |
Add post hooks for cleanup (#193)
* Add post hooks for cleanup * Switch to signal-based cleanup * Better exit handling |
||
|
|
3a975cc384 | Add md <> code comments | ||
|
|
57895ae342 | Update lock | ||
|
|
01ee59a96c | Restrict github token (#140) | ||
|
|
04cc24bf64 | improve nobash tests, fix cursor, reenable CI (#138) | ||
|
|
cb925556e8 | refactor instructions to return object with full/system/user/event/runtime properties, fix duplicate modes and json prompt extraction (#110) | ||
|
|
69b9b96ddd | Refactor (#109) | ||
|
|
101c666610 | Fix capitalization issues |