@straiffi/archon 1.7.6 → 1.7.8
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/dist/client/assets/ActivityView-DwfDSF4q.js +1 -0
- package/dist/client/assets/GitHubActionsDialog-3dMCYCSZ.js +1 -0
- package/dist/client/assets/VerificationDialog-CoTfXMyb.js +4 -0
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-D0PXGvSL.js → architectureDiagram-3BPJPVTR-Bdnc97XK.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-BCLAxnp5.js → blockDiagram-GPEHLZMM-boxM3She.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-DVZ5yZWV.js → c4Diagram-AAUBKEIU-CJDdF7xd.js} +1 -1
- package/dist/client/assets/channel-BGqdAcIT.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-BGJYj1Lu.js → chunk-2J33WTMH-ClpcO-Om.js} +1 -1
- package/dist/client/assets/{chunk-3OPIFGDE-DI5-yK3m.js → chunk-3OPIFGDE-CcuL_65J.js} +1 -1
- package/dist/client/assets/{chunk-5ZQYHXKU-Dnr5DDsb.js → chunk-5ZQYHXKU-CUGYZXkH.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-8HdFwn-P.js → chunk-727SXJPM-CApeLDyK.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-C7x9t2uJ.js → chunk-AQP2D5EJ-8FdSvF2D.js} +1 -1
- package/dist/client/assets/{chunk-CSCIHK7Q-CrhwRPh5.js → chunk-CSCIHK7Q-CZoIOF3a.js} +1 -1
- package/dist/client/assets/{chunk-KSCS5N6A-CdD62r5U.js → chunk-KSCS5N6A-BigFQI5y.js} +1 -1
- package/dist/client/assets/{chunk-L5ZTLDWV-CqQVvwc_.js → chunk-L5ZTLDWV-DIi3SHaT.js} +1 -1
- package/dist/client/assets/{chunk-LZXEDZCA-DLHrqoJ4.js → chunk-LZXEDZCA-B-Z0BcQy.js} +2 -2
- package/dist/client/assets/{chunk-ND2GUHAM-B0yOdiPF.js → chunk-ND2GUHAM-EBeba0mg.js} +1 -1
- package/dist/client/assets/{chunk-NZK2D7GU-boM-MocY.js → chunk-NZK2D7GU-DJBI2y53.js} +1 -1
- package/dist/client/assets/{chunk-O5CBEL6O-B0jgJn0t.js → chunk-O5CBEL6O-BqxdXhMd.js} +1 -1
- package/dist/client/assets/{chunk-WU5MYG2G-C_S_v-m2.js → chunk-WU5MYG2G-Coe1_Cbb.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-D5VuKRcj.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-B9GNqpRr.js +1 -0
- package/dist/client/assets/{dagre-BM42HDAG-DKl6Y8ya.js → dagre-BM42HDAG-tuTmeA0Y.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-6Lyn9DBp.js → diagram-2AECGRRQ-ILGCpG2F.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-wdS1vEkN.js → diagram-5GNKFQAL-C8qBZsgK.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-CPih29jF.js → diagram-KO2AKTUF-BXlcm_Zo.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-Cr5Px74d.js → diagram-LMA3HP47-B8V5EVbD.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-i0PjSScd.js → diagram-OG6HWLK6-CWsdOqcP.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-Dt4yOC9J.js → erDiagram-TEJ5UH35-DW8pgly2.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-Bf6kvajC.js → flowDiagram-I6XJVG4X-Djn7fOVR.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-DgxePMk_.js → ganttDiagram-6RSMTGT7-DZ421MEy.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-BZq2cmFS.js → gitGraphDiagram-PVQCEYII-CCNzLPuP.js} +1 -1
- package/dist/client/assets/index-BRc02lbW.js +273 -0
- package/dist/client/assets/index-CZO9HL1Z.css +2 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-Chij69KN.js → infoDiagram-5YYISTIA-Bd1gYgMp.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-3qHCLFgD.js → ishikawaDiagram-YF4QCWOH-txyMoatJ.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-DvRW8tbA.js → journeyDiagram-JHISSGLW-DhT3bmX6.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-DjR2T5I1.js → kanban-definition-UN3LZRKU-BBHt2hfC.js} +1 -1
- package/dist/client/assets/{line-B9OzrQ1Y.js → line-KGRcT7Qz.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-DYcDor4d.js → mermaid-parser.core-CDzt4_zR.js} +1 -1
- package/dist/client/assets/{mermaid.core-DG8QYo2u.js → mermaid.core-DD_-k1aB.js} +3 -3
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-f4QPF6b3.js → mindmap-definition-RKZ34NQL-DoctVTDl.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-CMkl2jBu.js → pieDiagram-4H26LBE5-DFlLEi70.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-BXaUHzJi.js → quadrantDiagram-W4KKPZXB-3YHOQgl7.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-B-Inuj_6.js → requirementDiagram-4Y6WPE33-X4fZVL9M.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-BjsmZFYI.js → sankeyDiagram-5OEKKPKP-BUPnMXeG.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-B1PAm6sX.js → sequenceDiagram-3UESZ5HK-D6QU3M64.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-pLONNEN8.js → stateDiagram-AJRCARHV-c35bu4qZ.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Ymy1_EHr.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-BLMkSXo_.js → timeline-definition-PNZ67QCA-CX2y6e_b.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-CEwGRegG.js → vennDiagram-CIIHVFJN-BDaWltjF.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-_md2IBXC.js → wardleyDiagram-YWT4CUSO-BpBRL2sb.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-DVzwfXHc.js → xychartDiagram-2RQKCTM6-Bqw7X0UT.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/db.js +460 -2
- package/dist/server/db.js.map +1 -1
- package/dist/server/index.js +389 -47
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/activeAutomationChild.js +31 -0
- package/dist/server/lib/activeAutomationChild.js.map +1 -0
- package/dist/server/lib/automationCheckGate.js +266 -0
- package/dist/server/lib/automationCheckGate.js.map +1 -0
- package/dist/server/lib/automationProgression.js +20 -21
- package/dist/server/lib/automationProgression.js.map +1 -1
- package/dist/server/lib/automationReconciliation.js +16 -2
- package/dist/server/lib/automationReconciliation.js.map +1 -1
- package/dist/server/lib/automationResumeDecision.js +4 -1
- package/dist/server/lib/automationResumeDecision.js.map +1 -1
- package/dist/server/lib/automationStageDetails.js +173 -41
- package/dist/server/lib/automationStageDetails.js.map +1 -1
- package/dist/server/lib/chatMessages.js +37 -8
- package/dist/server/lib/chatMessages.js.map +1 -1
- package/dist/server/lib/chatOperations.js +119 -68
- package/dist/server/lib/chatOperations.js.map +1 -1
- package/dist/server/lib/checkDefinitions.js +278 -0
- package/dist/server/lib/checkDefinitions.js.map +1 -0
- package/dist/server/lib/checkGateEvaluations.js +294 -0
- package/dist/server/lib/checkGateEvaluations.js.map +1 -0
- package/dist/server/lib/checkRunner.js +674 -0
- package/dist/server/lib/checkRunner.js.map +1 -0
- package/dist/server/lib/checkRuns.js +736 -0
- package/dist/server/lib/checkRuns.js.map +1 -0
- package/dist/server/lib/checkWorkspaceIdentity.js +207 -0
- package/dist/server/lib/checkWorkspaceIdentity.js.map +1 -0
- package/dist/server/lib/executionReconciliation.js +79 -2
- package/dist/server/lib/executionReconciliation.js.map +1 -1
- package/dist/server/lib/executionRetry.js +14 -6
- package/dist/server/lib/executionRetry.js.map +1 -1
- package/dist/server/lib/executionRetryContext.js +3 -3
- package/dist/server/lib/executionRetryContext.js.map +1 -1
- package/dist/server/lib/executionRunRetention.js +41 -3
- package/dist/server/lib/executionRunRetention.js.map +1 -1
- package/dist/server/lib/executionRuns.js +135 -2
- package/dist/server/lib/executionRuns.js.map +1 -1
- package/dist/server/lib/executionTimeline.js +52 -1
- package/dist/server/lib/executionTimeline.js.map +1 -1
- package/dist/server/lib/mobileAccess.js +44 -6
- package/dist/server/lib/mobileAccess.js.map +1 -1
- package/dist/server/lib/planModeQuestions.js +12 -7
- package/dist/server/lib/planModeQuestions.js.map +1 -1
- package/dist/server/lib/planQuestionOperations.js +246 -0
- package/dist/server/lib/planQuestionOperations.js.map +1 -0
- package/dist/server/lib/planQuestions.js +139 -0
- package/dist/server/lib/planQuestions.js.map +1 -0
- package/dist/server/lib/projectAutoConfig.js +47 -65
- package/dist/server/lib/projectAutoConfig.js.map +1 -1
- package/dist/server/lib/projects.js +87 -7
- package/dist/server/lib/projects.js.map +1 -1
- package/dist/server/lib/realtime.js +24 -1
- package/dist/server/lib/realtime.js.map +1 -1
- package/dist/server/lib/requiredCheckPolicy.js +68 -0
- package/dist/server/lib/requiredCheckPolicy.js.map +1 -0
- package/dist/server/lib/shutdown.js +4 -4
- package/dist/server/lib/shutdown.js.map +1 -1
- package/dist/server/lib/specArtifact.js +36 -38
- package/dist/server/lib/specArtifact.js.map +1 -1
- package/dist/server/lib/specMessages.js +31 -18
- package/dist/server/lib/specMessages.js.map +1 -1
- package/dist/server/lib/specOperations.js +129 -78
- package/dist/server/lib/specOperations.js.map +1 -1
- package/dist/server/lib/testCommandRunner.js +3 -2
- package/dist/server/lib/testCommandRunner.js.map +1 -1
- package/dist/server/lib/testSessions.js +2 -6
- package/dist/server/lib/testSessions.js.map +1 -1
- package/dist/server/lib/ticketAutomationCommit.js +185 -22
- package/dist/server/lib/ticketAutomationCommit.js.map +1 -1
- package/dist/server/lib/ticketFollowUpOperations.js +19 -6
- package/dist/server/lib/ticketFollowUpOperations.js.map +1 -1
- package/dist/server/lib/ticketMessages.js +38 -9
- package/dist/server/lib/ticketMessages.js.map +1 -1
- package/dist/server/lib/ticketWorkflowOperations.js +10 -1
- package/dist/server/lib/ticketWorkflowOperations.js.map +1 -1
- package/dist/server/lib/tickets.js +21 -1
- package/dist/server/lib/tickets.js.map +1 -1
- package/dist/server/lib/types.js +2 -0
- package/dist/server/lib/types.js.map +1 -1
- package/dist/server/workers/chat.js +48 -2
- package/dist/server/workers/chat.js.map +1 -1
- package/dist/server/workers/followUp.js +50 -2
- package/dist/server/workers/followUp.js.map +1 -1
- package/dist/server/workers/plan.js +57 -4
- package/dist/server/workers/plan.js.map +1 -1
- package/dist/server/workers/planFollowUp.js +51 -2
- package/dist/server/workers/planFollowUp.js.map +1 -1
- package/dist/server/workers/review.js +2 -10
- package/dist/server/workers/review.js.map +1 -1
- package/dist/server/workers/specSession.js +55 -5
- package/dist/server/workers/specSession.js.map +1 -1
- package/package.json +1 -1
- package/dist/client/assets/ActivityView-BrZY8vYf.js +0 -1
- package/dist/client/assets/GitHubActionsDialog-CUfdVw97.js +0 -1
- package/dist/client/assets/TestsDialog-FXcnLOyf.js +0 -4
- package/dist/client/assets/channel-BCPCrThZ.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-BdJlc_1i.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Bidw5Zc1.js +0 -1
- package/dist/client/assets/index-BJGHMaMz.css +0 -2
- package/dist/client/assets/index-CjRMYO1U.js +0 -273
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-kWPDF3wr.js +0 -1
package/dist/server/index.js
CHANGED
|
@@ -8,11 +8,11 @@ import { createHash, randomUUID } from 'crypto';
|
|
|
8
8
|
import cors from 'cors';
|
|
9
9
|
import { adjectives, animals, uniqueNamesGenerator } from 'unique-names-generator';
|
|
10
10
|
import db from './db.js';
|
|
11
|
-
import { deleteChatSession, getChatSession, listChatSessions
|
|
11
|
+
import { deleteChatSession, getChatSession, listChatSessions } from './lib/chats.js';
|
|
12
12
|
import { deleteSpecSession, getCreatedTicketIdsForSnapshot, getSpecSession, getSpecSnapshot, listSpecSessions } from './lib/specs.js';
|
|
13
13
|
import { createChatAttachment, createSpecAttachment, getAttachmentById, deleteChatAttachment, deleteProjectAttachments, deleteSpecSessionAttachment, getChatAttachmentForSession, getChatAttachmentLinkedMessageId, getSpecAttachmentForSession, getSpecAttachmentLinkedMessageId, MAX_CHAT_ATTACHMENTS_PER_TURN, MAX_CHAT_ATTACHMENT_SIZE_BYTES, MAX_SPEC_ATTACHMENTS_PER_TURN, MAX_SPEC_ATTACHMENT_SIZE_BYTES, readChatAttachmentContent, readSpecAttachmentContent, validateImageAttachment, } from './lib/attachments.js';
|
|
14
|
-
import { createProjectChatSession,
|
|
15
|
-
import { createProjectSpecSession, deleteProjectSpecSession, stopProjectSpecSessionResponse, submitProjectSpecSessionMessage, updateProjectSpecSessionMode, } from './lib/specOperations.js';
|
|
14
|
+
import { createProjectChatSession, resolveChatTool, resolveProjectChatPlanQuestionSet, stopProjectChatSessionResponse, submitProjectChatSessionMessage, updateProjectChatSessionMode, } from './lib/chatOperations.js';
|
|
15
|
+
import { createProjectSpecSession, deleteProjectSpecSession, resolveProjectSpecPlanQuestionSet, stopProjectSpecSessionResponse, submitProjectSpecSessionMessage, updateProjectSpecSessionMode, } from './lib/specOperations.js';
|
|
16
16
|
import { buildProjectSpecReviewRevisionDraft, getProjectSpecReviewFindings, startProjectSpecReview, stopProjectSpecReview, } from './lib/specReviewOperations.js';
|
|
17
17
|
import { resolveChatSessionTarget } from './lib/chatTargets.js';
|
|
18
18
|
import { createChatMessage, getChatMessage, updateChatMessageContent } from './lib/chatMessages.js';
|
|
@@ -52,7 +52,7 @@ import { reconcileAutomationStage } from './lib/automationReconciliation.js';
|
|
|
52
52
|
import { pausedRunBelongsToProgression, resolveAutomationResumeDecision } from './lib/automationResumeDecision.js';
|
|
53
53
|
import { getAutomationStageDetails, } from './lib/automationStageDetails.js';
|
|
54
54
|
import { buildAutomationFixInstruction, clearAutomationPause, countBlockingFindings, depthAtLeast, getBundleAutomationState, isAutomationDepth, recordAutomationPause, resolveAutomationConfig, upsertBundleAutomationState, } from './lib/ticketAutomation.js';
|
|
55
|
-
import { runAutomationCommitChain } from './lib/ticketAutomationCommit.js';
|
|
55
|
+
import { resolveAutomationCheckGateResume, runAutomationCommitChain } from './lib/ticketAutomationCommit.js';
|
|
56
56
|
import { getDiscoveredSkills, normalizeSkillNames } from './lib/skills.js';
|
|
57
57
|
import { clearAllPreviewProxies } from './lib/previewProxy.js';
|
|
58
58
|
import { createExecutionRun, addExecutionRunLink, addExecutionRunLinks, getExecutionRun, hasActiveBuildExecutionRunForTicket, getGitHubActionsExecutionDetails, getTestExecutionDetails, GitHubActionsDispatchConflictError, listExecutionRunLinks, listExecutionRuns, listLifecycleEvents, listProjectLifecycleEvents, toCanonicalLifecycleEvent, transitionExecutionRun, withExecutionRunTransaction, } from './lib/executionRuns.js';
|
|
@@ -61,7 +61,7 @@ import { listExecutionTimeline } from './lib/executionTimeline.js';
|
|
|
61
61
|
import { loadExecutionArtifactSummariesForRuns, openExecutionArtifactForRetrieval, } from './lib/executionArtifacts.js';
|
|
62
62
|
import { cleanupAllExecutionEvidence, EXECUTION_EVIDENCE_CLEANUP_INTERVAL_MS, resolveExecutionEvidenceRetentionDays, } from './lib/executionRunRetention.js';
|
|
63
63
|
import { ExecutionArtifactCleanupError, reconcileExecutionArtifactRegistrationIntents, reconcileFinalizedExecutionArtifacts, reconcileProjectExecutionArtifacts, resolveArtifactCleanupIntervalMs, } from './lib/executionArtifactRetention.js';
|
|
64
|
-
import { shutdownRealtimeServer } from './lib/shutdown.js';
|
|
64
|
+
import { closeHttpServer, shutdownRealtimeServer } from './lib/shutdown.js';
|
|
65
65
|
import { resolveClientBuildPaths, shouldServeClientApp, STATIC_CLIENT_DIRECTORY_OPTIONS, STATIC_CLIENT_INDEX_OPTIONS, } from './lib/staticClient.js';
|
|
66
66
|
import { deleteIntegrationConnection, getGitHubConnectionConfig, getJiraConnectionConfig, listIntegrationConnections, upsertIntegrationConnection, } from './lib/integrations/index.js';
|
|
67
67
|
import { createGitHubPullRequest, createOrFindGitHubPullRequest, findOpenGitHubPullRequest, findOpenGitHubPullRequestByHeadBranch, getGitHubPullRequestComments, getGitHubPullRequestReviews, getGitHubPullRequest, GitHubApiError, hasGitHubRemoteBranch, resolveGitHubRepository, validateGitHubConnection, } from './lib/integrations/github.js';
|
|
@@ -75,11 +75,18 @@ import { PlanningReferenceError, resolveExternalPlanningDescription } from './li
|
|
|
75
75
|
import { refreshIfNeeded as refreshAssignedIssues, clearCachedAssignedIssues } from './lib/integrations/assignedIssuesCache.js';
|
|
76
76
|
import { buildCommitMessagePrompt, normalizeGeneratedCommitMessage } from './lib/commitMessage.js';
|
|
77
77
|
import { BUNDLE_PULL_REQUEST_SYNC_TTL_MS, getBundlePullRequestRecord, hasOpenBundlePullRequest, serializeBundlePullRequest, shouldSyncBundlePullRequest, syncTrackedBundlePullRequestIfNeeded, upsertBundlePullRequest, upsertBundlePullRequestFromGitHub, } from './lib/bundlePullRequests.js';
|
|
78
|
-
import { countInboundProjectLinks, getEffectiveProject, getProjectById, getProjectTicketStats, hasProjectRunIde, listInterruptedProjectDeletions, listProjects, releaseInterruptedProjectDeletionClaims, replaceProjectLinks, serializeProject, updateProjectActiveTarget, validateProjectPayload, } from './lib/projects.js';
|
|
78
|
+
import { countInboundProjectLinks, getEffectiveProject, getProjectCheckDefinitions, getProjectById, getProjectTestCommands, getProjectTicketStats, hasProjectRunIde, listInterruptedProjectDeletions, listProjects, releaseInterruptedProjectDeletionClaims, replaceProjectLinks, serializeProject, updateProjectActiveTarget, validateProjectPayload, } from './lib/projects.js';
|
|
79
79
|
import { getActiveRunStatus, getActiveRunStatusForProject, getPreviewStatusForProject, hasRunConfig, isRunning, openIde, openTicketIdeInWorkspace, resolveProjectRunContextKey, runSetupCommandsInWorkspace, runTicketInWorkspace, stopAllTickets, stopTicket } from './lib/run.js';
|
|
80
|
-
import { clearAllTestSessions, createTestSession, deleteTestSession, discoverTestsForSession, doesTestFileExistForSession,
|
|
80
|
+
import { clearAllTestSessions, createTestSession, deleteTestSession, discoverTestsForSession, doesTestFileExistForSession, getTestSession, isTestSelectionSupported, startTestSessionRun, stopTestSessionRun } from './lib/testSessions.js';
|
|
81
|
+
import { closeCheckRunnerAdmissions, getCheckBatchAttempts, openCheckRunnerAdmissions, rerunCheckBatchAndSchedule, shutdownCheckRunner, startCheckBatch, stopCheckBatch } from './lib/checkRunner.js';
|
|
82
|
+
import { findActiveCheckBatchForTarget, getCheckAttemptDetails, getCheckBatchDetails, hasActiveProjectCheckBatch, isCheckAttemptCurrent, resolveCheckBatchRerunEligibility } from './lib/checkRuns.js';
|
|
83
|
+
import { getCheckDefinitionFingerprint } from './lib/checkDefinitions.js';
|
|
84
|
+
import { getCheckGateEvaluation } from './lib/checkGateEvaluations.js';
|
|
85
|
+
import { captureCheckWorkspaceIdentity } from './lib/checkWorkspaceIdentity.js';
|
|
81
86
|
import { getBundleBuildBlockerResponse, shouldStartBundleBuildNow, transitionTicketState, } from './lib/ticketWorkflowOperations.js';
|
|
82
87
|
import { submitPlanTicketFollowUp, submitTicketFollowUp, } from './lib/ticketFollowUpOperations.js';
|
|
88
|
+
import { closePendingTicketPlanQuestionSet, listPendingTicketPlanQuestionSetMessageIds, resolveTicketPlanQuestionSet, restoreClosedTicketPlanQuestionSets, } from './lib/planQuestionOperations.js';
|
|
89
|
+
import { hasPendingAskQuestionSet } from './lib/planQuestions.js';
|
|
83
90
|
import { canUseBuildControls, formatActiveRunConflict, getTicketRunReadinessError, resumeBuildTicketRun, runProjectTarget, runTicketRun, stopBuildTicketRun, stopReviewTicketRun, stopProjectTargetRun, stopTicketRun, } from './lib/ticketRunOperations.js';
|
|
84
91
|
import { isTicketAgentSettingsPayload, updateTicketAgentSettings, } from './lib/ticketSettingsOperations.js';
|
|
85
92
|
import { rerunTicketReviewOperation, resolveBundleReviewOwner, transitionBundleToReview } from './lib/bundleReviewOperations.js';
|
|
@@ -290,15 +297,90 @@ const serializeGitHubActionsExecutionDetail = (details) => {
|
|
|
290
297
|
jobs_count: details.jobs_count,
|
|
291
298
|
};
|
|
292
299
|
};
|
|
293
|
-
const
|
|
294
|
-
const
|
|
300
|
+
const serializeCheckAttemptExecutionDetail = (projectId, runId) => {
|
|
301
|
+
const attempt = getCheckAttemptDetails(projectId, runId);
|
|
302
|
+
return {
|
|
303
|
+
kind: 'check_attempt',
|
|
304
|
+
available: true,
|
|
305
|
+
attempt,
|
|
306
|
+
batch: attempt ? getCheckBatchDetails(projectId, attempt.batch_run_id) : null,
|
|
307
|
+
};
|
|
308
|
+
};
|
|
309
|
+
const hydrateCheckAttemptCurrency = async (project, batch, attempts) => {
|
|
310
|
+
const terminalAttempts = attempts.filter(attempt => attempt.outcome !== null);
|
|
311
|
+
if (terminalAttempts.length === 0) {
|
|
312
|
+
return attempts.map(attempt => ({ ...attempt, currency: 'pending' }));
|
|
313
|
+
}
|
|
314
|
+
const workspace = resolveExplicitProjectTestWorkspace(project, batch.target_kind, batch.target_bundle_id);
|
|
315
|
+
if ('error' in workspace) {
|
|
316
|
+
return attempts.map(attempt => ({
|
|
317
|
+
...attempt,
|
|
318
|
+
currency: attempt.outcome === null ? 'pending' : 'unavailable',
|
|
319
|
+
}));
|
|
320
|
+
}
|
|
321
|
+
const currentIdentity = await captureCheckWorkspaceIdentity(workspace.cwd);
|
|
322
|
+
const currentProject = getProjectById(project.id);
|
|
323
|
+
const definitionsById = new Map(getProjectCheckDefinitions(currentProject).map(definition => [definition.id, definition]));
|
|
324
|
+
return attempts.map(attempt => {
|
|
325
|
+
if (attempt.outcome === null) {
|
|
326
|
+
return { ...attempt, currency: 'pending' };
|
|
327
|
+
}
|
|
328
|
+
const definition = definitionsById.get(attempt.definition_id);
|
|
329
|
+
if (!definition || getCheckDefinitionFingerprint(definition) !== attempt.definition_fingerprint) {
|
|
330
|
+
return { ...attempt, currency: 'stale' };
|
|
331
|
+
}
|
|
332
|
+
const currency = currentIdentity.state === 'unavailable'
|
|
333
|
+
? 'unavailable'
|
|
334
|
+
: isCheckAttemptCurrent(attempt, currentIdentity) ? 'current' : 'stale';
|
|
335
|
+
return { ...attempt, currency };
|
|
336
|
+
});
|
|
337
|
+
};
|
|
338
|
+
const getCheckBatchRerunEligibility = (project, batch, selection) => {
|
|
339
|
+
const workspace = resolveExplicitProjectTestWorkspace(project, batch.target_kind, batch.target_bundle_id);
|
|
340
|
+
return resolveCheckBatchRerunEligibility({
|
|
341
|
+
projectId: project.id,
|
|
342
|
+
batchRunId: batch.execution_run_id,
|
|
343
|
+
availableDefinitions: getProjectCheckDefinitions(project),
|
|
344
|
+
selection,
|
|
345
|
+
targetAvailable: !('error' in workspace && typeof workspace.error === 'string' && workspace.error.includes('no longer exists')),
|
|
346
|
+
workspaceAvailable: !('error' in workspace),
|
|
347
|
+
});
|
|
348
|
+
};
|
|
349
|
+
const buildExecutionRunDetailResponse = async (project, run) => {
|
|
350
|
+
const checkBatch = run.kind === 'check_batch' ? getCheckBatchDetails(project.id, run.id) : null;
|
|
351
|
+
const retryEligibility = checkBatch
|
|
352
|
+
? getCheckBatchRerunEligibility(project, checkBatch)
|
|
353
|
+
: getExecutionRetryEligibility(project.id, run.id);
|
|
354
|
+
const checkBatchAttempts = checkBatch
|
|
355
|
+
? await hydrateCheckAttemptCurrency(project, checkBatch, getCheckBatchAttempts(project.id, run.id))
|
|
356
|
+
: [];
|
|
357
|
+
const checkAttemptDetail = run.kind === 'check_attempt' ? serializeCheckAttemptExecutionDetail(project.id, run.id) : null;
|
|
358
|
+
const checkAttempt = checkAttemptDetail?.attempt && checkAttemptDetail.batch
|
|
359
|
+
? (await hydrateCheckAttemptCurrency(project, checkAttemptDetail.batch, [checkAttemptDetail.attempt]))[0]
|
|
360
|
+
: checkAttemptDetail?.attempt ?? null;
|
|
295
361
|
const details = run.kind === 'test'
|
|
296
362
|
? serializeTestExecutionDetail(getTestExecutionDetails(project.id, run.id), retryEligibility)
|
|
297
|
-
: run.kind === '
|
|
298
|
-
? {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
:
|
|
363
|
+
: run.kind === 'check_batch'
|
|
364
|
+
? {
|
|
365
|
+
kind: 'check_batch',
|
|
366
|
+
available: true,
|
|
367
|
+
batch: checkBatch,
|
|
368
|
+
attempts: checkBatchAttempts,
|
|
369
|
+
retry_eligibility: retryEligibility,
|
|
370
|
+
action_kind: retryEligibility.eligible ? 'rerun' : null,
|
|
371
|
+
}
|
|
372
|
+
: run.kind === 'check_attempt'
|
|
373
|
+
? {
|
|
374
|
+
...checkAttemptDetail,
|
|
375
|
+
attempt: checkAttempt,
|
|
376
|
+
retry_eligibility: retryEligibility,
|
|
377
|
+
action_kind: null,
|
|
378
|
+
}
|
|
379
|
+
: run.kind === 'github_actions'
|
|
380
|
+
? { ...serializeGitHubActionsExecutionDetail(getGitHubActionsExecutionDetails(project.id, run.id)), retry_eligibility: retryEligibility }
|
|
381
|
+
: ['automation_commit', 'automation_push', 'automation_pull_request'].includes(run.kind)
|
|
382
|
+
? { kind: run.kind, available: true, automation_stage: getAutomationStageDetails(project.id, run.id), retry_eligibility: retryEligibility }
|
|
383
|
+
: { kind: run.kind, available: true, retry_eligibility: retryEligibility };
|
|
302
384
|
const artifacts = loadExecutionArtifactSummariesForRuns(project.id, [run.id], MAX_ARTIFACTS_PER_RUN).get(run.id)
|
|
303
385
|
?? { artifacts: [], artifact_count: 0 };
|
|
304
386
|
return {
|
|
@@ -1752,6 +1834,10 @@ const buildBundleTickets = (projectId, bundleId, broadcaster) => {
|
|
|
1752
1834
|
}
|
|
1753
1835
|
}
|
|
1754
1836
|
const movedTickets = [];
|
|
1837
|
+
const pendingQuestionMessageIds = new Map(orderedPlanTickets.map(ticket => [
|
|
1838
|
+
ticket.id,
|
|
1839
|
+
listPendingTicketPlanQuestionSetMessageIds(ticket.id),
|
|
1840
|
+
]));
|
|
1755
1841
|
const ticketSnapshots = orderedPlanTickets.map(ticket => db.prepare(`
|
|
1756
1842
|
SELECT id, project_id, state, tool, agent_status, agent_log, streaming_response, branch,
|
|
1757
1843
|
build_completed_at, run_setup_status, auto_park_dismissed_at, planning_context_json, lane_order
|
|
@@ -1765,6 +1851,7 @@ const buildBundleTickets = (projectId, bundleId, broadcaster) => {
|
|
|
1765
1851
|
const acceptBundleBuild = () => {
|
|
1766
1852
|
let activeSequenceId = null;
|
|
1767
1853
|
for (const [index, planTicket] of orderedPlanTickets.entries()) {
|
|
1854
|
+
closePendingTicketPlanQuestionSet(planTicket.id);
|
|
1768
1855
|
const buildTransition = prepareTicketForBuild(planTicket.id, {
|
|
1769
1856
|
ignoredBlockerTicketIds: bundleTicketIds,
|
|
1770
1857
|
});
|
|
@@ -1797,6 +1884,7 @@ const buildBundleTickets = (projectId, bundleId, broadcaster) => {
|
|
|
1797
1884
|
planning_context_json = ?, lane_order = ?, updated_at = CURRENT_TIMESTAMP
|
|
1798
1885
|
WHERE id = ? AND project_id = ?
|
|
1799
1886
|
`).run(snapshot.state, snapshot.tool, snapshot.agent_status, snapshot.agent_log, snapshot.streaming_response, snapshot.branch, snapshot.build_completed_at, snapshot.run_setup_status, snapshot.auto_park_dismissed_at, snapshot.planning_context_json, snapshot.lane_order, snapshot.id, snapshot.project_id);
|
|
1887
|
+
restoreClosedTicketPlanQuestionSets(snapshot.id, pendingQuestionMessageIds.get(snapshot.id) ?? []);
|
|
1800
1888
|
}
|
|
1801
1889
|
},
|
|
1802
1890
|
}
|
|
@@ -2406,20 +2494,10 @@ app.get('/projects/:id/chat-sessions/:sessionId/agent-runs', (req, res) => {
|
|
|
2406
2494
|
return res.json({ runs: listAgentRunsForParent('chat', chatSession.id) });
|
|
2407
2495
|
});
|
|
2408
2496
|
app.patch('/projects/:id/chat-sessions/:sessionId', (req, res) => {
|
|
2409
|
-
const
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
const existing = getProjectChatSession(project.id, req.params.sessionId);
|
|
2414
|
-
if (!existing) {
|
|
2415
|
-
return res.status(404).json({ error: 'Not found' });
|
|
2416
|
-
}
|
|
2417
|
-
const updated = updateChatSessionMode(existing.id, resolveChatMode(req.body.mode));
|
|
2418
|
-
if (!updated) {
|
|
2419
|
-
return res.status(404).json({ error: 'Not found' });
|
|
2420
|
-
}
|
|
2421
|
-
io.emit('chat-session:updated', updated);
|
|
2422
|
-
return res.json(updated);
|
|
2497
|
+
const result = updateProjectChatSessionMode({ projectId: req.params.id, chatSessionId: req.params.sessionId, payload: req.body, broadcaster: io });
|
|
2498
|
+
return result.ok
|
|
2499
|
+
? res.status(result.status).json(result.value)
|
|
2500
|
+
: res.status(result.status).json({ error: result.error, ...(result.value ? { chat_session: result.value } : {}) });
|
|
2423
2501
|
});
|
|
2424
2502
|
app.post('/projects/:id/chat-sessions/:sessionId/messages', (req, res) => {
|
|
2425
2503
|
const result = submitProjectChatSessionMessage({
|
|
@@ -2429,10 +2507,22 @@ app.post('/projects/:id/chat-sessions/:sessionId/messages', (req, res) => {
|
|
|
2429
2507
|
broadcaster: io,
|
|
2430
2508
|
});
|
|
2431
2509
|
if (!result.ok) {
|
|
2432
|
-
return res.status(result.status).json({ error: result.error });
|
|
2510
|
+
return res.status(result.status).json({ error: result.error, ...(result.value ? { chat_session: result.value } : {}) });
|
|
2433
2511
|
}
|
|
2434
2512
|
return res.status(result.status).json(result.value);
|
|
2435
2513
|
});
|
|
2514
|
+
app.post('/projects/:id/chat-sessions/:sessionId/plan-questions/:messageId/resolve', (req, res) => {
|
|
2515
|
+
const result = resolveProjectChatPlanQuestionSet({
|
|
2516
|
+
projectId: req.params.id,
|
|
2517
|
+
chatSessionId: req.params.sessionId,
|
|
2518
|
+
messageId: req.params.messageId,
|
|
2519
|
+
payload: req.body,
|
|
2520
|
+
broadcaster: io,
|
|
2521
|
+
});
|
|
2522
|
+
return result.ok
|
|
2523
|
+
? res.status(result.status).json(result.value)
|
|
2524
|
+
: res.status(result.status).json({ error: result.error, ...(result.value ? { chat_session: result.value } : {}) });
|
|
2525
|
+
});
|
|
2436
2526
|
app.post('/projects/:id/chat-sessions/:sessionId/attachments', (req, res) => {
|
|
2437
2527
|
const project = getProjectById(req.params.id);
|
|
2438
2528
|
if (!project) {
|
|
@@ -2720,7 +2810,7 @@ app.patch('/projects/:id/spec-sessions/:sessionId', (req, res) => {
|
|
|
2720
2810
|
broadcaster: io,
|
|
2721
2811
|
});
|
|
2722
2812
|
if (!result.ok) {
|
|
2723
|
-
return res.status(result.status).json({ error: result.error });
|
|
2813
|
+
return res.status(result.status).json({ error: result.error, ...(result.value ? { spec_session: result.value } : {}) });
|
|
2724
2814
|
}
|
|
2725
2815
|
return res.status(result.status).json(result.value);
|
|
2726
2816
|
});
|
|
@@ -2732,10 +2822,22 @@ app.post('/projects/:id/spec-sessions/:sessionId/messages', (req, res) => {
|
|
|
2732
2822
|
broadcaster: io,
|
|
2733
2823
|
});
|
|
2734
2824
|
if (!result.ok) {
|
|
2735
|
-
return res.status(result.status).json({ error: result.error });
|
|
2825
|
+
return res.status(result.status).json({ error: result.error, ...(result.value ? { spec_session: result.value } : {}) });
|
|
2736
2826
|
}
|
|
2737
2827
|
return res.status(result.status).json(result.value);
|
|
2738
2828
|
});
|
|
2829
|
+
app.post('/projects/:id/spec-sessions/:sessionId/plan-questions/:messageId/resolve', (req, res) => {
|
|
2830
|
+
const result = resolveProjectSpecPlanQuestionSet({
|
|
2831
|
+
projectId: req.params.id,
|
|
2832
|
+
specSessionId: req.params.sessionId,
|
|
2833
|
+
messageId: req.params.messageId,
|
|
2834
|
+
payload: req.body,
|
|
2835
|
+
broadcaster: io,
|
|
2836
|
+
});
|
|
2837
|
+
return result.ok
|
|
2838
|
+
? res.status(result.status).json(result.value)
|
|
2839
|
+
: res.status(result.status).json({ error: result.error, ...(result.value ? { spec_session: result.value } : {}) });
|
|
2840
|
+
});
|
|
2739
2841
|
app.post('/projects/:id/spec-sessions/:sessionId/attachments', (req, res) => {
|
|
2740
2842
|
const project = getProjectById(req.params.id);
|
|
2741
2843
|
if (!project) {
|
|
@@ -3606,6 +3708,9 @@ app.delete('/bundles/:id', (req, res) => {
|
|
|
3606
3708
|
if (result.project.active_target_kind === 'bundle' && result.project.active_target_bundle_id === bundle.id) {
|
|
3607
3709
|
return res.status(409).json({ error: 'Cannot delete the currently selected target bundle' });
|
|
3608
3710
|
}
|
|
3711
|
+
if (findActiveCheckBatchForTarget(result.project.id, 'bundle', bundle.id)) {
|
|
3712
|
+
return res.status(409).json({ error: 'Cannot delete a bundle while checks are running' });
|
|
3713
|
+
}
|
|
3609
3714
|
const affectedChatSessionIds = listChatSessions(result.project.id)
|
|
3610
3715
|
.filter(chatSession => chatSession.target_bundle_id === bundle.id)
|
|
3611
3716
|
.map(chatSession => chatSession.id);
|
|
@@ -3954,7 +4059,7 @@ app.post('/bundles/:id/follow-up', (req, res) => {
|
|
|
3954
4059
|
return res.status(response.status).json(response.value);
|
|
3955
4060
|
});
|
|
3956
4061
|
const AUTOMATION_SIDE_EFFECT_KINDS = new Set(['automation_commit', 'automation_push', 'automation_pull_request']);
|
|
3957
|
-
const AUTOMATION_WORKER_PAUSE_KINDS = new Set(['build', 'review', 'build_follow_up', 'automation_review_fix']);
|
|
4062
|
+
const AUTOMATION_WORKER_PAUSE_KINDS = new Set(['build', 'review', 'build_follow_up', 'automation_review_fix', 'check_batch']);
|
|
3958
4063
|
app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
3959
4064
|
const result = getRequiredProject(req);
|
|
3960
4065
|
if ('error' in result) {
|
|
@@ -4250,7 +4355,7 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
4250
4355
|
return res.status(followUpResult.status).json({ error: followUpResult.error });
|
|
4251
4356
|
}
|
|
4252
4357
|
}
|
|
4253
|
-
else if (decision.outcome === 'continue_commit_chain') {
|
|
4358
|
+
else if (decision.outcome === 'continue_commit_chain' || decision.outcome === 'rerun_required_checks') {
|
|
4254
4359
|
try {
|
|
4255
4360
|
if (!revalidateDecision()) {
|
|
4256
4361
|
return res.status(409).json({ error: 'Bundle automation state changed before Resume could start' });
|
|
@@ -4261,7 +4366,10 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
4261
4366
|
progressionRunId,
|
|
4262
4367
|
broadcaster: io,
|
|
4263
4368
|
});
|
|
4264
|
-
|
|
4369
|
+
const checkGateResume = decision.outcome === 'rerun_required_checks'
|
|
4370
|
+
? resolveAutomationCheckGateResume(result.project.id, progressionRunId)
|
|
4371
|
+
: undefined;
|
|
4372
|
+
await runAutomationCommitChain(owner, project, owner.bundle ?? null, automationConfig, io, progressionRunId, checkGateResume);
|
|
4265
4373
|
}
|
|
4266
4374
|
catch (error) {
|
|
4267
4375
|
return res.status(409).json({ error: error instanceof Error ? error.message : String(error) });
|
|
@@ -5473,7 +5581,7 @@ app.post('/projects', (req, res) => {
|
|
|
5473
5581
|
return res.status(400).json({ error: validation.error });
|
|
5474
5582
|
}
|
|
5475
5583
|
const projectCount = Number(db.prepare('SELECT COUNT(*) AS count FROM projects').get()?.count ?? 0);
|
|
5476
|
-
const { name, repo_path, linked_project_ids, run_setup, run_services, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, commit_message_rules, auto_park_stale_tickets, memory_enabled, scan_include_globs_json, scan_exclude_globs_json, worktree_sync, spec_aware_code_review_enabled, automation_depth, automation_review_fix_enabled, automation_pause_on_open_questions, automation_review_severity, automation_review_max_iterations, } = validation.values;
|
|
5584
|
+
const { name, repo_path, linked_project_ids, run_setup, run_services, check_definitions, required_check_definition_ids, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, commit_message_rules, auto_park_stale_tickets, memory_enabled, scan_include_globs_json, scan_exclude_globs_json, worktree_sync, spec_aware_code_review_enabled, automation_depth, automation_review_fix_enabled, automation_pause_on_open_questions, automation_review_severity, automation_review_max_iterations, } = validation.values;
|
|
5477
5585
|
if (!repo_path) {
|
|
5478
5586
|
return res.status(400).json({ error: 'repo_path must be a non-empty string' });
|
|
5479
5587
|
}
|
|
@@ -5493,6 +5601,8 @@ app.post('/projects', (req, res) => {
|
|
|
5493
5601
|
repo_path,
|
|
5494
5602
|
run_setup,
|
|
5495
5603
|
run_services,
|
|
5604
|
+
check_definitions,
|
|
5605
|
+
required_check_definition_ids,
|
|
5496
5606
|
test_commands,
|
|
5497
5607
|
run_ide,
|
|
5498
5608
|
preview_service_name,
|
|
@@ -5513,8 +5623,8 @@ app.post('/projects', (req, res) => {
|
|
|
5513
5623
|
automation_review_severity,
|
|
5514
5624
|
automation_review_max_iterations
|
|
5515
5625
|
)
|
|
5516
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5517
|
-
`).run(id, name, normalizedRepoPath, run_setup, run_services, test_commands, run_ide ?? null, preview_service_name ?? null, preview_path ?? null, preview_capability_mode ?? null, helper_model ?? null, helper_variant ?? null, commit_message_rules ?? null, auto_park_stale_tickets ?? 0, memory_enabled ?? 0, scan_include_globs_json ?? null, scan_exclude_globs_json ?? null, worktree_sync ?? null, spec_aware_code_review_enabled ?? 0, automation_depth ?? 'manual', automation_review_fix_enabled ?? 0, automation_pause_on_open_questions ?? 0, automation_review_severity ?? 'medium', automation_review_max_iterations ?? 3);
|
|
5626
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5627
|
+
`).run(id, name, normalizedRepoPath, run_setup, run_services, check_definitions, required_check_definition_ids ?? '[]', test_commands, run_ide ?? null, preview_service_name ?? null, preview_path ?? null, preview_capability_mode ?? null, helper_model ?? null, helper_variant ?? null, commit_message_rules ?? null, auto_park_stale_tickets ?? 0, memory_enabled ?? 0, scan_include_globs_json ?? null, scan_exclude_globs_json ?? null, worktree_sync ?? null, spec_aware_code_review_enabled ?? 0, automation_depth ?? 'manual', automation_review_fix_enabled ?? 0, automation_pause_on_open_questions ?? 0, automation_review_severity ?? 'medium', automation_review_max_iterations ?? 3);
|
|
5518
5628
|
replaceProjectLinks(id, linked_project_ids ?? []);
|
|
5519
5629
|
ensureProjectRootBundle(id);
|
|
5520
5630
|
if (projectCount === 0) {
|
|
@@ -6680,13 +6790,15 @@ app.patch('/projects/:id', (req, res) => {
|
|
|
6680
6790
|
});
|
|
6681
6791
|
}
|
|
6682
6792
|
}
|
|
6683
|
-
const { name, repo_path, linked_project_ids, run_setup, run_services, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, commit_message_rules, auto_park_stale_tickets, memory_enabled, scan_include_globs_json, scan_exclude_globs_json, worktree_sync, spec_aware_code_review_enabled, automation_depth, automation_review_fix_enabled, automation_pause_on_open_questions, automation_review_severity, automation_review_max_iterations, } = validation.values;
|
|
6793
|
+
const { name, repo_path, linked_project_ids, run_setup, run_services, check_definitions, required_check_definition_ids, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, commit_message_rules, auto_park_stale_tickets, memory_enabled, scan_include_globs_json, scan_exclude_globs_json, worktree_sync, spec_aware_code_review_enabled, automation_depth, automation_review_fix_enabled, automation_pause_on_open_questions, automation_review_severity, automation_review_max_iterations, } = validation.values;
|
|
6684
6794
|
db.prepare(`
|
|
6685
6795
|
UPDATE projects SET
|
|
6686
6796
|
name = ?,
|
|
6687
6797
|
repo_path = ?,
|
|
6688
6798
|
run_setup = ?,
|
|
6689
6799
|
run_services = ?,
|
|
6800
|
+
check_definitions = ?,
|
|
6801
|
+
required_check_definition_ids = ?,
|
|
6690
6802
|
test_commands = ?,
|
|
6691
6803
|
run_ide = ?,
|
|
6692
6804
|
preview_service_name = ?,
|
|
@@ -6708,7 +6820,7 @@ app.patch('/projects/:id', (req, res) => {
|
|
|
6708
6820
|
automation_review_max_iterations = ?,
|
|
6709
6821
|
updated_at = CURRENT_TIMESTAMP
|
|
6710
6822
|
WHERE id = ?
|
|
6711
|
-
`).run(name ?? project.name, repo_path ?? project.repo_path, hasOwn(validation.values, 'run_setup') ? run_setup : project.run_setup, hasOwn(validation.values, 'run_services') ? run_services : project.run_services, hasOwn(validation.values, 'test_commands') ? test_commands : project.test_commands, hasOwn(validation.values, 'run_ide') ? run_ide : project.run_ide, hasOwn(validation.values, 'preview_service_name') ? preview_service_name : project.preview_service_name, hasOwn(validation.values, 'preview_path') ? preview_path : project.preview_path, hasOwn(validation.values, 'preview_capability_mode') ? preview_capability_mode : project.preview_capability_mode, hasOwn(validation.values, 'helper_model') ? helper_model : project.helper_model, hasOwn(validation.values, 'helper_variant') ? helper_variant : project.helper_variant, hasOwn(validation.values, 'commit_message_rules') ? commit_message_rules : project.commit_message_rules, hasOwn(validation.values, 'auto_park_stale_tickets') ? auto_park_stale_tickets : project.auto_park_stale_tickets, hasOwn(validation.values, 'memory_enabled') ? memory_enabled : project.memory_enabled, hasOwn(validation.values, 'scan_include_globs_json') ? scan_include_globs_json : project.scan_include_globs_json, hasOwn(validation.values, 'scan_exclude_globs_json') ? scan_exclude_globs_json : project.scan_exclude_globs_json, hasOwn(validation.values, 'worktree_sync') ? worktree_sync : project.worktree_sync, hasOwn(validation.values, 'spec_aware_code_review_enabled') ? spec_aware_code_review_enabled : project.spec_aware_code_review_enabled, hasOwn(validation.values, 'automation_depth') ? automation_depth : project.automation_depth, hasOwn(validation.values, 'automation_review_fix_enabled') ? automation_review_fix_enabled : project.automation_review_fix_enabled, hasOwn(validation.values, 'automation_pause_on_open_questions') ? automation_pause_on_open_questions : project.automation_pause_on_open_questions, hasOwn(validation.values, 'automation_review_severity') ? automation_review_severity : project.automation_review_severity, hasOwn(validation.values, 'automation_review_max_iterations') ? automation_review_max_iterations : project.automation_review_max_iterations, req.params.id);
|
|
6823
|
+
`).run(name ?? project.name, repo_path ?? project.repo_path, hasOwn(validation.values, 'run_setup') ? run_setup : project.run_setup, hasOwn(validation.values, 'run_services') ? run_services : project.run_services, hasOwn(validation.values, 'check_definitions') ? check_definitions : project.check_definitions, hasOwn(validation.values, 'required_check_definition_ids') ? required_check_definition_ids : project.required_check_definition_ids, hasOwn(validation.values, 'test_commands') ? test_commands : project.test_commands, hasOwn(validation.values, 'run_ide') ? run_ide : project.run_ide, hasOwn(validation.values, 'preview_service_name') ? preview_service_name : project.preview_service_name, hasOwn(validation.values, 'preview_path') ? preview_path : project.preview_path, hasOwn(validation.values, 'preview_capability_mode') ? preview_capability_mode : project.preview_capability_mode, hasOwn(validation.values, 'helper_model') ? helper_model : project.helper_model, hasOwn(validation.values, 'helper_variant') ? helper_variant : project.helper_variant, hasOwn(validation.values, 'commit_message_rules') ? commit_message_rules : project.commit_message_rules, hasOwn(validation.values, 'auto_park_stale_tickets') ? auto_park_stale_tickets : project.auto_park_stale_tickets, hasOwn(validation.values, 'memory_enabled') ? memory_enabled : project.memory_enabled, hasOwn(validation.values, 'scan_include_globs_json') ? scan_include_globs_json : project.scan_include_globs_json, hasOwn(validation.values, 'scan_exclude_globs_json') ? scan_exclude_globs_json : project.scan_exclude_globs_json, hasOwn(validation.values, 'worktree_sync') ? worktree_sync : project.worktree_sync, hasOwn(validation.values, 'spec_aware_code_review_enabled') ? spec_aware_code_review_enabled : project.spec_aware_code_review_enabled, hasOwn(validation.values, 'automation_depth') ? automation_depth : project.automation_depth, hasOwn(validation.values, 'automation_review_fix_enabled') ? automation_review_fix_enabled : project.automation_review_fix_enabled, hasOwn(validation.values, 'automation_pause_on_open_questions') ? automation_pause_on_open_questions : project.automation_pause_on_open_questions, hasOwn(validation.values, 'automation_review_severity') ? automation_review_severity : project.automation_review_severity, hasOwn(validation.values, 'automation_review_max_iterations') ? automation_review_max_iterations : project.automation_review_max_iterations, req.params.id);
|
|
6712
6824
|
if (hasOwn(validation.values, 'linked_project_ids')) {
|
|
6713
6825
|
replaceProjectLinks(project.id, linked_project_ids ?? []);
|
|
6714
6826
|
}
|
|
@@ -6737,6 +6849,9 @@ const assertProjectDeletionEligible = (projectId, claimId) => {
|
|
|
6737
6849
|
if (stats.ticketCount > 0 || stats.worktreeCount > 0 || stats.bundleCount > 0) {
|
|
6738
6850
|
throw new ProjectDeletionError(409, 'Cannot delete a project with existing tickets, bundles, or worktrees');
|
|
6739
6851
|
}
|
|
6852
|
+
if (hasActiveProjectCheckBatch(projectId)) {
|
|
6853
|
+
throw new ProjectDeletionError(409, 'Cannot delete a project while checks are active');
|
|
6854
|
+
}
|
|
6740
6855
|
};
|
|
6741
6856
|
const completeClaimedProjectDeletion = async (projectId, claimId) => {
|
|
6742
6857
|
await runExclusiveExecutionArtifactMaintenance(() => reconcileProjectExecutionArtifacts(projectId, {
|
|
@@ -6763,6 +6878,19 @@ const completeClaimedProjectDeletion = async (projectId, claimId) => {
|
|
|
6763
6878
|
db.prepare("DELETE FROM execution_artifacts WHERE project_id = ? AND availability = 'deleted'").run(projectId);
|
|
6764
6879
|
deleteProjectAttachments(projectId);
|
|
6765
6880
|
db.prepare(`
|
|
6881
|
+
DELETE FROM check_gate_evaluation_attempts
|
|
6882
|
+
WHERE evaluation_id IN (SELECT id FROM check_gate_evaluations WHERE project_id = ?)
|
|
6883
|
+
`).run(projectId);
|
|
6884
|
+
db.prepare('DELETE FROM check_gate_evaluations WHERE project_id = ?').run(projectId);
|
|
6885
|
+
db.prepare(`
|
|
6886
|
+
DELETE FROM check_attempt_details
|
|
6887
|
+
WHERE execution_run_id IN (SELECT id FROM execution_runs WHERE project_id = ?)
|
|
6888
|
+
`).run(projectId);
|
|
6889
|
+
db.prepare(`
|
|
6890
|
+
DELETE FROM check_batch_details
|
|
6891
|
+
WHERE execution_run_id IN (SELECT id FROM execution_runs WHERE project_id = ?)
|
|
6892
|
+
`).run(projectId);
|
|
6893
|
+
db.prepare(`
|
|
6766
6894
|
UPDATE execution_runs
|
|
6767
6895
|
SET parent_run_id = NULL, retry_of_run_id = NULL
|
|
6768
6896
|
WHERE project_id = ?
|
|
@@ -7649,6 +7777,186 @@ app.post('/projects/:id/test-sessions', (req, res) => {
|
|
|
7649
7777
|
targetLabel: workspace.target_label,
|
|
7650
7778
|
}));
|
|
7651
7779
|
});
|
|
7780
|
+
app.post('/projects/:id/check-batches', async (req, res) => {
|
|
7781
|
+
const project = getProjectById(req.params.id);
|
|
7782
|
+
if (!project) {
|
|
7783
|
+
return res.status(404).json({ error: 'Not found' });
|
|
7784
|
+
}
|
|
7785
|
+
const body = req.body && typeof req.body === 'object' && !Array.isArray(req.body) ? req.body : null;
|
|
7786
|
+
const targetKind = body?.target_kind === 'bundle'
|
|
7787
|
+
? 'bundle'
|
|
7788
|
+
: body?.target_kind === 'repo_root'
|
|
7789
|
+
? 'repo_root'
|
|
7790
|
+
: null;
|
|
7791
|
+
if (!targetKind) {
|
|
7792
|
+
return res.status(400).json({ error: 'target_kind must be repo_root or bundle' });
|
|
7793
|
+
}
|
|
7794
|
+
const definitionIds = parseCheckDefinitionIds(body?.definition_ids);
|
|
7795
|
+
if (!definitionIds) {
|
|
7796
|
+
return res.status(400).json({ error: 'definition_ids must contain at least one unique check definition ID' });
|
|
7797
|
+
}
|
|
7798
|
+
const definitions = getProjectCheckDefinitions(project);
|
|
7799
|
+
const definitionsById = new Map(definitions.map(definition => [definition.id, definition]));
|
|
7800
|
+
const selectedDefinitions = definitionIds.map(id => definitionsById.get(id)).filter((definition) => Boolean(definition));
|
|
7801
|
+
if (selectedDefinitions.length !== definitionIds.length) {
|
|
7802
|
+
return res.status(409).json({ error: 'One or more selected check definitions are stale or unavailable' });
|
|
7803
|
+
}
|
|
7804
|
+
const targetBundleId = typeof body?.target_bundle_id === 'string' ? body.target_bundle_id.trim() || null : null;
|
|
7805
|
+
const workspace = resolveExplicitProjectTestWorkspace(project, targetKind, targetBundleId);
|
|
7806
|
+
if ('error' in workspace) {
|
|
7807
|
+
return res.status(Number(workspace.status)).json({ error: workspace.error });
|
|
7808
|
+
}
|
|
7809
|
+
try {
|
|
7810
|
+
const batch = await startCheckBatch({
|
|
7811
|
+
projectId: project.id,
|
|
7812
|
+
cwd: workspace.cwd,
|
|
7813
|
+
targetKind: workspace.target_kind,
|
|
7814
|
+
targetBundleId: workspace.target_bundle_id,
|
|
7815
|
+
targetLabel: workspace.target_label,
|
|
7816
|
+
selectedDefinitions,
|
|
7817
|
+
availableDefinitions: definitions,
|
|
7818
|
+
sourceReference: { kind: 'project', id: project.id },
|
|
7819
|
+
}, io);
|
|
7820
|
+
return res.status(201).json({
|
|
7821
|
+
batch,
|
|
7822
|
+
attempts: getCheckBatchAttempts(project.id, batch.execution_run_id),
|
|
7823
|
+
});
|
|
7824
|
+
}
|
|
7825
|
+
catch (error) {
|
|
7826
|
+
return res.status(409).json({ error: getErrorMessage(error) });
|
|
7827
|
+
}
|
|
7828
|
+
});
|
|
7829
|
+
const parseCheckDefinitionIds = (value) => {
|
|
7830
|
+
if (!Array.isArray(value) || value.length === 0 || value.some(id => typeof id !== 'string' || id.trim() === '')) {
|
|
7831
|
+
return null;
|
|
7832
|
+
}
|
|
7833
|
+
const ids = value.map(id => id.trim());
|
|
7834
|
+
return ids.length === new Set(ids).size ? ids : null;
|
|
7835
|
+
};
|
|
7836
|
+
const parseCheckRerunSelection = (body) => {
|
|
7837
|
+
const payload = body && typeof body === 'object' && !Array.isArray(body) ? body : {};
|
|
7838
|
+
if (payload.mode === undefined || payload.mode === 'all') {
|
|
7839
|
+
return { mode: 'all' };
|
|
7840
|
+
}
|
|
7841
|
+
if (payload.mode === 'failed_only') {
|
|
7842
|
+
return { mode: 'failed' };
|
|
7843
|
+
}
|
|
7844
|
+
if (payload.mode !== 'selected') {
|
|
7845
|
+
return { error: 'mode must be all, failed_only, or selected' };
|
|
7846
|
+
}
|
|
7847
|
+
const definitionIds = parseCheckDefinitionIds(payload.definition_ids);
|
|
7848
|
+
if (!definitionIds) {
|
|
7849
|
+
return { error: 'definition_ids must contain at least one unique source definition ID for selected reruns' };
|
|
7850
|
+
}
|
|
7851
|
+
return { mode: 'selected', definitionIds };
|
|
7852
|
+
};
|
|
7853
|
+
const rerunProjectCheckBatch = async (project, sourceBatchRunId, body) => {
|
|
7854
|
+
const sourceBatch = getCheckBatchDetails(project.id, sourceBatchRunId);
|
|
7855
|
+
if (!sourceBatch) {
|
|
7856
|
+
return { ok: false, status: 404, error: 'Check batch not found', reason: 'status_not_retryable' };
|
|
7857
|
+
}
|
|
7858
|
+
const selection = parseCheckRerunSelection(body);
|
|
7859
|
+
if ('error' in selection) {
|
|
7860
|
+
return { ok: false, status: 400, error: selection.error, reason: 'retry_input_unavailable' };
|
|
7861
|
+
}
|
|
7862
|
+
const eligibility = getCheckBatchRerunEligibility(project, sourceBatch, selection);
|
|
7863
|
+
if (!eligibility.eligible) {
|
|
7864
|
+
return {
|
|
7865
|
+
ok: false,
|
|
7866
|
+
status: 409,
|
|
7867
|
+
error: 'Check batch cannot be rerun with its current target and definitions',
|
|
7868
|
+
reason: eligibility.reason,
|
|
7869
|
+
};
|
|
7870
|
+
}
|
|
7871
|
+
const workspace = resolveExplicitProjectTestWorkspace(project, sourceBatch.target_kind, sourceBatch.target_bundle_id);
|
|
7872
|
+
if ('error' in workspace) {
|
|
7873
|
+
return { ok: false, status: Number(workspace.status), error: workspace.error, reason: 'retry_workspace_unavailable' };
|
|
7874
|
+
}
|
|
7875
|
+
try {
|
|
7876
|
+
const value = await rerunCheckBatchAndSchedule({
|
|
7877
|
+
projectId: project.id,
|
|
7878
|
+
sourceBatchRunId,
|
|
7879
|
+
cwd: workspace.cwd,
|
|
7880
|
+
availableDefinitions: getProjectCheckDefinitions(project),
|
|
7881
|
+
selection,
|
|
7882
|
+
}, io);
|
|
7883
|
+
return { ok: true, value };
|
|
7884
|
+
}
|
|
7885
|
+
catch (error) {
|
|
7886
|
+
return { ok: false, status: 409, error: getErrorMessage(error), reason: 'retry_input_unavailable' };
|
|
7887
|
+
}
|
|
7888
|
+
};
|
|
7889
|
+
app.get('/projects/:id/check-batches', (req, res) => {
|
|
7890
|
+
const project = getProjectById(req.params.id);
|
|
7891
|
+
if (!project) {
|
|
7892
|
+
return res.status(404).json({ error: 'Not found' });
|
|
7893
|
+
}
|
|
7894
|
+
const query = parseLifecycleListQuery(req.query);
|
|
7895
|
+
if ('error' in query) {
|
|
7896
|
+
return res.status(400).json({ error: query.error });
|
|
7897
|
+
}
|
|
7898
|
+
try {
|
|
7899
|
+
return res.json(listExecutionTimeline({
|
|
7900
|
+
project,
|
|
7901
|
+
kind: 'check_batch',
|
|
7902
|
+
statuses: query.statuses,
|
|
7903
|
+
cursor: query.cursor,
|
|
7904
|
+
limit: query.limit,
|
|
7905
|
+
}));
|
|
7906
|
+
}
|
|
7907
|
+
catch (error) {
|
|
7908
|
+
if (error instanceof Error && error.message === 'Invalid execution run cursor') {
|
|
7909
|
+
return res.status(400).json({ error: 'cursor must be a valid execution run cursor' });
|
|
7910
|
+
}
|
|
7911
|
+
throw error;
|
|
7912
|
+
}
|
|
7913
|
+
});
|
|
7914
|
+
app.get('/projects/:id/check-batches/:batchRunId', async (req, res) => {
|
|
7915
|
+
const project = getProjectById(req.params.id);
|
|
7916
|
+
if (!project) {
|
|
7917
|
+
return res.status(404).json({ error: 'Not found' });
|
|
7918
|
+
}
|
|
7919
|
+
const batch = getCheckBatchDetails(req.params.id, req.params.batchRunId);
|
|
7920
|
+
if (!batch) {
|
|
7921
|
+
return res.status(404).json({ error: 'Check batch not found' });
|
|
7922
|
+
}
|
|
7923
|
+
return res.json({
|
|
7924
|
+
batch,
|
|
7925
|
+
attempts: await hydrateCheckAttemptCurrency(project, batch, getCheckBatchAttempts(req.params.id, req.params.batchRunId)),
|
|
7926
|
+
});
|
|
7927
|
+
});
|
|
7928
|
+
app.get('/projects/:id/check-gate-evaluations/:evaluationId', (req, res) => {
|
|
7929
|
+
const evaluation = getCheckGateEvaluation(req.params.id, req.params.evaluationId);
|
|
7930
|
+
if (!evaluation) {
|
|
7931
|
+
return res.status(404).json({ error: 'Check gate evaluation not found' });
|
|
7932
|
+
}
|
|
7933
|
+
return res.json(evaluation);
|
|
7934
|
+
});
|
|
7935
|
+
app.post('/projects/:id/check-batches/:batchRunId/stop', async (req, res) => {
|
|
7936
|
+
if (!getCheckBatchDetails(req.params.id, req.params.batchRunId)) {
|
|
7937
|
+
return res.status(404).json({ error: 'Check batch not found' });
|
|
7938
|
+
}
|
|
7939
|
+
try {
|
|
7940
|
+
const batch = await stopCheckBatch(req.params.id, req.params.batchRunId, io);
|
|
7941
|
+
return res.json({
|
|
7942
|
+
batch,
|
|
7943
|
+
attempts: getCheckBatchAttempts(req.params.id, req.params.batchRunId),
|
|
7944
|
+
});
|
|
7945
|
+
}
|
|
7946
|
+
catch (error) {
|
|
7947
|
+
return res.status(409).json({ error: getErrorMessage(error) });
|
|
7948
|
+
}
|
|
7949
|
+
});
|
|
7950
|
+
app.post('/projects/:id/check-batches/:batchRunId/rerun', async (req, res) => {
|
|
7951
|
+
const project = getProjectById(req.params.id);
|
|
7952
|
+
if (!project) {
|
|
7953
|
+
return res.status(404).json({ error: 'Not found' });
|
|
7954
|
+
}
|
|
7955
|
+
const result = await rerunProjectCheckBatch(project, req.params.batchRunId, req.body);
|
|
7956
|
+
return result.ok
|
|
7957
|
+
? res.status(201).json(result.value)
|
|
7958
|
+
: res.status(result.status).json({ error: result.error });
|
|
7959
|
+
});
|
|
7652
7960
|
app.get('/projects/:id/execution-runs', (req, res) => {
|
|
7653
7961
|
const project = getProjectById(req.params.id);
|
|
7654
7962
|
if (!project) {
|
|
@@ -7733,7 +8041,7 @@ app.get('/projects/:id/execution-timeline', (req, res) => {
|
|
|
7733
8041
|
throw error;
|
|
7734
8042
|
}
|
|
7735
8043
|
});
|
|
7736
|
-
app.get('/projects/:id/execution-timeline/:runId', (req, res) => {
|
|
8044
|
+
app.get('/projects/:id/execution-timeline/:runId', async (req, res) => {
|
|
7737
8045
|
const project = getProjectById(req.params.id);
|
|
7738
8046
|
if (!project) {
|
|
7739
8047
|
return res.status(404).json({ error: 'Not found' });
|
|
@@ -7750,9 +8058,9 @@ app.get('/projects/:id/execution-timeline/:runId', (req, res) => {
|
|
|
7750
8058
|
if (scope.scopeEntity && !links.some(link => link.entity_type === scope.scopeEntity?.type && link.entity_id === scope.scopeEntity.id)) {
|
|
7751
8059
|
return res.status(404).json({ error: 'Execution run not found' });
|
|
7752
8060
|
}
|
|
7753
|
-
return res.json(buildExecutionRunDetailResponse(project, run));
|
|
8061
|
+
return res.json(await buildExecutionRunDetailResponse(project, run));
|
|
7754
8062
|
});
|
|
7755
|
-
app.get('/projects/:id/execution-runs/:runId', (req, res) => {
|
|
8063
|
+
app.get('/projects/:id/execution-runs/:runId', async (req, res) => {
|
|
7756
8064
|
const project = getProjectById(req.params.id);
|
|
7757
8065
|
if (!project) {
|
|
7758
8066
|
return res.status(404).json({ error: 'Not found' });
|
|
@@ -7761,7 +8069,7 @@ app.get('/projects/:id/execution-runs/:runId', (req, res) => {
|
|
|
7761
8069
|
if (!run) {
|
|
7762
8070
|
return res.status(404).json({ error: 'Execution run not found' });
|
|
7763
8071
|
}
|
|
7764
|
-
return res.json(buildExecutionRunDetailResponse(project, run));
|
|
8072
|
+
return res.json(await buildExecutionRunDetailResponse(project, run));
|
|
7765
8073
|
});
|
|
7766
8074
|
app.get('/projects/:id/execution-runs/:runId/artifacts/:artifactId', async (req, res) => {
|
|
7767
8075
|
const project = getProjectById(req.params.id);
|
|
@@ -7805,6 +8113,13 @@ app.post('/projects/:id/execution-runs/:runId/retry', async (req, res) => {
|
|
|
7805
8113
|
if (!project) {
|
|
7806
8114
|
return res.status(404).json({ error: 'Not found' });
|
|
7807
8115
|
}
|
|
8116
|
+
const source = getExecutionRun(project.id, req.params.runId);
|
|
8117
|
+
if (source?.kind === 'check_batch') {
|
|
8118
|
+
const result = await rerunProjectCheckBatch(project, source.id, req.body);
|
|
8119
|
+
return result.ok
|
|
8120
|
+
? res.status(201).json(result.value)
|
|
8121
|
+
: res.status(result.status).json({ error: result.error, reason: result.reason });
|
|
8122
|
+
}
|
|
7808
8123
|
const resolution = resolveExecutionRetry(project.id, req.params.runId);
|
|
7809
8124
|
const result = await dispatchExecutionRetry(resolution, io, async (retry) => {
|
|
7810
8125
|
const session = createTestSession({
|
|
@@ -7870,7 +8185,7 @@ app.get('/test-sessions/:id/discovery', (req, res) => {
|
|
|
7870
8185
|
if (!project) {
|
|
7871
8186
|
return res.status(404).json({ error: 'Project not found' });
|
|
7872
8187
|
}
|
|
7873
|
-
command =
|
|
8188
|
+
command = getProjectTestCommands(project).find(item => item.id === commandId) ?? null;
|
|
7874
8189
|
if (!command) {
|
|
7875
8190
|
return res.status(404).json({ error: 'Test command not found' });
|
|
7876
8191
|
}
|
|
@@ -7906,7 +8221,7 @@ app.post('/test-sessions/:id/run', async (req, res) => {
|
|
|
7906
8221
|
if (!selection) {
|
|
7907
8222
|
return res.status(400).json({ error: 'selection must be all, file, or case' });
|
|
7908
8223
|
}
|
|
7909
|
-
const command =
|
|
8224
|
+
const command = getProjectTestCommands(project).find(item => item.id === commandId) ?? null;
|
|
7910
8225
|
if (!command) {
|
|
7911
8226
|
return res.status(404).json({ error: 'Test command not found' });
|
|
7912
8227
|
}
|
|
@@ -8049,7 +8364,7 @@ app.post('/tickets/:id/follow-up', (req, res) => {
|
|
|
8049
8364
|
broadcaster: io,
|
|
8050
8365
|
});
|
|
8051
8366
|
if (!result.ok) {
|
|
8052
|
-
return res.status(result.status).json({ error: result.error });
|
|
8367
|
+
return res.status(result.status).json({ error: result.error, ...(result.value ? { ticket: result.value } : {}) });
|
|
8053
8368
|
}
|
|
8054
8369
|
return res.status(result.status).json(result.value);
|
|
8055
8370
|
});
|
|
@@ -8061,10 +8376,25 @@ app.post('/tickets/:id/plan-follow-up', (req, res) => {
|
|
|
8061
8376
|
broadcaster: io,
|
|
8062
8377
|
});
|
|
8063
8378
|
if (!result.ok) {
|
|
8064
|
-
return res.status(result.status).json({ error: result.error });
|
|
8379
|
+
return res.status(result.status).json({ error: result.error, ...(result.value ? { ticket: result.value } : {}) });
|
|
8065
8380
|
}
|
|
8066
8381
|
return res.status(result.status).json(result.value);
|
|
8067
8382
|
});
|
|
8383
|
+
app.post('/tickets/:id/plan-questions/:messageId/resolve', (req, res) => {
|
|
8384
|
+
const result = resolveTicketPlanQuestionSet({
|
|
8385
|
+
ticketId: req.params.id,
|
|
8386
|
+
requestedProjectId: getRequestedProjectId(req),
|
|
8387
|
+
messageId: req.params.messageId,
|
|
8388
|
+
setId: typeof req.body?.set_id === 'string' ? req.body.set_id : '',
|
|
8389
|
+
rawResolutions: req.body?.resolutions,
|
|
8390
|
+
dismissAll: req.body?.dismiss_all === true,
|
|
8391
|
+
broadcaster: io,
|
|
8392
|
+
});
|
|
8393
|
+
if (!result.ok) {
|
|
8394
|
+
return res.status(result.status).json({ error: result.error, ...(result.value ? { ticket: result.value } : {}) });
|
|
8395
|
+
}
|
|
8396
|
+
return res.status(result.status).json({ ticket: result.value, kind: result.kind, execution_context: result.executionContext });
|
|
8397
|
+
});
|
|
8068
8398
|
app.post('/tickets/:id/plan', (req, res) => {
|
|
8069
8399
|
const ticket = getTicket(req.params.id);
|
|
8070
8400
|
if (!ticket) {
|
|
@@ -8076,6 +8406,12 @@ app.post('/tickets/:id/plan', (req, res) => {
|
|
|
8076
8406
|
if (ticket.state !== 'plan') {
|
|
8077
8407
|
return res.status(400).json({ error: 'Only plan-state tickets can be planned' });
|
|
8078
8408
|
}
|
|
8409
|
+
if (hasPendingAskQuestionSet(ticket.messages)) {
|
|
8410
|
+
return res.status(409).json({
|
|
8411
|
+
error: 'Resolve or dismiss the pending question set before continuing.',
|
|
8412
|
+
ticket,
|
|
8413
|
+
});
|
|
8414
|
+
}
|
|
8079
8415
|
const bundleSelection = resolveRequestedBundleSelection(req.body, ticket.project_id);
|
|
8080
8416
|
if ('error' in bundleSelection) {
|
|
8081
8417
|
return res.status(bundleSelection.status).json({ error: bundleSelection.error });
|
|
@@ -8262,6 +8598,7 @@ export const startServer = ({ port = DEFAULT_PORT, host } = {}) => {
|
|
|
8262
8598
|
};
|
|
8263
8599
|
httpServer.once('error', handleError);
|
|
8264
8600
|
const { bindHost } = resolveDesktopServerHost(host);
|
|
8601
|
+
openCheckRunnerAdmissions();
|
|
8265
8602
|
httpServer.listen(port, bindHost, handleListening);
|
|
8266
8603
|
});
|
|
8267
8604
|
}
|
|
@@ -8285,6 +8622,9 @@ export const shutdownServer = async (signal) => {
|
|
|
8285
8622
|
return shutdownServerPromise;
|
|
8286
8623
|
}
|
|
8287
8624
|
shutdownRequested = true;
|
|
8625
|
+
closeCheckRunnerAdmissions();
|
|
8626
|
+
const httpServerClosePromise = httpServer.listening ? closeHttpServer(httpServer) : null;
|
|
8627
|
+
void httpServerClosePromise?.catch(() => undefined);
|
|
8288
8628
|
const pendingStartup = startServerPromise;
|
|
8289
8629
|
shutdownServerPromise = (async () => {
|
|
8290
8630
|
await Promise.all([
|
|
@@ -8301,6 +8641,7 @@ export const shutdownServer = async (signal) => {
|
|
|
8301
8641
|
}
|
|
8302
8642
|
await executionArtifactCleanupPromise;
|
|
8303
8643
|
await stopMobileAccess();
|
|
8644
|
+
await shutdownCheckRunner();
|
|
8304
8645
|
await clearAllTestSessions(io);
|
|
8305
8646
|
await clearAllGitHubActionsSessions();
|
|
8306
8647
|
await shutdownRealtimeServer({
|
|
@@ -8313,6 +8654,7 @@ export const shutdownServer = async (signal) => {
|
|
|
8313
8654
|
stopAllReviewAgents,
|
|
8314
8655
|
stopAllPlanAgents,
|
|
8315
8656
|
stopAllTickets,
|
|
8657
|
+
httpServerClosePromise,
|
|
8316
8658
|
});
|
|
8317
8659
|
})().finally(() => {
|
|
8318
8660
|
shutdownRequested = false;
|