@straiffi/archon 1.11.8 → 1.11.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 (112) hide show
  1. package/dist/client/assets/{ActivityView-BKLYfPXE.js → ActivityView-CgC2rnNq.js} +1 -1
  2. package/dist/client/assets/{GitHubActionsDialog-yqqwcP8z.js → GitHubActionsDialog-XXGwm-bw.js} +1 -1
  3. package/dist/client/assets/{VerificationDialog-DhyIDs1V.js → VerificationDialog-C8cDKxSG.js} +1 -1
  4. package/dist/client/assets/{architectureDiagram-3BPJPVTR-5krjaOU4.js → architectureDiagram-3BPJPVTR-BhdIU_o9.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-GPEHLZMM-Duo2q2gn.js → blockDiagram-GPEHLZMM-CfKNdNso.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AAUBKEIU-D2lxLmwB.js → c4Diagram-AAUBKEIU-BYOg4iNZ.js} +1 -1
  7. package/dist/client/assets/channel-4wFtrIng.js +1 -0
  8. package/dist/client/assets/{chunk-2J33WTMH-Dbo5lbSs.js → chunk-2J33WTMH-JmBb9p2l.js} +1 -1
  9. package/dist/client/assets/{chunk-3OPIFGDE-mjZrNxCg.js → chunk-3OPIFGDE-CuCn8uDI.js} +1 -1
  10. package/dist/client/assets/{chunk-5ZQYHXKU-C2nUnqLh.js → chunk-5ZQYHXKU-DaGbZz7d.js} +1 -1
  11. package/dist/client/assets/{chunk-727SXJPM-7eUgm0cn.js → chunk-727SXJPM-D7UwchWL.js} +1 -1
  12. package/dist/client/assets/{chunk-AQP2D5EJ-DouWpaE0.js → chunk-AQP2D5EJ-CxYpis5S.js} +1 -1
  13. package/dist/client/assets/{chunk-CSCIHK7Q-DesC0Fd7.js → chunk-CSCIHK7Q-DF4Jo5lo.js} +1 -1
  14. package/dist/client/assets/{chunk-KSCS5N6A-DAsFowjn.js → chunk-KSCS5N6A-Bul_8QaO.js} +1 -1
  15. package/dist/client/assets/{chunk-L5ZTLDWV-w7V9r0Cw.js → chunk-L5ZTLDWV-BQvBZt_1.js} +1 -1
  16. package/dist/client/assets/{chunk-LZXEDZCA-BCnxdvaJ.js → chunk-LZXEDZCA-BPsejGww.js} +2 -2
  17. package/dist/client/assets/{chunk-ND2GUHAM-DO8HdZCX.js → chunk-ND2GUHAM-D8AX2HAv.js} +1 -1
  18. package/dist/client/assets/{chunk-NZK2D7GU-37Rbzvm9.js → chunk-NZK2D7GU-CE21a8CK.js} +1 -1
  19. package/dist/client/assets/{chunk-O5CBEL6O-DTjB0GGI.js → chunk-O5CBEL6O-vZQ8YCvm.js} +1 -1
  20. package/dist/client/assets/{chunk-WU5MYG2G-CAonpz-x.js → chunk-WU5MYG2G-BpBNiw0j.js} +1 -1
  21. package/dist/client/assets/classDiagram-4FO5ZUOK-DNAwGSF5.js +1 -0
  22. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-cskOKMdS.js +1 -0
  23. package/dist/client/assets/{dagre-BM42HDAG-DDET9pbe.js → dagre-BM42HDAG-BqEPuJwq.js} +1 -1
  24. package/dist/client/assets/{diagram-2AECGRRQ-r_bu5Cgo.js → diagram-2AECGRRQ-BWLSNrm2.js} +1 -1
  25. package/dist/client/assets/{diagram-5GNKFQAL-QlPCzkUJ.js → diagram-5GNKFQAL-CUNUlEy4.js} +1 -1
  26. package/dist/client/assets/{diagram-KO2AKTUF-D5SzFOzP.js → diagram-KO2AKTUF-CgZcDZ0J.js} +1 -1
  27. package/dist/client/assets/{diagram-LMA3HP47-BJcWw3_s.js → diagram-LMA3HP47-DTfMha7_.js} +1 -1
  28. package/dist/client/assets/{diagram-OG6HWLK6-CpsR9p94.js → diagram-OG6HWLK6-ClO5evmB.js} +1 -1
  29. package/dist/client/assets/{erDiagram-TEJ5UH35-Dq-K6YrY.js → erDiagram-TEJ5UH35-BRW1HE33.js} +1 -1
  30. package/dist/client/assets/{flowDiagram-I6XJVG4X-39hKkH4n.js → flowDiagram-I6XJVG4X-tC2xALm9.js} +1 -1
  31. package/dist/client/assets/{ganttDiagram-6RSMTGT7-DaeBYOLz.js → ganttDiagram-6RSMTGT7-D7Bu6hss.js} +1 -1
  32. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CCeo9Czy.js → gitGraphDiagram-PVQCEYII-Cx1uYcGX.js} +1 -1
  33. package/dist/client/assets/index-CArcyEjQ.js +287 -0
  34. package/dist/client/assets/index-D_brbG-0.css +2 -0
  35. package/dist/client/assets/{infoDiagram-5YYISTIA-jgHH1Ta7.js → infoDiagram-5YYISTIA-bKT-ZR4K.js} +1 -1
  36. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-gYPsFHRd.js → ishikawaDiagram-YF4QCWOH-CrUBKkGR.js} +1 -1
  37. package/dist/client/assets/{journeyDiagram-JHISSGLW-C5m8h46L.js → journeyDiagram-JHISSGLW-CCmXkk8M.js} +1 -1
  38. package/dist/client/assets/{kanban-definition-UN3LZRKU-rag4UxAB.js → kanban-definition-UN3LZRKU-BmcV7QM-.js} +1 -1
  39. package/dist/client/assets/{line-CAWxQrG3.js → line-C5oH-IfR.js} +1 -1
  40. package/dist/client/assets/{mermaid-parser.core-C5Hj2V3U.js → mermaid-parser.core-BFyFUctK.js} +1 -1
  41. package/dist/client/assets/{mermaid.core-BxRoDLxv.js → mermaid.core-DXkHU1mX.js} +3 -3
  42. package/dist/client/assets/{mindmap-definition-RKZ34NQL-BUV37aab.js → mindmap-definition-RKZ34NQL-DhErnbAy.js} +1 -1
  43. package/dist/client/assets/{pieDiagram-4H26LBE5-CMwVVhpZ.js → pieDiagram-4H26LBE5-B8pkGeer.js} +1 -1
  44. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-DYHRB097.js → quadrantDiagram-W4KKPZXB-BkYWz3t_.js} +1 -1
  45. package/dist/client/assets/{requirementDiagram-4Y6WPE33-BLtNTWtR.js → requirementDiagram-4Y6WPE33-DB6jC89I.js} +1 -1
  46. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-Bcd-UVHZ.js → sankeyDiagram-5OEKKPKP-BeAslLcb.js} +1 -1
  47. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-Die4-puP.js → sequenceDiagram-3UESZ5HK-BgXC1aKi.js} +1 -1
  48. package/dist/client/assets/{stateDiagram-AJRCARHV-TBbOyQST.js → stateDiagram-AJRCARHV-Dj3Qlk0A.js} +1 -1
  49. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-I1Ry2lsa.js +1 -0
  50. package/dist/client/assets/{timeline-definition-PNZ67QCA-CZ3E6e1X.js → timeline-definition-PNZ67QCA-C8ncLV_K.js} +1 -1
  51. package/dist/client/assets/{vennDiagram-CIIHVFJN-CY_kofMf.js → vennDiagram-CIIHVFJN-jgBSpqC1.js} +1 -1
  52. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-TzBIV1Er.js → wardleyDiagram-YWT4CUSO-DfGD5Kys.js} +1 -1
  53. package/dist/client/assets/{xychartDiagram-2RQKCTM6-BTvKU2GO.js → xychartDiagram-2RQKCTM6-Dyk41E2n.js} +1 -1
  54. package/dist/client/index.html +2 -2
  55. package/dist/server/index.js +124 -63
  56. package/dist/server/index.js.map +1 -1
  57. package/dist/server/lib/agent.js +23 -7
  58. package/dist/server/lib/agent.js.map +1 -1
  59. package/dist/server/lib/agentProvider.js +62 -15
  60. package/dist/server/lib/agentProvider.js.map +1 -1
  61. package/dist/server/lib/codexAgentProvider.js +186 -123
  62. package/dist/server/lib/codexAgentProvider.js.map +1 -1
  63. package/dist/server/lib/diffExplanation.js +485 -0
  64. package/dist/server/lib/diffExplanation.js.map +1 -0
  65. package/dist/server/lib/diffExplanationJobs.js +605 -0
  66. package/dist/server/lib/diffExplanationJobs.js.map +1 -0
  67. package/dist/server/lib/diffExplanationRoutes.js +236 -0
  68. package/dist/server/lib/diffExplanationRoutes.js.map +1 -0
  69. package/dist/server/lib/dockerSandboxExecutionEnvironment.js +27 -4
  70. package/dist/server/lib/dockerSandboxExecutionEnvironment.js.map +1 -1
  71. package/dist/server/lib/dockerSandboxReleaseProfiles.js +1 -1
  72. package/dist/server/lib/dockerSandboxReleaseProfiles.js.map +1 -1
  73. package/dist/server/lib/executionTargets.js +1 -1
  74. package/dist/server/lib/executionTargets.js.map +1 -1
  75. package/dist/server/lib/git.js +205 -35
  76. package/dist/server/lib/git.js.map +1 -1
  77. package/dist/server/lib/lightweightPrompt.js +284 -52
  78. package/dist/server/lib/lightweightPrompt.js.map +1 -1
  79. package/dist/server/lib/oneShotCancellation.js +54 -0
  80. package/dist/server/lib/oneShotCancellation.js.map +1 -0
  81. package/dist/server/lib/opencodeExecutionReliability.js +46 -2
  82. package/dist/server/lib/opencodeExecutionReliability.js.map +1 -1
  83. package/dist/server/lib/projectMemoryScan.js +3 -46
  84. package/dist/server/lib/projectMemoryScan.js.map +1 -1
  85. package/dist/server/lib/promptDiffSafety.js +53 -0
  86. package/dist/server/lib/promptDiffSafety.js.map +1 -0
  87. package/dist/server/lib/repositorySecrets.js +69 -0
  88. package/dist/server/lib/repositorySecrets.js.map +1 -0
  89. package/dist/server/lib/tickets.js +3 -2
  90. package/dist/server/lib/tickets.js.map +1 -1
  91. package/dist/server/lib/types.js.map +1 -1
  92. package/dist/server/lib/unifiedDiff.js +459 -0
  93. package/dist/server/lib/unifiedDiff.js.map +1 -0
  94. package/dist/server/lib/workspaceDiff.js +156 -0
  95. package/dist/server/lib/workspaceDiff.js.map +1 -0
  96. package/dist/server/lib/workspaceDiffRoutes.js +63 -0
  97. package/dist/server/lib/workspaceDiffRoutes.js.map +1 -0
  98. package/dist/server/lib/workspaceTargets.js +88 -0
  99. package/dist/server/lib/workspaceTargets.js.map +1 -0
  100. package/dist/server/workers/chat.js +71 -1
  101. package/dist/server/workers/chat.js.map +1 -1
  102. package/dist/server/workers/plan.js +24 -2
  103. package/dist/server/workers/plan.js.map +1 -1
  104. package/dist/server/workers/review.js +19 -13
  105. package/dist/server/workers/review.js.map +1 -1
  106. package/package.json +1 -1
  107. package/dist/client/assets/channel-Cf2U7nU4.js +0 -1
  108. package/dist/client/assets/classDiagram-4FO5ZUOK-CK8VsU-L.js +0 -1
  109. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-RvySOEwt.js +0 -1
  110. package/dist/client/assets/index-B9-M1vEw.js +0 -287
  111. package/dist/client/assets/index-CbYT3N3U.css +0 -2
  112. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-BRotXz7h.js +0 -1
