@straiffi/archon 1.11.8 → 1.11.10

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 (142) hide show
  1. package/dist/client/assets/{ActivityView-BKLYfPXE.js → ActivityView-RnE0983a.js} +1 -1
  2. package/dist/client/assets/{GitHubActionsDialog-yqqwcP8z.js → GitHubActionsDialog-PaNJhVzv.js} +1 -1
  3. package/dist/client/assets/{VerificationDialog-DhyIDs1V.js → VerificationDialog-PGrCJ1DJ.js} +1 -1
  4. package/dist/client/assets/{architectureDiagram-3BPJPVTR-5krjaOU4.js → architectureDiagram-3BPJPVTR-CKd1Dmpz.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-GPEHLZMM-Duo2q2gn.js → blockDiagram-GPEHLZMM-M3OjRhA0.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AAUBKEIU-D2lxLmwB.js → c4Diagram-AAUBKEIU-VWBcGTXO.js} +1 -1
  7. package/dist/client/assets/channel-CBQk1IQf.js +1 -0
  8. package/dist/client/assets/{chunk-2J33WTMH-Dbo5lbSs.js → chunk-2J33WTMH-C4WpCUzU.js} +1 -1
  9. package/dist/client/assets/{chunk-3OPIFGDE-mjZrNxCg.js → chunk-3OPIFGDE-BQ-j87T6.js} +1 -1
  10. package/dist/client/assets/{chunk-5ZQYHXKU-C2nUnqLh.js → chunk-5ZQYHXKU-BCZvyT9u.js} +1 -1
  11. package/dist/client/assets/{chunk-727SXJPM-7eUgm0cn.js → chunk-727SXJPM-DZP_PMNz.js} +1 -1
  12. package/dist/client/assets/{chunk-AQP2D5EJ-DouWpaE0.js → chunk-AQP2D5EJ-Cejit8wh.js} +1 -1
  13. package/dist/client/assets/{chunk-CSCIHK7Q-DesC0Fd7.js → chunk-CSCIHK7Q-CvXC4rkf.js} +1 -1
  14. package/dist/client/assets/{chunk-KSCS5N6A-DAsFowjn.js → chunk-KSCS5N6A-hTgJWrr3.js} +1 -1
  15. package/dist/client/assets/{chunk-L5ZTLDWV-w7V9r0Cw.js → chunk-L5ZTLDWV-bSK1mut3.js} +1 -1
  16. package/dist/client/assets/{chunk-LZXEDZCA-BCnxdvaJ.js → chunk-LZXEDZCA-Y2hnSpRr.js} +2 -2
  17. package/dist/client/assets/{chunk-ND2GUHAM-DO8HdZCX.js → chunk-ND2GUHAM-BiHH5lS5.js} +1 -1
  18. package/dist/client/assets/{chunk-NZK2D7GU-37Rbzvm9.js → chunk-NZK2D7GU-DdxcwTTc.js} +1 -1
  19. package/dist/client/assets/{chunk-O5CBEL6O-DTjB0GGI.js → chunk-O5CBEL6O-BZIbgKJr.js} +1 -1
  20. package/dist/client/assets/{chunk-WU5MYG2G-CAonpz-x.js → chunk-WU5MYG2G-D3yvqt9P.js} +1 -1
  21. package/dist/client/assets/classDiagram-4FO5ZUOK-BjizLQoS.js +1 -0
  22. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-B6qpU-kO.js +1 -0
  23. package/dist/client/assets/{dagre-BM42HDAG-DDET9pbe.js → dagre-BM42HDAG-CZQZPoaH.js} +1 -1
  24. package/dist/client/assets/{diagram-2AECGRRQ-r_bu5Cgo.js → diagram-2AECGRRQ-DU7zsX_b.js} +1 -1
  25. package/dist/client/assets/{diagram-5GNKFQAL-QlPCzkUJ.js → diagram-5GNKFQAL-BCCoHKlp.js} +1 -1
  26. package/dist/client/assets/{diagram-KO2AKTUF-D5SzFOzP.js → diagram-KO2AKTUF-BR_GNJLN.js} +1 -1
  27. package/dist/client/assets/{diagram-LMA3HP47-BJcWw3_s.js → diagram-LMA3HP47-B6LZOUTc.js} +1 -1
  28. package/dist/client/assets/{diagram-OG6HWLK6-CpsR9p94.js → diagram-OG6HWLK6-w89pjkBe.js} +1 -1
  29. package/dist/client/assets/{erDiagram-TEJ5UH35-Dq-K6YrY.js → erDiagram-TEJ5UH35-BReR8ngL.js} +1 -1
  30. package/dist/client/assets/{flowDiagram-I6XJVG4X-39hKkH4n.js → flowDiagram-I6XJVG4X-CGwpJ9iQ.js} +1 -1
  31. package/dist/client/assets/{ganttDiagram-6RSMTGT7-DaeBYOLz.js → ganttDiagram-6RSMTGT7-BcOPz4K9.js} +1 -1
  32. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CCeo9Czy.js → gitGraphDiagram-PVQCEYII-m6-x38H5.js} +1 -1
  33. package/dist/client/assets/index-D_brbG-0.css +2 -0
  34. package/dist/client/assets/index-yUckGfMu.js +293 -0
  35. package/dist/client/assets/{infoDiagram-5YYISTIA-jgHH1Ta7.js → infoDiagram-5YYISTIA-11iWCl_6.js} +1 -1
  36. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-gYPsFHRd.js → ishikawaDiagram-YF4QCWOH-DbeJ14eh.js} +1 -1
  37. package/dist/client/assets/{journeyDiagram-JHISSGLW-C5m8h46L.js → journeyDiagram-JHISSGLW-BYFdU9SL.js} +1 -1
  38. package/dist/client/assets/{kanban-definition-UN3LZRKU-rag4UxAB.js → kanban-definition-UN3LZRKU-Dyc52bol.js} +1 -1
  39. package/dist/client/assets/{line-CAWxQrG3.js → line-CBauYkto.js} +1 -1
  40. package/dist/client/assets/{mermaid-parser.core-C5Hj2V3U.js → mermaid-parser.core-BBbL8arn.js} +1 -1
  41. package/dist/client/assets/{mermaid.core-BxRoDLxv.js → mermaid.core-CB2mIiiP.js} +3 -3
  42. package/dist/client/assets/{mindmap-definition-RKZ34NQL-BUV37aab.js → mindmap-definition-RKZ34NQL-CvWYGfWn.js} +1 -1
  43. package/dist/client/assets/{pieDiagram-4H26LBE5-CMwVVhpZ.js → pieDiagram-4H26LBE5-BsbIF0rU.js} +1 -1
  44. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-DYHRB097.js → quadrantDiagram-W4KKPZXB-Bvs09UnJ.js} +1 -1
  45. package/dist/client/assets/{requirementDiagram-4Y6WPE33-BLtNTWtR.js → requirementDiagram-4Y6WPE33-B3U5xSYZ.js} +1 -1
  46. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-Bcd-UVHZ.js → sankeyDiagram-5OEKKPKP-Dds8cy2g.js} +1 -1
  47. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-Die4-puP.js → sequenceDiagram-3UESZ5HK-BoMb08Ay.js} +1 -1
  48. package/dist/client/assets/{stateDiagram-AJRCARHV-TBbOyQST.js → stateDiagram-AJRCARHV-atm7kYDg.js} +1 -1
  49. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-EYJsus1Q.js +1 -0
  50. package/dist/client/assets/{timeline-definition-PNZ67QCA-CZ3E6e1X.js → timeline-definition-PNZ67QCA-C7U--kCB.js} +1 -1
  51. package/dist/client/assets/{vennDiagram-CIIHVFJN-CY_kofMf.js → vennDiagram-CIIHVFJN-BoPWwPSI.js} +1 -1
  52. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-TzBIV1Er.js → wardleyDiagram-YWT4CUSO-CJ6Y7g3b.js} +1 -1
  53. package/dist/client/assets/{xychartDiagram-2RQKCTM6-BTvKU2GO.js → xychartDiagram-2RQKCTM6-rN5shXue.js} +1 -1
  54. package/dist/client/index.html +2 -2
  55. package/dist/server/db.js +2 -0
  56. package/dist/server/db.js.map +1 -1
  57. package/dist/server/index.js +212 -238
  58. package/dist/server/index.js.map +1 -1
  59. package/dist/server/lib/agent.js +23 -7
  60. package/dist/server/lib/agent.js.map +1 -1
  61. package/dist/server/lib/agentProvider.js +62 -15
  62. package/dist/server/lib/agentProvider.js.map +1 -1
  63. package/dist/server/lib/bundleCleanupManagement.js +181 -0
  64. package/dist/server/lib/bundleCleanupManagement.js.map +1 -0
  65. package/dist/server/lib/bundleCleanupRoutes.js +36 -0
  66. package/dist/server/lib/bundleCleanupRoutes.js.map +1 -0
  67. package/dist/server/lib/bundleDeletion.js +257 -0
  68. package/dist/server/lib/bundleDeletion.js.map +1 -0
  69. package/dist/server/lib/bundleDeletionRoutes.js +73 -0
  70. package/dist/server/lib/bundleDeletionRoutes.js.map +1 -0
  71. package/dist/server/lib/bundleDeletionSchema.js +134 -0
  72. package/dist/server/lib/bundleDeletionSchema.js.map +1 -0
  73. package/dist/server/lib/bundleDeletionStore.js +18 -0
  74. package/dist/server/lib/bundleDeletionStore.js.map +1 -0
  75. package/dist/server/lib/bundleWorktreeCleanup.js +160 -0
  76. package/dist/server/lib/bundleWorktreeCleanup.js.map +1 -0
  77. package/dist/server/lib/codexAgentProvider.js +186 -123
  78. package/dist/server/lib/codexAgentProvider.js.map +1 -1
  79. package/dist/server/lib/diffExplanation.js +485 -0
  80. package/dist/server/lib/diffExplanation.js.map +1 -0
  81. package/dist/server/lib/diffExplanationJobs.js +605 -0
  82. package/dist/server/lib/diffExplanationJobs.js.map +1 -0
  83. package/dist/server/lib/diffExplanationRoutes.js +236 -0
  84. package/dist/server/lib/diffExplanationRoutes.js.map +1 -0
  85. package/dist/server/lib/dockerSandboxCleanup.js +68 -0
  86. package/dist/server/lib/dockerSandboxCleanup.js.map +1 -0
  87. package/dist/server/lib/dockerSandboxEnvironmentOperations.js +4 -3
  88. package/dist/server/lib/dockerSandboxEnvironmentOperations.js.map +1 -1
  89. package/dist/server/lib/dockerSandboxExecutionEnvironment.js +35 -59
  90. package/dist/server/lib/dockerSandboxExecutionEnvironment.js.map +1 -1
  91. package/dist/server/lib/dockerSandboxReleaseProfiles.js +1 -1
  92. package/dist/server/lib/dockerSandboxReleaseProfiles.js.map +1 -1
  93. package/dist/server/lib/executionEnvironmentOperations.js +7 -2
  94. package/dist/server/lib/executionEnvironmentOperations.js.map +1 -1
  95. package/dist/server/lib/executionEnvironmentReconciliation.js +34 -169
  96. package/dist/server/lib/executionEnvironmentReconciliation.js.map +1 -1
  97. package/dist/server/lib/executionEnvironmentTargetCleanup.js +232 -0
  98. package/dist/server/lib/executionEnvironmentTargetCleanup.js.map +1 -0
  99. package/dist/server/lib/executionEnvironments.js +1 -0
  100. package/dist/server/lib/executionEnvironments.js.map +1 -1
  101. package/dist/server/lib/executionTargets.js +1 -1
  102. package/dist/server/lib/executionTargets.js.map +1 -1
  103. package/dist/server/lib/git.js +205 -35
  104. package/dist/server/lib/git.js.map +1 -1
  105. package/dist/server/lib/i18n/resources.js +10 -0
  106. package/dist/server/lib/i18n/resources.js.map +1 -1
  107. package/dist/server/lib/lightweightPrompt.js +284 -52
  108. package/dist/server/lib/lightweightPrompt.js.map +1 -1
  109. package/dist/server/lib/oneShotCancellation.js +54 -0
  110. package/dist/server/lib/oneShotCancellation.js.map +1 -0
  111. package/dist/server/lib/opencodeExecutionReliability.js +46 -2
  112. package/dist/server/lib/opencodeExecutionReliability.js.map +1 -1
  113. package/dist/server/lib/projectMemoryScan.js +3 -46
  114. package/dist/server/lib/projectMemoryScan.js.map +1 -1
  115. package/dist/server/lib/promptDiffSafety.js +53 -0
  116. package/dist/server/lib/promptDiffSafety.js.map +1 -0
  117. package/dist/server/lib/repositorySecrets.js +69 -0
  118. package/dist/server/lib/repositorySecrets.js.map +1 -0
  119. package/dist/server/lib/tickets.js +3 -2
  120. package/dist/server/lib/tickets.js.map +1 -1
  121. package/dist/server/lib/types.js.map +1 -1
  122. package/dist/server/lib/unifiedDiff.js +459 -0
  123. package/dist/server/lib/unifiedDiff.js.map +1 -0
  124. package/dist/server/lib/workspaceDiff.js +156 -0
  125. package/dist/server/lib/workspaceDiff.js.map +1 -0
  126. package/dist/server/lib/workspaceDiffRoutes.js +63 -0
  127. package/dist/server/lib/workspaceDiffRoutes.js.map +1 -0
  128. package/dist/server/lib/workspaceTargets.js +88 -0
  129. package/dist/server/lib/workspaceTargets.js.map +1 -0
  130. package/dist/server/workers/chat.js +71 -1
  131. package/dist/server/workers/chat.js.map +1 -1
  132. package/dist/server/workers/plan.js +24 -2
  133. package/dist/server/workers/plan.js.map +1 -1
  134. package/dist/server/workers/review.js +19 -13
  135. package/dist/server/workers/review.js.map +1 -1
  136. package/package.json +1 -1
  137. package/dist/client/assets/channel-Cf2U7nU4.js +0 -1
  138. package/dist/client/assets/classDiagram-4FO5ZUOK-CK8VsU-L.js +0 -1
  139. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-RvySOEwt.js +0 -1
  140. package/dist/client/assets/index-B9-M1vEw.js +0 -287
  141. package/dist/client/assets/index-CbYT3N3U.css +0 -2
  142. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-BRotXz7h.js +0 -1
