limn-review 0.1.123
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +110 -0
- package/README.md +133 -0
- package/dist/ai/runtime.js +535 -0
- package/dist/api/plugins/auth.js +169 -0
- package/dist/api/plugins/error-handler.js +114 -0
- package/dist/api/plugins/rate-limit.js +855 -0
- package/dist/api/plugins/security.js +321 -0
- package/dist/api/routes/activity.js +103 -0
- package/dist/api/routes/ai-runtime.js +67 -0
- package/dist/api/routes/auth.js +194 -0
- package/dist/api/routes/billing.js +209 -0
- package/dist/api/routes/bot-triage.js +1289 -0
- package/dist/api/routes/conflicts.js +730 -0
- package/dist/api/routes/contact.js +318 -0
- package/dist/api/routes/daily-brief.js +58 -0
- package/dist/api/routes/flow.js +71 -0
- package/dist/api/routes/health.js +7 -0
- package/dist/api/routes/insights.js +200 -0
- package/dist/api/routes/me.js +458 -0
- package/dist/api/routes/mentions.js +22 -0
- package/dist/api/routes/mergers.js +8 -0
- package/dist/api/routes/ml-labels.js +109 -0
- package/dist/api/routes/open-prs.js +35 -0
- package/dist/api/routes/prs.js +1633 -0
- package/dist/api/routes/reactions.js +217 -0
- package/dist/api/routes/repos.js +563 -0
- package/dist/api/routes/search.js +41 -0
- package/dist/api/routes/threads.js +118 -0
- package/dist/api/routes/timeline.js +150 -0
- package/dist/api/routes/users.js +67 -0
- package/dist/api/routes/webhooks.js +181 -0
- package/dist/api/routes/workspaces.js +322 -0
- package/dist/app.js +274 -0
- package/dist/ascii.js +19 -0
- package/dist/auth/account.js +482 -0
- package/dist/auth/crypto.js +46 -0
- package/dist/bot-triage/resolve.js +59 -0
- package/dist/cli.js +388 -0
- package/dist/coding/activity.js +80 -0
- package/dist/coding/agent.js +288 -0
- package/dist/coding/ai-fix/auto-fix.js +212 -0
- package/dist/coding/ai-fix/manager.js +370 -0
- package/dist/coding/ai-fix/persist.js +213 -0
- package/dist/coding/ai-fix/pr-context.js +58 -0
- package/dist/coding/ai-fix/prompts.js +138 -0
- package/dist/coding/ai-fix/review-seed.js +357 -0
- package/dist/coding/ai-fix/routes.js +425 -0
- package/dist/coding/git-ops.js +321 -0
- package/dist/coding/git.js +184 -0
- package/dist/coding/merge.js +262 -0
- package/dist/coding/safe-path.js +75 -0
- package/dist/coding/schema.js +43 -0
- package/dist/config.js +648 -0
- package/dist/conflict/diff.js +304 -0
- package/dist/conflict/git.js +118 -0
- package/dist/conflict/hash.js +73 -0
- package/dist/conflict/janitor.js +218 -0
- package/dist/conflict/land.js +675 -0
- package/dist/conflict/model-types.js +2 -0
- package/dist/conflict/model.js +1081 -0
- package/dist/conflict/parse.js +142 -0
- package/dist/conflict/seam.js +136 -0
- package/dist/conflict/session.js +532 -0
- package/dist/conflict/suggestion.js +398 -0
- package/dist/conflict/tokens.js +72 -0
- package/dist/data-dir.js +112 -0
- package/dist/db/actor-lanes.js +213 -0
- package/dist/db/automation-ids.js +97 -0
- package/dist/db/automation-output.js +349 -0
- package/dist/db/blast-radius-query.js +36 -0
- package/dist/db/blast-radius.js +226 -0
- package/dist/db/bot-dormancy.js +115 -0
- package/dist/db/bot-overlap.js +579 -0
- package/dist/db/bot-volume.js +722 -0
- package/dist/db/bot-window.js +35 -0
- package/dist/db/branch-queries.js +310 -0
- package/dist/db/change-shape.js +243 -0
- package/dist/db/changepoint.js +75 -0
- package/dist/db/cleanup.js +36 -0
- package/dist/db/client.js +102 -0
- package/dist/db/code-loc.js +388 -0
- package/dist/db/credits.js +79 -0
- package/dist/db/daily-brief.js +406 -0
- package/dist/db/dependency-cards.js +145 -0
- package/dist/db/erase-account.js +255 -0
- package/dist/db/export-account.js +189 -0
- package/dist/db/failing-checks.js +124 -0
- package/dist/db/file-coupling.js +197 -0
- package/dist/db/flow-detail.js +661 -0
- package/dist/db/flow-pointers.js +197 -0
- package/dist/db/flow-settings.js +67 -0
- package/dist/db/forecast.js +114 -0
- package/dist/db/line-overlap.js +81 -0
- package/dist/db/merge-queue.js +189 -0
- package/dist/db/migrate.js +17 -0
- package/dist/db/migrations/0000_purple_ben_grimm.sql +155 -0
- package/dist/db/migrations/0001_colorful_ozymandias.sql +31 -0
- package/dist/db/migrations/0002_famous_sersi.sql +9 -0
- package/dist/db/migrations/0003_clever_shinobi_shaw.sql +3 -0
- package/dist/db/migrations/0004_pale_scalphunter.sql +1 -0
- package/dist/db/migrations/0005_daffy_guardian.sql +2 -0
- package/dist/db/migrations/0006_cute_violations.sql +45 -0
- package/dist/db/migrations/0007_dark_anthem.sql +2 -0
- package/dist/db/migrations/0008_multitenant_accounts.sql +44 -0
- package/dist/db/migrations/0009_lean_bodies.sql +13 -0
- package/dist/db/migrations/0010_lean_bodies_nullable.sql +56 -0
- package/dist/db/migrations/0011_pr_diff_size.sql +10 -0
- package/dist/db/migrations/0012_findings_included_default.sql +7 -0
- package/dist/db/migrations/0013_review_routing.sql +9 -0
- package/dist/db/migrations/0014_account_last_active.sql +6 -0
- package/dist/db/migrations/0015_repos_inbox_watch.sql +9 -0
- package/dist/db/migrations/0016_repo_viewer_permission.sql +7 -0
- package/dist/db/migrations/0017_account_display_name.sql +7 -0
- package/dist/db/migrations/0018_finding_comment_kind.sql +7 -0
- package/dist/db/migrations/0019_finding_file_in_diff.sql +7 -0
- package/dist/db/migrations/0020_claude_review_cost_telemetry.sql +11 -0
- package/dist/db/migrations/0021_account_feed_last_seen.sql +7 -0
- package/dist/db/migrations/0022_ci_status_events.sql +21 -0
- package/dist/db/migrations/0023_ai_usage.sql +21 -0
- package/dist/db/migrations/0024_pr_head_ref_name.sql +6 -0
- package/dist/db/migrations/0025_account_billing.sql +7 -0
- package/dist/db/migrations/0026_ai_credit_allowance.sql +5 -0
- package/dist/db/migrations/0027_user_github_type.sql +6 -0
- package/dist/db/migrations/0028_bot_review_classification.sql +21 -0
- package/dist/db/migrations/0029_bot_mute_rules.sql +19 -0
- package/dist/db/migrations/0030_teams.sql +32 -0
- package/dist/db/migrations/0031_events_pr_idx.sql +7 -0
- package/dist/db/migrations/0032_benchmark_contributions.sql +29 -0
- package/dist/db/migrations/0033_thread_addressed_confidence.sql +9 -0
- package/dist/db/migrations/0034_thread_resolved_at.sql +6 -0
- package/dist/db/migrations/0035_pr_first_review_requested_at.sql +5 -0
- package/dist/db/migrations/0036_search_index.sql +25 -0
- package/dist/db/migrations/0037_author_indexes.sql +15 -0
- package/dist/db/migrations/0038_auto_merge_requests.sql +37 -0
- package/dist/db/migrations/0039_branch_status.sql +44 -0
- package/dist/db/migrations/0040_pr_review_decision.sql +10 -0
- package/dist/db/migrations/0041_branch_commit_detail.sql +38 -0
- package/dist/db/migrations/0042_repo_reviewers.sql +176 -0
- package/dist/db/migrations/0043_account_reviewers.sql +131 -0
- package/dist/db/migrations/0044_workspaces.sql +138 -0
- package/dist/db/migrations/0045_workspace_reviewers.sql +180 -0
- package/dist/db/migrations/0046_drop_inbox_watch.sql +25 -0
- package/dist/db/migrations/0047_ml_comment_labels.sql +40 -0
- package/dist/db/migrations/0048_ml_vendor_severity.sql +31 -0
- package/dist/db/migrations/0049_seat_pricing_repo_description.sql +21 -0
- package/dist/db/migrations/0050_users_app_slug.sql +12 -0
- package/dist/db/migrations/0051_auto_merge_queue.sql +17 -0
- package/dist/db/migrations/0052_trunk_ci_status_events.sql +43 -0
- package/dist/db/migrations/0053_automation_roles.sql +79 -0
- package/dist/db/migrations/0054_automation_vendor_kinds.sql +514 -0
- package/dist/db/migrations/0055_auto_merge_phase.sql +12 -0
- package/dist/db/migrations/0056_pr_mentions.sql +22 -0
- package/dist/db/migrations/0057_large_pr_threshold.sql +14 -0
- package/dist/db/migrations/0058_pending_mute.sql +53 -0
- package/dist/db/migrations/0059_merge_queue_state.sql +33 -0
- package/dist/db/migrations/0060_drop_my_turn_dismissals.sql +33 -0
- package/dist/db/migrations/0061_auto_merge_durable_anchors.sql +27 -0
- package/dist/db/migrations/0062_blast_radius_config.sql +21 -0
- package/dist/db/migrations/0063_repo_file_coupling.sql +33 -0
- package/dist/db/migrations/0064_pr_content_kind.sql +21 -0
- package/dist/db/migrations/0065_workspace_flow_settings.sql +13 -0
- package/dist/db/migrations/0066_review_request_events.sql +31 -0
- package/dist/db/migrations/0067_pr_security_signals.sql +86 -0
- package/dist/db/migrations/0068_my_turn_settings.sql +18 -0
- package/dist/db/migrations/0069_my_turn_dismissals.sql +41 -0
- package/dist/db/migrations/0070_claude_review_follow_up_ticket.sql +22 -0
- package/dist/db/migrations/0071_claude_review_trigger.sql +11 -0
- package/dist/db/migrations/0072_commit_message_headline.sql +13 -0
- package/dist/db/migrations/0073_claude_review_chat.sql +31 -0
- package/dist/db/migrations/0074_adopt_agentic_tables.sql +98 -0
- package/dist/db/migrations/0075_review_lens_drop_learnings.sql +15 -0
- package/dist/db/migrations/0076_claude_review_threads.sql +17 -0
- package/dist/db/migrations/0077_claude_review_ci_failures.sql +10 -0
- package/dist/db/migrations/0078_ai_fix_change_report.sql +17 -0
- package/dist/db/migrations/0079_claude_finding_story.sql +16 -0
- package/dist/db/migrations/meta/0000_snapshot.json +1116 -0
- package/dist/db/migrations/meta/0001_snapshot.json +1321 -0
- package/dist/db/migrations/meta/0002_snapshot.json +1375 -0
- package/dist/db/migrations/meta/0003_snapshot.json +1396 -0
- package/dist/db/migrations/meta/0004_snapshot.json +1416 -0
- package/dist/db/migrations/meta/0005_snapshot.json +1430 -0
- package/dist/db/migrations/meta/0006_snapshot.json +1745 -0
- package/dist/db/migrations/meta/0007_snapshot.json +1759 -0
- package/dist/db/migrations/meta/_journal.json +566 -0
- package/dist/db/migrations-pg/0000_tired_alex_power.sql +268 -0
- package/dist/db/migrations-pg/0001_dizzy_mockingbird.sql +3 -0
- package/dist/db/migrations-pg/0002_glorious_ikaris.sql +4 -0
- package/dist/db/migrations-pg/0003_melted_secret_warriors.sql +2 -0
- package/dist/db/migrations-pg/0004_numerous_triton.sql +1 -0
- package/dist/db/migrations-pg/0005_thick_blockbuster.sql +2 -0
- package/dist/db/migrations-pg/0006_majestic_dracula.sql +1 -0
- package/dist/db/migrations-pg/0007_same_human_torch.sql +2 -0
- package/dist/db/migrations-pg/0008_slimy_toxin.sql +1 -0
- package/dist/db/migrations-pg/0009_fuzzy_jubilee.sql +4 -0
- package/dist/db/migrations-pg/0010_late_hammerhead.sql +1 -0
- package/dist/db/migrations-pg/0011_happy_leech.sql +16 -0
- package/dist/db/migrations-pg/0012_woozy_captain_midlands.sql +16 -0
- package/dist/db/migrations-pg/0013_yielding_nicolaos.sql +1 -0
- package/dist/db/migrations-pg/0014_wakeful_bill_hollister.sql +2 -0
- package/dist/db/migrations-pg/0015_aspiring_black_tom.sql +1 -0
- package/dist/db/migrations-pg/0016_cute_ghost_rider.sql +30 -0
- package/dist/db/migrations-pg/0017_far_changeling.sql +24 -0
- package/dist/db/migrations-pg/0018_fancy_miss_america.sql +1 -0
- package/dist/db/migrations-pg/0019_benchmark_contributions.sql +22 -0
- package/dist/db/migrations-pg/0020_majestic_edwin_jarvis.sql +3 -0
- package/dist/db/migrations-pg/0021_green_salo.sql +1 -0
- package/dist/db/migrations-pg/0022_wise_baron_strucker.sql +1 -0
- package/dist/db/migrations-pg/0023_search_index.sql +19 -0
- package/dist/db/migrations-pg/0024_author_indexes.sql +9 -0
- package/dist/db/migrations-pg/0025_auto_merge_requests.sql +26 -0
- package/dist/db/migrations-pg/0026_branch_status.sql +32 -0
- package/dist/db/migrations-pg/0027_pr_review_decision.sql +5 -0
- package/dist/db/migrations-pg/0028_branch_commit_detail.sql +9 -0
- package/dist/db/migrations-pg/0029_repo_reviewers.sql +127 -0
- package/dist/db/migrations-pg/0030_account_reviewers.sql +77 -0
- package/dist/db/migrations-pg/0031_workspaces.sql +131 -0
- package/dist/db/migrations-pg/0032_workspace_reviewers.sql +165 -0
- package/dist/db/migrations-pg/0033_drop_inbox_watch.sql +14 -0
- package/dist/db/migrations-pg/0034_ml_comment_labels.sql +33 -0
- package/dist/db/migrations-pg/0035_ml_vendor_severity.sql +16 -0
- package/dist/db/migrations-pg/0036_seat_pricing_repo_description.sql +12 -0
- package/dist/db/migrations-pg/0037_users_app_slug.sql +9 -0
- package/dist/db/migrations-pg/0038_auto_merge_queue.sql +10 -0
- package/dist/db/migrations-pg/0039_trunk_ci_status_events.sql +27 -0
- package/dist/db/migrations-pg/0040_automation_roles.sql +63 -0
- package/dist/db/migrations-pg/0041_automation_vendor_kinds.sql +488 -0
- package/dist/db/migrations-pg/0042_auto_merge_phase.sql +8 -0
- package/dist/db/migrations-pg/0043_pr_mentions.sql +19 -0
- package/dist/db/migrations-pg/0044_large_pr_threshold.sql +6 -0
- package/dist/db/migrations-pg/0045_pending_mute.sql +26 -0
- package/dist/db/migrations-pg/0046_merge_queue_state.sql +18 -0
- package/dist/db/migrations-pg/0047_drop_my_turn_dismissals.sql +21 -0
- package/dist/db/migrations-pg/0048_auto_merge_durable_anchors.sql +27 -0
- package/dist/db/migrations-pg/0049_blast_radius_config.sql +8 -0
- package/dist/db/migrations-pg/0050_repo_file_coupling.sql +13 -0
- package/dist/db/migrations-pg/0051_pr_content_kind.sql +6 -0
- package/dist/db/migrations-pg/0052_workspace_flow_settings.sql +4 -0
- package/dist/db/migrations-pg/0053_review_request_events.sql +18 -0
- package/dist/db/migrations-pg/0054_pr_security_signals.sql +59 -0
- package/dist/db/migrations-pg/0055_my_turn_settings.sql +6 -0
- package/dist/db/migrations-pg/0056_my_turn_dismissals.sql +21 -0
- package/dist/db/migrations-pg/0057_claude_review_follow_up_ticket.sql +8 -0
- package/dist/db/migrations-pg/0058_claude_review_trigger.sql +4 -0
- package/dist/db/migrations-pg/0059_commit_message_headline.sql +3 -0
- package/dist/db/migrations-pg/0060_claude_review_chat.sql +24 -0
- package/dist/db/migrations-pg/0061_adopt_agentic_tables.sql +79 -0
- package/dist/db/migrations-pg/0062_review_lens_drop_learnings.sql +5 -0
- package/dist/db/migrations-pg/0063_claude_review_threads.sql +7 -0
- package/dist/db/migrations-pg/0064_claude_review_ci_failures.sql +4 -0
- package/dist/db/migrations-pg/0065_ai_fix_change_report.sql +9 -0
- package/dist/db/migrations-pg/0066_claude_finding_story.sql +7 -0
- package/dist/db/migrations-pg/meta/0000_snapshot.json +2137 -0
- package/dist/db/migrations-pg/meta/0001_snapshot.json +2143 -0
- package/dist/db/migrations-pg/meta/0002_snapshot.json +2170 -0
- package/dist/db/migrations-pg/meta/0003_snapshot.json +2182 -0
- package/dist/db/migrations-pg/meta/0004_snapshot.json +2188 -0
- package/dist/db/migrations-pg/meta/0005_snapshot.json +2201 -0
- package/dist/db/migrations-pg/meta/0006_snapshot.json +2207 -0
- package/dist/db/migrations-pg/meta/0007_snapshot.json +2219 -0
- package/dist/db/migrations-pg/meta/0008_snapshot.json +2226 -0
- package/dist/db/migrations-pg/meta/0009_snapshot.json +2250 -0
- package/dist/db/migrations-pg/meta/0010_snapshot.json +2256 -0
- package/dist/db/migrations-pg/meta/0011_snapshot.json +2412 -0
- package/dist/db/migrations-pg/meta/0012_snapshot.json +2528 -0
- package/dist/db/migrations-pg/meta/0013_snapshot.json +2534 -0
- package/dist/db/migrations-pg/meta/0014_snapshot.json +2547 -0
- package/dist/db/migrations-pg/meta/0015_snapshot.json +2553 -0
- package/dist/db/migrations-pg/meta/0016_snapshot.json +2774 -0
- package/dist/db/migrations-pg/meta/0017_snapshot.json +2999 -0
- package/dist/db/migrations-pg/meta/0018_snapshot.json +3020 -0
- package/dist/db/migrations-pg/meta/0020_snapshot.json +3228 -0
- package/dist/db/migrations-pg/meta/0021_snapshot.json +3234 -0
- package/dist/db/migrations-pg/meta/0022_snapshot.json +3240 -0
- package/dist/db/migrations-pg/meta/_journal.json +475 -0
- package/dist/db/ml-labels.js +1666 -0
- package/dist/db/my-turn-dismissals.js +343 -0
- package/dist/db/my-turn-settings.js +34 -0
- package/dist/db/pending-classify.js +46 -0
- package/dist/db/pending-mute.js +171 -0
- package/dist/db/pending-tabs.js +238 -0
- package/dist/db/period-metrics.js +820 -0
- package/dist/db/person-period.js +763 -0
- package/dist/db/pr-intervals.js +930 -0
- package/dist/db/pr-liveness.js +240 -0
- package/dist/db/pr-mentions.js +279 -0
- package/dist/db/pr-merge-queue-stamp.js +91 -0
- package/dist/db/queries.js +14631 -0
- package/dist/db/reaction-targets.js +94 -0
- package/dist/db/repo-activity.js +171 -0
- package/dist/db/retention.js +138 -0
- package/dist/db/review-threads-for-review.js +240 -0
- package/dist/db/run-migrations.js +24 -0
- package/dist/db/schema.pg.js +1220 -0
- package/dist/db/schema.sqlite.js +1801 -0
- package/dist/db/search.js +317 -0
- package/dist/db/security-alerts.js +218 -0
- package/dist/db/synthesis-input.js +565 -0
- package/dist/db/triage.js +320 -0
- package/dist/db/usage.js +63 -0
- package/dist/db/work-plan.js +1086 -0
- package/dist/db/working-hours.js +161 -0
- package/dist/feed/my-turn.js +122 -0
- package/dist/github/actions-logs.js +397 -0
- package/dist/github/auth.js +86 -0
- package/dist/github/branch-queries.js +229 -0
- package/dist/github/client.js +454 -0
- package/dist/github/codeowners.js +221 -0
- package/dist/github/commit-checks.js +105 -0
- package/dist/github/compare.js +110 -0
- package/dist/github/diff-anchor.js +227 -0
- package/dist/github/mutations.js +706 -0
- package/dist/github/pr-file-content.js +254 -0
- package/dist/github/pr-liveness.js +161 -0
- package/dist/github/queries.js +875 -0
- package/dist/github/rate-budget.js +129 -0
- package/dist/github/reactions.js +161 -0
- package/dist/github/reviewer-suggest.js +104 -0
- package/dist/github/team-reviewers.js +96 -0
- package/dist/index.js +184 -0
- package/dist/merge/auto-merge-runner.js +960 -0
- package/dist/ml/severity-client.js +218 -0
- package/dist/pr/detail-enricher.js +26 -0
- package/dist/pro/bind.js +302 -0
- package/dist/pro/contract.js +37 -0
- package/dist/pro/migrate.js +64 -0
- package/dist/review/agent-context.js +127 -0
- package/dist/review/agent.js +385 -0
- package/dist/review/agentic.js +59 -0
- package/dist/review/auth.js +94 -0
- package/dist/review/chat-agent.js +179 -0
- package/dist/review/claude-review/ai-ready.js +16 -0
- package/dist/review/claude-review/auto-settings.js +139 -0
- package/dist/review/claude-review/auto.js +244 -0
- package/dist/review/claude-review/chat.js +489 -0
- package/dist/review/claude-review/ci-failures.js +350 -0
- package/dist/review/claude-review/follow-up.js +310 -0
- package/dist/review/claude-review/manager.js +686 -0
- package/dist/review/claude-review/persist.js +996 -0
- package/dist/review/claude-review/prompts.js +357 -0
- package/dist/review/claude-review/routes.js +518 -0
- package/dist/review/claude-review/routing.js +61 -0
- package/dist/review/claude-review/specialists.js +190 -0
- package/dist/review/claude-review/threads.js +265 -0
- package/dist/review/claude-review/ticket.js +282 -0
- package/dist/review/clone-hygiene.js +380 -0
- package/dist/review/clone-manager.js +367 -0
- package/dist/review/llm.js +113 -0
- package/dist/review/model-options.js +48 -0
- package/dist/review/plugin-providers.js +12 -0
- package/dist/review/post-review.js +216 -0
- package/dist/review/post-seam.js +161 -0
- package/dist/review/prepare.js +168 -0
- package/dist/review/pricing.js +17 -0
- package/dist/review/schema.js +156 -0
- package/dist/review/submit-map.js +41 -0
- package/dist/review/usage.js +90 -0
- package/dist/shared/claude-review.js +335 -0
- package/dist/shared/conflict-fold.js +151 -0
- package/dist/shared/conflicts.js +30 -0
- package/dist/shared/flow-settings.js +191 -0
- package/dist/shared/index.js +8 -0
- package/dist/shared/my-turn-settings.js +238 -0
- package/dist/shared/pending-rules.js +281 -0
- package/dist/shared/product.js +54 -0
- package/dist/shared/types.js +896 -0
- package/dist/status.js +386 -0
- package/dist/sync/adaptive.js +185 -0
- package/dist/sync/app-attribution.js +66 -0
- package/dist/sync/auth-notices.js +36 -0
- package/dist/sync/backfill-ci-history.js +315 -0
- package/dist/sync/backfill-commit-headlines.js +136 -0
- package/dist/sync/backfill-pr-security.js +175 -0
- package/dist/sync/backfill-review-requests.js +97 -0
- package/dist/sync/benchmark-rollup.js +50 -0
- package/dist/sync/bot-detection.js +309 -0
- package/dist/sync/bot-resolution-markers.js +30 -0
- package/dist/sync/branch-status.js +634 -0
- package/dist/sync/classify-change-shape.js +150 -0
- package/dist/sync/commit-files.js +74 -0
- package/dist/sync/derive-thread-state.js +99 -0
- package/dist/sync/drain-review-threads.js +212 -0
- package/dist/sync/hydrate-detail.js +482 -0
- package/dist/sync/mention-scan.js +89 -0
- package/dist/sync/ml-enrichment.js +478 -0
- package/dist/sync/pr-change-signal.js +96 -0
- package/dist/sync/pr-liveness-sweep.js +139 -0
- package/dist/sync/pr-settle.js +381 -0
- package/dist/sync/refresh-pr.js +163 -0
- package/dist/sync/reparse-vendor-badges.js +495 -0
- package/dist/sync/resync-after-write.js +280 -0
- package/dist/sync/review-fingerprint.js +117 -0
- package/dist/sync/reviewer-behavior.js +103 -0
- package/dist/sync/reviewer-classify.js +503 -0
- package/dist/sync/routing-files.js +97 -0
- package/dist/sync/scheduled-jobs.js +12 -0
- package/dist/sync/scheduler.js +159 -0
- package/dist/sync/security-detect.js +521 -0
- package/dist/sync/sync-manager.js +750 -0
- package/dist/sync/sync-one-pr.js +205 -0
- package/dist/sync/sync-repo.js +400 -0
- package/dist/sync/unsettled-prs.js +506 -0
- package/dist/sync/upsert.js +1190 -0
- package/package.json +56 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/assets/AiFixTab-CQP6PT5u.js +2 -0
- package/public/assets/ClaudeReviewTab-DQJ-58ZA.js +8 -0
- package/public/assets/PrBotBehaviourTab-CCnFrWbC.js +1 -0
- package/public/assets/archivo-latin-600-BN-JkNrl.woff2 +0 -0
- package/public/assets/highlight-BEHUn5zE.css +10 -0
- package/public/assets/highlight-CIIZLa6Z.js +12 -0
- package/public/assets/index-BDnYs6zg.js +73 -0
- package/public/assets/index-mCuI--RC.css +1 -0
- package/public/assets/markdown-I_JLLVAZ.js +32 -0
- package/public/assets/perfProbe-Da13EhTE.js +1 -0
- package/public/assets/tanstack-5qsxxaLe.js +17 -0
- package/public/assets/vis-C3BCkP5U.js +1280 -0
- package/public/assets/vis-ziCVehMy.css +1 -0
- package/public/favicon-16.png +0 -0
- package/public/favicon-32.png +0 -0
- package/public/favicon-48.png +0 -0
- package/public/favicon.svg +5 -0
- package/public/index.html +41 -0
- package/public-landing/apple-touch-icon.png +0 -0
- package/public-landing/assets/archivo-latin-500-BWmvx2Mg.woff2 +0 -0
- package/public-landing/assets/archivo-latin-600-BN-JkNrl.woff2 +0 -0
- package/public-landing/assets/index-CA3eEGXO.js +40 -0
- package/public-landing/assets/index-naQrQ6Fe.css +1 -0
- package/public-landing/assets/jetbrains-mono-latin-400-RruCX7RW.woff2 +0 -0
- package/public-landing/assets/newsreader-latin-400-DExrRRrN.woff2 +0 -0
- package/public-landing/assets/newsreader-latin-400-italic-b0I-AwbV.woff2 +0 -0
- package/public-landing/bots/index.html +156 -0
- package/public-landing/contact/index.html +156 -0
- package/public-landing/cookies/index.html +156 -0
- package/public-landing/demo/limn-walkthrough-poster.jpg +0 -0
- package/public-landing/demo/limn-walkthrough.cues.json +44 -0
- package/public-landing/demo/limn-walkthrough.mp4 +0 -0
- package/public-landing/favicon-16.png +0 -0
- package/public-landing/favicon-32.png +0 -0
- package/public-landing/favicon-48.png +0 -0
- package/public-landing/favicon.svg +5 -0
- package/public-landing/features/index.html +156 -0
- package/public-landing/for-developers/index.html +156 -0
- package/public-landing/for-managers/index.html +156 -0
- package/public-landing/how-it-works/index.html +156 -0
- package/public-landing/how-we-measure/index.html +156 -0
- package/public-landing/icon-192.png +0 -0
- package/public-landing/icon-512.png +0 -0
- package/public-landing/index.html +156 -0
- package/public-landing/insights/index.html +156 -0
- package/public-landing/og-image.png +0 -0
- package/public-landing/pricing/index.html +156 -0
- package/public-landing/privacy/index.html +156 -0
- package/public-landing/pro/index.html +156 -0
- package/public-landing/reviews/index.html +156 -0
- package/public-landing/robots.txt +8 -0
- package/public-landing/shots/benchmark.png +0 -0
- package/public-landing/shots/bot-roi.png +0 -0
- package/public-landing/shots/bot-settings.png +0 -0
- package/public-landing/shots/chronology.png +0 -0
- package/public-landing/shots/feed.png +0 -0
- package/public-landing/shots/flow-metrics.png +0 -0
- package/public-landing/shots/free-reports.png +0 -0
- package/public-landing/shots/pending-board.png +0 -0
- package/public-landing/shots/pending-card.png +0 -0
- package/public-landing/shots/period-report.png +0 -0
- package/public-landing/shots/pr-detail.png +0 -0
- package/public-landing/shots/pr-threads.png +0 -0
- package/public-landing/shots/reach.png +0 -0
- package/public-landing/shots/repo-rows.png +0 -0
- package/public-landing/shots/severity-findings.png +0 -0
- package/public-landing/shots/severity-strip.png +0 -0
- package/public-landing/site.webmanifest +14 -0
- package/public-landing/sitemap.xml +51 -0
- package/public-landing/terms/index.html +156 -0
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { and, desc, eq } from 'drizzle-orm';
|
|
2
|
+
import { getFixPrContext } from './pr-context.js';
|
|
3
|
+
import { getClaudeReviewById } from '../../review/claude-review/persist.js';
|
|
4
|
+
import { pickReviewNonce } from '../../review/claude-review/prompts.js';
|
|
5
|
+
import { buildReviewSeed } from './review-seed.js';
|
|
6
|
+
import { buildFixSystemPrompt, buildFixUserPrompt } from './prompts.js';
|
|
7
|
+
import { insertQueuedFix, markFixCancelled, markFixFailed, markFixRunning, reconcileOrphanedFixes, saveFixSuccess, } from './persist.js';
|
|
8
|
+
// In-memory job manager for AI Fix's agentic fixer (CORE, free, local-only — it left the plugin
|
|
9
|
+
// with Claude Review; the PR summary and the CI-failure analysis stayed Pro). A SINGLE global slot (concurrency 1) carries every
|
|
10
|
+
// fix-generate run in the process, so no two ever run concurrently (a write run is costly,
|
|
11
|
+
// and running one at a time means the agent never needs to mutate process.env for auth, so
|
|
12
|
+
// it can't race a Claude Review). One job per PR (the `claimed` guard). Pushing a finished
|
|
13
|
+
// fix is NOT a job: POST …/push is synchronous and never takes the slot.
|
|
14
|
+
const MAX_CONCURRENT = 1;
|
|
15
|
+
const MAX_QUEUED = 20;
|
|
16
|
+
// ---- the single-slot scheduler ----
|
|
17
|
+
const running = new Set(); // prIds with a fix run in flight
|
|
18
|
+
const claimed = new Set(); // prIds claimed (queued OR running)
|
|
19
|
+
const pending = [];
|
|
20
|
+
function enqueue(prId, run) {
|
|
21
|
+
if (running.size < MAX_CONCURRENT) {
|
|
22
|
+
void execRun(prId, run);
|
|
23
|
+
return true; // started immediately
|
|
24
|
+
}
|
|
25
|
+
pending.push({ prId, run });
|
|
26
|
+
return false; // queued
|
|
27
|
+
}
|
|
28
|
+
async function execRun(prId, run) {
|
|
29
|
+
running.add(prId);
|
|
30
|
+
try {
|
|
31
|
+
await run();
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
/* every job body handles its own errors; this is only a backstop */
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
running.delete(prId);
|
|
38
|
+
pump();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function pump() {
|
|
42
|
+
while (running.size < MAX_CONCURRENT && pending.length > 0) {
|
|
43
|
+
const next = pending.shift();
|
|
44
|
+
if (!next)
|
|
45
|
+
break;
|
|
46
|
+
void execRun(next.prId, next.run);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// ---- fix-generate state ----
|
|
50
|
+
const fixIdByPr = new Map();
|
|
51
|
+
const progressByFix = new Map();
|
|
52
|
+
const controllers = new Map(); // fixId → controller
|
|
53
|
+
const fixStreamSubs = new Map();
|
|
54
|
+
export function subscribeFixStream(prId, cb) {
|
|
55
|
+
let set = fixStreamSubs.get(prId);
|
|
56
|
+
if (!set) {
|
|
57
|
+
set = new Set();
|
|
58
|
+
fixStreamSubs.set(prId, set);
|
|
59
|
+
}
|
|
60
|
+
set.add(cb);
|
|
61
|
+
return () => {
|
|
62
|
+
const s = fixStreamSubs.get(prId);
|
|
63
|
+
if (!s)
|
|
64
|
+
return;
|
|
65
|
+
s.delete(cb);
|
|
66
|
+
if (s.size === 0)
|
|
67
|
+
fixStreamSubs.delete(prId);
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function emitFixStream(prId, e) {
|
|
71
|
+
const set = fixStreamSubs.get(prId);
|
|
72
|
+
if (!set)
|
|
73
|
+
return;
|
|
74
|
+
for (const cb of set) {
|
|
75
|
+
try {
|
|
76
|
+
cb(e);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
/* a broken subscriber must not break the run */
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Load the review a 'review' seed names and render its items. null when the review is missing,
|
|
85
|
+
* not this account's, not THIS PR's, or did not succeed. Never throws.
|
|
86
|
+
*/
|
|
87
|
+
export async function loadReviewSeed(ctx, input) {
|
|
88
|
+
if (input.reviewId == null)
|
|
89
|
+
return null;
|
|
90
|
+
try {
|
|
91
|
+
const review = await getClaudeReviewById(ctx, input.reviewId, input.accountId);
|
|
92
|
+
if (!review || review.prId !== input.prId || review.status !== 'succeeded')
|
|
93
|
+
return null;
|
|
94
|
+
return { review, seed: buildReviewSeed(review, { nonce: pickReviewNonce }) };
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
ctx.log.warn({ err }, 'ai-fix: loading the review seed failed');
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Start a fix seeded from a Claude review — the ONE entry the auto-review agent calls (it passes
|
|
103
|
+
* `trigger: 'auto'`); the manual "Fix from review" route goes through the same path. Same queue,
|
|
104
|
+
* slot, worktree and guards as every fix; nothing is pushed until a person presses Push.
|
|
105
|
+
*/
|
|
106
|
+
export function startReviewFix(ctx, input) {
|
|
107
|
+
return startFix(ctx, {
|
|
108
|
+
accountId: input.accountId,
|
|
109
|
+
prId: input.prId,
|
|
110
|
+
model: input.model,
|
|
111
|
+
seed: 'review',
|
|
112
|
+
sourceReviewId: input.reviewId,
|
|
113
|
+
trigger: input.trigger,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
export async function startFix(ctx, input) {
|
|
117
|
+
const { accountId, prId } = input;
|
|
118
|
+
const auth = ctx.llm.detectAuth();
|
|
119
|
+
if (auth.status !== 'ok')
|
|
120
|
+
return { status: 'no_auth', message: auth.message };
|
|
121
|
+
// Hard agentic cap: refuse a run once the account's monthly agent credit allowance is spent
|
|
122
|
+
// (metered per calendar month; local accounts are unmetered → never blocked).
|
|
123
|
+
if ((await ctx.aiCredits.check(accountId)).agentBlocked)
|
|
124
|
+
return { status: 'credits_exhausted' };
|
|
125
|
+
const instruction = (input.instruction ?? '').trim();
|
|
126
|
+
if (input.seed === 'plain' && instruction === '')
|
|
127
|
+
return { status: 'no_instruction' };
|
|
128
|
+
if (claimed.has(prId))
|
|
129
|
+
return { status: 'already_running' };
|
|
130
|
+
if (pending.length >= MAX_QUEUED)
|
|
131
|
+
return { status: 'busy' };
|
|
132
|
+
// Reserve the PR SYNCHRONOUSLY, in the same tick as the check above and BEFORE any await, so
|
|
133
|
+
// two concurrent starts (a manual click and `maybeStartAutoFix`) can't both pass the guard.
|
|
134
|
+
// ⚠ EVERY bail path below this line releases the claim, or this PR's fixer wedges for the
|
|
135
|
+
// process's lifetime.
|
|
136
|
+
claimed.add(prId);
|
|
137
|
+
try {
|
|
138
|
+
const pr = await getFixPrContext(ctx, accountId, prId);
|
|
139
|
+
if (!pr) {
|
|
140
|
+
claimed.delete(prId);
|
|
141
|
+
return { status: 'not_found' };
|
|
142
|
+
}
|
|
143
|
+
// The review seed is resolved BEFORE GitHub is asked anything: a review with nothing to fix
|
|
144
|
+
// must not spend two GitHub calls on its way to a refusal.
|
|
145
|
+
//
|
|
146
|
+
// ⚠ The rendered prompt is FROZEN HERE, at launch, and the run may start later (one global
|
|
147
|
+
// slot). That is intended: the stored items must describe what the agent was actually given.
|
|
148
|
+
let reviewItems = null;
|
|
149
|
+
let sentRefs = [];
|
|
150
|
+
let seed;
|
|
151
|
+
if (input.seed === 'review') {
|
|
152
|
+
const loaded = await loadReviewSeed(ctx, {
|
|
153
|
+
accountId,
|
|
154
|
+
prId,
|
|
155
|
+
reviewId: input.sourceReviewId,
|
|
156
|
+
});
|
|
157
|
+
if (!loaded) {
|
|
158
|
+
claimed.delete(prId);
|
|
159
|
+
return { status: 'review_unavailable' };
|
|
160
|
+
}
|
|
161
|
+
if (loaded.seed.sentRefs.length === 0) {
|
|
162
|
+
claimed.delete(prId);
|
|
163
|
+
return { status: 'nothing_to_fix' };
|
|
164
|
+
}
|
|
165
|
+
reviewItems = loaded.seed.items;
|
|
166
|
+
sentRefs = loaded.seed.sentRefs;
|
|
167
|
+
seed = { kind: 'review', text: loaded.seed.text };
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
seed = { kind: 'plain', text: instruction };
|
|
171
|
+
}
|
|
172
|
+
const headInfo = await ctx.github.fetchPrHeadInfo(accountId, pr.owner, pr.name, pr.number);
|
|
173
|
+
const baseSha = headInfo.headSha;
|
|
174
|
+
if (!baseSha) {
|
|
175
|
+
claimed.delete(prId);
|
|
176
|
+
return { status: 'no_head' };
|
|
177
|
+
}
|
|
178
|
+
const diff = await ctx.github
|
|
179
|
+
.fetchPrDiff(accountId, pr.owner, pr.name, pr.number)
|
|
180
|
+
.catch(() => '');
|
|
181
|
+
const systemPrompt = buildFixSystemPrompt();
|
|
182
|
+
const prompt = buildFixUserPrompt({ pr, diff, seed });
|
|
183
|
+
const trigger = input.trigger ?? 'manual';
|
|
184
|
+
const fixId = await insertQueuedFix(ctx, {
|
|
185
|
+
accountId,
|
|
186
|
+
repoId: pr.repoId,
|
|
187
|
+
prId,
|
|
188
|
+
baseSha,
|
|
189
|
+
model: input.model,
|
|
190
|
+
seed: input.seed,
|
|
191
|
+
sourceReviewId: input.seed === 'review' ? (input.sourceReviewId ?? null) : null,
|
|
192
|
+
prompt,
|
|
193
|
+
trigger,
|
|
194
|
+
reviewItems,
|
|
195
|
+
});
|
|
196
|
+
fixIdByPr.set(prId, fixId);
|
|
197
|
+
const job = {
|
|
198
|
+
fixId,
|
|
199
|
+
accountId,
|
|
200
|
+
prId,
|
|
201
|
+
owner: pr.owner,
|
|
202
|
+
name: pr.name,
|
|
203
|
+
prNumber: pr.number,
|
|
204
|
+
baseSha,
|
|
205
|
+
model: input.model,
|
|
206
|
+
systemPrompt,
|
|
207
|
+
prompt,
|
|
208
|
+
sentRefs,
|
|
209
|
+
};
|
|
210
|
+
const immediate = enqueue(prId, () => launchFix(ctx, job));
|
|
211
|
+
if (!immediate) {
|
|
212
|
+
emitFixStream(prId, {
|
|
213
|
+
type: 'progress',
|
|
214
|
+
status: 'queued',
|
|
215
|
+
fixId,
|
|
216
|
+
progress: { phase: 'fetching_diff' },
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
return { status: 'queued', fixId };
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
claimed.delete(prId);
|
|
223
|
+
fixIdByPr.delete(prId);
|
|
224
|
+
ctx.log.warn({ err }, 'ai-fix startFix failed');
|
|
225
|
+
return { status: 'not_found' };
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async function launchFix(ctx, job) {
|
|
229
|
+
const controller = new AbortController();
|
|
230
|
+
controllers.set(job.fixId, controller);
|
|
231
|
+
const seed = { phase: 'cloning' };
|
|
232
|
+
progressByFix.set(job.fixId, seed);
|
|
233
|
+
emitFixStream(job.prId, {
|
|
234
|
+
type: 'progress',
|
|
235
|
+
status: 'running',
|
|
236
|
+
fixId: job.fixId,
|
|
237
|
+
progress: seed,
|
|
238
|
+
});
|
|
239
|
+
const onProgress = (p) => {
|
|
240
|
+
const prog = p;
|
|
241
|
+
progressByFix.set(job.fixId, prog);
|
|
242
|
+
emitFixStream(job.prId, {
|
|
243
|
+
type: 'progress',
|
|
244
|
+
status: 'running',
|
|
245
|
+
fixId: job.fixId,
|
|
246
|
+
progress: prog,
|
|
247
|
+
});
|
|
248
|
+
};
|
|
249
|
+
let finalStatus = 'failed';
|
|
250
|
+
try {
|
|
251
|
+
await markFixRunning(ctx, job.fixId);
|
|
252
|
+
const result = await ctx.coding.generateFix({
|
|
253
|
+
accountId: job.accountId,
|
|
254
|
+
owner: job.owner,
|
|
255
|
+
name: job.name,
|
|
256
|
+
prNumber: job.prNumber,
|
|
257
|
+
baseSha: job.baseSha,
|
|
258
|
+
model: job.model,
|
|
259
|
+
systemPrompt: job.systemPrompt,
|
|
260
|
+
prompt: job.prompt,
|
|
261
|
+
abortController: controller,
|
|
262
|
+
onProgress,
|
|
263
|
+
});
|
|
264
|
+
if (result.aborted || controller.signal.aborted) {
|
|
265
|
+
await markFixCancelled(ctx, job.fixId);
|
|
266
|
+
finalStatus = 'cancelled';
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
emitFixStream(job.prId, {
|
|
270
|
+
type: 'progress',
|
|
271
|
+
status: 'running',
|
|
272
|
+
fixId: job.fixId,
|
|
273
|
+
progress: { phase: 'persisting' },
|
|
274
|
+
});
|
|
275
|
+
await saveFixSuccess(ctx, job.fixId, result, job.sentRefs);
|
|
276
|
+
finalStatus = 'succeeded';
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
catch (err) {
|
|
280
|
+
if (controller.signal.aborted) {
|
|
281
|
+
await markFixCancelled(ctx, job.fixId).catch(() => { });
|
|
282
|
+
finalStatus = 'cancelled';
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
const m = err instanceof Error ? err.message : String(err);
|
|
286
|
+
await markFixFailed(ctx, job.fixId, m).catch(() => { });
|
|
287
|
+
finalStatus = 'failed';
|
|
288
|
+
ctx.log.warn({ err }, 'ai-fix run failed');
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
finally {
|
|
292
|
+
claimed.delete(job.prId);
|
|
293
|
+
controllers.delete(job.fixId);
|
|
294
|
+
progressByFix.delete(job.fixId);
|
|
295
|
+
fixIdByPr.delete(job.prId);
|
|
296
|
+
emitFixStream(job.prId, {
|
|
297
|
+
type: 'done',
|
|
298
|
+
status: finalStatus,
|
|
299
|
+
fixId: job.fixId,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
export function requestFixCancel(ctx, prId) {
|
|
304
|
+
const fixId = fixIdByPr.get(prId);
|
|
305
|
+
if (fixId != null && running.has(prId)) {
|
|
306
|
+
controllers.get(fixId)?.abort();
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
const idx = pending.findIndex((j) => j.prId === prId);
|
|
310
|
+
if (idx >= 0 && fixId != null) {
|
|
311
|
+
pending.splice(idx, 1);
|
|
312
|
+
claimed.delete(prId);
|
|
313
|
+
fixIdByPr.delete(prId);
|
|
314
|
+
void markFixCancelled(ctx, fixId).catch(() => { });
|
|
315
|
+
emitFixStream(prId, { type: 'done', status: 'cancelled', fixId });
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
export function isFixRunning(prId) {
|
|
321
|
+
return claimed.has(prId);
|
|
322
|
+
}
|
|
323
|
+
export async function getFixStatus(ctx, accountId, prId) {
|
|
324
|
+
// The DB lookup goes FIRST, and it is what establishes ownership.
|
|
325
|
+
//
|
|
326
|
+
// `running` / `claimed` / `fixIdByPr` are process-global Sets and Maps keyed by prId alone,
|
|
327
|
+
// with no notion of who owns the job. Reading them before this query meant a foreign PR with
|
|
328
|
+
// a job in flight returned that job's fixId and live `progress` to whoever asked — another
|
|
329
|
+
// tenant's phase and activity, from a repository the caller cannot see. Every started fix
|
|
330
|
+
// inserts its row (with accountId) BEFORE it runs, so "no row for this account" is a reliable
|
|
331
|
+
// "not yours", and the in-memory maps are consulted only to upgrade a row we already know
|
|
332
|
+
// belongs to the caller into a live status.
|
|
333
|
+
const t = ctx.schema.aiFixes;
|
|
334
|
+
const rows = (await ctx.db
|
|
335
|
+
.select({ id: t.id, status: t.status })
|
|
336
|
+
.from(t)
|
|
337
|
+
.where(and(eq(t.accountId, accountId), eq(t.prId, prId)))
|
|
338
|
+
.orderBy(desc(t.id))
|
|
339
|
+
.limit(1)
|
|
340
|
+
.execute());
|
|
341
|
+
const row = rows[0];
|
|
342
|
+
if (!row)
|
|
343
|
+
return { status: 'idle', fixId: null, progress: null };
|
|
344
|
+
// Live in-memory state, but only for a fix id this account actually owns.
|
|
345
|
+
const liveFixId = fixIdByPr.get(prId) ?? null;
|
|
346
|
+
if (liveFixId === row.id) {
|
|
347
|
+
if (running.has(prId)) {
|
|
348
|
+
return {
|
|
349
|
+
status: 'running',
|
|
350
|
+
fixId: row.id,
|
|
351
|
+
progress: progressByFix.get(row.id) ?? { phase: 'cloning' },
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
if (claimed.has(prId)) {
|
|
355
|
+
return { status: 'queued', fixId: row.id, progress: { phase: 'fetching_diff' } };
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return { status: row.status, fixId: row.id, progress: null };
|
|
359
|
+
}
|
|
360
|
+
export async function reconcileFixesOnStartup(ctx) {
|
|
361
|
+
try {
|
|
362
|
+
const n = await reconcileOrphanedFixes(ctx);
|
|
363
|
+
if (n > 0)
|
|
364
|
+
ctx.log.info({ n }, 'ai-fix: reconciled orphaned runs on startup');
|
|
365
|
+
}
|
|
366
|
+
catch (err) {
|
|
367
|
+
ctx.log.warn({ err }, 'ai-fix: startup reconcile failed');
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { and, desc, eq } from 'drizzle-orm';
|
|
2
|
+
import { normalizeChangeReport } from './review-seed.js';
|
|
3
|
+
export async function insertQueuedFix(ctx, input) {
|
|
4
|
+
const t = ctx.schema.aiFixes;
|
|
5
|
+
const items = input.reviewItems;
|
|
6
|
+
const rows = (await ctx.db
|
|
7
|
+
.insert(t)
|
|
8
|
+
.values({
|
|
9
|
+
accountId: input.accountId,
|
|
10
|
+
repoId: input.repoId,
|
|
11
|
+
prId: input.prId,
|
|
12
|
+
baseSha: input.baseSha,
|
|
13
|
+
status: 'queued',
|
|
14
|
+
model: input.model,
|
|
15
|
+
seed: input.seed,
|
|
16
|
+
sourceReviewId: input.sourceReviewId,
|
|
17
|
+
prompt: input.prompt,
|
|
18
|
+
trigger: input.trigger ?? 'manual',
|
|
19
|
+
// Omitted (→ NULL) on a plain run: null means "this run had no review items".
|
|
20
|
+
...(items != null ? { reviewItems: JSON.stringify(items) } : {}),
|
|
21
|
+
})
|
|
22
|
+
.returning({ id: t.id })
|
|
23
|
+
.execute());
|
|
24
|
+
const id = rows[0]?.id;
|
|
25
|
+
if (id == null)
|
|
26
|
+
throw new Error('insertQueuedFix: no id returned');
|
|
27
|
+
return id;
|
|
28
|
+
}
|
|
29
|
+
export async function markFixRunning(ctx, id) {
|
|
30
|
+
const t = ctx.schema.aiFixes;
|
|
31
|
+
await ctx.db.update(t).set({ status: 'running' }).where(eq(t.id, id)).execute();
|
|
32
|
+
}
|
|
33
|
+
export async function saveFixSuccess(ctx, id, data,
|
|
34
|
+
// The refs the run was SHOWN ([] for a plain run) — the set the agent's report is validated
|
|
35
|
+
// against.
|
|
36
|
+
sentRefs) {
|
|
37
|
+
const t = ctx.schema.aiFixes;
|
|
38
|
+
const report = normalizeChangeReport(data.report, sentRefs, data.filesChanged);
|
|
39
|
+
await ctx.db
|
|
40
|
+
.update(t)
|
|
41
|
+
.set({
|
|
42
|
+
status: 'succeeded',
|
|
43
|
+
summary: data.summary,
|
|
44
|
+
commitMessage: data.commitMessage,
|
|
45
|
+
patch: data.patch,
|
|
46
|
+
filesChanged: JSON.stringify(data.filesChanged),
|
|
47
|
+
costUsd: data.costUsd,
|
|
48
|
+
inputTokens: data.usage.inputTokens,
|
|
49
|
+
outputTokens: data.usage.outputTokens,
|
|
50
|
+
numTurns: data.numTurns,
|
|
51
|
+
finishedAt: new Date(),
|
|
52
|
+
changeReport: JSON.stringify(report),
|
|
53
|
+
})
|
|
54
|
+
.where(eq(t.id, id))
|
|
55
|
+
.execute();
|
|
56
|
+
// Record the agentic-fixer spend on the shared AI-usage ledger (agent seam). Reads
|
|
57
|
+
// accountId/prId/model back off the row. Best-effort — the whole block (incl. the
|
|
58
|
+
// read-back SELECT) is guarded so a ledger error never flips an already-succeeded fix.
|
|
59
|
+
if (data.costUsd != null && Number.isFinite(data.costUsd) && data.costUsd > 0) {
|
|
60
|
+
try {
|
|
61
|
+
const row = (await ctx.db
|
|
62
|
+
.select({ accountId: t.accountId, prId: t.prId, model: t.model })
|
|
63
|
+
.from(t)
|
|
64
|
+
.where(eq(t.id, id))
|
|
65
|
+
.limit(1)
|
|
66
|
+
.execute())[0];
|
|
67
|
+
if (row)
|
|
68
|
+
await ctx.recordAiUsage({
|
|
69
|
+
accountId: row.accountId,
|
|
70
|
+
seam: 'agent',
|
|
71
|
+
feature: 'ai_fix',
|
|
72
|
+
model: row.model,
|
|
73
|
+
costUsd: data.costUsd,
|
|
74
|
+
inputTokens: data.usage.inputTokens,
|
|
75
|
+
outputTokens: data.usage.outputTokens,
|
|
76
|
+
prId: row.prId,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
/* ledger write is best-effort — never break the fix save */
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export async function markFixFailed(ctx, id, error) {
|
|
85
|
+
const t = ctx.schema.aiFixes;
|
|
86
|
+
await ctx.db
|
|
87
|
+
.update(t)
|
|
88
|
+
.set({ status: 'failed', error: error.slice(0, 4000), finishedAt: new Date() })
|
|
89
|
+
.where(eq(t.id, id))
|
|
90
|
+
.execute();
|
|
91
|
+
}
|
|
92
|
+
export async function markFixCancelled(ctx, id) {
|
|
93
|
+
const t = ctx.schema.aiFixes;
|
|
94
|
+
await ctx.db
|
|
95
|
+
.update(t)
|
|
96
|
+
.set({ status: 'cancelled', finishedAt: new Date() })
|
|
97
|
+
.where(eq(t.id, id))
|
|
98
|
+
.execute();
|
|
99
|
+
}
|
|
100
|
+
export async function markFixPushed(ctx, id, input) {
|
|
101
|
+
const t = ctx.schema.aiFixes;
|
|
102
|
+
await ctx.db
|
|
103
|
+
.update(t)
|
|
104
|
+
.set({
|
|
105
|
+
pushedBranch: input.pushedBranch,
|
|
106
|
+
pushedPrNumber: input.pushedPrNumber ?? null,
|
|
107
|
+
pushedPrUrl: input.pushedPrUrl ?? null,
|
|
108
|
+
pushedAt: new Date(),
|
|
109
|
+
})
|
|
110
|
+
.where(eq(t.id, id))
|
|
111
|
+
.execute();
|
|
112
|
+
}
|
|
113
|
+
export async function getFixById(ctx, accountId, id) {
|
|
114
|
+
const t = ctx.schema.aiFixes;
|
|
115
|
+
const rows = (await ctx.db
|
|
116
|
+
.select()
|
|
117
|
+
.from(t)
|
|
118
|
+
.where(and(eq(t.id, id), eq(t.accountId, accountId)))
|
|
119
|
+
.limit(1)
|
|
120
|
+
.execute());
|
|
121
|
+
return rows[0] ?? null;
|
|
122
|
+
}
|
|
123
|
+
export async function getLatestFix(ctx, accountId, prId) {
|
|
124
|
+
const t = ctx.schema.aiFixes;
|
|
125
|
+
const rows = (await ctx.db
|
|
126
|
+
.select()
|
|
127
|
+
.from(t)
|
|
128
|
+
.where(and(eq(t.accountId, accountId), eq(t.prId, prId)))
|
|
129
|
+
.orderBy(desc(t.id))
|
|
130
|
+
.limit(1)
|
|
131
|
+
.execute());
|
|
132
|
+
return rows[0] ?? null;
|
|
133
|
+
}
|
|
134
|
+
export async function listFixHistory(ctx, accountId, prId, limit = 20) {
|
|
135
|
+
const t = ctx.schema.aiFixes;
|
|
136
|
+
return (await ctx.db
|
|
137
|
+
.select()
|
|
138
|
+
.from(t)
|
|
139
|
+
.where(and(eq(t.accountId, accountId), eq(t.prId, prId)))
|
|
140
|
+
.orderBy(desc(t.id))
|
|
141
|
+
.limit(limit)
|
|
142
|
+
.execute());
|
|
143
|
+
}
|
|
144
|
+
// Heal rows left `queued`/`running` by a crash/restart (no live job can resume them).
|
|
145
|
+
export async function reconcileOrphanedFixes(ctx) {
|
|
146
|
+
const t = ctx.schema.aiFixes;
|
|
147
|
+
const rows = (await ctx.db
|
|
148
|
+
.update(t)
|
|
149
|
+
.set({
|
|
150
|
+
status: 'failed',
|
|
151
|
+
error: 'interrupted by a server restart',
|
|
152
|
+
finishedAt: new Date(),
|
|
153
|
+
})
|
|
154
|
+
.where(eq(t.status, 'queued'))
|
|
155
|
+
.returning({ id: t.id })
|
|
156
|
+
.execute());
|
|
157
|
+
const rows2 = (await ctx.db
|
|
158
|
+
.update(t)
|
|
159
|
+
.set({
|
|
160
|
+
status: 'failed',
|
|
161
|
+
error: 'interrupted by a server restart',
|
|
162
|
+
finishedAt: new Date(),
|
|
163
|
+
})
|
|
164
|
+
.where(eq(t.status, 'running'))
|
|
165
|
+
.returning({ id: t.id })
|
|
166
|
+
.execute());
|
|
167
|
+
return rows.length + rows2.length;
|
|
168
|
+
}
|
|
169
|
+
export function parseFilesChanged(json) {
|
|
170
|
+
if (!json)
|
|
171
|
+
return [];
|
|
172
|
+
try {
|
|
173
|
+
const v = JSON.parse(json);
|
|
174
|
+
return Array.isArray(v) ? v : [];
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return [];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* The report JSON columns, parsed DEFENSIVELY: null for NULL and for anything unparseable. These
|
|
182
|
+
* are read by `GET /api/pro/prs/:id/ai-fix`, which loads the whole AI Fix tab, so a throw here
|
|
183
|
+
* would take the tab down over a malformed blob rather than degrading to "no report".
|
|
184
|
+
*/
|
|
185
|
+
export function parseReviewItems(json) {
|
|
186
|
+
const v = parseJson(json);
|
|
187
|
+
return Array.isArray(v) ? v : null;
|
|
188
|
+
}
|
|
189
|
+
export function parseChangeReport(json) {
|
|
190
|
+
const v = parseJson(json);
|
|
191
|
+
if (v == null || typeof v !== 'object' || Array.isArray(v))
|
|
192
|
+
return null;
|
|
193
|
+
const o = v;
|
|
194
|
+
return {
|
|
195
|
+
changes: Array.isArray(o.changes) ? o.changes : [],
|
|
196
|
+
unaddressed: Array.isArray(o.unaddressed) ? o.unaddressed : [],
|
|
197
|
+
notReported: Array.isArray(o.notReported) ? o.notReported : [],
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
export function parseTrigger(v) {
|
|
201
|
+
return v === 'auto' ? 'auto' : 'manual';
|
|
202
|
+
}
|
|
203
|
+
function parseJson(json) {
|
|
204
|
+
if (json == null || json === '')
|
|
205
|
+
return null;
|
|
206
|
+
try {
|
|
207
|
+
return JSON.parse(json);
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=persist.js.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { and, eq } from 'drizzle-orm';
|
|
2
|
+
export async function getFixPrContext(ctx, accountId, prId) {
|
|
3
|
+
const pr = ctx.schema.pullRequests;
|
|
4
|
+
const repos = ctx.schema.repos;
|
|
5
|
+
const rows = (await ctx.db
|
|
6
|
+
.select({
|
|
7
|
+
prId: pr.id,
|
|
8
|
+
repoId: pr.repoId,
|
|
9
|
+
owner: repos.owner,
|
|
10
|
+
name: repos.name,
|
|
11
|
+
number: pr.number,
|
|
12
|
+
title: pr.title,
|
|
13
|
+
body: pr.body,
|
|
14
|
+
headSha: pr.headSha,
|
|
15
|
+
baseRefName: pr.baseRefName,
|
|
16
|
+
defaultBranch: repos.defaultBranch,
|
|
17
|
+
authorId: pr.authorId,
|
|
18
|
+
state: pr.state,
|
|
19
|
+
})
|
|
20
|
+
.from(pr)
|
|
21
|
+
.innerJoin(repos, eq(pr.repoId, repos.id))
|
|
22
|
+
.where(and(eq(pr.id, prId), eq(pr.accountId, accountId)))
|
|
23
|
+
.limit(1)
|
|
24
|
+
.execute());
|
|
25
|
+
const row = rows[0];
|
|
26
|
+
if (!row)
|
|
27
|
+
return null;
|
|
28
|
+
return {
|
|
29
|
+
prId: row.prId,
|
|
30
|
+
repoId: row.repoId,
|
|
31
|
+
owner: row.owner,
|
|
32
|
+
name: row.name,
|
|
33
|
+
repoFullName: `${row.owner}/${row.name}`,
|
|
34
|
+
number: row.number,
|
|
35
|
+
title: row.title,
|
|
36
|
+
body: row.body,
|
|
37
|
+
headSha: row.headSha,
|
|
38
|
+
baseRefName: row.baseRefName,
|
|
39
|
+
defaultBranch: row.defaultBranch,
|
|
40
|
+
authorId: row.authorId,
|
|
41
|
+
state: row.state,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
// The viewer's WRITE permission on the PR's repo (for the push gate + branch picker).
|
|
45
|
+
// Read from the synced repos.viewerPermission; the actual push still fails loud if the
|
|
46
|
+
// token can't push, but this hides the controls / server-guards early.
|
|
47
|
+
export async function getViewerCanPush(ctx, accountId, repoId) {
|
|
48
|
+
const repos = ctx.schema.repos;
|
|
49
|
+
const rows = (await ctx.db
|
|
50
|
+
.select({ perm: repos.viewerPermission })
|
|
51
|
+
.from(repos)
|
|
52
|
+
.where(and(eq(repos.id, repoId), eq(repos.accountId, accountId)))
|
|
53
|
+
.limit(1)
|
|
54
|
+
.execute());
|
|
55
|
+
const perm = rows[0]?.perm ?? '';
|
|
56
|
+
return ['WRITE', 'MAINTAIN', 'ADMIN'].includes(perm);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=pr-context.js.map
|