@@ -10,6 +10,10 @@ import { adjectives, animals, uniqueNamesGenerator } from 'unique-names-generato
10
10
  import db from './db.js';
11
11
  import { pruneAllRepositoryGraphScans } from './lib/repositoryGraphRetention.js';
12
12
  import { registerRepositoryGraphRoutes } from './lib/repositoryGraphRoutes.js';
13
+ import { registerWorkspaceDiffRoutes } from './lib/workspaceDiffRoutes.js';
14
+ import { getWorkspaceDiffAsync } from './lib/workspaceDiff.js';
15
+ import { registerDiffExplanationRoutes, registerDiffExplanationSocketHandlers, } from './lib/diffExplanationRoutes.js';
16
+ import { openDiffExplanationJobAdmissions, shutdownDiffExplanationJobs, } from './lib/diffExplanationJobs.js';
13
17
  import { registerExecutionEnvironmentRoutes } from './lib/executionEnvironmentRoutes.js';
14
18
  import { registerExecutionProfileRoutes } from './lib/executionProfileRoutes.js';
15
19
  import { executionProfileSupportsChecks, hasConfiguredCheckPolicy } from './lib/executionProfileCheckPolicy.js';
@@ -34,7 +38,7 @@ import { resolveSessionTargetForExecution } from './lib/sessionTargets.js';
34
38
  import { createChatMessage, getChatMessage, updateChatMessageContent } from './lib/chatMessages.js';