@@ -1,5 +1,9 @@
1
1
  import { markRetryableOpenCodeDatabaseLock, retryOpenCodeDatabaseLocks } from './opencodeExecutionReliability.js';
2
+ import { getAbortReason, observeChildProcessClose, OneShotCancellationTerminationError, terminateOneShotProcess, } from './oneShotCancellation.js';
2
3
  const runOneShotAgentOnce = (agentProvider, executionProvider, request, executionContext) => {
4
+ if (request.signal?.aborted) {
5
+ return Promise.reject(getAbortReason(request.signal));
6
+ }
3
7
  const invocation = agentProvider.buildInvocation(request);
4
8
  return new Promise((resolve, reject) => {
5
9
  let handle;
@@ -25,20 +29,54 @@ const runOneShotAgentOnce = (agentProvider, executionProvider, request, executio
25
29
  const stdoutChunks = [];
26
30
  const stderrChunks = [];
27
31
  let settled = false;
28
- handle.child.stdout?.on('data', chunk => stdoutChunks.push(Buffer.from(chunk)));
29
- handle.child.stderr?.on('data', chunk => stderrChunks.push(Buffer.from(chunk)));
30
- void handle.placementFailure.then(failure => {
31
- if (!failure || settled) {
32
+ let cancellationRequested = false;
33
+ const childClose = request.signal ? observeChildProcessClose(handle.child) : null;
34
+ const onStdoutData = (chunk) => stdoutChunks.push(Buffer.from(chunk));
35
+ const onStderrData = (chunk) => stderrChunks.push(Buffer.from(chunk));
36
+ const cleanup = () => {
37
+ request.signal?.removeEventListener('abort', onAbort);
38
+ handle.child.stdout?.removeListener?.('data', onStdoutData);
39
+ handle.child.stderr?.removeListener?.('data', onStderrData);
40
+ handle.child.removeListener?.('close', onClose);
41
+ handle.child.removeListener?.('error', onError);
42
+ childClose?.dispose();
43
+ };
44
+ const rejectOnce = (error) => {
45
+ if (settled || cancellationRequested) {
32
46
  return;
33
47
  }
34
48
  settled = true;
35
- reject(agentProvider.classifyExecutionFailure(failure));
36
- });
37
- handle.child.on('close', code => {
38
- if (settled) {
49
+ cleanup();
50
+ reject(error);
51
+ };
52
+ const onAbort = () => {
53
+ if (settled || cancellationRequested) {
54
+ return;
55
+ }
56
+ cancellationRequested = true;
57
+ const reason = getAbortReason(request.signal);
58
+ void terminateOneShotProcess(handle.interrupt, childClose.promise).then(() => {
59
+ if (settled) {
60
+ return;
61
+ }
62
+ settled = true;
63
+ cleanup();
64
+ reject(reason);
65
+ }, error => {
66
+ if (settled) {
67
+ return;
68
+ }
69
+ settled = true;
70
+ cleanup();
71
+ reject(new OneShotCancellationTerminationError(reason, { cause: error }));
72
+ });
73
+ };
74
+ const onClose = (code) => {
75
+ if (settled || cancellationRequested) {
39
76
  return;
40
77
  }
41
78
  settled = true;
79
+ cleanup();
42
80
  const stderr = Buffer.concat(stderrChunks).toString().trim();
43
81
  const result = agentProvider.interpretOneShotOutput(Buffer.concat(stdoutChunks).toString(), stderr);
44
82
  if (result.error) {
@@ -56,18 +94,27 @@ const runOneShotAgentOnce = (agentProvider, executionProvider, request, executio
56
94
  reject(request.retryOpenCodeDatabaseLocks && stdoutChunks.length === 0
57
95
  ? markRetryableOpenCodeDatabaseLock(error)
58
96
  : error);
59
- });
60
- handle.child.on('error', error => {
61
- if (settled) {
62
- return;
97
+ };
98
+ const onError = (error) => rejectOnce(agentProvider.classifyExecutionFailure(error));
99
+ handle.child.stdout?.on('data', onStdoutData);
100
+ handle.child.stderr?.on('data', onStderrData);
101
+ handle.child.on('close', onClose);
102
+ handle.child.on('error', onError);
103
+ request.signal?.addEventListener('abort', onAbort, { once: true });
104
+ if (request.signal?.aborted) {
105
+ onAbort();
106
+ }
107
+ void handle.placementFailure.then(failure => {
108
+ if (failure) {
109
+ rejectOnce(agentProvider.classifyExecutionFailure(failure));
63
110
  }
64
- settled = true;
65
- reject(agentProvider.classifyExecutionFailure(error));
66
111
  });
67
112
  });
68
113
  };
69
114
  export const runOneShotAgent = (agentProvider, executionProvider, request, executionContext) => {
70
115
  const run = () => runOneShotAgentOnce(agentProvider, executionProvider, request, executionContext);
71
- return request.retryOpenCodeDatabaseLocks ? retryOpenCodeDatabaseLocks(run) : run();
116
+ return request.retryOpenCodeDatabaseLocks
117
+ ? retryOpenCodeDatabaseLocks(run, { signal: request.signal })
118
+ : run();
72
119
  };
73
120
  //# sourceMappingURL=agentProvider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"agentProvider.js","sourceRoot":"","sources":["../../../server/lib/agentProvider.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,iCAAiC,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAA;AA8EjH,MAAM,mBAAmB,GAAG,CAC1B,aAA4B,EAC5B,iBAA+C,EAC/C,OAA4B,EAC5B,gBAAyC,EACxB,EAAE;IACnB,MAAM,UAAU,GAAG,aAAa,CAAC,eAAe,CAAC,OAAO,CAAC,CAAA;IAEzD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,MAA2D,CAAA;QAC/D,IAAI,CAAC;YACH,MAAM,GAAG,iBAAiB,CAAC,OAAO,CAAC;gBACjC,KAAK,EAAE,OAAO;gBACd,MAAM,EAAE,gBAAgB,CAAC,MAAM;gBAC/B,OAAO,EAAE,UAAU,CAAC,OAAO;gBAC3B,IAAI,EAAE,UAAU,CAAC,IAAI;gBACrB,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,KAAK,EAAE,UAAU,CAAC,KAAK;gBACvB,mBAAmB,EAAE,gBAAgB,CAAC,mBAAmB;gBACzD,kBAAkB,EAAE,gBAAgB,CAAC,kBAAkB;gBACvD,QAAQ,EAAE,KAAK;gBACf,uBAAuB,EAAE,gBAAgB,CAAC,uBAAuB;gBACjE,wBAAwB,EAAE,gBAAgB,CAAC,wBAAwB;aACpE,CAAC,CAAA;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,aAAa,CAAC,wBAAwB,CAAC,KAAK,CAAC,CAAC,CAAA;YACrD,OAAM;QACR,CAAC;QAED,MAAM,YAAY,GAAa,EAAE,CAAA;QACjC,MAAM,YAAY,GAAa,EAAE,CAAA;QACjC,IAAI,OAAO,GAAG,KAAK,CAAA;QAEnB,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAC/E,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAE/E,KAAK,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;YAC1C,IAAI,CAAC,OAAO,IAAI,OAAO,EAAE,CAAC;gBACxB,OAAM;YACR,CAAC;YACD,OAAO,GAAG,IAAI,CAAA;YACd,MAAM,CAAC,aAAa,CAAC,wBAAwB,CAAC,OAAO,CAAC,CAAC,CAAA;QACzD,CAAC,CAAC,CAAA;QAEF,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE;YAC9B,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAM;YACR,CAAC;YACD,OAAO,GAAG,IAAI,CAAA;YAEd,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAA;YAC5D,MAAM,MAAM,GAAG,aAAa,CAAC,sBAAsB,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAA;YACnG,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;gBACjB,MAAM,CAAC,OAAO,CAAC,0BAA0B,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;oBACpE,CAAC,CAAC,iCAAiC,CAAC,MAAM,CAAC,KAAK,CAAC;oBACjD,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;gBACjB,OAAM;YACR,CAAC;YAED,IAAI,IAAI,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAChC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;gBACtB,OAAM;YACR,CAAC;YAED,MAAM,aAAa,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,4BAA4B,IAAI,EAAE,CAAA;YAClG,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,IAAI,aAAa,CAAC,CAAA;YACjE,MAAM,CAAC,OAAO,CAAC,0BAA0B,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;gBACpE,CAAC,CAAC,iCAAiC,CAAC,KAAK,CAAC;gBAC1C,CAAC,CAAC,KAAK,CAAC,CAAA;QACZ,CAAC,CAAC,CAAA;QAEF,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE;YAC/B,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAM;YACR,CAAC;YACD,OAAO,GAAG,IAAI,CAAA;YACd,MAAM,CAAC,aAAa,CAAC,wBAAwB,CAAC,KAAK,CAAC,CAAC,CAAA;QACvD,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,aAA4B,EAC5B,iBAA+C,EAC/C,OAA4B,EAC5B,gBAAyC,EACxB,EAAE;IACnB,MAAM,GAAG,GAAG,GAAG,EAAE,CAAC,mBAAmB,CAAC,aAAa,EAAE,iBAAiB,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAA;IAClG,OAAO,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,0BAA0B,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAA;AACrF,CAAC,CAAA"}
1
+ {"version":3,"file":"agentProvider.js","sourceRoot":"","sources":["../../../server/lib/agentProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,181 @@
1
+ import { randomUUID } from 'crypto';
2
+ import db from '../db.js';
3
+ import { recoverStaleBundleDeletionClaims } from './bundleDeletion.js';
4
+ import { getBundleDeletionJob } from './bundleDeletionStore.js';
5
+ import { createDefaultDockerSandboxAdapter, inspectionMatches, resolveExpectedDockerSandbox } from './executionEnvironmentTargetCleanup.js';
6
+ import { translateServerMessage } from './i18n/index.js';
7
+ export class BundleCleanupConflict extends Error {
8
+ }
9
+ const requireJob = (projectId, bundleId) => {
10
+ const job = getBundleDeletionJob(projectId, bundleId);
11
+ if (!job) {
12
+ throw new BundleCleanupConflict(translateServerMessage('bundleCleanup.notFound'));
13
+ }
14
+ return job;
15
+ };
16
+ const environmentRows = (job) => db.prepare('SELECT * FROM execution_environments WHERE project_id = ? AND target_key = ?')
17
+ .all(job.project_id, job.target_key);
18
+ const assertIdle = (job) => {
19
+ if (job.claim_id) {
20
+ throw new BundleCleanupConflict(translateServerMessage('bundleCleanup.active'));
21
+ }
22
+ const busy = db.prepare(`SELECT 1 FROM execution_environments environment
23
+ WHERE project_id = ? AND target_key = ? AND (operation_claim_id IS NOT NULL OR cleanup_claim_id IS NOT NULL
24
+ OR EXISTS (SELECT 1 FROM execution_environment_run_bindings binding
25
+ WHERE binding.environment_id = environment.id AND binding.project_id = environment.project_id AND binding.released_at IS NULL)) LIMIT 1`)
26
+ .get(job.project_id, job.target_key);
27
+ if (busy) {
28
+ throw new BundleCleanupConflict(translateServerMessage('bundleCleanup.resourcesBusy'));
29
+ }
30
+ };
31
+ const remainingResources = (job, rows = environmentRows(job)) => {
32
+ if (job.status === 'abandoned') {
33
+ return JSON.parse(job.resources_json);
34
+ }
35
+ if (job.status === 'completed') {
36
+ return [];
37
+ }
38
+ const resources = rows.filter(row => row.lifecycle_state !== 'retired').map(row => ({
39
+ kind: 'sandbox', name: row.external_id ?? row.id, state: row.lifecycle_state,
40
+ }));
41
+ if (job.phase === 'sandboxes' || job.phase === 'worktree') {
42
+ resources.push({ kind: 'worktree', name: job.workspace_path });
43
+ }
44
+ resources.push({ kind: 'branch', name: job.branch });
45
+ return resources;
46
+ };
47
+ export const serializeBundleCleanup = (job) => ({
48
+ bundle_id: job.bundle_id, project_id: job.project_id, project_name: job.project_name,
49
+ branch: job.branch, workspace_path: job.workspace_path, phase: job.phase, status: job.status,
50
+ attempts: job.attempts, retry_at: job.retry_at, last_error: job.last_error, failure_code: job.failure_code,
51
+ quarantined: Boolean(job.quarantined), active: Boolean(job.claim_id),
52
+ is_archived: !db.prepare('SELECT 1 FROM projects WHERE id = ?').get(job.project_id),
53
+ remaining_resources: remainingResources(job),
54
+ });
55
+ export const listBundleCleanups = (projectId) => db.prepare(`SELECT * FROM bundle_deletion_jobs job
56
+ WHERE project_id = ? OR (status = 'abandoned' AND NOT EXISTS (SELECT 1 FROM projects WHERE id = job.project_id))
57
+ ORDER BY created_at DESC`).all(projectId).map(serializeBundleCleanup);
58
+ export const retryBundleCleanup = (projectId, bundleId) => {
59
+ recoverStaleBundleDeletionClaims();
60
+ return db.transaction(() => {
61
+ const job = requireJob(projectId, bundleId);
62
+ assertIdle(job);
63
+ if (!['pending', 'blocked'].includes(job.status) || !db.prepare('SELECT 1 FROM projects WHERE id = ? AND deletion_started_at IS NULL').get(projectId)) {
64
+ throw new BundleCleanupConflict(translateServerMessage('bundleCleanup.retryNotAllowed'));
65
+ }
66
+ db.prepare(`UPDATE bundle_deletion_jobs SET status = 'pending', retry_attempts = 0, retry_at = NULL,
67
+ failure_code = NULL, last_error = NULL WHERE bundle_id = ? AND project_id = ?`).run(bundleId, projectId);
68
+ return requireJob(projectId, bundleId);
69
+ }).immediate();
70
+ };
71
+ const withManagementClaim = async (projectId, bundleId, status, action) => {
72
+ recoverStaleBundleDeletionClaims();
73
+ const claimId = randomUUID();
74
+ const job = db.transaction(() => {
75
+ const current = requireJob(projectId, bundleId);
76
+ assertIdle(current);
77
+ if (status === 'abandoned' ? current.status !== 'abandoned' : !['pending', 'blocked'].includes(current.status)) {
78
+ throw new BundleCleanupConflict(translateServerMessage('bundleCleanup.statusChanged'));
79
+ }
80
+ db.prepare('UPDATE bundle_deletion_jobs SET claim_id = ?, claimed_at = ? WHERE bundle_id = ? AND project_id = ?')
81
+ .run(claimId, new Date().toISOString(), bundleId, projectId);
82
+ return current;
83
+ }).immediate();
84
+ const heartbeat = setInterval(() => {
85
+ db.prepare('UPDATE bundle_deletion_jobs SET claimed_at = ? WHERE bundle_id = ? AND claim_id = ?')
86
+ .run(new Date().toISOString(), bundleId, claimId);
87
+ }, 30_000);
88
+ heartbeat.unref();
89
+ try {
90
+ await action(job, claimId);
91
+ }
92
+ finally {
93
+ clearInterval(heartbeat);
94
+ db.prepare('UPDATE bundle_deletion_jobs SET claim_id = NULL, claimed_at = NULL WHERE bundle_id = ? AND claim_id = ?')
95
+ .run(bundleId, claimId);
96
+ }
97
+ return requireJob(projectId, bundleId);
98
+ };
99
+ const verifyStoppedOrAbsent = async (rows, adapter) => {
100
+ const resources = [];
101
+ let safe = true;
102
+ for (const row of rows.filter(row => row.lifecycle_state !== 'retired')) {
103
+ let state = 'unverified';
104
+ try {
105
+ const expected = resolveExpectedDockerSandbox(row);
106
+ const observed = await adapter.inspect(expected.name, { timeoutMs: 10_000 });
107
+ if (!observed) {
108
+ state = 'absent';
109
+ }
110
+ else if (inspectionMatches(expected, observed)) {
111
+ state = observed.state;
112
+ }
113
+ }
114
+ catch {
115
+ // Failed observation is not termination proof and must preserve quarantine.
116
+ }
117
+ if (state !== 'absent' && state !== 'stopped') {
118
+ safe = false;
119
+ }
120
+ resources.push({ kind: 'sandbox', name: row.external_id ?? row.id, state });
121
+ }
122
+ return { safe, resources };
123
+ };
124
+ export const abandonBundleCleanup = (projectId, bundleId, adapter = createDefaultDockerSandboxAdapter()) => withManagementClaim(projectId, bundleId, 'pending', async (job, claimId) => {
125
+ const rows = environmentRows(job);
126
+ const proof = await verifyStoppedOrAbsent(rows, adapter);
127
+ return db.transaction(() => {
128
+ const current = requireJob(projectId, bundleId);
129
+ if (current.claim_id !== claimId || current.status !== job.status) {
130
+ throw new BundleCleanupConflict(translateServerMessage('bundleCleanup.claimLost'));
131
+ }
132
+ const snapshot = rows.map(row => ({ environment: row,
133
+ events: db.prepare('SELECT * FROM execution_environment_events WHERE project_id = ? AND environment_id = ?').all(projectId, row.id),
134
+ bindings: db.prepare('SELECT * FROM execution_environment_run_bindings WHERE project_id = ? AND environment_id = ?').all(projectId, row.id),
135
+ }));
136
+ const resources = [...proof.resources, ...remainingResources(job, []).filter(resource => resource.kind !== 'sandbox')];
137
+ db.prepare(`UPDATE bundle_deletion_jobs SET status = 'abandoned', retry_at = NULL, abandoned_at = ?,
138
+ quarantined = ?, resources_json = ?, environment_snapshot_json = ? WHERE bundle_id = ? AND claim_id = ?`)
139
+ .run(new Date().toISOString(), proof.safe ? 0 : 1, JSON.stringify(resources), JSON.stringify(snapshot), bundleId, claimId);
140
+ if (!proof.safe) {
141
+ for (const path of new Set([job.workspace_path, ...rows.filter(row => row.lifecycle_state !== 'retired').map(row => row.target_workspace_path)])) {
142
+ db.prepare('INSERT OR IGNORE INTO bundle_cleanup_quarantines (bundle_id, workspace_path, repo_path, branch) VALUES (?, ?, ?, ?)')
143
+ .run(bundleId, path, job.repo_path, job.branch);
144
+ }
145
+ }
146
+ return requireJob(projectId, bundleId);
147
+ }).immediate();
148
+ });
149
+ export const releaseBundleCleanupQuarantine = (projectId, bundleId, adapter = createDefaultDockerSandboxAdapter()) => withManagementClaim(projectId, bundleId, 'abandoned', async (job, claimId) => {
150
+ let snapshots;
151
+ try {
152
+ const parsed = JSON.parse(job.environment_snapshot_json);
153
+ const states = ['queued', 'preparing', 'ready', 'running', 'stopped', 'retiring', 'cleanup_pending', 'retired', 'failed', 'unavailable'];
154
+ if (!Array.isArray(parsed) || (job.quarantined && parsed.length === 0)
155
+ || !parsed.every(item => item && typeof item === 'object' && item.environment
156
+ && item.environment.project_id === job.project_id && item.environment.target_key === job.target_key
157
+ && typeof item.environment.id === 'string' && typeof item.environment.target_workspace_path === 'string'
158
+ && states.includes(item.environment.lifecycle_state))) {
159
+ throw new Error('Invalid archived ownership evidence');
160
+ }
161
+ snapshots = parsed;
162
+ }
163
+ catch {
164
+ throw new BundleCleanupConflict(translateServerMessage('bundleCleanup.archiveUnverified'));
165
+ }
166
+ const proof = await verifyStoppedOrAbsent(snapshots.map(snapshot => snapshot.environment), adapter);
167
+ if (!proof.safe) {
168
+ throw new BundleCleanupConflict(translateServerMessage('bundleCleanup.terminationUnverified'));
169
+ }
170
+ return db.transaction(() => {
171
+ if (requireJob(projectId, bundleId).claim_id !== claimId) {
172
+ throw new BundleCleanupConflict(translateServerMessage('bundleCleanup.claimLost'));
173
+ }
174
+ db.prepare('DELETE FROM bundle_cleanup_quarantines WHERE bundle_id = ?').run(bundleId);
175
+ const resources = [...proof.resources, ...remainingResources(job).filter(resource => resource.kind !== 'sandbox')];
176
+ db.prepare('UPDATE bundle_deletion_jobs SET quarantined = 0, resources_json = ? WHERE bundle_id = ? AND claim_id = ?')
177
+ .run(JSON.stringify(resources), bundleId, claimId);
178
+ return requireJob(projectId, bundleId);
179
+ }).immediate();
180
+ });
181
+ //# sourceMappingURL=bundleCleanupManagement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundleCleanupManagement.js","sourceRoot":"","sources":["../../../server/lib/bundleCleanupManagement.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { abandonBundleCleanup, BundleCleanupConflict, listBundleCleanups, releaseBundleCleanupQuarantine, retryBundleCleanup, serializeBundleCleanup } from './bundleCleanupManagement.js';
2
+ import { attemptBundleDeletion } from './bundleDeletion.js';
3
+ export const registerBundleCleanupRoutes = (app) => {
4
+ const projectId = (req) => typeof req.query.project_id === 'string' ? req.query.project_id : '';
5
+ app.get('/bundle-cleanups', (req, res) => {
6
+ if (!projectId(req)) {
7
+ return res.status(400).json({ error: 'project_id is required' });
8
+ }
9
+ return res.json({ jobs: listBundleCleanups(projectId(req)) });
10
+ });
11
+ const actions = {
12
+ retry: async (projectId, bundleId) => {
13
+ retryBundleCleanup(projectId, bundleId);
14
+ return (await attemptBundleDeletion(projectId, bundleId));
15
+ },
16
+ abandon: abandonBundleCleanup,
17
+ 'release-quarantine': releaseBundleCleanupQuarantine,
18
+ };
19
+ for (const [action, perform] of Object.entries(actions)) {
20
+ app.post(`/bundle-cleanups/:id/${action}`, async (req, res) => {
21
+ if (!projectId(req)) {
22
+ return res.status(400).json({ error: 'project_id is required' });
23
+ }
24
+ try {
25
+ return res.json({ job: serializeBundleCleanup(await perform(projectId(req), req.params.id)) });
26
+ }
27
+ catch (error) {
28
+ if (error instanceof BundleCleanupConflict) {
29
+ return res.status(409).json({ error: error.message });
30
+ }
31
+ throw error;
32
+ }
33
+ });
34
+ }
35
+ };
36
+ //# sourceMappingURL=bundleCleanupRoutes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundleCleanupRoutes.js","sourceRoot":"","sources":["../../../server/lib/bundleCleanupRoutes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,8BAA8B,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAA;AAC1L,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAE3D,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,GAAY,EAAE,EAAE;IAC1D,MAAM,SAAS,GAAG,CAAC,GAAY,EAAE,EAAE,CAAC,OAAO,GAAG,CAAC,KAAK,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAA;IACxG,GAAG,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACvC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAC,CAAA;QAClE,CAAC;QACD,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,kBAAkB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAA;IAC/D,CAAC,CAAC,CAAA;IACF,MAAM,OAAO,GAAG;QACd,KAAK,EAAE,KAAK,EAAE,SAAiB,EAAE,QAAgB,EAAE,EAAE;YACnD,kBAAkB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;YACvC,OAAO,CAAC,MAAM,qBAAqB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAE,CAAA;QAC5D,CAAC;QACD,OAAO,EAAE,oBAAoB;QAC7B,oBAAoB,EAAE,8BAA8B;KACrD,CAAA;IACD,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACxD,GAAG,CAAC,IAAI,CAAC,wBAAwB,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;YAC5D,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;gBACpB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAC,CAAA;YAClE,CAAC;YACD,IAAI,CAAC;gBACH,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,sBAAsB,CAAC,MAAM,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAA;YAChG,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,KAAK,YAAY,qBAAqB,EAAE,CAAC;oBAC3C,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;gBACvD,CAAC;gBACD,MAAM,KAAK,CAAA;YACb,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;AACH,CAAC,CAAA"}
@@ -0,0 +1,257 @@
1
+ import { randomUUID } from 'crypto';
2
+ import { resolve } from 'path';
3
+ import db from '../db.js';
4
+ import { getBundleDeletionJob } from './bundleDeletionStore.js';
5
+ import { deleteBundleBranch, deleteOwnedBundleWorktree } from './bundleWorktreeCleanup.js';
6
+ import { DockerSandboxCollisionFailure, SbxContractFailure } from './dockerSandboxExecutionEnvironment.js';
7
+ import { cleanupExecutionEnvironmentsForTarget } from './executionEnvironmentReconciliation.js';
8
+ import { requestExecutionEnvironmentRetirement } from './executionEnvironments.js';
9
+ import { createExecutionTargetKey } from './executionTargets.js';
10
+ import { resolveWorktreePath } from './git.js';
11
+ import { countBundleTickets, deleteBundle, getBundle, isProjectRootBundle } from './tickets.js';
12
+ export class BundleDeletionConflict extends Error {
13
+ }
14
+ export const beginBundleDeletion = (input) => {
15
+ // Resolve before the transaction; every mutable condition is rechecked inside it.
16
+ const bundle = getBundle(input.bundleId, input.project.id);
17
+ const existing = getBundleDeletionJob(input.project.id, input.bundleId);
18
+ if (existing) {
19
+ return existing;
20
+ }
21
+ if (!bundle) {
22
+ throw new BundleDeletionConflict('Bundle not found');
23
+ }
24
+ const workspacePath = resolveWorktreePath(bundle.branch, input.project);
25
+ return db.transaction(() => {
26
+ const current = getBundle(input.bundleId, input.project.id);
27
+ const project = db.prepare('SELECT * FROM projects WHERE id = ?').get(input.project.id);
28
+ if (!current || !project || project.deletion_started_at || current.branch !== bundle.branch || project.repo_path !== input.project.repo_path) {
29
+ throw new BundleDeletionConflict('Bundle target changed before deletion could be accepted');
30
+ }
31
+ input.assertEligible();
32
+ if (isProjectRootBundle(current) || countBundleTickets(current.id, project.id) > 0
33
+ || (project.active_target_kind === 'bundle' && project.active_target_bundle_id === current.id)) {
34
+ throw new BundleDeletionConflict('Bundle is still in use');
35
+ }
36
+ const targetKey = createExecutionTargetKey(project.id, { kind: 'bundle-worktree', bundleId: current.id, branch: current.branch });
37
+ const busy = db.prepare(`SELECT 1 FROM execution_environments environment
38
+ WHERE environment.project_id = ? AND environment.target_key = ? AND (
39
+ environment.operation_claim_id IS NOT NULL OR environment.cleanup_claim_id IS NOT NULL
40
+ OR EXISTS (SELECT 1 FROM execution_environment_run_bindings binding
41
+ WHERE binding.environment_id = environment.id AND binding.project_id = environment.project_id AND binding.released_at IS NULL))
42
+ LIMIT 1`).get(project.id, targetKey);
43
+ if (busy) {
44
+ throw new BundleDeletionConflict('Bundle execution environment is still in use');
45
+ }
46
+ db.prepare(`INSERT INTO bundle_deletion_jobs
47
+ (bundle_id, project_id, project_name, target_key, repo_path, branch, workspace_path, created_at)
48
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(current.id, project.id, project.name, targetKey, project.repo_path, current.branch, workspacePath, new Date().toISOString());
49
+ const environments = db.prepare(`SELECT id FROM execution_environments
50
+ WHERE project_id = ? AND target_key = ? AND lifecycle_state <> 'retired'`).all(project.id, targetKey);
51
+ for (const environment of environments) {
52
+ requestExecutionEnvironmentRetirement({ projectId: project.id, environmentId: environment.id });
53
+ }
54
+ deleteBundle(current.id, project.id);
55
+ return getBundleDeletionJob(project.id, current.id);
56
+ }).immediate();
57
+ };
58
+ const defaults = {
59
+ cleanup: cleanupExecutionEnvironmentsForTarget,
60
+ removeWorktree: deleteOwnedBundleWorktree,
61
+ removeBranch: deleteBundleBranch,
62
+ };
63
+ const inFlight = new Map();
64
+ const CLAIM_TTL_MS = 5 * 60_000;
65
+ const MAX_AUTOMATIC_ATTEMPTS = 8;
66
+ const permanentFailureCode = (error, visited = new Set()) => {
67
+ if (!(error instanceof Error) || visited.has(error)) {
68
+ return null;
69
+ }
70
+ visited.add(error);
71
+ if (error instanceof DockerSandboxCollisionFailure) {
72
+ return 'ownership_mismatch';
73
+ }
74
+ if (error instanceof SbxContractFailure && !/^sbx \S+ exited with code \d+/.test(error.message)) {
75
+ return 'sandbox_contract';
76
+ }
77
+ if (/identity changed|incomplete.*ownership|ownership.*incomplete|Refusing to remove a repository root workspace|no longer a registered worktree|Bundle cleanup requires a branch, repository/i.test(error.message)) {
78
+ return 'ownership_mismatch';
79
+ }
80
+ const causes = error instanceof AggregateError ? [...error.errors, error.cause] : [error.cause];
81
+ for (const cause of causes) {
82
+ const code = permanentFailureCode(cause, visited);
83
+ if (code) {
84
+ return code;
85
+ }
86
+ }
87
+ return null;
88
+ };
89
+ export const recoverStaleBundleDeletionClaims = () => {
90
+ const cutoff = new Date(Date.now() - CLAIM_TTL_MS).toISOString();
91
+ const jobs = db.prepare(`SELECT * FROM bundle_deletion_jobs
92
+ WHERE status IN ('pending', 'blocked', 'abandoned') AND claim_id IS NOT NULL AND claimed_at < ?`)
93
+ .all(cutoff);
94
+ let recovered = 0;
95
+ for (const job of jobs) {
96
+ if (inFlight.has(`${job.project_id}:${job.bundle_id}`)) {
97
+ continue;
98
+ }
99
+ recovered += db.transaction(() => {
100
+ const freshEnvironment = db.prepare(`SELECT 1 FROM execution_environments
101
+ WHERE cleanup_claim_id = ? AND (cleanup_claimed_at IS NULL OR cleanup_claimed_at >= ?) LIMIT 1`)
102
+ .get(job.claim_id, cutoff);
103
+ if (freshEnvironment) {
104
+ return 0;
105
+ }
106
+ const result = db.prepare(`UPDATE bundle_deletion_jobs SET claim_id = NULL, claimed_at = NULL
107
+ WHERE bundle_id = ? AND project_id = ? AND claim_id = ? AND claimed_at < ?
108
+ AND status IN ('pending', 'blocked', 'abandoned')`)
109
+ .run(job.bundle_id, job.project_id, job.claim_id, cutoff);
110
+ if (result.changes === 1) {
111
+ db.prepare(`UPDATE execution_environments SET cleanup_claim_id = NULL, cleanup_claimed_at = NULL
112
+ WHERE project_id = ? AND target_key = ? AND cleanup_claim_id = ? AND cleanup_claimed_at < ?`)
113
+ .run(job.project_id, job.target_key, job.claim_id, cutoff);
114
+ }
115
+ return result.changes;
116
+ }).immediate();
117
+ }
118
+ // Legacy environment operations may predate target reservation. Recover only
119
+ // expired, unbound ownership, never abandoned archives or fresh job claims.
120
+ db.prepare(`UPDATE execution_environments SET operation_claim_id = NULL, operation_kind = NULL, operation_claimed_at = NULL
121
+ WHERE operation_claimed_at < ? AND NOT EXISTS (
122
+ SELECT 1 FROM execution_environment_run_bindings binding WHERE binding.environment_id = execution_environments.id
123
+ AND binding.project_id = execution_environments.project_id AND binding.released_at IS NULL)
124
+ AND EXISTS (SELECT 1 FROM bundle_deletion_jobs job WHERE job.project_id = execution_environments.project_id
125
+ AND job.target_key = execution_environments.target_key AND job.status IN ('pending', 'blocked')
126
+ AND (job.claim_id IS NULL OR job.claimed_at < ?))`).run(cutoff, cutoff);
127
+ return recovered;
128
+ };
129
+ const runClaimedJob = async (job, dependencies) => {
130
+ db.prepare(`UPDATE bundle_deletion_jobs SET status = 'blocked', retry_at = NULL, failure_code = 'retry_exhausted'
131
+ WHERE bundle_id = ? AND status = 'pending' AND phase <> 'completed' AND claim_id IS NULL AND retry_attempts >= ?`)
132
+ .run(job.bundle_id, MAX_AUTOMATIC_ATTEMPTS);
133
+ const claimId = randomUUID();
134
+ const claimed = db.prepare(`UPDATE bundle_deletion_jobs SET claim_id = ?, claimed_at = ?, attempts = attempts + 1,
135
+ retry_attempts = retry_attempts + 1
136
+ WHERE bundle_id = ? AND status = 'pending' AND phase <> 'completed' AND retry_attempts < ? AND claim_id IS NULL
137
+ AND (retry_at IS NULL OR retry_at <= ?)`).run(claimId, new Date().toISOString(), job.bundle_id, MAX_AUTOMATIC_ATTEMPTS, new Date().toISOString());
138
+ if (claimed.changes !== 1) {
139
+ return getBundleDeletionJob(job.project_id, job.bundle_id);
140
+ }
141
+ const heartbeat = setInterval(() => {
142
+ const now = new Date().toISOString();
143
+ db.transaction(() => {
144
+ const result = db.prepare("UPDATE bundle_deletion_jobs SET claimed_at = ? WHERE bundle_id = ? AND claim_id = ? AND status = 'pending'")
145
+ .run(now, job.bundle_id, claimId);
146
+ if (result.changes === 1) {
147
+ db.prepare('UPDATE execution_environments SET cleanup_claimed_at = ? WHERE project_id = ? AND target_key = ? AND cleanup_claim_id = ?')
148
+ .run(now, job.project_id, job.target_key, claimId);
149
+ }
150
+ }).immediate();
151
+ }, 30_000);
152
+ heartbeat.unref();
153
+ const advance = (phase) => {
154
+ const result = db.prepare(`UPDATE bundle_deletion_jobs SET phase = ?, status = ?, retry_at = NULL,
155
+ last_error = NULL, failure_code = NULL WHERE bundle_id = ? AND claim_id = ? AND status = 'pending'`)
156
+ .run(phase, phase === 'completed' ? 'completed' : 'pending', job.bundle_id, claimId);
157
+ if (result.changes !== 1) {
158
+ throw new Error('Bundle deletion claim was lost');
159
+ }
160
+ job.phase = phase;
161
+ };
162
+ try {
163
+ const requireOwnedProject = () => {
164
+ const project = db.prepare('SELECT * FROM projects WHERE id = ?').get(job.project_id);
165
+ if (!project || project.repo_path !== job.repo_path) {
166
+ throw new Error('Bundle deletion repository identity changed');
167
+ }
168
+ if (project.deletion_started_at) {
169
+ throw new Error('Project deletion is already cleaning this workspace');
170
+ }
171
+ const current = getBundleDeletionJob(job.project_id, job.bundle_id);
172
+ if (current?.claim_id !== claimId || current.status !== 'pending') {
173
+ throw new Error('Bundle deletion claim was lost');
174
+ }
175
+ return project;
176
+ };
177
+ requireOwnedProject();
178
+ if (job.phase === 'sandboxes') {
179
+ requireOwnedProject();
180
+ await dependencies.cleanup({ projectId: job.project_id, targetKind: 'bundle-worktree', targetKey: job.target_key,
181
+ claimId, commandOptions: { timeoutMs: 10_000 } });
182
+ advance('worktree');
183
+ }
184
+ if (job.phase === 'worktree') {
185
+ if (resolve(job.workspace_path) === resolve(job.repo_path)) {
186
+ throw new Error('Bundle deletion workspace identity changed');
187
+ }
188
+ await dependencies.removeWorktree(job.branch, requireOwnedProject(), job.workspace_path);
189
+ advance('branch');
190
+ }
191
+ if (job.phase === 'branch') {
192
+ const result = await dependencies.removeBranch(job.branch, requireOwnedProject());
193
+ if (result === 'failed') {
194
+ throw new Error('Bundle branch cleanup failed');
195
+ }
196
+ const warning = result === 'unmerged' ? `Git branch "${job.branch}" was kept because it contains commits that are not merged into its base branch.`
197
+ : result === 'checked-out' ? `Git branch "${job.branch}" was kept because it is still checked out in another worktree.` : null;
198
+ requireOwnedProject();
199
+ db.prepare("UPDATE bundle_deletion_jobs SET branch_warning = ?, completed_at = ? WHERE bundle_id = ? AND claim_id = ? AND status = 'pending'")
200
+ .run(warning, new Date().toISOString(), job.bundle_id, claimId);
201
+ advance('completed');
202
+ }
203
+ }
204
+ catch (error) {
205
+ const attempts = getBundleDeletionJob(job.project_id, job.bundle_id)?.retry_attempts ?? 1;
206
+ const delay = Math.min(15 * 60_000, 5_000 * 2 ** Math.min(8, attempts - 1));
207
+ const permanentCode = permanentFailureCode(error);
208
+ const blocked = permanentCode !== null || attempts >= MAX_AUTOMATIC_ATTEMPTS;
209
+ db.prepare(`UPDATE bundle_deletion_jobs SET status = ?, retry_at = ?, last_error = ?, failure_code = ?
210
+ WHERE bundle_id = ? AND claim_id = ? AND status = 'pending'`)
211
+ .run(blocked ? 'blocked' : 'pending', blocked ? null : new Date(Date.now() + delay).toISOString(), (error instanceof Error ? error.message : String(error)).slice(0, 4000), permanentCode ?? (blocked ? 'retry_exhausted' : 'transient'), job.bundle_id, claimId);
212
+ }
213
+ finally {
214
+ clearInterval(heartbeat);
215
+ db.prepare('UPDATE bundle_deletion_jobs SET claim_id = NULL, claimed_at = NULL WHERE bundle_id = ? AND claim_id = ?').run(job.bundle_id, claimId);
216
+ }
217
+ return getBundleDeletionJob(job.project_id, job.bundle_id);
218
+ };
219
+ export const runBundleDeletionJob = (projectId, bundleId, dependencies = defaults) => {
220
+ const key = `${projectId}:${bundleId}`;
221
+ const running = inFlight.get(key);
222
+ if (running) {
223
+ return running;
224
+ }
225
+ const job = getBundleDeletionJob(projectId, bundleId);
226
+ if (!job || job.status !== 'pending' || job.phase === 'completed') {
227
+ return Promise.resolve(job);
228
+ }
229
+ recoverStaleBundleDeletionClaims();
230
+ const promise = runClaimedJob(job, dependencies).finally(() => inFlight.delete(key));
231
+ inFlight.set(key, promise);
232
+ return promise;
233
+ };
234
+ export const attemptBundleDeletion = async (projectId, bundleId) => {
235
+ let timer;
236
+ try {
237
+ return await Promise.race([
238
+ runBundleDeletionJob(projectId, bundleId),
239
+ new Promise(resolve => {
240
+ timer = setTimeout(() => resolve(getBundleDeletionJob(projectId, bundleId)), 1_000);
241
+ }),
242
+ ]);
243
+ }
244
+ finally {
245
+ clearTimeout(timer);
246
+ }
247
+ };
248
+ export const resumeBundleDeletions = async () => {
249
+ recoverStaleBundleDeletionClaims();
250
+ const jobs = db.prepare(`SELECT bundle_id, project_id FROM bundle_deletion_jobs
251
+ WHERE status = 'pending' AND phase <> 'completed' AND (retry_at IS NULL OR retry_at <= ?)
252
+ ORDER BY created_at LIMIT 20`).all(new Date().toISOString());
253
+ for (const job of jobs) {
254
+ await runBundleDeletionJob(job.project_id, job.bundle_id);
255
+ }
256
+ };
257
+ //# sourceMappingURL=bundleDeletion.js.map