neondeck 1.0.0-beta.7 → 1.0.0-beta.9

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.
Files changed (109) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/config/dashboard.json +9 -2
  3. package/config/dashboard.schema.json +8 -3
  4. package/dist/assets/{app-BBIuPG77.js → app-ChpCY1hX.js} +335 -19
  5. package/dist/assets/app-ChpCY1hX.js.map +1 -0
  6. package/dist/assets/{autopilot-CnHJWMaF.js → autopilot-CMaU1iZd.js} +142 -30
  7. package/dist/assets/autopilot-CMaU1iZd.js.map +1 -0
  8. package/dist/assets/briefing-Bxg1ctBW.js +2 -0
  9. package/dist/assets/{briefing-hpGB8E6A.js → briefing-Cf4ZpqPr.js} +4 -4
  10. package/dist/assets/{briefing-hpGB8E6A.js.map → briefing-Cf4ZpqPr.js.map} +1 -1
  11. package/dist/assets/{briefings-DpDKxWsT.js → briefings-DtOcw7y6.js} +30 -16
  12. package/dist/assets/briefings-DtOcw7y6.js.map +1 -0
  13. package/dist/assets/{command-run-Dwt42N9e.js → command-run-B9uZYF9u.js} +4 -4
  14. package/dist/assets/{command-run-Dwt42N9e.js.map → command-run-B9uZYF9u.js.map} +1 -1
  15. package/dist/assets/command-run-CNnU2WOV.js +2 -0
  16. package/dist/assets/{display-assistant-Dwqj18Ka.js → display-assistant-DEh-qGUl.js} +6 -6
  17. package/dist/assets/{display-assistant-Dwqj18Ka.js.map → display-assistant-DEh-qGUl.js.map} +1 -1
  18. package/dist/assets/fix-pr-ci-DFdWMhjV.js +2 -0
  19. package/dist/assets/{fix-pr-ci-C7c6tPL_.js → fix-pr-ci-DolohowP.js} +2 -2
  20. package/dist/assets/{fix-pr-ci-C7c6tPL_.js.map → fix-pr-ci-DolohowP.js.map} +1 -1
  21. package/dist/assets/migrations/20260714213018_pr_reviews/migration.sql +34 -0
  22. package/dist/assets/migrations/20260714213018_pr_reviews/snapshot.json +7809 -0
  23. package/dist/assets/pr-reviews-CbYcf9Mj.js +522 -0
  24. package/dist/assets/pr-reviews-CbYcf9Mj.js.map +1 -0
  25. package/dist/assets/{prepare-pr-worktree-GIZ8cMUj.js → prepare-pr-worktree-CwalJPIV.js} +1 -1
  26. package/dist/assets/{prepare-pr-worktree-CckbyTsB.js → prepare-pr-worktree-D3gy9Uof.js} +3 -3
  27. package/dist/assets/{prepare-pr-worktree-CckbyTsB.js.map → prepare-pr-worktree-D3gy9Uof.js.map} +1 -1
  28. package/dist/assets/{push-pr-autofix-w85pTU9W.js → push-pr-autofix-DUXYeBds.js} +1 -1
  29. package/dist/assets/{push-pr-autofix-CQTtK2HC.js → push-pr-autofix-OOoR6FIp.js} +3 -3
  30. package/dist/assets/{push-pr-autofix-CQTtK2HC.js.map → push-pr-autofix-OOoR6FIp.js.map} +1 -1
  31. package/dist/assets/{review-pr-for-human-C_OT_LFW.js → review-pr-for-human-5oA8Lnwm.js} +100 -27
  32. package/dist/assets/review-pr-for-human-5oA8Lnwm.js.map +1 -0
  33. package/dist/assets/{review-pr-for-human-BQjIMKW2.js → review-pr-for-human-CTIL0RRy.js} +1 -1
  34. package/dist/assets/{safety-EnvDg7cY.js → safety-S_Ek3ETH.js} +20 -4
  35. package/dist/assets/safety-S_Ek3ETH.js.map +1 -0
  36. package/dist/assets/{scheduled-agent-instruction-B_K6qYBk.js → scheduled-agent-instruction-C7Xz50tm.js} +1 -1
  37. package/dist/assets/{scheduled-agent-instruction-BdSpSyHS.js → scheduled-agent-instruction-CEpmfBNA.js} +3 -3
  38. package/dist/assets/{scheduled-agent-instruction-BdSpSyHS.js.map → scheduled-agent-instruction-CEpmfBNA.js.map} +1 -1
  39. package/dist/assets/{scheduler-tick-DdiWIWU2.js → scheduler-tick-DO_6nnY9.js} +4 -4
  40. package/dist/assets/{scheduler-tick-DdiWIWU2.js.map → scheduler-tick-DO_6nnY9.js.map} +1 -1
  41. package/dist/assets/{scheduler-workflow-DoAFm2Pl.js → scheduler-workflow-CwK7no2d.js} +2 -2
  42. package/dist/assets/{scheduler-workflow-DoAFm2Pl.js.map → scheduler-workflow-CwK7no2d.js.map} +1 -1
  43. package/dist/assets/skills/neon-pr-review/SKILL.md +1 -1
  44. package/dist/assets/skills/neondeck/SKILL.md +1 -1
  45. package/dist/assets/{triage-pr-event-BIDtSjo4.js → triage-pr-event-C2g4s1dt.js} +1 -1
  46. package/dist/assets/{triage-pr-event-Dnetiswf.js → triage-pr-event-CCNMZZPE.js} +3 -3
  47. package/dist/assets/{triage-pr-event-Dnetiswf.js.map → triage-pr-event-CCNMZZPE.js.map} +1 -1
  48. package/dist/assets/{verify-then-push-pr-autofix-CBrpMv6H.js → verify-then-push-pr-autofix-9Dzuwky_.js} +1 -1
  49. package/dist/assets/{verify-then-push-pr-autofix-DaM3R_IS.js → verify-then-push-pr-autofix-CHEU7V9v.js} +3 -3
  50. package/dist/assets/{verify-then-push-pr-autofix-DaM3R_IS.js.map → verify-then-push-pr-autofix-CHEU7V9v.js.map} +1 -1
  51. package/dist/config/dashboard.json +9 -2
  52. package/dist/config/dashboard.schema.json +8 -3
  53. package/dist/server.mjs +14 -14
  54. package/dist/skills/neon-pr-review/SKILL.md +1 -1
  55. package/dist/skills/neondeck/SKILL.md +1 -1
  56. package/package.json +1 -1
  57. package/src/modules/commands/handlers/queue.ts +30 -14
  58. package/src/modules/commands/schemas.ts +4 -2
  59. package/src/modules/commands/summaries.ts +16 -0
  60. package/src/modules/config/mutations/dashboard.ts +16 -2
  61. package/src/modules/github/index.ts +1 -0
  62. package/src/modules/github/pull-requests.ts +30 -3
  63. package/src/modules/github/queue.ts +8 -0
  64. package/src/modules/github/reviews.ts +25 -0
  65. package/src/modules/pr-events/index.ts +1 -0
  66. package/src/modules/pr-review-assist/actions.ts +53 -13
  67. package/src/modules/pr-review-assist/schemas.ts +18 -6
  68. package/src/modules/pr-review-assist/service.ts +45 -9
  69. package/src/modules/pr-reviews/events.ts +40 -0
  70. package/src/modules/pr-reviews/index.ts +4 -0
  71. package/src/modules/pr-reviews/service.ts +655 -0
  72. package/src/modules/pr-reviews/store.ts +186 -0
  73. package/src/modules/pr-reviews/types.ts +48 -0
  74. package/src/modules/reports/service.ts +21 -6
  75. package/src/modules/safety/policy-entries.ts +46 -1
  76. package/src/runtime-home/app-db/migrations/20260714213018_pr_reviews/migration.sql +34 -0
  77. package/src/runtime-home/app-db/migrations/20260714213018_pr_reviews/snapshot.json +7809 -0
  78. package/src/runtime-home/app-db/schema.ts +44 -0
  79. package/src/runtime-home/bootstrap.ts +6 -0
  80. package/src/runtime-home/dashboard-migrations.ts +101 -0
  81. package/src/runtime-home/files.ts +5 -5
  82. package/src/runtime-home/index.ts +1 -0
  83. package/src/runtime-home/schemas.ts +7 -4
  84. package/src/server/create-app.ts +4 -0
  85. package/src/server/events/review-stream.ts +45 -0
  86. package/src/server/learning-hooks.ts +42 -0
  87. package/src/server/routes/github.ts +191 -4
  88. package/src/server/routes/reports.ts +2 -1
  89. package/src/server/routes/reviews.ts +257 -0
  90. package/src/skills/neon-pr-review/SKILL.md +1 -1
  91. package/src/skills/neondeck/SKILL.md +1 -1
  92. package/web/dist/assets/GitHubPrReview-CybXdsHd.js +3 -0
  93. package/web/dist/assets/{MultiFileView-CJTrTPMb.js → MultiFileView-BRXAY80r.js} +7 -7
  94. package/web/dist/assets/PrReviewPopoutPage-BAlIvAZL.js +1 -0
  95. package/web/dist/assets/index-D1VwAK0m.css +2 -0
  96. package/web/dist/assets/index-DLt_8zyR.js +44 -0
  97. package/web/dist/assets/{surfaces-CZfR7WsZ.js → surfaces-i0d1kpFn.js} +1 -1
  98. package/web/dist/index.html +2 -2
  99. package/dist/assets/app-BBIuPG77.js.map +0 -1
  100. package/dist/assets/autopilot-CnHJWMaF.js.map +0 -1
  101. package/dist/assets/briefing-CjRucNjn.js +0 -2
  102. package/dist/assets/briefings-DpDKxWsT.js.map +0 -1
  103. package/dist/assets/command-run-ujx2nwT9.js +0 -2
  104. package/dist/assets/fix-pr-ci-CXG_U_NI.js +0 -2
  105. package/dist/assets/review-pr-for-human-C_OT_LFW.js.map +0 -1
  106. package/dist/assets/safety-EnvDg7cY.js.map +0 -1
  107. package/web/dist/assets/GitHubPrReview-BFLAbu4J.js +0 -2
  108. package/web/dist/assets/index-2gqnSuHv.css +0 -2
  109. package/web/dist/assets/index-BNCOb0p_.js +0 -44
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0-beta.9
4
+
5
+ ### Patch Changes
6
+
7
+ - [#118](https://github.com/pandemicsyn/neondeck/pull/118) [`df66b2c`](https://github.com/pandemicsyn/neondeck/commit/df66b2c3b4e1dc75a97534cdc0db99396245062f) Thanks [@pandemicsyn](https://github.com/pandemicsyn)! - Keep ready dashboard notifications visible for one hour by default and allow configuring them to remain until dismissed.
8
+
9
+ - [#115](https://github.com/pandemicsyn/neondeck/pull/115) [`1e692f4`](https://github.com/pandemicsyn/neondeck/commit/1e692f44819715e102135ef2bd896c208786d310) Thanks [@pandemicsyn](https://github.com/pandemicsyn)! - Correct PR branch freshness findings, replace stale Neon-generated review artifacts on re-review while preserving human edits, and speed up the focused review pop-out loading path.
10
+
11
+ - [#116](https://github.com/pandemicsyn/neondeck/pull/116) [`8c972c0`](https://github.com/pandemicsyn/neondeck/commit/8c972c0e1898b5e0462d59d69badfc4e08f87dba) Thanks [@pandemicsyn](https://github.com/pandemicsyn)! - Complete the PR review workbench with in-dashboard artifacts, explicit finding anchors, manual report-only anchoring, workbench-started Neon reviews, reliable behind detection, and reserved whole-review submission.
12
+
13
+ - [#117](https://github.com/pandemicsyn/neondeck/pull/117) [`fc0618d`](https://github.com/pandemicsyn/neondeck/commit/fc0618dc456c67aa5362baf772ef2499a95d55ad) Thanks [@pandemicsyn](https://github.com/pandemicsyn)! - Add the Reviews tab once to existing dashboard work regions while preserving the configured default tab and respecting later user removal.
14
+
15
+ ## 1.0.0-beta.8
16
+
17
+ ### Minor Changes
18
+
19
+ - [#113](https://github.com/pandemicsyn/neondeck/pull/113) [`8eaa12d`](https://github.com/pandemicsyn/neondeck/commit/8eaa12df1c4b0344f1f7a11f0fbf491d76678be0) Thanks [@pandemicsyn](https://github.com/pandemicsyn)! - Add a durable PR review inbox with live chat and dashboard updates, queue-independent review workbenches, linked local reports, and explicit whole-review GitHub submission.
20
+
3
21
  ## 1.0.0-beta.7
4
22
 
5
23
  ### Minor Changes
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "$schema": "./dashboard.schema.json",
3
+ "schemaVersion": 1,
3
4
  "display": {
4
5
  "preset": "xeneon-edge",
5
6
  "width": 2560,
@@ -13,7 +14,7 @@
13
14
  "toasts": {
14
15
  "enabled": true,
15
16
  "minimumLevel": "ready",
16
- "readyDurationMs": 6000,
17
+ "readyDurationMs": 3600000,
17
18
  "maxVisible": 3
18
19
  }
19
20
  },
@@ -47,8 +48,14 @@
47
48
  "row": 1,
48
49
  "columnSpan": 4,
49
50
  "rowSpan": 5,
50
- "defaultTab": "github",
51
+ "defaultTab": "reviews",
51
52
  "tabs": [
53
+ {
54
+ "id": "reviews",
55
+ "title": "REVIEWS",
56
+ "pluginId": "reviews-panel",
57
+ "config": {}
58
+ },
52
59
  {
53
60
  "id": "github",
54
61
  "title": "GITHUB",
@@ -4,11 +4,16 @@
4
4
  "title": "Neondeck dashboard config",
5
5
  "type": "object",
6
6
  "additionalProperties": true,
7
- "required": ["display", "theme", "layout"],
7
+ "required": ["schemaVersion", "display", "theme", "layout"],
8
8
  "properties": {
9
9
  "$schema": {
10
10
  "type": "string"
11
11
  },
12
+ "schemaVersion": {
13
+ "type": "integer",
14
+ "minimum": 1,
15
+ "description": "Version marker for one-time dashboard layout migrations."
16
+ },
12
17
  "display": {
13
18
  "type": "object",
14
19
  "additionalProperties": false,
@@ -66,8 +71,8 @@
66
71
  },
67
72
  "readyDurationMs": {
68
73
  "type": "integer",
69
- "default": 6000,
70
- "description": "Automatically dismiss ready toasts after this many milliseconds. Values are clamped from 1000 through 60000."
74
+ "default": 3600000,
75
+ "description": "Automatically dismiss ready toasts after this many milliseconds. Use 0 to keep them until dismissed. Nonzero values are clamped from 1000 through 86400000."
71
76
  },
72
77
  "maxVisible": {
73
78
  "type": "integer",
@@ -1,7 +1,8 @@
1
- import { $ as readLearningOperatorState, $n as listExecutionApprovals, $r as resolveMcpApprovalWithPaths, $t as getGitHubPrFiles, A as readReport, Aa as isRegisteredProvider, Ai as switchChatSession, An as listTaskRecords, At as reviewKiloResult, Ba as ConfigValidationError, Bi as listMemories, Bt as readRepoFile, Ca as markNotificationRead, Ci as createChatSession, Cn as resolvePrReference, Cr as localApiAuthHeader, D as fixPrReviewFeedback, Da as nonEmptyStringSchema, Di as pinChatSession, E as commentPrAutofixResult, Ea as subscribeNotificationEvents, Ei as listChatSessionCommandEvents, Er as readLocalApiToken, Ga as asJsonValue, Gi as replayConfigEventsAfter, Gr as readPublicMcpOAuthLogin, Gt as pushPrAutofix, Ha as parseDashboardConfig, Hi as upsertMemory, Ht as searchRepoFiles, I as recordConversationTurnAndMaybeQueueLearning, Ia as ensureRuntimeHome, J as rejectSkillPatchCandidate, Ja as okAction, Jn as verifyPrWorktree, Jr as removeMcpServer, Jt as deleteGitHubPrReviewDraft, K as applySkillPatchCandidate, Ka as failedAction, Ki as subscribeConfigEvents, Kr as startMcpOAuthLogin, L as recordHandledPrFromWorkflowResult, La as ensureRuntimeHomeSync, Li as decideMemoryCandidate, Lt as patchRepoFiles, Ma as readProviderConfigSync, Mi as formatChatSessionServerSentEvent, Mn as removeTask, Mr as listRuntimeSkills, Mt as recordDocsDriftFixTaskBoundary, Na as loadNeondeckEnv, Nn as setTaskEnabled, Nr as loadRuntimeSkill, Oa as parseInput, Oi as renameChatSession, On as createAgentInstructionTask, Ot as listKiloResultStates, Pi as subscribeChatSessionEvents, Pr as reloadRuntimeSkills, Pt as fixPrCiFailure, Qn as runApprovedExecution, Qr as listMcpAudit, Qt as getGitHubPrFileDiff, Ra as readRuntimeJson, Ri as listMemoryCandidates, Rt as readRepoCheckoutStatus, Sa as listNotifications, Si as archiveChatSession, Sn as setPrWatchPolling, Sr as bearerToken, St as searchKiloSessions, T as approvePreparedDiffPushWithPolicy, Ta as formatNotificationServerSentEvent, Ti as linkChatSessionContext, Tr as localApiTokenQueryParam, Ua as runtimePaths, Ui as readNeonSessionState, Ur as completeMcpOAuthCallback, Ut as writeRepoFile, Va as parseAppConfig, Vi as listMemoryEvents, Vr as getMcpRegistry, Vt as replaceRepoFile, Wi as formatConfigServerSentEvent, Wn as settleScheduledTaskWorkflowRun, Wr as logoutMcpOAuthServer, Wt as listRepoEditEvents, Xi as readRepoRegistrySnapshot, Xn as syncExeDevCheckout, Xr as updateMcpServer, Xt as getGitHubPrBranchPermissions, Y as restoreSkillPatchCandidate, Ya as __commonJSMin, Yi as readRepoHealthSnapshot, Yr as setMcpServerEnabled, Yt as deleteGitHubPrReviewDraftComment, Za as __require, Zi as repoFullName, Zr as listMcpApprovals, Zt as getGitHubPrEventState, _a as readApprovalRecord, _i as refreshChatSessionSummary, _n as addRefWatch, _t as readKiloSession, aa as releaseWorktreeLock, an as postGitHubPrComment, ar as readExecutionPolicy, ba as setWorkflowSummaryRunId, bi as readChatSessionMessages, bn as listRefWatches, bt as readKiloSessionMessages, c as abandonPreparedDiffWithRevisionAbort, cn as postGitHubPrThreadReply, cr as readProviderConfig, d as failAutopilotAdmission, dn as refreshPrWatchEventState, dr as updateWorktreePolicy, dt as readKiloTaskDiff, ea as listWorktrees, en as getGitHubPrRequestedChanges, er as requestExecutionApproval, et as readWorkflowObservability, f as recordAutopilotAdmissionRun, fa as readPreparedDiffChangedFiles, fr as updateExecutionPolicy, ft as readKiloTaskEvents, ga as runPreparedDiffVerification, gi as referenceChatSession, gn as addPrWatch, gr as triagePrEvent, h as settleAutopilotAdmissionTriage, ha as requestPreparedDiffRevision, hr as reloadConfig, ht as startKiloTask, i as runAutopilotRecoveryAction, ia as readWorktreeStatus, ii as listGitHubPrQueue, in as patchGitHubPrReviewDraftComment, it as listLearningReviews, j as readReportHtml, ja as providerRuntimeRegistrations, ji as updateChatSessionCommandEvent, jn as readTaskRecord, jr as readRuntimeStatus, jt as verifyKiloResult, k as listReports, ka as installFlueExecutionContextTracker, ki as restoreChatSession, kn as createBriefingTask, kt as promoteKiloResult, l as runPreparedDiffRevision, la as listPreparedDiffs, ln as postGitHubPrThreadResolution, lr as updateProviderConfig, lt as abortKiloTask, m as settleAutopilotAdmissionPrepare, ma as readPreparedDiffSummary, mi as listChatSessionActivity, mr as updateDashboardLayout, mt as readKiloTaskStatus, na as createWorktree, ni as getGitHubPullRequest, nn as getGitHubPrReviewThreads, nr as checkExecutionPolicy, nt as loadAutomationLearningMemoryContext, o as readAutopilotState, oa as syncWorktree, on as postGitHubPrReview, p as recordAutopilotAdmissionTerminalFact, pa as readPreparedDiffFileDiff, pr as applyDashboardPreset, pt as readKiloTaskSessions, q as listSkillPatchCandidates, qa as invalidInputAction, qn as preparePrWorktree, qr as addMcpServer, r as readAutopilotRecoveryOptions, ra as lockWorktree, rn as listPrWatchEventWatermarks, rt as attachLearningReviewRunId, s as autopilotStateSchema, sn as postGitHubPrReviewDraftComment, sr as updateRepoAutopilotPolicy, ta as cleanupWorktrees, tn as getGitHubPrReviewDraft, tr as resolveExecutionApproval, tt as recordFlueObservation, u as beginAutopilotAdmissionPrepare, ua as openPreparedDiffWorktree, un as putGitHubPrReviewDraft, ur as updateAgentModels, ut as listKiloTasks, va as addWorkflowSummary, vi as listChatSessions, vt as readKiloSessionChildren, wa as resolveNotification, wi as createChatSessionCommandEvent, wr as localApiTokenMatches, xa as addNotification, xi as searchChatSessions, xn as removePrWatch, xr as readHostMetrics, xt as readUnavailableSessionAdapter, ya as listWorkflowSummaries, yi as readChatSession, yn as listPrWatches, yt as readKiloSessionDiff, zi as archiveMemory, zt as readRepoDiff } from "./autopilot-CnHJWMaF.js";
2
- import { a as readBriefingState, c as settleBriefingObservation, i as readBriefingRunDetails, l as updateBriefingProfile, o as rotateBriefingSession, r as linkBriefingWorkflowObservation, s as runBriefingNow } from "./briefings-DpDKxWsT.js";
3
- import { a as runSchedulerTick, c as rollbackQuietly, d as supportedCommands, n as readSafetyPolicy, o as isSqliteBusy, s as readMetadataValue } from "./safety-EnvDg7cY.js";
4
- import { a as review_conversation_for_learning_default, r as review_pr_batch_for_learning_default, s as curate_learning_store_default, t as scheduler_tick_default } from "./scheduler-tick-DdiWIWU2.js";
1
+ import { $ as readLearningOperatorState, $a as okAction, $i as readRepoHealthSnapshot, $n as runApprovedExecution, $r as listMcpAudit, $t as getGitHubPrFiles, A as readReport, Aa as subscribeNotificationEvents, Ai as listChatSessionCommandEvents, An as createBriefingTask, At as reviewKiloResult, Bt as readRepoFile, Ca as listWorkflowSummaries, Ci as readChatSession, Cn as setPrWatchPolling, Cr as bearerToken, D as fixPrReviewFeedback, Da as markNotificationRead, Di as createChatSession, Dr as readLocalApiToken, E as commentPrAutofixResult, Ea as listNotifications, Ei as archiveChatSession, Er as localApiTokenQueryParam, Fa as isRegisteredProvider, Fi as updateChatSessionCommandEvent, Fr as reloadRuntimeSkills, Ga as ConfigValidationError, Gi as listMemoryEvents, Gn as settleScheduledTaskWorkflowRun, Gr as logoutMcpOAuthServer, Gt as pushPrAutofix, Ha as ensureRuntimeHomeSync, Hi as listMemoryCandidates, Hr as getMcpRegistry, Ht as searchRepoFiles, I as recordConversationTurnAndMaybeQueueLearning, Ia as providerRuntimeRegistrations, Ii as formatChatSessionServerSentEvent, J as rejectSkillPatchCandidate, Ja as runtimePaths, Ji as formatConfigServerSentEvent, Jn as preparePrWorktree, Jr as addMcpServer, Jt as deleteGitHubPrReviewDraft, K as applySkillPatchCandidate, Ka as parseAppConfig, Ki as upsertMemory, Kr as readPublicMcpOAuthLogin, L as recordHandledPrFromWorkflowResult, La as readProviderConfigSync, Lt as patchRepoFiles, Ma as parseInput, Mi as renameChatSession, Mn as readTaskRecord, Mr as readRuntimeStatus, Mt as recordDocsDriftFixTaskBoundary, Ni as restoreChatSession, Nn as removeTask, Nr as listRuntimeSkills, Oa as resolveNotification, Oi as createChatSessionCommandEvent, Ot as listKiloResultStates, Pa as installFlueExecutionContextTracker, Pi as switchChatSession, Pn as setTaskEnabled, Pr as loadRuntimeSkill, Pt as fixPrCiFailure, Qa as invalidInputAction, Qr as listMcpApprovals, Qt as getGitHubPrFileDiff, Ra as loadNeondeckEnv, Ri as subscribeChatSessionEvents, Rt as readRepoCheckoutStatus, Sa as addWorkflowSummary, Si as listChatSessions, Sn as removePrWatch, Sr as readHostMetrics, St as searchKiloSessions, T as approvePreparedDiffPushWithPolicy, Ta as addNotification, Ti as searchChatSessions, Tr as localApiTokenMatches, Ua as readRuntimeJson, Ui as archiveMemory, Ut as writeRepoFile, Va as ensureRuntimeHome, Vi as decideMemoryCandidate, Vt as replaceRepoFile, Wi as listMemories, Wr as completeMcpOAuthCallback, Wt as listRepoEditEvents, Xa as asJsonValue, Xi as subscribeConfigEvents, Xr as setMcpServerEnabled, Xt as getGitHubPrBranchPermissions, Y as restoreSkillPatchCandidate, Yi as replayConfigEventsAfter, Yn as verifyPrWorktree, Yr as removeMcpServer, Yt as deleteGitHubPrReviewDraftComment, Za as failedAction, Zn as syncExeDevCheckout, Zr as updateMcpServer, Zt as getGitHubPrEventState, _a as readPreparedDiffFileDiff, _n as addPrWatch, _r as triagePrEvent, _t as readKiloSession, aa as cleanupWorktrees, ai as listGitHubPrQueue, an as postGitHubPrComment, ba as runPreparedDiffVerification, bi as referenceChatSession, bn as listPrWatches, bt as readKiloSessionMessages, c as abandonPreparedDiffWithRevisionAbort, ca as readWorktreeStatus, cn as postGitHubPrThreadReply, cr as updateRepoAutopilotPolicy, d as failAutopilotAdmission, dn as refreshPrWatchEventState, dr as updateAgentModels, dt as readKiloTaskDiff, ea as readRepoRegistrySnapshot, ei as resolveMcpApprovalWithPaths, en as getGitHubPrRequestedChanges, eo as __commonJSMin, er as listExecutionApprovals, et as readWorkflowObservability, f as recordAutopilotAdmissionRun, fr as updateWorktreePolicy, ft as readKiloTaskEvents, ga as readPreparedDiffChangedFiles, gr as reloadConfig, h as settleAutopilotAdmissionTriage, hr as updateDashboardLayout, ht as startKiloTask, i as runAutopilotRecoveryAction, ia as listWorktrees, in as patchGitHubPrReviewDraftComment, it as listLearningReviews, j as readReportHtml, ja as nonEmptyStringSchema, ji as pinChatSession, jn as listTaskRecords, jt as verifyKiloResult, k as listReports, ka as formatNotificationServerSentEvent, ki as linkChatSessionContext, kn as createAgentInstructionTask, kt as promoteKiloResult, l as runPreparedDiffRevision, la as releaseWorktreeLock, ln as postGitHubPrThreadResolution, lr as readProviderConfig, lt as abortKiloTask, m as settleAutopilotAdmissionPrepare, ma as openPreparedDiffWorktree, mr as applyDashboardPreset, mt as readKiloTaskStatus, nn as getGitHubPrReviewThreads, no as __require, nr as resolveExecutionApproval, nt as loadAutomationLearningMemoryContext, o as readAutopilotState, oa as createWorktree, on as postGitHubPrReview, or as readExecutionPolicy, p as recordAutopilotAdmissionTerminalFact, pa as listPreparedDiffs, pr as updateExecutionPolicy, pt as readKiloTaskSessions, q as listSkillPatchCandidates, qa as parseDashboardConfig, qi as readNeonSessionState, qr as startMcpOAuthLogin, r as readAutopilotRecoveryOptions, ri as getGitHubPullRequest, rn as listPrWatchEventWatermarks, rr as checkExecutionPolicy, rt as attachLearningReviewRunId, s as autopilotStateSchema, sa as lockWorktree, sn as postGitHubPrReviewDraftComment, ta as repoFullName, tn as getGitHubPrReviewDraft, tr as requestExecutionApproval, tt as recordFlueObservation, u as beginAutopilotAdmissionPrepare, ua as syncWorktree, un as putGitHubPrReviewDraft, ur as updateProviderConfig, ut as listKiloTasks, va as readPreparedDiffSummary, vi as listChatSessionActivity, vn as addRefWatch, vt as readKiloSessionChildren, wa as setWorkflowSummaryRunId, wi as readChatSessionMessages, wn as resolvePrReference, wr as localApiAuthHeader, xa as readApprovalRecord, xi as refreshChatSessionSummary, xn as listRefWatches, xt as readUnavailableSessionAdapter, ya as requestPreparedDiffRevision, yt as readKiloSessionDiff, zt as readRepoDiff } from "./autopilot-CMaU1iZd.js";
2
+ import { a as readBriefingState, c as settleBriefingObservation, i as readBriefingRunDetails, l as updateBriefingProfile, o as rotateBriefingSession, r as linkBriefingWorkflowObservation, s as runBriefingNow } from "./briefings-DtOcw7y6.js";
3
+ import { a as recentPrReviews, c as reservePrReviewSubmission, d as readPrReview, f as readPrReviewForTarget, i as failPrReview, l as startPrReview, m as subscribePrReviewEvents, n as beginPrReviewSubmissionAttempt, o as reconcilePrReviewSubmission, p as formatPrReviewServerSentEvent, s as releasePrReviewSubmission, t as attachPrReviewAttemptRun, u as submitPrReview } from "./pr-reviews-CbYcf9Mj.js";
4
+ import { a as runSchedulerTick, c as rollbackQuietly, d as supportedCommands, n as readSafetyPolicy, o as isSqliteBusy, s as readMetadataValue } from "./safety-S_Ek3ETH.js";
5
+ import { a as review_conversation_for_learning_default, r as review_pr_batch_for_learning_default, s as curate_learning_store_default, t as scheduler_tick_default } from "./scheduler-tick-DO_6nnY9.js";
5
6
  import { getRun, invoke, listRuns, observe, registerProvider } from "@flue/runtime";
6
7
  import { createHash, randomUUID } from "node:crypto";
7
8
  import * as v from "valibot";
@@ -379,6 +380,41 @@ function createNotificationEventRoutes() {
379
380
  return routes;
380
381
  }
381
382
  //#endregion
383
+ //#region src/server/events/review-stream.ts
384
+ function createReviewEventRoutes() {
385
+ const routes = new Hono();
386
+ routes.get("/reviews", () => {
387
+ const encoder = new TextEncoder();
388
+ let cleanup = () => {};
389
+ const stream = new ReadableStream({
390
+ start(controller) {
391
+ const send = (value) => controller.enqueue(encoder.encode(value));
392
+ send(": connected\n\n");
393
+ const unsubscribe = subscribePrReviewEvents((event) => {
394
+ send(formatPrReviewServerSentEvent(event));
395
+ });
396
+ const heartbeat = setInterval(() => {
397
+ send(`: heartbeat ${Date.now()}\n\n`);
398
+ }, 25e3);
399
+ cleanup = () => {
400
+ clearInterval(heartbeat);
401
+ unsubscribe();
402
+ };
403
+ },
404
+ cancel() {
405
+ cleanup();
406
+ }
407
+ });
408
+ return new Response(stream, { headers: {
409
+ "cache-control": "no-cache, no-transform",
410
+ connection: "keep-alive",
411
+ "content-type": "text/event-stream; charset=utf-8",
412
+ "x-accel-buffering": "no"
413
+ } });
414
+ });
415
+ return routes;
416
+ }
417
+ //#endregion
382
418
  //#region src/server/events/session-stream.ts
383
419
  function createSessionEventRoutes() {
384
420
  const routes = new Hono();
@@ -423,6 +459,11 @@ function installFlueObservationHandlers(paths, dependencies = {}) {
423
459
  const unsubscribe = (dependencies.observe ?? observe)((event, context) => {
424
460
  const contextHome = flueContextRuntimeHome(context);
425
461
  if (contextHome && contextHome !== paths.home) return;
462
+ if (event.type === "run_start") try {
463
+ linkPrReviewRunObservation(event, paths);
464
+ } catch (error) {
465
+ console.error("[neondeck] failed to link PR review run", error);
466
+ }
426
467
  if (event.type === "run_end") {
427
468
  recordFlueObservation(event, paths).then(async () => {
428
469
  const terminalFact = autopilotTerminalFact(event);
@@ -454,6 +495,9 @@ function installFlueObservationHandlers(paths, dependencies = {}) {
454
495
  linkBriefingWorkflowObservation(event, paths).catch((error) => {
455
496
  console.error("[neondeck] failed to link briefing workflow run", error);
456
497
  });
498
+ Promise.resolve().then(() => settlePrReviewObservation(event, paths)).catch((error) => {
499
+ console.error("[neondeck] failed to settle PR review run", error);
500
+ });
457
501
  const learningReviewId = learningReviewResultId(event);
458
502
  if (learningReviewId) Promise.resolve().then(() => attachLearningReviewRunId({
459
503
  reviewId: learningReviewId,
@@ -509,6 +553,22 @@ function installFlueObservationHandlers(paths, dependencies = {}) {
509
553
  });
510
554
  observationHandlerUnsubscribers.set(paths.home, unsubscribe);
511
555
  }
556
+ function settlePrReviewObservation(event, paths) {
557
+ if (!event.isError) return null;
558
+ return failPrReview({
559
+ runId: event.runId,
560
+ allowReady: true,
561
+ message: "The Flue review workflow failed. Inspect the guarded run for details."
562
+ }, paths);
563
+ }
564
+ function linkPrReviewRunObservation(event, paths) {
565
+ if (event.workflowName !== "review-pr-for-human") return null;
566
+ if (!event.input || typeof event.input !== "object") return null;
567
+ const reviewId = stringField(event.input, "reviewId");
568
+ const attemptId = stringField(event.input, "attemptId");
569
+ if (!reviewId || !attemptId) return null;
570
+ return attachPrReviewAttemptRun(reviewId, attemptId, event.runId, paths);
571
+ }
512
572
  function flueContextRuntimeHome(context) {
513
573
  const value = context?.env?.NEONDECK_HOME;
514
574
  return typeof value === "string" && value ? value : void 0;
@@ -518,7 +578,7 @@ async function startPrepareAfterTriage(event, paths) {
518
578
  const admission = await beginAutopilotAdmissionPrepare({ triageRunId: event.runId }, paths);
519
579
  if (!admission) return;
520
580
  try {
521
- const { runId } = await invoke((await import("./prepare-pr-worktree-GIZ8cMUj.js")).default, { input: {
581
+ const { runId } = await invoke((await import("./prepare-pr-worktree-CwalJPIV.js")).default, { input: {
522
582
  repoId: admission.repoId,
523
583
  prNumber: admission.prNumber,
524
584
  eventId: admission.eventFingerprint,
@@ -721,7 +781,7 @@ async function dispatchApprovedPreparedDiffPush(approval, paths, dependencies) {
721
781
  try {
722
782
  receipt = await invokeWorkflowForRuntimeHome(paths, dispatch, invokeWorkflow);
723
783
  } catch (error) {
724
- const message = errorMessage$3(error);
784
+ const message = errorMessage$4(error);
725
785
  await recordDispatchFailure(preparedDiff, mode, dispatch, approvalId, message, paths);
726
786
  return {
727
787
  ...withDispatchData(approval, {
@@ -752,7 +812,7 @@ async function dispatchApprovedPreparedDiffPush(approval, paths, dependencies) {
752
812
  dispatchedAt: (/* @__PURE__ */ new Date()).toISOString()
753
813
  }
754
814
  }, paths).catch(async (error) => {
755
- const message = errorMessage$3(error);
815
+ const message = errorMessage$4(error);
756
816
  await recordDispatchSummaryFailure(preparedDiff, mode, dispatch, approvalId, receipt.runId, message, paths);
757
817
  return null;
758
818
  });
@@ -801,8 +861,8 @@ async function invokeWorkflowForRuntimeHome(paths, dispatch, invokeWorkflow) {
801
861
  return invokeWorkflow(dispatch.workflow, dispatch.input);
802
862
  }
803
863
  async function invokeAutopilotWorkflow(workflow, input) {
804
- if (workflow === "push-pr-autofix") return invokeWorkflow((await import("./push-pr-autofix-w85pTU9W.js")).default, { input });
805
- return invokeWorkflow((await import("./verify-then-push-pr-autofix-CBrpMv6H.js")).default, { input });
864
+ if (workflow === "push-pr-autofix") return invokeWorkflow((await import("./push-pr-autofix-DUXYeBds.js")).default, { input });
865
+ return invokeWorkflow((await import("./verify-then-push-pr-autofix-9Dzuwky_.js")).default, { input });
806
866
  }
807
867
  async function recordDispatchFailure(preparedDiff, mode, dispatch, approvalId, error, paths) {
808
868
  await addWorkflowSummary({
@@ -859,7 +919,7 @@ async function recordDispatchSummaryFailure(preparedDiff, mode, dispatch, approv
859
919
  async function invokeWorkflow(workflow, request) {
860
920
  return invoke(workflow, request);
861
921
  }
862
- function errorMessage$3(error) {
922
+ function errorMessage$4(error) {
863
923
  return error instanceof Error ? error.message : String(error);
864
924
  }
865
925
  function withDispatchData(approval, dispatch, message) {
@@ -1359,8 +1419,98 @@ function createGitHubRoutes(paths) {
1359
1419
  routes.post("/prs/:owner/:repo/:number/reviews", async (c) => {
1360
1420
  const target = prTargetFromParams(c.req.param("owner"), c.req.param("repo"), c.req.param("number"));
1361
1421
  if (!target.ok) return c.json(target.result, 400);
1362
- const result = await postGitHubPrReview(target.input, await safeJsonBody(c), paths);
1363
- return c.json(result, result.ok ? 200 : 400);
1422
+ const reviewInputObject = objectField$1(await safeJsonBody(c));
1423
+ const requestedHeadSha = typeof reviewInputObject.headSha === "string" ? reviewInputObject.headSha : null;
1424
+ const durableReview = readPrReviewForTarget(target.input.repo, target.input.prNumber, paths);
1425
+ if (durableReview && (durableReview.status !== "ready" || !requestedHeadSha || durableReview.headSha !== requestedHeadSha)) return c.json({
1426
+ ok: false,
1427
+ action: "github_pr_review_post",
1428
+ changed: false,
1429
+ message: durableReview.status !== "ready" ? "The durable review must be ready before it can be submitted." : "The durable review does not match the pull request head being submitted.",
1430
+ data: { reviewId: durableReview.id }
1431
+ }, 409);
1432
+ const verdict = reviewInputObject.verdict;
1433
+ if (verdict !== "comment" && verdict !== "approve" && verdict !== "request-changes") return c.json({
1434
+ ok: false,
1435
+ action: "github_pr_review_post",
1436
+ changed: false,
1437
+ message: "A review verdict is required."
1438
+ }, 400);
1439
+ const reserved = durableReview ? reservePrReviewSubmission({
1440
+ repoFullName: target.input.repo,
1441
+ prNumber: target.input.prNumber,
1442
+ headSha: requestedHeadSha ?? "",
1443
+ verdict
1444
+ }, paths) : null;
1445
+ if (durableReview && !reserved) return c.json({
1446
+ ok: false,
1447
+ action: "github_pr_review_post",
1448
+ changed: false,
1449
+ message: "The durable review changed before submission could be reserved. Refresh and try again.",
1450
+ data: { reviewId: durableReview.id }
1451
+ }, 409);
1452
+ const releaseReservation = () => {
1453
+ if (reserved) releasePrReviewSubmission({
1454
+ reviewId: reserved.id,
1455
+ headSha: reserved.headSha
1456
+ }, paths);
1457
+ };
1458
+ const endSubmissionAttempt = reserved ? beginPrReviewSubmissionAttempt(reserved.id) : () => {};
1459
+ let githubAccepted = false;
1460
+ try {
1461
+ const savedDraftResult = await putGitHubPrReviewDraft(target.input, {
1462
+ headSha: requestedHeadSha ?? "",
1463
+ body: typeof reviewInputObject.body === "string" ? reviewInputObject.body : null,
1464
+ verdict,
1465
+ reanchorHeadSha: true
1466
+ }, paths);
1467
+ if (!savedDraftResult.ok) {
1468
+ releaseReservation();
1469
+ return c.json(savedDraftResult, 400);
1470
+ }
1471
+ const savedDraft = objectField$1(objectField$1(savedDraftResult.data).draft);
1472
+ if (typeof savedDraft.id !== "string") {
1473
+ releaseReservation();
1474
+ return c.json({
1475
+ ok: false,
1476
+ action: "github_pr_review_post",
1477
+ changed: false,
1478
+ message: "Review draft could not be saved before submission."
1479
+ }, 409);
1480
+ }
1481
+ const result = await postGitHubPrReview(target.input, {
1482
+ draftId: savedDraft.id,
1483
+ headSha: requestedHeadSha ?? "",
1484
+ commentIds: Array.isArray(reviewInputObject.commentIds) ? reviewInputObject.commentIds.filter((id) => typeof id === "string") : void 0
1485
+ }, paths);
1486
+ if (!result.ok) {
1487
+ releaseReservation();
1488
+ return c.json(result, 400);
1489
+ }
1490
+ githubAccepted = true;
1491
+ const data = objectField$1(result.data);
1492
+ const draft = objectField$1(data.draft);
1493
+ const review = objectField$1(data.review);
1494
+ const submittedVerdict = prReviewVerdict(draft.verdict) ?? verdict;
1495
+ const submitted = reserved ? submitPrReview({
1496
+ reviewId: reserved.id,
1497
+ verdict: submittedVerdict,
1498
+ githubReviewUrl: typeof review.url === "string" ? review.url : null
1499
+ }, paths) : null;
1500
+ if (reserved && !submitted) return c.json({
1501
+ ok: false,
1502
+ action: "github_pr_review_post",
1503
+ changed: true,
1504
+ message: "GitHub accepted the review, but Neondeck could not settle its reserved durable review record.",
1505
+ data: result.data
1506
+ }, 409);
1507
+ return c.json(result, 200);
1508
+ } catch (error) {
1509
+ if (!githubAccepted) releaseReservation();
1510
+ throw error;
1511
+ } finally {
1512
+ endSubmissionAttempt();
1513
+ }
1364
1514
  });
1365
1515
  routes.post("/prs/:owner/:repo/:number/review-threads/:threadId/reply", async (c) => {
1366
1516
  const target = prTargetFromParams(c.req.param("owner"), c.req.param("repo"), c.req.param("number"));
@@ -1402,6 +1552,12 @@ function createGitHubRoutes(paths) {
1402
1552
  });
1403
1553
  return routes;
1404
1554
  }
1555
+ function objectField$1(value) {
1556
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
1557
+ }
1558
+ function prReviewVerdict(value) {
1559
+ return value === "comment" || value === "approve" || value === "request-changes" ? value : null;
1560
+ }
1405
1561
  function prTargetFromParams(owner, repo, numberText) {
1406
1562
  const number = Number(numberText);
1407
1563
  if (!Number.isInteger(number) || number <= 0) return {
@@ -1604,7 +1760,7 @@ async function registerHandoffPr(rawInput, paths = runtimePaths(), dependencies
1604
1760
  queued: true
1605
1761
  });
1606
1762
  } catch (error) {
1607
- const message = errorMessage$2(error);
1763
+ const message = errorMessage$3(error);
1608
1764
  const audit = await addHandoffAudit({
1609
1765
  event: "register-pr",
1610
1766
  source,
@@ -1698,7 +1854,7 @@ async function externalReviewQueueAllowed(paths) {
1698
1854
  }
1699
1855
  async function invokeReviewPrWorkflow(input) {
1700
1856
  const { invoke } = await import("@flue/runtime");
1701
- return invoke((await import("./review-pr-for-human-BQjIMKW2.js")).default, { input });
1857
+ return invoke((await import("./review-pr-for-human-CTIL0RRy.js")).default, { input });
1702
1858
  }
1703
1859
  async function addHandoffAudit(summary, paths, options = {}) {
1704
1860
  return addWorkflowSummary({
@@ -1743,7 +1899,7 @@ function failResult(action, message, data = {}) {
1743
1899
  function displaySource(source) {
1744
1900
  return source.replace(/^(?:external|ci):/, "");
1745
1901
  }
1746
- function errorMessage$2(error) {
1902
+ function errorMessage$3(error) {
1747
1903
  if (error instanceof Error) return error.message;
1748
1904
  return String(error);
1749
1905
  }
@@ -2599,9 +2755,9 @@ async function stageDocsDriftFix(rawInput, paths = runtimePaths(), dependencies
2599
2755
  allowedDocsPaths: docsFixAllowedDocsPaths(summary)
2600
2756
  }, paths);
2601
2757
  } catch (error) {
2602
- return failedAction("docs_drift_stage_fix", `Could not record docs drift fix boundary: ${errorMessage$1(error)}.`, {
2758
+ return failedAction("docs_drift_stage_fix", `Could not record docs drift fix boundary: ${errorMessage$2(error)}.`, {
2603
2759
  requires: ["docsDriftBoundary"],
2604
- errors: [errorMessage$1(error)]
2760
+ errors: [errorMessage$2(error)]
2605
2761
  });
2606
2762
  }
2607
2763
  const learningMemoryContext = await loadAutomationLearningMemoryContext(paths, {
@@ -2745,7 +2901,7 @@ function stringArrayConfig(value) {
2745
2901
  function objectConfig(value) {
2746
2902
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
2747
2903
  }
2748
- function errorMessage$1(error) {
2904
+ function errorMessage$2(error) {
2749
2905
  return error instanceof Error ? error.message : String(error);
2750
2906
  }
2751
2907
  //#endregion
@@ -2754,6 +2910,7 @@ function createReportApiRoutes(paths) {
2754
2910
  const routes = new Hono();
2755
2911
  routes.get("/reports", async (c) => {
2756
2912
  const kind = c.req.query("kind")?.trim() || void 0;
2913
+ const excludeKind = c.req.query("excludeKind")?.trim() || void 0;
2757
2914
  const limitText = c.req.query("limit");
2758
2915
  const limit = limitText ? Number(limitText) : void 0;
2759
2916
  if (limitText && (limit === void 0 || !Number.isFinite(limit) || limit <= 0)) return c.json({
@@ -2768,6 +2925,7 @@ function createReportApiRoutes(paths) {
2768
2925
  action: "reports_list",
2769
2926
  items: await listReports(paths, {
2770
2927
  kind,
2928
+ excludeKind,
2771
2929
  limit
2772
2930
  }),
2773
2931
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -2840,6 +2998,162 @@ function createReportFileRoutes(paths) {
2840
2998
  return routes;
2841
2999
  }
2842
3000
  //#endregion
3001
+ //#region src/server/routes/reviews.ts
3002
+ function createReviewRoutes(paths) {
3003
+ const routes = new Hono();
3004
+ routes.get("/reviews", async (c) => {
3005
+ const repo = c.req.query("repo")?.trim();
3006
+ const prNumber = Number(c.req.query("prNumber"));
3007
+ if (repo && Number.isInteger(prNumber) && prNumber > 0) {
3008
+ const review = readPrReviewForTarget(repo, prNumber, paths);
3009
+ const items = review ? [review] : [];
3010
+ return c.json({
3011
+ ok: true,
3012
+ action: "pr_reviews_list",
3013
+ changed: false,
3014
+ items,
3015
+ groups: groupReviews(items, [])
3016
+ });
3017
+ }
3018
+ const reviews = recentPrReviews(paths);
3019
+ const queueResult = await listGitHubPrQueue(paths);
3020
+ const awaiting = (queueResult.ok ? queueFromResult(queueResult.data) : { items: [] }).items.filter((item) => item.relations.includes("review-requested")).map((pullRequest) => ({
3021
+ pullRequest,
3022
+ review: reviews.find((review) => review.repoFullName.toLowerCase() === pullRequest.repo.toLowerCase() && review.prNumber === pullRequest.number) ?? null
3023
+ }));
3024
+ return c.json({
3025
+ ok: true,
3026
+ action: "pr_reviews_list",
3027
+ changed: false,
3028
+ items: reviews,
3029
+ groups: groupReviews(reviews, awaiting),
3030
+ queueIssues: queueResult.ok ? [] : queueResult.errors ?? []
3031
+ });
3032
+ });
3033
+ routes.get("/reviews/:id", (c) => {
3034
+ const review = readPrReview(c.req.param("id"), paths);
3035
+ return review ? c.json({
3036
+ ok: true,
3037
+ action: "pr_review_read",
3038
+ changed: false,
3039
+ review
3040
+ }) : c.json({
3041
+ ok: false,
3042
+ action: "pr_review_read",
3043
+ changed: false,
3044
+ message: "PR review not found."
3045
+ }, 404);
3046
+ });
3047
+ routes.post("/reviews", async (c) => {
3048
+ const body = objectBody(await safeJsonBody(c));
3049
+ const ref = typeof body.ref === "string" ? body.ref.trim() : "";
3050
+ if (!ref) return c.json({
3051
+ ok: false,
3052
+ action: "pr_review_start",
3053
+ changed: false,
3054
+ message: "A pull request reference is required.",
3055
+ requires: ["ref"]
3056
+ }, 400);
3057
+ try {
3058
+ const result = await startPrReview({
3059
+ ref,
3060
+ origin: reviewOrigin(body.origin)
3061
+ }, paths);
3062
+ return c.json({
3063
+ ok: true,
3064
+ action: "pr_review_start",
3065
+ changed: true,
3066
+ message: `Started review for ${result.review.repoFullName}#${result.review.prNumber}.`,
3067
+ ...result
3068
+ }, 202);
3069
+ } catch (error) {
3070
+ return c.json({
3071
+ ok: false,
3072
+ action: "pr_review_start",
3073
+ changed: false,
3074
+ message: errorMessage$1(error)
3075
+ }, 400);
3076
+ }
3077
+ });
3078
+ routes.post("/reviews/:id/review", async (c) => {
3079
+ const existing = readPrReview(c.req.param("id"), paths);
3080
+ if (!existing) return c.json({
3081
+ ok: false,
3082
+ action: "pr_review_restart",
3083
+ changed: false,
3084
+ message: "PR review not found."
3085
+ }, 404);
3086
+ try {
3087
+ const result = await startPrReview({
3088
+ ref: existing.ref,
3089
+ origin: "panel"
3090
+ }, paths);
3091
+ return c.json({
3092
+ ok: true,
3093
+ action: "pr_review_restart",
3094
+ changed: true,
3095
+ message: `Re-reviewing ${existing.repoFullName}#${existing.prNumber}.`,
3096
+ ...result
3097
+ }, 202);
3098
+ } catch (error) {
3099
+ return c.json({
3100
+ ok: false,
3101
+ action: "pr_review_restart",
3102
+ changed: false,
3103
+ message: errorMessage$1(error)
3104
+ }, 400);
3105
+ }
3106
+ });
3107
+ routes.post("/reviews/:id/reconcile", async (c) => {
3108
+ try {
3109
+ const result = await reconcilePrReviewSubmission({ reviewId: c.req.param("id") }, paths);
3110
+ const message = result.outcome === "submitted" ? "Recovered the submitted review from GitHub." : result.outcome === "ready" ? "GitHub has no matching review; the local draft is ready to submit again." : result.outcome === "pending" ? "GitHub has not reported the review yet. Wait a moment, then check again." : `The review is already ${result.review.status}.`;
3111
+ return c.json({
3112
+ ok: true,
3113
+ action: "pr_review_submission_reconcile",
3114
+ changed: result.outcome === "submitted" || result.outcome === "ready",
3115
+ message,
3116
+ review: result.review,
3117
+ reviewId: result.review.id,
3118
+ runId: result.review.runId ?? ""
3119
+ }, result.outcome === "pending" ? 202 : 200);
3120
+ } catch (error) {
3121
+ const message = errorMessage$1(error);
3122
+ return c.json({
3123
+ ok: false,
3124
+ action: "pr_review_submission_reconcile",
3125
+ changed: false,
3126
+ message
3127
+ }, message === "GITHUB_TOKEN is not configured." ? 503 : 502);
3128
+ }
3129
+ });
3130
+ return routes;
3131
+ }
3132
+ function groupReviews(reviews, awaiting) {
3133
+ return {
3134
+ awaiting,
3135
+ inProgress: reviews.filter((review) => review.status === "reviewing" || review.status === "submitting"),
3136
+ needsAction: reviews.filter((review) => review.status === "ready" || review.status === "failed"),
3137
+ submitted: reviews.filter((review) => review.status === "submitted")
3138
+ };
3139
+ }
3140
+ function queueFromResult(value) {
3141
+ if (!value || typeof value !== "object" || Array.isArray(value)) return { items: [] };
3142
+ const queue = value.queue;
3143
+ if (!queue || typeof queue !== "object" || Array.isArray(queue)) return { items: [] };
3144
+ const items = queue.items;
3145
+ return { items: Array.isArray(items) ? items : [] };
3146
+ }
3147
+ function objectBody(value) {
3148
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
3149
+ }
3150
+ function reviewOrigin(value) {
3151
+ return value === "chat" || value === "panel" || value === "api" ? value : "api";
3152
+ }
3153
+ function errorMessage$1(error) {
3154
+ return error instanceof Error ? error.message : String(error);
3155
+ }
3156
+ //#endregion
2843
3157
  //#region src/server/routes/repos.ts
2844
3158
  function createReposRoutes(paths) {
2845
3159
  const routes = new Hono();
@@ -3594,6 +3908,7 @@ async function createApp(options = {}) {
3594
3908
  app.route("/api", createRuntimeRoutes(paths));
3595
3909
  app.route("/api/events", createConfigEventRoutes());
3596
3910
  app.route("/api/events", createNotificationEventRoutes());
3911
+ app.route("/api/events", createReviewEventRoutes());
3597
3912
  app.route("/api/events", createSessionEventRoutes());
3598
3913
  app.route("/api/safety", createSafetyRoutes(paths));
3599
3914
  app.route("/api/execution", createExecutionRoutes(paths));
@@ -3617,6 +3932,7 @@ async function createApp(options = {}) {
3617
3932
  app.route("/api/skills", createSkillRoutes(paths));
3618
3933
  app.route("/api/commands", createCommandRoutes());
3619
3934
  app.route("/api", createReportApiRoutes(paths));
3935
+ app.route("/api", createReviewRoutes(paths));
3620
3936
  app.route("/api/workflows", createWorkflowRoutes(paths));
3621
3937
  app.route("/api/github", createGitHubRoutes(paths));
3622
3938
  app.use("/api/flue/agents/display-assistant/*", displayAssistantLearningMiddleware(paths));
@@ -3656,4 +3972,4 @@ var app_default = await createApp();
3656
3972
  //#endregion
3657
3973
  export { app_default as default };
3658
3974
 
3659
- //# sourceMappingURL=app-BBIuPG77.js.map
3975
+ //# sourceMappingURL=app-ChpCY1hX.js.map