35
39
  import { attachChatTicketProposalsToMessageContent } from './lib/chatTicketProposals.js';
36
40
  import { createCorsOriginResolver, parseCorsOrigins } from './lib/cors.js';
37
- import { abortGitOperation, branchExistsLocally, commitGitChanges, continueGitRebase, createWorktreeAsync, deleteLocalBranch, deleteWorktree, getActiveRebaseBranch, getBundleReviewDiffForCwd, getGitDiffDataAsync, getGitDiffFiles, getGitDiffFilesAsync, getGitStatus, getGitStatusAsync, getGitUnifiedDiff, getGitUnifiedDiffAsync, getLocalProjectBranch, getProjectBranch, getProjectBranchAsync, getProjectBranchSuggestions, getRebaseStagedDiff, getReviewDiffDataAsync, getSelectedGitUnifiedDiff, getUnpushedCommitDiffForCwd, getUnpushedCommitSummaryForCwd, getUnpushedCommitSummaryForCwdAsync, hasKnownBranchRef, listUnpushedCommitsForCwd, pullGitBranch, pushCurrentBranch, rebaseGitBranchOntoBase, resolveExistingWorktreePath, resolveWorktreePath, skipGitRebaseStep, squashUnpushedCommitsForCwd, summarizeTicketDiffFiles, switchProjectBranch, syncWorktreeFilesAsync, undoLatestUnpushedCommitForCwd, UnpushedCommitDiffSelectionError } from './lib/git.js';
41
+ import { abortGitOperation, branchExistsLocally, commitGitChanges, continueGitRebase, createWorktreeAsync, deleteLocalBranch, deleteWorktree, getActiveRebaseBranch, getGitDiffFiles, getGitDiffFilesAsync, getGitStatus, getGitStatusAsync, getGitUnifiedDiff, getLocalProjectBranch, getProjectBranch, getProjectBranchAsync, getProjectBranchSuggestions, getRebaseStagedDiff, getSelectedGitUnifiedDiff, getUnpushedCommitDiffForCwd, getUnpushedCommitSummaryForCwd, getUnpushedCommitSummaryForCwdAsync, hasKnownBranchRef, listUnpushedCommitsForCwd, pullGitBranch, pushCurrentBranch, rebaseGitBranchOntoBase, resolveExistingWorktreePath, resolveWorktreePath, skipGitRebaseStep, squashUnpushedCommitsForCwd, summarizeTicketDiffFiles, switchProjectBranch, syncWorktreeFilesAsync, undoLatestUnpushedCommitForCwd, UnpushedCommitDiffSelectionError } from './lib/git.js';
38
42
  import config from './lib/config.js';
39
43
  import { getActiveTool, initializeActiveTool, setActiveTool } from './lib/activeTool.js';
40
44
  import { autoConfigProject, ProjectAutoConfigError } from './lib/projectAutoConfig.js';
@@ -44,7 +48,7 @@ import { resolveDesktopServerHost } from './lib/desktopServerHost.js';
44
48
  import { emitMobileRealtime, getMobileAccessStatus, regenerateMobilePairingChallenge, revokeMobileSession, setMobileAccessStatusChangeListener, startMobileAccess, stopMobileAccess, } from './lib/mobileAccess.js';
45
49
  import { validateDesktopMobileAccessRequest } from './lib/mobileAccessSecurity.js';
46
50
  import { refreshChatSessionUsageSnapshot, refreshSpecSessionUsageSnapshot, stopAllBuildAgents, stopAllReviewAgents, stopBuildAgent, stopReviewAgent } from './lib/agent.js';
47
- import { runTrackedLightweightPrompt } from './lib/lightweightPrompt.js';
51
+ import { openLightweightPromptAdmissions, runTrackedLightweightPrompt, shutdownLightweightPrompts, } from './lib/lightweightPrompt.js';
48
52
  import { advanceBuildChain, startBuildChain, stopBuildChain } from './lib/buildChains.js';
49
53
  import { activateQueuedBuildSequenceTicket, bindBuildSequenceTicketExecutionRun, compensateBuildSequenceAcceptance, enqueueBundledBuild, getDependencyOrderedBundleTicketIds, getBuildSequenceIdForTicket, getBuildSequenceTicketExecutionProfileSelection, hasActiveSequenceForBundle, releaseQueuedBuildSequenceTicketExecutionRunForRetry, removeTicketFromBuildSequence, stopBuildSequence, } from './lib/buildSequences.js';
50
54
  import { resumeRemainingBundleBuilds } from './lib/bundleBuildRecovery.js';
@@ -107,7 +111,7 @@ import { getExecutionEnvironmentContextForRun } from './lib/executionEnvironment
107
111
  import { PlanningReferenceError, resolveExternalPlanningDescription } from './lib/integrations/planning.js';
108
112
  import { refreshIfNeeded as refreshAssignedIssues, clearCachedAssignedIssues } from './lib/integrations/assignedIssuesCache.js';
109
113
  import { buildCommitMessagePrompt, normalizeGeneratedCommitMessage } from './lib/commitMessage.js';
110
- import { BUNDLE_PULL_REQUEST_SYNC_TTL_MS, getBundlePullRequestRecord, hasOpenBundlePullRequest, serializeBundlePullRequest, shouldSyncBundlePullRequest, syncTrackedBundlePullRequestIfNeeded, upsertBundlePullRequest, upsertBundlePullRequestFromGitHub, } from './lib/bundlePullRequests.js';
114
+ import { BUNDLE_PULL_REQUEST_SYNC_TTL_MS, getBundlePullRequestRecord, hasOpenBundlePullRequest, hasTerminalBundlePullRequest, serializeBundlePullRequest, shouldSyncBundlePullRequest, syncTrackedBundlePullRequestIfNeeded, upsertBundlePullRequest, upsertBundlePullRequestFromGitHub, } from './lib/bundlePullRequests.js';
111
115
  import { countInboundProjectLinks, getEffectiveProject, getProjectCheckDefinitions, getProjectById, getProjectTestCommands, getProjectTicketStats, hasProjectRunIde, listInterruptedProjectDeletions, listProjects, releaseInterruptedProjectDeletionClaims, replaceProjectLinks, serializeProject, updateProjectActiveTarget, validateProjectRequiredCheckPolicyReferences, validateProjectPayload, } from './lib/projects.js';
112
116
  import { forceReleaseProjectRunCleanup, getActiveRunStatus, getActiveRunStatusForProject, getLastRunStatusForProject, getPreviewStatusForProject, hasRunConfig, isRunning, openIde, openTicketIdeInWorkspace, resolveProjectRunContextKey, runTicketInWorkspace, stopAllTickets, stopTicket } from './lib/run.js';
113
117
  import { deleteProjectServiceRunLogs, getServiceRunLogs } from './lib/serviceRunLogs.js';
@@ -1130,6 +1134,22 @@ const matchesTicketProjectContext = (ticket, req) => {
1130
1134
  }
1131
1135
  return ticket.project_id === requestedProjectId;
1132
1136
  };
1137
+ const isTerminalBundleTarget = (projectId, bundleId) => {
1138
+ return Boolean(bundleId
1139
+ && hasTerminalBundlePullRequest(getBundlePullRequestRecord(bundleId, projectId)));
1140
+ };
1141
+ const normalizeTerminalBundleGitStatus = (status, projectId, bundleId) => {
1142
+ if (!isTerminalBundleTarget(projectId, bundleId)) {
1143
+ return status;
1144
+ }
1145
+ return {
1146
+ ...status,
1147
+ ahead: 0,
1148
+ can_push: false,
1149
+ can_force_push_with_lease: false,
1150
+ };
1151
+ };
1152
+ const TERMINAL_BUNDLE_BRANCH_ERROR = 'This bundle pull request is already merged or closed. Start follow-up work on a new branch instead.';
1133
1153
  const getTicketRouteContext = (ticketId) => {
1134
1154
  return getTicketRouteContextStatement.get(ticketId);
1135
1155
  };
@@ -2282,29 +2302,27 @@ const getCurrentReviewWorkspaceSignature = async (ticket) => {
2282
2302
  return (await getGitStatusAsync(workspace.cwd, workspace.branch, workspace.project)).workspace_signature ?? null;
2283
2303
  };
2284
2304
  const getCurrentReviewDiffState = async (ticket) => {
2285
- if (!ticket) {
2305
+ if (!ticket?.project_id) {
2286
2306
  return null;
2287
2307
  }
2288
- const workspace = resolveTicketDiffWorkspaceFromContext(ticket);
2289
- if ('error' in workspace) {
2308
+ const target = { kind: 'ticket', ticket_id: ticket.id };
2309
+ const reviewDiff = await getWorkspaceDiffAsync({
2310
+ projectId: ticket.project_id,
2311
+ target,
2312
+ scope: 'review',
2313
+ });
2314
+ const uncommittedDiff = await getWorkspaceDiffAsync({
2315
+ projectId: ticket.project_id,
2316
+ target,
2317
+ scope: 'uncommitted',
2318
+ });
2319
+ if (!reviewDiff.available || !uncommittedDiff.available) {
2290
2320
  return null;
2291
2321
  }
2292
- const trackedPullRequest = ticket.project_id && ticket.worktree_bundle_id
2293
- ? getBundlePullRequestRecord(ticket.worktree_bundle_id, ticket.project_id)
2294
- : null;
2295
- const [reviewDiff, uncommittedDiff] = await Promise.all([
2296
- getReviewDiffDataAsync({
2297
- cwd: workspace.cwd,
2298
- branch: workspace.branch,
2299
- project: workspace.project,
2300
- pullRequestBaseBranch: trackedPullRequest?.baseBranch ?? null,
2301
- }),
2302
- getGitUnifiedDiffAsync(workspace.cwd),
2303
- ]);
2304
2322
  return {
2305
2323
  diffText: reviewDiff.diff,
2306
- uncommittedDiffSignature: computeDiffSignature(uncommittedDiff),
2307
- changeScope: (reviewDiff.diff === uncommittedDiff ? 'uncommitted' : 'branch_diff'),
2324
+ uncommittedDiffSignature: computeDiffSignature(uncommittedDiff.diff),
2325
+ changeScope: (reviewDiff.diff === uncommittedDiff.diff ? 'uncommitted' : 'branch_diff'),
2308
2326
  };
2309
2327
  };
2310
2328
  const getBundleReviewFindings = async (projectId, bundleId) => {
@@ -2740,6 +2758,8 @@ const emitRunStateForContext = (ticket, isRunningValue) => {
2740
2758
  };
2741
2759
  app.use(express.json({ limit: '5mb' }));
2742
2760
  registerRepositoryGraphRoutes(app, { emitter: io });
2761
+ registerWorkspaceDiffRoutes(app);
2762
+ registerDiffExplanationRoutes(app);
2743
2763
  registerExecutionEnvironmentRoutes(app, {
2744
2764
  emitter: io,
2745
2765
  runner: dockerSandboxRunner,
@@ -3054,16 +3074,15 @@ app.get('/projects/:id/chat-sessions/:sessionId/diff', async (req, res) => {
3054
3074
  if (!chatSession) {
3055
3075
  return res.status(404).json({ error: 'Not found' });
3056
3076
  }
3057
- const workspace = resolveChatSessionTarget(chatSession, project);
3058
- if (!workspace.target_available || !workspace.cwd) {
3059
- return res.status(409).json({ error: workspace.target_unavailable_reason ?? 'Chat target is unavailable.' });
3060
- }
3061
- const diffData = await getGitDiffDataAsync(workspace.cwd);
3062
- return res.json({
3063
- stats: summarizeTicketDiffFiles(diffData.files),
3064
- files: diffData.files,
3065
- diff: diffData.diff,
3077
+ const diff = await getWorkspaceDiffAsync({
3078
+ projectId: project.id,
3079
+ target: { kind: 'chat_session', chat_session_id: chatSession.id },
3080
+ scope: 'uncommitted',
3066
3081
  });
3082
+ if (!diff.available) {
3083
+ return res.status(diff.reason === 'target_not_found' ? 404 : 409).json({ ...diff, error: diff.message });
3084
+ }
3085
+ return res.json({ stats: diff.stats, files: diff.files, diff: diff.diff });
3067
3086
  });
3068
3087
  app.get('/projects/:id/chat-sessions/:sessionId/git-status', async (req, res) => {
3069
3088
  const project = getProjectById(req.params.id);
@@ -3694,15 +3713,17 @@ app.get('/tickets/:id/diff', async (req, res) => {
3694
3713
  }
3695
3714
  const workspace = resolveTicketDiffWorkspaceFromContext(ticket);
3696
3715
  if ('error' in workspace) {
3697
- const { status, error } = workspace;
3698
- return res.status(status).json({ error });
3716
+ return res.status(workspace.status).json({ error: workspace.error });
3699
3717
  }
3700
- const diffData = await getGitDiffDataAsync(workspace.cwd);
3701
- return res.json({
3702
- stats: summarizeTicketDiffFiles(diffData.files),
3703
- files: diffData.files,
3704
- diff: diffData.diff,
3718
+ const diff = await getWorkspaceDiffAsync({
3719
+ projectId: ticket.project_id,
3720
+ target: { kind: 'ticket', ticket_id: ticket.id },
3721
+ scope: 'uncommitted',
3705
3722
  });
3723
+ if (!diff.available) {
3724
+ return res.status(diff.reason === 'target_not_found' ? 404 : 409).json({ ...diff, error: diff.message });
3725
+ }
3726
+ return res.json({ stats: diff.stats, files: diff.files, diff: diff.diff });
3706
3727
  });
3707
3728
  app.get('/tickets/:id/committed-diff', (req, res) => {
3708
3729
  const ticket = getTicketRouteContext(req.params.id);
@@ -3731,7 +3752,8 @@ app.get('/tickets/:id/git-status', async (req, res) => {
3731
3752
  return res.status(workspace.status).json({ error: workspace.error });
3732
3753
  }
3733
3754
  const refreshRemote = req.query.refresh_remote === 'true';
3734
- return res.json(await getGitStatusAsync(workspace.cwd, workspace.branch, workspace.project, { refreshRemote }));
3755
+ const status = await getGitStatusAsync(workspace.cwd, workspace.branch, workspace.project, { refreshRemote });
3756
+ return res.json(normalizeTerminalBundleGitStatus(status, workspace.project.id, ticket.worktree_bundle_id));
3735
3757
  });
3736
3758
  app.get('/tickets/:id/rebase-staged-diff', (req, res) => {
3737
3759
  const ticket = getTicketRouteContext(req.params.id);
@@ -3833,6 +3855,9 @@ app.get('/tickets/:id/git-commits', async (req, res) => {
3833
3855
  if ('error' in workspace) {
3834
3856
  return res.status(workspace.status).json({ error: workspace.error });
3835
3857
  }
3858
+ if (isTerminalBundleTarget(workspace.project.id, ticket.worktree_bundle_id)) {
3859
+ return res.json({ commits: [], total_count: 0 });
3860
+ }
3836
3861
  const summary = await getUnpushedCommitSummaryForCwdAsync(workspace.cwd, workspace.branch, workspace.project);
3837
3862
  return res.json({ commits: summary.commits, total_count: summary.totalCount });
3838
3863
  });
@@ -3896,7 +3921,7 @@ app.get('/bundles/:id/review-findings', async (req, res) => {
3896
3921
  }
3897
3922
  return res.json(response);
3898
3923
  });
3899
- app.get('/bundles/:id/review-diff', (req, res) => {
3924
+ app.get('/bundles/:id/review-diff', async (req, res) => {
3900
3925
  const result = getRequiredProject(req);
3901
3926
  if ('error' in result) {
3902
3927
  return res.status(400).json({ error: result.error });
@@ -3908,21 +3933,24 @@ app.get('/bundles/:id/review-diff', (req, res) => {
3908
3933
  if (isProjectRootBundle(bundle)) {
3909
3934
  return res.status(409).json({ error: 'Bundle diff is not available for the project root bundle.' });
3910
3935
  }
3911
- const worktreePath = resolveWorktreePath(bundle.branch, result.project);
3912
- if (!existsSync(worktreePath)) {
3913
- return res.status(409).json({ error: 'The bundle worktree is not ready yet.' });
3914
- }
3915
- const trackedPullRequest = getBundlePullRequestRecord(bundle.id, result.project.id);
3916
- const reviewDiff = getBundleReviewDiffForCwd({
3917
- cwd: worktreePath,
3918
- branch: bundle.branch,
3919
- project: result.project,
3920
- pullRequestBaseBranch: trackedPullRequest?.baseBranch ?? null,
3936
+ const reviewDiff = await getWorkspaceDiffAsync({
3937
+ projectId: result.project.id,
3938
+ target: { kind: 'bundle', bundle_id: bundle.id },
3939
+ scope: 'review',
3921
3940
  });
3922
- if (!reviewDiff) {
3923
- return res.status(409).json({ error: 'Bundle diff is not available until Archon can resolve a review base for this bundle.' });
3941
+ if (!reviewDiff.available) {
3942
+ return res.status(409).json({ ...reviewDiff, error: reviewDiff.message });
3924
3943
  }
3925
- return res.json(reviewDiff);
3944
+ return res.json({
3945
+ stats: reviewDiff.stats,
3946
+ files: reviewDiff.files,
3947
+ diff: reviewDiff.diff,
3948
+ diff_signature: reviewDiff.diff_signature,
3949
+ head_branch: reviewDiff.workspace.branch,
3950
+ base_branch: reviewDiff.base.branch,
3951
+ base_commit: reviewDiff.base.commit,
3952
+ source: 'worktree_base',
3953
+ });
3926
3954
  });
3927
3955
  app.get('/bundles/:id/pull-request/comments', async (req, res) => {
3928
3956
  const result = getRequiredProject(req);
@@ -7964,16 +7992,18 @@ app.get('/projects/:id/diff', async (req, res) => {
7964
7992
  if (!project) {
7965
7993
  return res.status(404).json({ error: 'Not found' });
7966
7994
  }
7967
- const workspace = resolveProjectTargetGitWorkspace(project);
7968
- if ('error' in workspace) {
7969
- return res.status(workspace.status).json({ error: workspace.error });
7970
- }
7971
- const diffData = await getGitDiffDataAsync(workspace.cwd);
7972
- return res.json({
7973
- stats: summarizeTicketDiffFiles(diffData.files),
7974
- files: diffData.files,
7975
- diff: diffData.diff,
7995
+ const target = project.active_target_kind === 'bundle' && project.active_target_bundle_id
7996
+ ? { kind: 'bundle', bundle_id: project.active_target_bundle_id }
7997
+ : { kind: 'repo_root' };
7998
+ const diff = await getWorkspaceDiffAsync({
7999
+ projectId: project.id,
8000
+ target,
8001
+ scope: 'uncommitted',
7976
8002
  });
8003
+ if (!diff.available) {
8004
+ return res.status(diff.reason === 'target_not_found' ? 404 : 409).json({ ...diff, error: diff.message });
8005
+ }
8006
+ return res.json({ stats: diff.stats, files: diff.files, diff: diff.diff });
7977
8007
  });
7978
8008
  app.get('/projects/:id/committed-diff', (req, res) => {
7979
8009
  const project = getProjectById(req.params.id);
@@ -7996,7 +8026,8 @@ app.get('/projects/:id/git-status', async (req, res) => {
7996
8026
  return res.status(workspace.status).json({ error: workspace.error });
7997
8027
  }
7998
8028
  const refreshRemote = req.query.refresh_remote === 'true';
7999
- return res.json(await getGitStatusAsync(workspace.cwd, workspace.branch, workspace.project, { refreshRemote }));
8029
+ const status = await getGitStatusAsync(workspace.cwd, workspace.branch, workspace.project, { refreshRemote });
8030
+ return res.json(normalizeTerminalBundleGitStatus(status, workspace.project.id, workspace.target_bundle_id));
8000
8031
  });
8001
8032
  app.get('/projects/:id/git-commits', async (req, res) => {
8002
8033
  const project = getProjectById(req.params.id);
@@ -8007,6 +8038,9 @@ app.get('/projects/:id/git-commits', async (req, res) => {
8007
8038
  if ('error' in workspace) {
8008
8039
  return res.status(workspace.status).json({ error: workspace.error });
8009
8040
  }
8041
+ if (isTerminalBundleTarget(workspace.project.id, workspace.target_bundle_id)) {
8042
+ return res.json({ commits: [], total_count: 0 });
8043
+ }
8010
8044
  const summary = await getUnpushedCommitSummaryForCwdAsync(workspace.cwd, workspace.branch, workspace.project);
8011
8045
  return res.json({ commits: summary.commits, total_count: summary.totalCount });
8012
8046
  });
@@ -8037,7 +8071,7 @@ app.post('/projects/:id/git-commit', async (req, res) => {
8037
8071
  }
8038
8072
  return res.json({
8039
8073
  ticket: null,
8040
- git_status: getGitStatus(workspace.cwd, workspace.branch, workspace.project),
8074
+ git_status: normalizeTerminalBundleGitStatus(getGitStatus(workspace.cwd, workspace.branch, workspace.project), workspace.project.id, workspace.target_bundle_id),
8041
8075
  });
8042
8076
  });
8043
8077
  app.post('/projects/:id/git-commit-message', async (req, res) => {
@@ -8207,6 +8241,9 @@ app.post('/projects/:id/git-push', async (req, res) => {
8207
8241
  if ('error' in workspace) {
8208
8242
  return res.status(workspace.status).json({ error: workspace.error });
8209
8243
  }
8244
+ if (isTerminalBundleTarget(workspace.project.id, workspace.target_bundle_id)) {
8245
+ return res.status(409).json({ error: TERMINAL_BUNDLE_BRANCH_ERROR });
8246
+ }
8210
8247
  const forceWithLease = req.body?.force_with_lease === true;
8211
8248
  if (forceWithLease) {
8212
8249
  try {
@@ -8248,6 +8285,9 @@ app.post('/projects/:id/git-undo-latest', async (req, res) => {
8248
8285
  if ('error' in workspace) {
8249
8286
  return res.status(workspace.status).json({ error: workspace.error });
8250
8287
  }
8288
+ if (isTerminalBundleTarget(workspace.project.id, workspace.target_bundle_id)) {
8289
+ return res.status(409).json({ error: TERMINAL_BUNDLE_BRANCH_ERROR });
8290
+ }
8251
8291
  try {
8252
8292
  await undoLatestUnpushedCommitForCwd(workspace.cwd, workspace.branch, workspace.project);
8253
8293
  }
@@ -8271,6 +8311,9 @@ app.post('/projects/:id/git-squash', async (req, res) => {
8271
8311
  if ('error' in workspace) {
8272
8312
  return res.status(workspace.status).json({ error: workspace.error });
8273
8313
  }
8314
+ if (isTerminalBundleTarget(workspace.project.id, workspace.target_bundle_id)) {
8315
+ return res.status(409).json({ error: TERMINAL_BUNDLE_BRANCH_ERROR });
8316
+ }
8274
8317
  const message = typeof req.body?.message === 'string' ? req.body.message.trim() : '';
8275
8318
  if (!message) {
8276
8319
  return res.status(400).json({ error: 'message is required' });
@@ -9399,7 +9442,7 @@ app.post('/tickets/:id/git-commit', async (req, res) => {
9399
9442
  const updated = reconcileRunContextTickets(previousTickets, ticket.id);
9400
9443
  return res.json({
9401
9444
  ticket: updated,
9402
- git_status: getGitStatus(workspace.cwd, workspace.branch, workspace.project),
9445
+ git_status: normalizeTerminalBundleGitStatus(getGitStatus(workspace.cwd, workspace.branch, workspace.project), workspace.project.id, ticket.worktree_bundle_id),
9403
9446
  });
9404
9447
  });
9405
9448
  app.post('/tickets/:id/git-commit-message', async (req, res) => {
@@ -9475,6 +9518,9 @@ app.post('/tickets/:id/git-push', async (req, res) => {
9475
9518
  if ('error' in workspace) {
9476
9519
  return res.status(workspace.status).json({ error: workspace.error });
9477
9520
  }
9521
+ if (isTerminalBundleTarget(workspace.project.id, ticket.worktree_bundle_id)) {
9522
+ return res.status(409).json({ error: TERMINAL_BUNDLE_BRANCH_ERROR });
9523
+ }
9478
9524
  const previousTickets = listTicketsInRunContext(ticket);
9479
9525
  const forceWithLease = req.body?.force_with_lease === true;
9480
9526
  if (forceWithLease) {
@@ -9525,6 +9571,9 @@ app.post('/tickets/:id/git-undo-latest', async (req, res) => {
9525
9571
  if ('error' in workspace) {
9526
9572
  return res.status(workspace.status).json({ error: workspace.error });
9527
9573
  }
9574
+ if (isTerminalBundleTarget(workspace.project.id, ticket.worktree_bundle_id)) {
9575
+ return res.status(409).json({ error: TERMINAL_BUNDLE_BRANCH_ERROR });
9576
+ }
9528
9577
  const previousTickets = listTicketsInRunContext(ticket);
9529
9578
  try {
9530
9579
  await undoLatestUnpushedCommitForCwd(workspace.cwd, workspace.branch, workspace.project);
@@ -9553,6 +9602,9 @@ app.post('/tickets/:id/git-squash', async (req, res) => {
9553
9602
  if ('error' in workspace) {
9554
9603
  return res.status(workspace.status).json({ error: workspace.error });
9555
9604
  }
9605
+ if (isTerminalBundleTarget(workspace.project.id, ticket.worktree_bundle_id)) {
9606
+ return res.status(409).json({ error: TERMINAL_BUNDLE_BRANCH_ERROR });
9607
+ }
9556
9608
  const message = typeof req.body?.message === 'string' ? req.body.message.trim() : '';
9557
9609
  if (!message) {
9558
9610
  return res.status(400).json({ error: 'message is required' });
@@ -10575,6 +10627,7 @@ app.use((error, _req, res, next) => {
10575
10627
  io.on('connection', (socket) => {
10576
10628
  console.log('client connected:', socket.id);
10577
10629
  registerTerminalSocketHandlers(io, socket);
10630
+ registerDiffExplanationSocketHandlers(socket);
10578
10631
  });
10579
10632
  const DEFAULT_PORT = 3001;
10580
10633
  const APPROVAL_RECONCILIATION_INTERVAL_MS = 30_000;
@@ -10746,6 +10799,8 @@ export const startServer = ({ port = DEFAULT_PORT, host } = {}) => {
10746
10799
  return Promise.reject(new Error('Server startup was cancelled by shutdown'));
10747
10800
  }
10748
10801
  if (httpServer.listening) {
10802
+ openLightweightPromptAdmissions();
10803
+ openDiffExplanationJobAdmissions();
10749
10804
  return Promise.resolve(resolveStartResult(host));
10750
10805
  }
10751
10806
  if (startServerPromise) {
@@ -10781,11 +10836,15 @@ export const startServer = ({ port = DEFAULT_PORT, host } = {}) => {
10781
10836
  };
10782
10837
  httpServer.once('error', handleError);
10783
10838
  const { bindHost } = resolveDesktopServerHost(host);
10839
+ openLightweightPromptAdmissions();
10840
+ openDiffExplanationJobAdmissions();
10784
10841
  openCheckRunnerAdmissions();
10785
10842
  httpServer.listen(port, bindHost, handleListening);
10786
10843
  });
10787
10844
  }
10788
10845
  catch (error) {
10846
+ await shutdownDiffExplanationJobs();
10847
+ await shutdownLightweightPrompts();
10789
10848
  await clearAllGitHubActionsSessions();
10790
10849
  throw error;
10791
10850
  }
@@ -10812,9 +10871,11 @@ export const shutdownServer = async (signal) => {
10812
10871
  void httpServerClosePromise?.catch(() => undefined);
10813
10872
  const pendingStartup = startServerPromise;
10814
10873
  shutdownServerPromise = (async () => {
10874
+ await shutdownDiffExplanationJobs();
10815
10875
  await Promise.all([
10816
10876
  pendingStartup?.catch(() => undefined),
10817
10877
  stopAllPlanAgents(),
10878
+ shutdownLightweightPrompts(),
10818
10879
  ]);
10819
10880
  if (executionEvidenceCleanupTimer) {
10820
10881
  clearInterval(executionEvidenceCleanupTimer);