@neuralnomads/codenomad-dev 0.20.0-dev-20260911-ea37f97b → 0.20.0-dev-20260917-aa51cbb9

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 (191) hide show
  1. package/README.md +2 -2
  2. package/dist/api-types.js.map +1 -1
  3. package/dist/opencode/automation-plugin.test.js +32 -38
  4. package/dist/opencode/automation-plugin.test.js.map +1 -1
  5. package/dist/opencode/compatibility/contract-fixture.js +15 -0
  6. package/dist/opencode/compatibility/contract-fixture.js.map +1 -0
  7. package/dist/opencode/compatibility/events.js +9 -0
  8. package/dist/opencode/compatibility/events.js.map +1 -0
  9. package/dist/opencode/compatibility/location.js +89 -0
  10. package/dist/opencode/compatibility/location.js.map +1 -0
  11. package/dist/opencode/compatibility/location.test.js +92 -0
  12. package/dist/opencode/compatibility/location.test.js.map +1 -0
  13. package/dist/opencode/compatibility/negotiate.js +73 -0
  14. package/dist/opencode/compatibility/negotiate.js.map +1 -0
  15. package/dist/opencode/compatibility/negotiate.test.js +61 -0
  16. package/dist/opencode/compatibility/negotiate.test.js.map +1 -0
  17. package/dist/opencode/compatibility/proxy-locations.js +137 -0
  18. package/dist/opencode/compatibility/proxy-locations.js.map +1 -0
  19. package/dist/opencode/compatibility/proxy-locations.test.js +42 -0
  20. package/dist/opencode/compatibility/proxy-locations.test.js.map +1 -0
  21. package/dist/opencode/compatibility/proxy.js +60 -0
  22. package/dist/opencode/compatibility/proxy.js.map +1 -0
  23. package/dist/opencode/compatibility/proxy.test.js +47 -0
  24. package/dist/opencode/compatibility/proxy.test.js.map +1 -0
  25. package/dist/opencode/compatibility/requests.js +54 -0
  26. package/dist/opencode/compatibility/requests.js.map +1 -0
  27. package/dist/opencode/compatibility/runtime.js +26 -0
  28. package/dist/opencode/compatibility/runtime.js.map +1 -0
  29. package/dist/opencode/compatibility/transport.js +112 -0
  30. package/dist/opencode/compatibility/transport.js.map +1 -0
  31. package/dist/opencode/compatibility/transport.test.js +83 -0
  32. package/dist/opencode/compatibility/transport.test.js.map +1 -0
  33. package/dist/opencode/session-pruning/location.js +16 -0
  34. package/dist/opencode/session-pruning/location.js.map +1 -0
  35. package/dist/opencode/session-pruning/package.test.js +37 -0
  36. package/dist/opencode/session-pruning/package.test.js.map +1 -0
  37. package/dist/opencode/session-pruning/plugin.js +5 -2
  38. package/dist/opencode/session-pruning/plugin.js.map +1 -1
  39. package/dist/opencode/session-pruning/preview-store.js +3 -2
  40. package/dist/opencode/session-pruning/preview-store.js.map +1 -1
  41. package/dist/opencode/session-pruning/pruning.test.js +7 -5
  42. package/dist/opencode/session-pruning/pruning.test.js.map +1 -1
  43. package/dist/opencode/session-pruning/service.js +5 -4
  44. package/dist/opencode/session-pruning/service.js.map +1 -1
  45. package/dist/opencode/session-pruning/service.test.js +65 -53
  46. package/dist/opencode/session-pruning/service.test.js.map +1 -1
  47. package/dist/permissions/opencode-replier.js +2 -2
  48. package/dist/permissions/opencode-replier.js.map +1 -1
  49. package/dist/permissions/opencode-replier.test.js +1 -1
  50. package/dist/permissions/opencode-replier.test.js.map +1 -1
  51. package/dist/permissions/opencode-yolo-metadata.js.map +1 -1
  52. package/dist/permissions/opencode-yolo-metadata.test.js.map +1 -1
  53. package/dist/plugins/session-pruning/plugin.mjs +422 -271
  54. package/dist/server/__tests__/instance-proxy-legacy.test.js +111 -0
  55. package/dist/server/__tests__/instance-proxy-legacy.test.js.map +1 -0
  56. package/dist/server/__tests__/instance-proxy.test.js +95 -34
  57. package/dist/server/__tests__/instance-proxy.test.js.map +1 -1
  58. package/dist/server/http-server.js +147 -237
  59. package/dist/server/http-server.js.map +1 -1
  60. package/dist/server/routes/session-pruning.js +4 -2
  61. package/dist/server/routes/session-pruning.js.map +1 -1
  62. package/dist/server/routes/session-pruning.test.js +16 -2
  63. package/dist/server/routes/session-pruning.test.js.map +1 -1
  64. package/dist/server/routes/worktrees.js +1 -0
  65. package/dist/server/routes/worktrees.js.map +1 -1
  66. package/dist/server/routes/worktrees.test.js +54 -1
  67. package/dist/server/routes/worktrees.test.js.map +1 -1
  68. package/dist/workspaces/host-opencode-service.test.js +56 -7
  69. package/dist/workspaces/host-opencode-service.test.js.map +1 -1
  70. package/dist/workspaces/instance-client.js.map +1 -1
  71. package/dist/workspaces/instance-events.js +71 -57
  72. package/dist/workspaces/instance-events.js.map +1 -1
  73. package/dist/workspaces/instance-events.test.js +122 -9
  74. package/dist/workspaces/instance-events.test.js.map +1 -1
  75. package/dist/workspaces/manager.js +24 -39
  76. package/dist/workspaces/manager.js.map +1 -1
  77. package/dist/workspaces/manager.test.js +43 -7
  78. package/dist/workspaces/manager.test.js.map +1 -1
  79. package/dist/workspaces/opencode-cli-service.js +56 -29
  80. package/dist/workspaces/opencode-cli-service.js.map +1 -1
  81. package/dist/workspaces/opencode-connection-generation.test.js +57 -0
  82. package/dist/workspaces/opencode-connection-generation.test.js.map +1 -0
  83. package/dist/workspaces/opencode-service.js +70 -16
  84. package/dist/workspaces/opencode-service.js.map +1 -1
  85. package/dist/workspaces/opencode-service.test.js +3 -4
  86. package/dist/workspaces/opencode-service.test.js.map +1 -1
  87. package/dist/workspaces/worktree-session-evacuation.js +31 -11
  88. package/dist/workspaces/worktree-session-evacuation.js.map +1 -1
  89. package/dist/workspaces/worktree-session-evacuation.test.js +211 -0
  90. package/dist/workspaces/worktree-session-evacuation.test.js.map +1 -1
  91. package/dist/workspaces/wsl-opencode-service.test.js +64 -24
  92. package/dist/workspaces/wsl-opencode-service.test.js.map +1 -1
  93. package/package.json +3 -3
  94. package/public/assets/App-BROhwPo9.js +54 -0
  95. package/public/assets/App-BROhwPo9.js.map +1 -0
  96. package/public/assets/{FilesTab-CRpGDs1B.js → FilesTab-DY3Vr-3r.js} +3 -3
  97. package/public/assets/{FilesTab-CRpGDs1B.js.map → FilesTab-DY3Vr-3r.js.map} +1 -1
  98. package/public/assets/{GitChangesTab-DxFebTiq.js → GitChangesTab-BD6CJngE.js} +3 -3
  99. package/public/assets/{GitChangesTab-DxFebTiq.js.map → GitChangesTab-BD6CJngE.js.map} +1 -1
  100. package/public/assets/{SplitFilePanel-DgTMrrO-.js → SplitFilePanel-BdY6d_6m.js} +2 -2
  101. package/public/assets/{SplitFilePanel-DgTMrrO-.js.map → SplitFilePanel-BdY6d_6m.js.map} +1 -1
  102. package/public/assets/StatusTab-C-_2yaPa.js +2 -0
  103. package/public/assets/StatusTab-C-_2yaPa.js.map +1 -0
  104. package/public/assets/{align-justify-v_yPXSys.js → align-justify-DI8VTZ4G.js} +2 -2
  105. package/public/assets/{align-justify-v_yPXSys.js.map → align-justify-DI8VTZ4G.js.map} +1 -1
  106. package/public/assets/{bundle-full-HzL_Jtqk.js → bundle-full-D_M65Eb_.js} +2 -2
  107. package/public/assets/{bundle-full-HzL_Jtqk.js.map → bundle-full-D_M65Eb_.js.map} +1 -1
  108. package/public/assets/{client-state-CF-PJ0GG.js → client-state-CWCzCOz6.js} +2 -2
  109. package/public/assets/{client-state-CF-PJ0GG.js.map → client-state-CWCzCOz6.js.map} +1 -1
  110. package/public/assets/{diff-viewer-BFDQBDVy.js → diff-viewer-CGPsYEr3.js} +2 -2
  111. package/public/assets/{diff-viewer-BFDQBDVy.js.map → diff-viewer-CGPsYEr3.js.map} +1 -1
  112. package/public/assets/index-B8-lAqjr.js +2 -0
  113. package/public/assets/index-B8-lAqjr.js.map +1 -0
  114. package/public/assets/index-BeUewQym.js +2 -0
  115. package/public/assets/index-BeUewQym.js.map +1 -0
  116. package/public/assets/index-BxRFHpJd.js +2 -0
  117. package/public/assets/{index-D2UHSOG5.js.map → index-BxRFHpJd.js.map} +1 -1
  118. package/public/assets/index-Bzv1kU-S.js +2 -0
  119. package/public/assets/{index-CTKYD_VJ.js.map → index-Bzv1kU-S.js.map} +1 -1
  120. package/public/assets/index-C9tI4CRG.js +2 -0
  121. package/public/assets/index-C9tI4CRG.js.map +1 -0
  122. package/public/assets/index-CGYHFZ6F.js +2 -0
  123. package/public/assets/index-CGYHFZ6F.js.map +1 -0
  124. package/public/assets/{index-CHYmKl10.js → index-CMEAjBFp.js} +2 -2
  125. package/public/assets/index-CMEAjBFp.js.map +1 -0
  126. package/public/assets/index-Cazypvyc.js +2 -0
  127. package/public/assets/index-Cazypvyc.js.map +1 -0
  128. package/public/assets/index-CwqzofWP.js +2 -0
  129. package/public/assets/index-CwqzofWP.js.map +1 -0
  130. package/public/assets/index-Da9yOa6q.js +2 -0
  131. package/public/assets/index-Da9yOa6q.js.map +1 -0
  132. package/public/assets/{index-RK9gf0uk.js → index-Ddjkirek.js} +2 -2
  133. package/public/assets/{index-RK9gf0uk.js.map → index-Ddjkirek.js.map} +1 -1
  134. package/public/assets/index-HHgvEOAH.js +3 -0
  135. package/public/assets/index-HHgvEOAH.js.map +1 -0
  136. package/public/assets/index-MeFffHqb.css +1 -0
  137. package/public/assets/loading-CdhHiSED.css +1 -0
  138. package/public/assets/{loading-BSB82snY.js → loading-Gqh6NPUr.js} +2 -2
  139. package/public/assets/{loading-BSB82snY.js.map → loading-Gqh6NPUr.js.map} +1 -1
  140. package/public/assets/main-Cbx7KdTr.js +38 -0
  141. package/public/assets/main-Cbx7KdTr.js.map +1 -0
  142. package/public/assets/{markdown-DtidoL2Z.js → markdown-BWnjyvnX.js} +4 -4
  143. package/public/assets/{markdown-DtidoL2Z.js.map → markdown-BWnjyvnX.js.map} +1 -1
  144. package/public/assets/monaco-viewer-BEJIiZWL.js +27 -0
  145. package/public/assets/monaco-viewer-BEJIiZWL.js.map +1 -0
  146. package/public/assets/{startup-state-settings-card-BY741Rt0.js → startup-state-settings-card-C4E2Itop.js} +2 -2
  147. package/public/assets/{startup-state-settings-card-BY741Rt0.js.map → startup-state-settings-card-C4E2Itop.js.map} +1 -1
  148. package/public/assets/{terminal-square-BdC_jwm3.js → terminal-square-C3wutdV0.js} +2 -2
  149. package/public/assets/{terminal-square-BdC_jwm3.js.map → terminal-square-C3wutdV0.js.map} +1 -1
  150. package/public/assets/tool-call-DNsAQ_m4.js +60 -0
  151. package/public/assets/tool-call-DNsAQ_m4.js.map +1 -0
  152. package/public/assets/{unified-picker-C0eo1chJ.js → unified-picker-tB7y17Qz.js} +2 -2
  153. package/public/assets/unified-picker-tB7y17Qz.js.map +1 -0
  154. package/public/assets/{wrap-text-BuMtdvMx.js → wrap-text-CkBxzt04.js} +2 -2
  155. package/public/assets/{wrap-text-BuMtdvMx.js.map → wrap-text-CkBxzt04.js.map} +1 -1
  156. package/public/index.html +4 -4
  157. package/public/loading.html +5 -5
  158. package/public/sw.js +1 -1
  159. package/public/sw.js.map +1 -1
  160. package/public/assets/App-0-FcHzPS.js +0 -44
  161. package/public/assets/App-0-FcHzPS.js.map +0 -1
  162. package/public/assets/StatusTab-LKUA9cbH.js +0 -2
  163. package/public/assets/StatusTab-LKUA9cbH.js.map +0 -1
  164. package/public/assets/index-8iPlm2r0.js +0 -2
  165. package/public/assets/index-8iPlm2r0.js.map +0 -1
  166. package/public/assets/index-B185mC7m.js +0 -2
  167. package/public/assets/index-B185mC7m.js.map +0 -1
  168. package/public/assets/index-Bbj1xF8i.js +0 -2
  169. package/public/assets/index-Bbj1xF8i.js.map +0 -1
  170. package/public/assets/index-BsmFN3k1.js +0 -2
  171. package/public/assets/index-BsmFN3k1.js.map +0 -1
  172. package/public/assets/index-Bz_OQJYA.css +0 -1
  173. package/public/assets/index-C52CfqK3.js +0 -2
  174. package/public/assets/index-C52CfqK3.js.map +0 -1
  175. package/public/assets/index-CHYmKl10.js.map +0 -1
  176. package/public/assets/index-CTKYD_VJ.js +0 -2
  177. package/public/assets/index-D2UHSOG5.js +0 -2
  178. package/public/assets/index-DDoB_Z36.js +0 -2
  179. package/public/assets/index-DDoB_Z36.js.map +0 -1
  180. package/public/assets/index-DcwAtZw-.js +0 -3
  181. package/public/assets/index-DcwAtZw-.js.map +0 -1
  182. package/public/assets/index-DxUD3RYr.js +0 -2
  183. package/public/assets/index-DxUD3RYr.js.map +0 -1
  184. package/public/assets/loading-CmEVQgyj.css +0 -1
  185. package/public/assets/main-CZztwrfv.js +0 -48
  186. package/public/assets/main-CZztwrfv.js.map +0 -1
  187. package/public/assets/monaco-viewer-CpxDfB-6.js +0 -27
  188. package/public/assets/monaco-viewer-CpxDfB-6.js.map +0 -1
  189. package/public/assets/tool-call-DbtCsJ4x.js +0 -60
  190. package/public/assets/tool-call-DbtCsJ4x.js.map +0 -1
  191. package/public/assets/unified-picker-C0eo1chJ.js.map +0 -1
@@ -1,4 +1,5 @@
1
1
  import { normalizeWslUncPath } from "./worktree-directory";
2
+ import { moveSessionToLocation, readLocationRef, sameLocation } from "../opencode/compatibility/location";
2
3
  const PAGE_SIZE = 200;
3
4
  const MAX_PAGES = 10_000;
4
5
  const MAX_SESSIONS = 1_000_000;
@@ -118,13 +119,27 @@ async function waitForInventory(client, project, predicate) {
118
119
  }
119
120
  export async function evacuateWorktreeSessions(params) {
120
121
  const identity = async (directory) => normalizeDirectory(await params.resolveDirectoryIdentity?.(directory) ?? directory);
122
+ // Containing-worktree identity intentionally groups descendant sessions. It
123
+ // cannot identify a project root or destination: nested repositories differ.
124
+ const exactDirectory = async (directory) => {
125
+ const resolved = params.resolveExactDirectory ? await params.resolveExactDirectory(directory) : directory;
126
+ return resolved === undefined ? undefined : normalizeDirectory(resolved);
127
+ };
128
+ const [projectDirectory, targetDirectory, rootDirectory] = await Promise.all([
129
+ exactDirectory(params.projectDirectory), exactDirectory(params.targetDirectory), exactDirectory(params.rootDirectory),
130
+ ]);
131
+ if (projectDirectory === undefined || targetDirectory === undefined || rootDirectory === undefined) {
132
+ throw new Error("Unable to resolve owned directories before deleting worktree");
133
+ }
121
134
  const target = await identity(params.targetDirectory);
122
135
  const matchesTarget = async (directory) => await identity(directory) === target;
123
136
  const projects = await params.client.project.list();
124
137
  let project;
125
138
  for (const candidate of projects) {
126
- if (normalizeDirectory(candidate.canonical) === normalizeDirectory(params.projectDirectory)
127
- || (await Promise.all(candidate.sandboxes.map(matchesTarget))).some(Boolean)) {
139
+ // Resolve aliases without collapsing a nested repository or nested sandbox
140
+ // into its containing worktree. Both comparisons require exact directories.
141
+ if (await exactDirectory(candidate.canonical) === projectDirectory
142
+ || (await Promise.all(candidate.sandboxes.map(exactDirectory))).includes(targetDirectory)) {
128
143
  project = candidate;
129
144
  break;
130
145
  }
@@ -141,14 +156,23 @@ export async function evacuateWorktreeSessions(params) {
141
156
  };
142
157
  await assertInactive();
143
158
  const moved = [];
159
+ // Resolve before any move; a directory-only fallback could silently move a
160
+ // legacy session into a different native workspace.
161
+ const rootLocation = readLocationRef(await params.client.location.get({ location: { directory: params.rootDirectory } }));
162
+ if (await exactDirectory(rootLocation.directory) !== rootDirectory) {
163
+ throw new Error("OpenCode resolved a foreign evacuation destination");
164
+ }
144
165
  try {
145
166
  for (const session of affected) {
146
167
  await assertInactive();
147
168
  const original = { ...session, location: { ...session.location } };
148
- await params.client.session.move({ sessionID: session.id, directory: params.rootDirectory });
169
+ await moveSessionToLocation(params.client, session.id, rootLocation);
149
170
  moved.push(original);
150
171
  }
151
- await waitForInventory(params.client, project.id, async (current) => (!(await Promise.all(current.map((session) => matchesTarget(session.location.directory)))).some(Boolean)));
172
+ await waitForInventory(params.client, project.id, async (current) => (moved.every((original) => {
173
+ const session = current.find((candidate) => candidate.id === original.id);
174
+ return session && sameLocation(readLocationRef(session.location), rootLocation);
175
+ }) && !(await Promise.all(current.map((session) => matchesTarget(session.location.directory)))).some(Boolean)));
152
176
  const finalInventory = await inventorySessions(params.client, project.id);
153
177
  const finalAffected = (await Promise.all(finalInventory.map(async (session) => (await matchesTarget(session.location.directory) ? session : undefined)))).filter((session) => Boolean(session));
154
178
  await assertInactive(finalAffected);
@@ -160,22 +184,18 @@ export async function evacuateWorktreeSessions(params) {
160
184
  const rollbackErrors = [];
161
185
  for (const session of moved.reverse()) {
162
186
  try {
163
- await params.client.session.move({
164
- sessionID: session.id,
165
- directory: session.location.directory,
166
- workspaceID: session.location.workspaceID,
167
- });
187
+ await moveSessionToLocation(params.client, session.id, readLocationRef(session.location));
168
188
  }
169
189
  catch (rollbackError) {
170
190
  rollbackErrors.push(rollbackError);
171
191
  }
172
192
  }
173
193
  try {
174
- const expected = new Set(moved.map((session) => session.id));
194
+ const expected = new Map(moved.map((session) => [session.id, readLocationRef(session.location)]));
175
195
  await waitForInventory(params.client, project.id, async (current) => {
176
196
  const restored = current.filter((session) => expected.has(session.id));
177
197
  return restored.length === expected.size
178
- && (await Promise.all(restored.map((session) => matchesTarget(session.location.directory)))).every(Boolean);
198
+ && restored.every((session) => sameLocation(readLocationRef(session.location), expected.get(session.id)));
179
199
  });
180
200
  }
181
201
  catch (rollbackError) {
@@ -1 +1 @@
1
- {"version":3,"file":"worktree-session-evacuation.js","sourceRoot":"","sources":["../../src/workspaces/worktree-session-evacuation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { OpenCodeClient, SessionInfo } from \"@opencode-ai/client\"\nimport { normalizeWslUncPath } from \"./worktree-directory\"\n\nconst PAGE_SIZE = 200\nconst MAX_PAGES = 10_000\nconst MAX_SESSIONS = 1_000_000\nconst MUTATION_DRAIN_TIMEOUT_MS = 30_000\n\nfunction normalizeDirectory(directory: string): string {\n const wsl = normalizeWslUncPath(directory)\n if (wsl) return wsl\n const normalized = directory.trim().replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\") || \"/\"\n return /^[A-Za-z]:\\//.test(normalized) || normalized.startsWith(\"//\") ? normalized.toLowerCase() : normalized\n}\n\nexport class WorktreeDeletionFence {\n private readonly queues = new Map<string, Promise<unknown>>()\n private readonly blocked = new Map<string, number>()\n private readonly active = new Map<string, number>()\n private readonly idleWaiters = new Map<string, Set<() => void>>()\n\n constructor(private readonly mutationDrainTimeoutMs = MUTATION_DRAIN_TIMEOUT_MS) {}\n\n isBlocked(directory: string): boolean {\n return this.blocked.has(normalizeDirectory(directory))\n }\n\n enter(directories: string[]): (() => void) | undefined {\n const normalized = [...new Set(directories.map(normalizeDirectory))]\n if (normalized.some((directory) => this.blocked.has(directory))) return undefined\n for (const directory of normalized) this.active.set(directory, (this.active.get(directory) ?? 0) + 1)\n\n let released = false\n return () => {\n if (released) return\n released = true\n for (const directory of normalized) {\n const count = this.active.get(directory) ?? 0\n if (count > 1) {\n this.active.set(directory, count - 1)\n continue\n }\n this.active.delete(directory)\n for (const resolve of this.idleWaiters.get(directory) ?? []) resolve()\n this.idleWaiters.delete(directory)\n }\n }\n }\n\n run<T>(key: string, directories: string[], operation: () => Promise<T>): Promise<T> {\n const normalizedKey = normalizeDirectory(key)\n const blocked = [...new Set(directories.map(normalizeDirectory))]\n for (const directory of blocked) this.blocked.set(directory, (this.blocked.get(directory) ?? 0) + 1)\n\n const previous = this.queues.get(normalizedKey) ?? Promise.resolve()\n const current = previous.catch(() => {}).then(async () => {\n await Promise.all(blocked.map((directory) => this.waitForIdle(directory)))\n return operation()\n })\n this.queues.set(normalizedKey, current)\n return current.finally(() => {\n for (const directory of blocked) {\n const count = this.blocked.get(directory) ?? 0\n if (count > 1) this.blocked.set(directory, count - 1)\n else this.blocked.delete(directory)\n }\n if (this.queues.get(normalizedKey) === current) this.queues.delete(normalizedKey)\n })\n }\n\n private waitForIdle(directory: string): Promise<void> {\n if (!this.active.has(directory)) return Promise.resolve()\n return new Promise((resolve, reject) => {\n const waiters = this.idleWaiters.get(directory) ?? new Set()\n const done = () => {\n clearTimeout(timer)\n resolve()\n }\n waiters.add(done)\n this.idleWaiters.set(directory, waiters)\n const timer = setTimeout(() => {\n waiters.delete(done)\n if (!waiters.size) this.idleWaiters.delete(directory)\n reject(new Error(\"Timed out waiting for worktree mutations to finish\"))\n }, this.mutationDrainTimeoutMs)\n })\n }\n}\n\nasync function inventorySessions(client: OpenCodeClient, project: string): Promise<SessionInfo[]> {\n const sessions = new Map<string, SessionInfo>()\n const cursors = new Set<string>()\n let cursor: string | undefined\n\n for (let pageCount = 0; pageCount < MAX_PAGES; pageCount += 1) {\n const page = await client.session.list(cursor ? { cursor } : { project, limit: PAGE_SIZE, order: \"asc\" })\n for (const session of page.data) {\n sessions.set(session.id, session)\n if (sessions.size > MAX_SESSIONS) throw new Error(\"Session inventory exceeded its safety limit\")\n }\n\n cursor = page.cursor.next ?? undefined\n if (!cursor) return Array.from(sessions.values())\n if (cursors.has(cursor)) throw new Error(`Repeated session inventory cursor: ${cursor}`)\n cursors.add(cursor)\n }\n\n throw new Error(\"Session inventory exceeded its page limit\")\n}\n\nasync function waitForInventory(\n client: OpenCodeClient,\n project: string,\n predicate: (sessions: SessionInfo[]) => boolean | Promise<boolean>,\n): Promise<void> {\n for (let attempt = 0; attempt < 100; attempt += 1) {\n if (await predicate(await inventorySessions(client, project))) return\n await new Promise((resolve) => setTimeout(resolve, 50))\n }\n throw new Error(\"Timed out waiting for session moves\")\n}\n\nexport async function evacuateWorktreeSessions(params: {\n client: OpenCodeClient\n projectDirectory: string\n targetDirectory: string\n rootDirectory: string\n resolveDirectoryIdentity?: (directory: string) => Promise<string | undefined>\n remove: () => Promise<void>\n}): Promise<void> {\n const identity = async (directory: string) => normalizeDirectory(\n await params.resolveDirectoryIdentity?.(directory) ?? directory,\n )\n const target = await identity(params.targetDirectory)\n const matchesTarget = async (directory: string) => await identity(directory) === target\n const projects = await params.client.project.list()\n let project: (typeof projects)[number] | undefined\n for (const candidate of projects) {\n if (normalizeDirectory(candidate.canonical) === normalizeDirectory(params.projectDirectory)\n || (await Promise.all(candidate.sandboxes.map(matchesTarget))).some(Boolean)) {\n project = candidate\n break\n }\n }\n if (!project) throw new Error(\"Unable to resolve the OpenCode project before deleting worktree\")\n const sessions = await inventorySessions(params.client, project.id)\n const affected = (await Promise.all(sessions.map(async (session) => (\n await matchesTarget(session.location.directory) ? session : undefined\n )))).filter((session): session is SessionInfo => Boolean(session))\n const assertInactive = async (candidates = affected) => {\n const active = await params.client.session.active()\n const blockers = candidates.filter((session) => Object.prototype.hasOwnProperty.call(active, session.id))\n if (blockers.length) throw new Error(`Active sessions block worktree deletion: ${blockers.map((session) => session.id).join(\", \")}`)\n }\n await assertInactive()\n\n const moved: SessionInfo[] = []\n try {\n for (const session of affected) {\n await assertInactive()\n const original = { ...session, location: { ...session.location } }\n await params.client.session.move({ sessionID: session.id, directory: params.rootDirectory })\n moved.push(original)\n }\n await waitForInventory(params.client, project.id, async (current) => (\n !(await Promise.all(current.map((session) => matchesTarget(session.location.directory)))).some(Boolean)\n ))\n const finalInventory = await inventorySessions(params.client, project.id)\n const finalAffected = (await Promise.all(finalInventory.map(async (session) => (\n await matchesTarget(session.location.directory) ? session : undefined\n )))).filter((session): session is SessionInfo => Boolean(session))\n await assertInactive(finalAffected)\n if (finalAffected.length) throw new Error(\"Sessions appeared in the worktree during deletion\")\n await params.remove()\n } catch (error) {\n const rollbackErrors: unknown[] = []\n for (const session of moved.reverse()) {\n try {\n await params.client.session.move({\n sessionID: session.id,\n directory: session.location.directory,\n workspaceID: session.location.workspaceID,\n })\n } catch (rollbackError) {\n rollbackErrors.push(rollbackError)\n }\n }\n try {\n const expected = new Set(moved.map((session) => session.id))\n await waitForInventory(params.client, project.id, async (current) => {\n const restored = current.filter((session) => expected.has(session.id))\n return restored.length === expected.size\n && (await Promise.all(restored.map((session) => matchesTarget(session.location.directory)))).every(Boolean)\n })\n } catch (rollbackError) {\n rollbackErrors.push(rollbackError)\n }\n if (rollbackErrors.length) {\n throw new AggregateError([error, ...rollbackErrors], \"Session evacuation failed and could not be rolled back\")\n }\n throw error\n }\n}\n"]}
1
+ {"version":3,"file":"worktree-session-evacuation.js","sourceRoot":"","sources":["../../src/workspaces/worktree-session-evacuation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { OpenCodeClient, SessionInfo } from \"@opencode/client\"\nimport { normalizeWslUncPath } from \"./worktree-directory\"\nimport { moveSessionToLocation, readLocationRef, sameLocation } from \"../opencode/compatibility/location\"\n\nconst PAGE_SIZE = 200\nconst MAX_PAGES = 10_000\nconst MAX_SESSIONS = 1_000_000\nconst MUTATION_DRAIN_TIMEOUT_MS = 30_000\n\nfunction normalizeDirectory(directory: string): string {\n const wsl = normalizeWslUncPath(directory)\n if (wsl) return wsl\n const normalized = directory.trim().replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\") || \"/\"\n return /^[A-Za-z]:\\//.test(normalized) || normalized.startsWith(\"//\") ? normalized.toLowerCase() : normalized\n}\n\nexport class WorktreeDeletionFence {\n private readonly queues = new Map<string, Promise<unknown>>()\n private readonly blocked = new Map<string, number>()\n private readonly active = new Map<string, number>()\n private readonly idleWaiters = new Map<string, Set<() => void>>()\n\n constructor(private readonly mutationDrainTimeoutMs = MUTATION_DRAIN_TIMEOUT_MS) {}\n\n isBlocked(directory: string): boolean {\n return this.blocked.has(normalizeDirectory(directory))\n }\n\n enter(directories: string[]): (() => void) | undefined {\n const normalized = [...new Set(directories.map(normalizeDirectory))]\n if (normalized.some((directory) => this.blocked.has(directory))) return undefined\n for (const directory of normalized) this.active.set(directory, (this.active.get(directory) ?? 0) + 1)\n\n let released = false\n return () => {\n if (released) return\n released = true\n for (const directory of normalized) {\n const count = this.active.get(directory) ?? 0\n if (count > 1) {\n this.active.set(directory, count - 1)\n continue\n }\n this.active.delete(directory)\n for (const resolve of this.idleWaiters.get(directory) ?? []) resolve()\n this.idleWaiters.delete(directory)\n }\n }\n }\n\n run<T>(key: string, directories: string[], operation: () => Promise<T>): Promise<T> {\n const normalizedKey = normalizeDirectory(key)\n const blocked = [...new Set(directories.map(normalizeDirectory))]\n for (const directory of blocked) this.blocked.set(directory, (this.blocked.get(directory) ?? 0) + 1)\n\n const previous = this.queues.get(normalizedKey) ?? Promise.resolve()\n const current = previous.catch(() => {}).then(async () => {\n await Promise.all(blocked.map((directory) => this.waitForIdle(directory)))\n return operation()\n })\n this.queues.set(normalizedKey, current)\n return current.finally(() => {\n for (const directory of blocked) {\n const count = this.blocked.get(directory) ?? 0\n if (count > 1) this.blocked.set(directory, count - 1)\n else this.blocked.delete(directory)\n }\n if (this.queues.get(normalizedKey) === current) this.queues.delete(normalizedKey)\n })\n }\n\n private waitForIdle(directory: string): Promise<void> {\n if (!this.active.has(directory)) return Promise.resolve()\n return new Promise((resolve, reject) => {\n const waiters = this.idleWaiters.get(directory) ?? new Set()\n const done = () => {\n clearTimeout(timer)\n resolve()\n }\n waiters.add(done)\n this.idleWaiters.set(directory, waiters)\n const timer = setTimeout(() => {\n waiters.delete(done)\n if (!waiters.size) this.idleWaiters.delete(directory)\n reject(new Error(\"Timed out waiting for worktree mutations to finish\"))\n }, this.mutationDrainTimeoutMs)\n })\n }\n}\n\nasync function inventorySessions(client: OpenCodeClient, project: string): Promise<SessionInfo[]> {\n const sessions = new Map<string, SessionInfo>()\n const cursors = new Set<string>()\n let cursor: string | undefined\n\n for (let pageCount = 0; pageCount < MAX_PAGES; pageCount += 1) {\n const page = await client.session.list(cursor ? { cursor } : { project, limit: PAGE_SIZE, order: \"asc\" })\n for (const session of page.data) {\n sessions.set(session.id, session)\n if (sessions.size > MAX_SESSIONS) throw new Error(\"Session inventory exceeded its safety limit\")\n }\n\n cursor = page.cursor.next ?? undefined\n if (!cursor) return Array.from(sessions.values())\n if (cursors.has(cursor)) throw new Error(`Repeated session inventory cursor: ${cursor}`)\n cursors.add(cursor)\n }\n\n throw new Error(\"Session inventory exceeded its page limit\")\n}\n\nasync function waitForInventory(\n client: OpenCodeClient,\n project: string,\n predicate: (sessions: SessionInfo[]) => boolean | Promise<boolean>,\n): Promise<void> {\n for (let attempt = 0; attempt < 100; attempt += 1) {\n if (await predicate(await inventorySessions(client, project))) return\n await new Promise((resolve) => setTimeout(resolve, 50))\n }\n throw new Error(\"Timed out waiting for session moves\")\n}\n\nexport async function evacuateWorktreeSessions(params: {\n client: OpenCodeClient\n projectDirectory: string\n targetDirectory: string\n rootDirectory: string\n resolveDirectoryIdentity?: (directory: string) => Promise<string | undefined>\n resolveExactDirectory?: (directory: string) => Promise<string | undefined>\n remove: () => Promise<void>\n}): Promise<void> {\n const identity = async (directory: string) => normalizeDirectory(\n await params.resolveDirectoryIdentity?.(directory) ?? directory,\n )\n // Containing-worktree identity intentionally groups descendant sessions. It\n // cannot identify a project root or destination: nested repositories differ.\n const exactDirectory = async (directory: string) => {\n const resolved = params.resolveExactDirectory ? await params.resolveExactDirectory(directory) : directory\n return resolved === undefined ? undefined : normalizeDirectory(resolved)\n }\n const [projectDirectory, targetDirectory, rootDirectory] = await Promise.all([\n exactDirectory(params.projectDirectory), exactDirectory(params.targetDirectory), exactDirectory(params.rootDirectory),\n ])\n if (projectDirectory === undefined || targetDirectory === undefined || rootDirectory === undefined) {\n throw new Error(\"Unable to resolve owned directories before deleting worktree\")\n }\n const target = await identity(params.targetDirectory)\n const matchesTarget = async (directory: string) => await identity(directory) === target\n const projects = await params.client.project.list()\n let project: (typeof projects)[number] | undefined\n for (const candidate of projects) {\n // Resolve aliases without collapsing a nested repository or nested sandbox\n // into its containing worktree. Both comparisons require exact directories.\n if (await exactDirectory(candidate.canonical) === projectDirectory\n || (await Promise.all(candidate.sandboxes.map(exactDirectory))).includes(targetDirectory)) {\n project = candidate\n break\n }\n }\n if (!project) throw new Error(\"Unable to resolve the OpenCode project before deleting worktree\")\n const sessions = await inventorySessions(params.client, project.id)\n const affected = (await Promise.all(sessions.map(async (session) => (\n await matchesTarget(session.location.directory) ? session : undefined\n )))).filter((session): session is SessionInfo => Boolean(session))\n const assertInactive = async (candidates = affected) => {\n const active = await params.client.session.active()\n const blockers = candidates.filter((session) => Object.prototype.hasOwnProperty.call(active, session.id))\n if (blockers.length) throw new Error(`Active sessions block worktree deletion: ${blockers.map((session) => session.id).join(\", \")}`)\n }\n await assertInactive()\n\n const moved: SessionInfo[] = []\n // Resolve before any move; a directory-only fallback could silently move a\n // legacy session into a different native workspace.\n const rootLocation = readLocationRef(await params.client.location.get({ location: { directory: params.rootDirectory } }))\n if (await exactDirectory(rootLocation.directory) !== rootDirectory) {\n throw new Error(\"OpenCode resolved a foreign evacuation destination\")\n }\n try {\n for (const session of affected) {\n await assertInactive()\n const original = { ...session, location: { ...session.location } }\n await moveSessionToLocation(params.client, session.id, rootLocation)\n moved.push(original)\n }\n await waitForInventory(params.client, project.id, async (current) => (\n moved.every((original) => {\n const session = current.find((candidate) => candidate.id === original.id)\n return session && sameLocation(readLocationRef(session.location), rootLocation)\n }) && !(await Promise.all(current.map((session) => matchesTarget(session.location.directory)))).some(Boolean)\n ))\n const finalInventory = await inventorySessions(params.client, project.id)\n const finalAffected = (await Promise.all(finalInventory.map(async (session) => (\n await matchesTarget(session.location.directory) ? session : undefined\n )))).filter((session): session is SessionInfo => Boolean(session))\n await assertInactive(finalAffected)\n if (finalAffected.length) throw new Error(\"Sessions appeared in the worktree during deletion\")\n await params.remove()\n } catch (error) {\n const rollbackErrors: unknown[] = []\n for (const session of moved.reverse()) {\n try {\n await moveSessionToLocation(params.client, session.id, readLocationRef(session.location))\n } catch (rollbackError) {\n rollbackErrors.push(rollbackError)\n }\n }\n try {\n const expected = new Map(moved.map((session) => [session.id, readLocationRef(session.location)]))\n await waitForInventory(params.client, project.id, async (current) => {\n const restored = current.filter((session) => expected.has(session.id))\n return restored.length === expected.size\n && restored.every((session) => sameLocation(readLocationRef(session.location), expected.get(session.id)!))\n })\n } catch (rollbackError) {\n rollbackErrors.push(rollbackError)\n }\n if (rollbackErrors.length) {\n throw new AggregateError([error, ...rollbackErrors], \"Session evacuation failed and could not be rolled back\")\n }\n throw error\n }\n}\n"]}
@@ -1,9 +1,14 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { describe, it } from "node:test";
3
+ import { mkdir, mkdtemp, realpath, rm, symlink } from "node:fs/promises";
4
+ import os from "node:os";
5
+ import path from "node:path";
3
6
  import { evacuateWorktreeSessions, WorktreeDeletionFence } from "./worktree-session-evacuation";
7
+ import { readLocationContext } from "../opencode/compatibility/location";
4
8
  function session(id, directory, parentID) {
5
9
  return { id, parentID, projectID: "project", location: { directory }, cost: 0, tokens: {}, time: { created: 1, updated: 1 } };
6
10
  }
11
+ const location = { get: async ({ location }) => location };
7
12
  describe("evacuateWorktreeSessions", () => {
8
13
  it("serializes deletion attempts for the same worktree", async () => {
9
14
  const fence = new WorktreeDeletionFence();
@@ -55,6 +60,7 @@ describe("evacuateWorktreeSessions", () => {
55
60
  const state = new Map([root, child, grandchild].map((item) => [item.id, item]));
56
61
  let removed = false;
57
62
  const client = {
63
+ location,
58
64
  project: {
59
65
  list: async () => [{ id: "project", canonical: "/repo", sandboxes: ["/repo/worktree"], time: { created: 1, updated: 1 } }],
60
66
  },
@@ -91,6 +97,7 @@ describe("evacuateWorktreeSessions", () => {
91
97
  let current = aliased;
92
98
  let removed = false;
93
99
  const client = {
100
+ location,
94
101
  project: { list: async () => [{ id: "project", canonical: "/repo", sandboxes: ["/repo/worktree"], time: { created: 1, updated: 1 } }] },
95
102
  session: {
96
103
  list: async () => ({ data: [current], cursor: {} }),
@@ -109,10 +116,164 @@ describe("evacuateWorktreeSessions", () => {
109
116
  assert.equal(current.location.directory, "/repo");
110
117
  assert.equal(removed, true);
111
118
  });
119
+ it("resolves the project canonical path without collapsing containing-worktree identity", async () => {
120
+ const shortRoot = "C:\\Users\\RUNNER~1\\Temp\\repo";
121
+ const longRoot = "C:/Users/runneradmin/Temp/repo";
122
+ const target = `${shortRoot}\\worktree`;
123
+ const original = { directory: target, workspaceID: "native-original" };
124
+ const current = { ...session("session", target), location: original };
125
+ const moves = [];
126
+ const client = {
127
+ location: { get: async () => ({ directory: longRoot }) },
128
+ project: { list: async () => [
129
+ { id: "foreign", canonical: "C:/Users/runneradmin/Temp/repo-other", sandboxes: [] },
130
+ { id: "project", canonical: longRoot, sandboxes: [] },
131
+ ] },
132
+ session: {
133
+ list: async ({ project }) => {
134
+ assert.equal(project, "project");
135
+ return { data: [current], cursor: {} };
136
+ },
137
+ active: async () => ({}),
138
+ move: async ({ directory }, options) => {
139
+ const location = readLocationContext(options?.headers["x-codenomad-location"], "legacy") ?? { directory };
140
+ moves.push(location);
141
+ current.location = location;
142
+ },
143
+ },
144
+ };
145
+ await assert.rejects(evacuateWorktreeSessions({
146
+ client, projectDirectory: shortRoot, targetDirectory: target, rootDirectory: shortRoot,
147
+ resolveDirectoryIdentity: async (directory) => directory === shortRoot || directory === longRoot
148
+ ? "owned:root" : directory === target ? "owned:worktree" : undefined,
149
+ resolveExactDirectory: async (directory) => directory === shortRoot || directory === longRoot
150
+ ? longRoot : directory === target ? target : undefined,
151
+ remove: async () => {
152
+ assert.deepEqual(current.location, { directory: longRoot });
153
+ throw new Error("Synthetic Git removal refusal");
154
+ },
155
+ }), /Synthetic Git removal refusal/);
156
+ assert.deepEqual(moves, [{ directory: longRoot }, original]);
157
+ assert.deepEqual(current.location, original);
158
+ });
159
+ it("inventories the exact project when nested projects and sandboxes share its worktree identity", async () => {
160
+ const inventories = [];
161
+ let removed = false;
162
+ const client = {
163
+ location,
164
+ project: { list: async () => [
165
+ { id: "nested", canonical: "/repo/nested", sandboxes: [] },
166
+ { id: "nested-sandbox", canonical: "/other", sandboxes: ["/repo/worktree/nested"] },
167
+ { id: "project", canonical: "/repo", sandboxes: ["/repo/worktree"] },
168
+ ] },
169
+ session: {
170
+ list: async ({ project }) => {
171
+ inventories.push(project);
172
+ return { data: project === "project" ? [session("active", "/repo/worktree/child")] : [], cursor: {} };
173
+ },
174
+ active: async () => ({ active: { type: "running" } }),
175
+ move: async () => { assert.fail("Active sessions must not move"); },
176
+ },
177
+ };
178
+ await assert.rejects(evacuateWorktreeSessions({
179
+ client, projectDirectory: "/repo", targetDirectory: "/repo/worktree", rootDirectory: "/repo",
180
+ resolveDirectoryIdentity: async (directory) => directory.startsWith("/repo/worktree") ? "/repo/worktree" : "/repo",
181
+ resolveExactDirectory: async (directory) => directory.startsWith("/repo") ? directory : undefined,
182
+ remove: async () => { removed = true; },
183
+ }), /Active sessions block worktree deletion: active/);
184
+ assert.deepEqual(inventories, ["project"]);
185
+ assert.equal(removed, false);
186
+ });
187
+ it("keeps WSL directory translation exact and refuses an owned nested evacuation destination", async () => {
188
+ const hostRoot = "\\\\wsl.localhost\\Ubuntu\\home\\fixture\\repo";
189
+ const hostTarget = `${hostRoot}\\worktree`;
190
+ const translate = async (directory) => directory === hostRoot ? "/home/fixture/repo"
191
+ : directory === hostTarget ? "/home/fixture/repo/worktree" : directory;
192
+ let destination = "/home/fixture/repo/nested";
193
+ let removed = false;
194
+ let moved = false;
195
+ const client = {
196
+ location: { get: async () => ({ directory: destination }) },
197
+ project: { list: async () => [{ id: "project", canonical: "/home/fixture/repo", sandboxes: [] }] },
198
+ session: {
199
+ list: async () => ({ data: [], cursor: {} }),
200
+ active: async () => ({}),
201
+ move: async () => { moved = true; },
202
+ },
203
+ };
204
+ const params = {
205
+ client, projectDirectory: hostRoot, targetDirectory: hostTarget, rootDirectory: hostRoot,
206
+ resolveDirectoryIdentity: async (directory) => (await translate(directory)).includes("/worktree") ? "owned:worktree" : "owned:root",
207
+ resolveExactDirectory: translate,
208
+ remove: async () => { removed = true; },
209
+ };
210
+ await assert.rejects(evacuateWorktreeSessions(params), /foreign evacuation destination/);
211
+ assert.equal(removed, false);
212
+ assert.equal(moved, false);
213
+ destination = "/home/fixture/repo";
214
+ await evacuateWorktreeSessions(params);
215
+ assert.equal(removed, true);
216
+ });
217
+ it("fails closed when an exact directory cannot be resolved", async () => {
218
+ await assert.rejects(evacuateWorktreeSessions({
219
+ client: {}, projectDirectory: "/repo", targetDirectory: "/repo/worktree", rootDirectory: "/repo",
220
+ resolveDirectoryIdentity: async () => "owned:root",
221
+ resolveExactDirectory: async () => undefined,
222
+ remove: async () => { assert.fail("Unresolved directories must not be deleted"); },
223
+ }), /Unable to resolve owned directories/);
224
+ });
225
+ it("resolves a real junction/symlink project alias without admitting a foreign destination", async () => {
226
+ const temp = path.join(os.tmpdir(), "opencode");
227
+ await mkdir(temp, { recursive: true });
228
+ const root = await mkdtemp(path.join(temp, "evacuation-alias-"));
229
+ try {
230
+ const canonical = path.join(root, "repository");
231
+ const alias = path.join(root, "alias");
232
+ const target = path.join(root, "worktree");
233
+ const foreign = path.join(root, "foreign");
234
+ await Promise.all([canonical, target, foreign].map(directory => mkdir(directory)));
235
+ await symlink(canonical, alias, process.platform === "win32" ? "junction" : "dir");
236
+ let destination = await realpath(canonical);
237
+ let current = session("session", target);
238
+ const moves = [];
239
+ const client = {
240
+ location: { get: async () => ({ directory: destination }) },
241
+ project: { list: async () => [{ id: "project", canonical: destination, sandboxes: [] }] },
242
+ session: {
243
+ list: async () => ({ data: [current], cursor: {} }),
244
+ active: async () => ({}),
245
+ move: async ({ directory }) => {
246
+ moves.push(directory);
247
+ current = { ...current, location: { directory } };
248
+ },
249
+ },
250
+ };
251
+ const params = {
252
+ client, projectDirectory: alias, targetDirectory: target, rootDirectory: alias,
253
+ resolveDirectoryIdentity: (directory) => realpath(directory),
254
+ resolveExactDirectory: (directory) => realpath(directory),
255
+ remove: async () => { throw new Error("Synthetic Git removal refusal"); },
256
+ };
257
+ await assert.rejects(evacuateWorktreeSessions(params), /Synthetic Git removal refusal/);
258
+ assert.deepEqual(moves, [destination, target]);
259
+ assert.equal(current.location.directory, target);
260
+ moves.length = 0;
261
+ destination = foreign;
262
+ await assert.rejects(evacuateWorktreeSessions(params), /Unable to resolve the OpenCode project/);
263
+ assert.deepEqual(moves, []);
264
+ client.project.list = async () => [{ id: "project", canonical, sandboxes: [] }];
265
+ await assert.rejects(evacuateWorktreeSessions(params), /foreign evacuation destination/);
266
+ assert.deepEqual(moves, []);
267
+ }
268
+ finally {
269
+ await rm(root, { recursive: true, force: true });
270
+ }
271
+ });
112
272
  it("evacuates sessions nested under the target worktree identity", async () => {
113
273
  let current = session("nested", "/repo/worktree/nested");
114
274
  let removed = false;
115
275
  const client = {
276
+ location,
116
277
  project: { list: async () => [{ id: "project", canonical: "/repo", sandboxes: ["/repo/worktree"], time: { created: 1, updated: 1 } }] },
117
278
  session: {
118
279
  list: async () => ({ data: [current], cursor: {} }),
@@ -135,6 +296,7 @@ describe("evacuateWorktreeSessions", () => {
135
296
  const current = session("session", "/repo/worktree");
136
297
  const moves = [];
137
298
  const client = {
299
+ location,
138
300
  project: { list: async () => [{ id: "project", canonical: "/repo", sandboxes: ["/repo/worktree"], time: { created: 1, updated: 1 } }] },
139
301
  session: {
140
302
  list: async () => ({ data: [current], cursor: {} }),
@@ -158,6 +320,7 @@ describe("evacuateWorktreeSessions", () => {
158
320
  let listCalls = 0;
159
321
  let removed = false;
160
322
  const client = {
323
+ location,
161
324
  project: { list: async () => [{ id: "project", canonical: "/repo", sandboxes: ["/repo/worktree"], time: { created: 1, updated: 1 } }] },
162
325
  session: {
163
326
  list: async () => {
@@ -175,5 +338,53 @@ describe("evacuateWorktreeSessions", () => {
175
338
  assert.equal(removed, false);
176
339
  assert.equal(current.location.directory, "/repo/worktree");
177
340
  });
341
+ it("uses the resolved destination and restores exact legacy identity on rollback", async () => {
342
+ const original = { directory: "/repo/worktree", workspaceID: "native-original" };
343
+ const destination = { directory: "/repo", workspaceID: "native-root" };
344
+ const current = { ...session("session", original.directory), location: original };
345
+ const moves = [];
346
+ const client = {
347
+ location: { get: async () => destination },
348
+ project: { list: async () => [{ id: "project", canonical: "/repo", sandboxes: [original.directory] }] },
349
+ session: {
350
+ list: async () => ({ data: [current], cursor: {} }),
351
+ active: async () => ({}),
352
+ move: async ({ directory }, options) => {
353
+ const resolved = readLocationContext(options?.headers["x-codenomad-location"], "legacy");
354
+ assert.equal(resolved.directory, directory);
355
+ moves.push(resolved);
356
+ current.location = { directory: resolved.directory, workspaceID: resolved.workspaceID };
357
+ },
358
+ },
359
+ };
360
+ await assert.rejects(evacuateWorktreeSessions({
361
+ client, projectDirectory: "/repo", targetDirectory: original.directory, rootDirectory: destination.directory,
362
+ remove: async () => { throw new Error("Git removal failed"); },
363
+ }), /Git removal failed/);
364
+ assert.deepEqual(moves, [destination, original]);
365
+ assert.deepEqual(current.location, original);
366
+ });
367
+ it("does not report rollback success when only the directory was restored", async () => {
368
+ const original = { directory: "/repo/worktree", workspaceID: "native-original" };
369
+ const current = { ...session("session", original.directory), location: original };
370
+ let moves = 0;
371
+ const client = {
372
+ location,
373
+ project: { list: async () => [{ id: "project", canonical: "/repo", sandboxes: [original.directory] }] },
374
+ session: {
375
+ list: async () => ({ data: [current], cursor: {} }),
376
+ active: async () => ({}),
377
+ move: async ({ directory }) => {
378
+ moves++;
379
+ current.location = { directory, workspaceID: undefined };
380
+ },
381
+ },
382
+ };
383
+ await assert.rejects(evacuateWorktreeSessions({
384
+ client, projectDirectory: "/repo", targetDirectory: original.directory, rootDirectory: "/repo",
385
+ remove: async () => { throw new Error("Git removal failed"); },
386
+ }), /could not be rolled back/);
387
+ assert.equal(moves, 2);
388
+ });
178
389
  });
179
390
  //# sourceMappingURL=worktree-session-evacuation.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"worktree-session-evacuation.test.js","sourceRoot":"","sources":["../../src/workspaces/worktree-session-evacuation.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\"\nimport { describe, it } from \"node:test\"\nimport type { OpenCodeClient, SessionInfo } from \"@opencode-ai/client\"\nimport { evacuateWorktreeSessions, WorktreeDeletionFence } from \"./worktree-session-evacuation\"\n\nfunction session(id: string, directory: string, parentID?: string): SessionInfo {\n return { id, parentID, projectID: \"project\", location: { directory }, cost: 0, tokens: {}, time: { created: 1, updated: 1 } } as SessionInfo\n}\n\ndescribe(\"evacuateWorktreeSessions\", () => {\n it(\"serializes deletion attempts for the same worktree\", async () => {\n const fence = new WorktreeDeletionFence()\n let release!: () => void\n const gate = new Promise<void>((resolve) => { release = resolve })\n const calls: string[] = []\n const first = fence.run(\"/repo/worktree\", [\"/repo/worktree\"], async () => {\n calls.push(\"first:start\")\n await gate\n calls.push(\"first:end\")\n })\n const second = fence.run(\"/repo/worktree\", [\"/repo/worktree\"], async () => { calls.push(\"second\") })\n\n await new Promise((resolve) => setImmediate(resolve))\n assert.deepEqual(calls, [\"first:start\"])\n assert.equal(fence.isBlocked(\"/repo/worktree/\"), true)\n release()\n await Promise.all([first, second])\n assert.deepEqual(calls, [\"first:start\", \"first:end\", \"second\"])\n assert.equal(fence.isBlocked(\"/repo/worktree\"), false)\n })\n\n it(\"waits for admitted mutations before deleting and rejects later admission\", async () => {\n const fence = new WorktreeDeletionFence()\n const releaseMutation = fence.enter([\"/repo/worktree\"])\n assert.ok(releaseMutation)\n const calls: string[] = []\n const deletion = fence.run(\"/repo/worktree\", [\"/repo/worktree\"], async () => { calls.push(\"delete\") })\n\n await new Promise((resolve) => setImmediate(resolve))\n assert.deepEqual(calls, [])\n assert.equal(fence.enter([\"/repo/worktree\"]), undefined)\n releaseMutation()\n await deletion\n assert.deepEqual(calls, [\"delete\"])\n })\n\n it(\"fails deletion closed when an admitted mutation does not finish\", async () => {\n const fence = new WorktreeDeletionFence(1)\n const releaseMutation = fence.enter([\"/repo/worktree\"])\n assert.ok(releaseMutation)\n\n await assert.rejects(\n fence.run(\"/repo/worktree\", [\"/repo/worktree\"], async () => {}),\n /Timed out waiting for worktree mutations/,\n )\n assert.equal(fence.isBlocked(\"/repo/worktree\"), false)\n releaseMutation()\n })\n\n it(\"finds later-page sessions and waits for their asynchronous moves\", async () => {\n const moves: Array<{ sessionID: string; directory: string }> = []\n const lists: unknown[] = []\n let listCall = 0\n const root = session(\"old-root\", \"/repo/worktree\")\n const child = session(\"old-child\", \"/repo/worktree\", root.id)\n const grandchild = session(\"old-grandchild\", \"/repo/worktree\", child.id)\n const state = new Map([root, child, grandchild].map((item) => [item.id, item]))\n let removed = false\n const client = {\n project: {\n list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }],\n },\n session: {\n list: async (input: unknown) => {\n lists.push(input)\n listCall += 1\n if (listCall === 1) return { data: [session(\"loaded\", \"/repo\")], cursor: { next: \"older\" } }\n if (listCall === 2) return { data: [root, child, grandchild], cursor: {} }\n return { data: [session(\"loaded\", \"/repo\"), ...state.values()], cursor: {} }\n },\n active: async () => ({}),\n move: async (input: { sessionID: string; directory: string }) => {\n moves.push(input)\n setImmediate(() => state.set(input.sessionID, { ...state.get(input.sessionID)!, location: { directory: input.directory } }))\n },\n },\n } as unknown as OpenCodeClient\n\n await evacuateWorktreeSessions({\n client, projectDirectory: \"/repo\", targetDirectory: \"/repo/worktree\", rootDirectory: \"/repo\",\n remove: async () => { removed = true },\n })\n\n assert.deepEqual(moves.map(({ sessionID }) => sessionID), [root.id, child.id, grandchild.id])\n assert.equal(removed, true)\n assert.ok(listCall > 3)\n assert.ok(lists.every((input: any) => input.cursor\n ? Object.keys(input).length === 1\n : input.project === \"project\" && input.directory === undefined))\n })\n\n it(\"evacuates sessions whose directory resolves to the target alias\", async () => {\n const aliased = session(\"aliased\", \"/repo/alias\")\n let current = aliased\n let removed = false\n const client = {\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }] },\n session: {\n list: async () => ({ data: [current], cursor: {} }),\n active: async () => ({}),\n move: async (input: { directory: string }) => { current = { ...current, location: { directory: input.directory } } },\n },\n } as unknown as OpenCodeClient\n\n await evacuateWorktreeSessions({\n client,\n projectDirectory: \"/repo\",\n targetDirectory: \"/repo/worktree\",\n rootDirectory: \"/repo\",\n resolveDirectoryIdentity: async (directory) => directory === \"/repo/alias\" ? \"/repo/worktree\" : directory,\n remove: async () => { removed = true },\n })\n\n assert.equal(current.location.directory, \"/repo\")\n assert.equal(removed, true)\n })\n\n it(\"evacuates sessions nested under the target worktree identity\", async () => {\n let current = session(\"nested\", \"/repo/worktree/nested\")\n let removed = false\n const client = {\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }] },\n session: {\n list: async () => ({ data: [current], cursor: {} }),\n active: async () => ({}),\n move: async (input: { directory: string }) => { current = { ...current, location: { directory: input.directory } } },\n },\n } as unknown as OpenCodeClient\n\n await evacuateWorktreeSessions({\n client,\n projectDirectory: \"/repo\",\n targetDirectory: \"/repo/worktree\",\n rootDirectory: \"/repo\",\n resolveDirectoryIdentity: async (directory) => directory.startsWith(\"/repo/worktree\") ? \"workspace:worktree\" : \"workspace:root\",\n remove: async () => { removed = true },\n })\n\n assert.equal(current.location.directory, \"/repo\")\n assert.equal(removed, true)\n })\n\n it(\"rolls sessions back when Git removal fails\", async () => {\n const current = session(\"session\", \"/repo/worktree\")\n const moves: string[] = []\n const client = {\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }] },\n session: {\n list: async () => ({ data: [current], cursor: {} }),\n active: async () => ({}),\n move: async ({ directory }: { directory: string }) => {\n moves.push(directory)\n current.location = { directory }\n },\n },\n } as unknown as OpenCodeClient\n\n await assert.rejects(evacuateWorktreeSessions({\n client, projectDirectory: \"/repo\", targetDirectory: \"/repo/worktree\", rootDirectory: \"/repo\",\n remove: async () => { throw new Error(\"Git removal failed\") },\n }), /Git removal failed/)\n assert.deepEqual(moves, [\"/repo\", \"/repo/worktree\"])\n assert.equal(current.location.directory, \"/repo/worktree\")\n })\n\n it(\"re-inventories active sessions immediately before removal\", async () => {\n const current = session(\"session\", \"/repo/worktree\")\n const intruder = session(\"intruder\", \"/repo/worktree\")\n let listCalls = 0\n let removed = false\n const client = {\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }] },\n session: {\n list: async () => {\n listCalls += 1\n return { data: listCalls >= 3 ? [current, intruder] : [current], cursor: {} }\n },\n active: async () => ({ intruder: { type: \"running\" } }),\n move: async ({ directory }: { directory: string }) => { current.location = { directory } },\n },\n } as unknown as OpenCodeClient\n\n await assert.rejects(evacuateWorktreeSessions({\n client, projectDirectory: \"/repo\", targetDirectory: \"/repo/worktree\", rootDirectory: \"/repo\",\n remove: async () => { removed = true },\n }), /Active sessions block worktree deletion: intruder/)\n\n assert.equal(removed, false)\n assert.equal(current.location.directory, \"/repo/worktree\")\n })\n})\n"]}
1
+ {"version":3,"file":"worktree-session-evacuation.test.js","sourceRoot":"","sources":["../../src/workspaces/worktree-session-evacuation.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AACxE,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAA;AAC/F,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAA;AAExE,SAAS,OAAO,CAAC,EAAU,EAAE,SAAiB,EAAE,QAAiB;IAC/D,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,EAAiB,CAAA;AAC9I,CAAC;AAED,MAAM,QAAQ,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAuC,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAA;AAE/F,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,KAAK,GAAG,IAAI,qBAAqB,EAAE,CAAA;QACzC,IAAI,OAAoB,CAAA;QACxB,MAAM,IAAI,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,GAAG,OAAO,GAAG,OAAO,CAAA,CAAC,CAAC,CAAC,CAAA;QAClE,MAAM,KAAK,GAAa,EAAE,CAAA;QAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,gBAAgB,CAAC,EAAE,KAAK,IAAI,EAAE;YACvE,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;YACzB,MAAM,IAAI,CAAA;YACV,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QACzB,CAAC,CAAC,CAAA;QACF,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,gBAAgB,CAAC,EAAE,KAAK,IAAI,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA,CAAC,CAAC,CAAC,CAAA;QAEpG,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAA;QACrD,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,aAAa,CAAC,CAAC,CAAA;QACxC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,iBAAiB,CAAC,EAAE,IAAI,CAAC,CAAA;QACtD,OAAO,EAAE,CAAA;QACT,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAA;QAClC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,aAAa,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAA;QAC/D,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,gBAAgB,CAAC,EAAE,KAAK,CAAC,CAAA;IACxD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,0EAA0E,EAAE,KAAK,IAAI,EAAE;QACxF,MAAM,KAAK,GAAG,IAAI,qBAAqB,EAAE,CAAA;QACzC,MAAM,eAAe,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAA;QACvD,MAAM,CAAC,EAAE,CAAC,eAAe,CAAC,CAAA;QAC1B,MAAM,KAAK,GAAa,EAAE,CAAA;QAC1B,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,gBAAgB,CAAC,EAAE,KAAK,IAAI,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA,CAAC,CAAC,CAAC,CAAA;QAEtG,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAA;QACrD,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QAC3B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,gBAAgB,CAAC,CAAC,EAAE,SAAS,CAAC,CAAA;QACxD,eAAe,EAAE,CAAA;QACjB,MAAM,QAAQ,CAAA;QACd,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAA;IACrC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,KAAK,GAAG,IAAI,qBAAqB,CAAC,CAAC,CAAC,CAAA;QAC1C,MAAM,eAAe,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAA;QACvD,MAAM,CAAC,EAAE,CAAC,eAAe,CAAC,CAAA;QAE1B,MAAM,MAAM,CAAC,OAAO,CAClB,KAAK,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,gBAAgB,CAAC,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,CAAC,EAC/D,0CAA0C,CAC3C,CAAA;QACD,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,gBAAgB,CAAC,EAAE,KAAK,CAAC,CAAA;QACtD,eAAe,EAAE,CAAA;IACnB,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAChF,MAAM,KAAK,GAAoD,EAAE,CAAA;QACjE,MAAM,KAAK,GAAc,EAAE,CAAA;QAC3B,IAAI,QAAQ,GAAG,CAAC,CAAA;QAChB,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAA;QAClD,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE,CAAC,CAAA;QAC7D,MAAM,UAAU,GAAG,OAAO,CAAC,gBAAgB,EAAE,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,CAAA;QACxE,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;QAC/E,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,MAAM,MAAM,GAAG;YACb,QAAQ;YACR,OAAO,EAAE;gBACP,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC;aAC3H;YACD,OAAO,EAAE;gBACP,IAAI,EAAE,KAAK,EAAE,KAAc,EAAE,EAAE;oBAC7B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;oBACjB,QAAQ,IAAI,CAAC,CAAA;oBACb,IAAI,QAAQ,KAAK,CAAC;wBAAE,OAAO,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAA;oBAC5F,IAAI,QAAQ,KAAK,CAAC;wBAAE,OAAO,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAA;oBAC1E,OAAO,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAA;gBAC9E,CAAC;gBACD,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;gBACxB,IAAI,EAAE,KAAK,EAAE,KAA+C,EAAE,EAAE;oBAC9D,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;oBACjB,YAAY,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAE,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAA;gBAC9H,CAAC;aACF;SAC2B,CAAA;QAE9B,MAAM,wBAAwB,CAAC;YAC7B,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,EAAE,OAAO;YAC5F,MAAM,EAAE,KAAK,IAAI,EAAE,GAAG,OAAO,GAAG,IAAI,CAAA,CAAC,CAAC;SACvC,CAAC,CAAA;QAEF,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC,CAAA;QAC7F,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;QAC3B,MAAM,CAAC,EAAE,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAA;QACvB,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,KAAU,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM;YAChD,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC;YACjC,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAA;IACpE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,EAAE,aAAa,CAAC,CAAA;QACjD,IAAI,OAAO,GAAG,OAAO,CAAA;QACrB,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,MAAM,MAAM,GAAG;YACb,QAAQ;YACR,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE;YACvI,OAAO,EAAE;gBACP,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;gBACnD,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;gBACxB,IAAI,EAAE,KAAK,EAAE,KAA4B,EAAE,EAAE,GAAG,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,EAAE,CAAA,CAAC,CAAC;aACrH;SAC2B,CAAA;QAE9B,MAAM,wBAAwB,CAAC;YAC7B,MAAM;YACN,gBAAgB,EAAE,OAAO;YACzB,eAAe,EAAE,gBAAgB;YACjC,aAAa,EAAE,OAAO;YACtB,wBAAwB,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC,SAAS,KAAK,aAAa,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS;YACzG,MAAM,EAAE,KAAK,IAAI,EAAE,GAAG,OAAO,GAAG,IAAI,CAAA,CAAC,CAAC;SACvC,CAAC,CAAA;QAEF,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;QACjD,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;IAC7B,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qFAAqF,EAAE,KAAK,IAAI,EAAE;QACnG,MAAM,SAAS,GAAG,iCAAiC,CAAA;QACnD,MAAM,QAAQ,GAAG,gCAAgC,CAAA;QACjD,MAAM,MAAM,GAAG,GAAG,SAAS,YAAY,CAAA;QACvC,MAAM,QAAQ,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAA;QACtE,MAAM,OAAO,GAAG,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAA;QACrE,MAAM,KAAK,GAAc,EAAE,CAAA;QAC3B,MAAM,MAAM,GAAG;YACb,QAAQ,EAAE,EAAE,GAAG,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,EAAE;YACxD,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;oBAC3B,EAAE,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,sCAAsC,EAAE,SAAS,EAAE,EAAE,EAAE;oBACnF,EAAE,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE;iBACtD,EAAE;YACH,OAAO,EAAE;gBACP,IAAI,EAAE,KAAK,EAAE,EAAE,OAAO,EAAuB,EAAE,EAAE;oBAC/C,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;oBAChC,OAAO,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAA;gBACxC,CAAC;gBACD,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;gBACxB,IAAI,EAAE,KAAK,EAAE,EAAE,SAAS,EAAyB,EAAE,OAA6C,EAAE,EAAE;oBAClG,MAAM,QAAQ,GAAG,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,sBAAsB,CAAC,EAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,CAAA;oBACzG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;oBACpB,OAAO,CAAC,QAAQ,GAAG,QAA2B,CAAA;gBAChD,CAAC;aACF;SAC2B,CAAA;QAC9B,MAAM,MAAM,CAAC,OAAO,CAAC,wBAAwB,CAAC;YAC5C,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS;YACtF,wBAAwB,EAAE,KAAK,EAAC,SAAS,EAAC,EAAE,CAAC,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,QAAQ;gBAC5F,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS;YACtE,qBAAqB,EAAE,KAAK,EAAC,SAAS,EAAC,EAAE,CAAC,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,QAAQ;gBACzF,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;YACxD,MAAM,EAAE,KAAK,IAAI,EAAE;gBACjB,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAA;gBAC3D,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAA;YAClD,CAAC;SACF,CAAC,EAAE,+BAA+B,CAAC,CAAA;QACpC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAA;QAC5D,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,8FAA8F,EAAE,KAAK,IAAI,EAAE;QAC5G,MAAM,WAAW,GAAa,EAAE,CAAA;QAChC,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,MAAM,MAAM,GAAG;YACb,QAAQ;YACR,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;oBAC3B,EAAE,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,EAAE,EAAE;oBAC1D,EAAE,EAAE,EAAE,gBAAgB,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,uBAAuB,CAAC,EAAE;oBACnF,EAAE,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,gBAAgB,CAAC,EAAE;iBACrE,EAAE;YACH,OAAO,EAAE;gBACP,IAAI,EAAE,KAAK,EAAE,EAAE,OAAO,EAAuB,EAAE,EAAE;oBAC/C,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;oBACzB,OAAO,EAAE,IAAI,EAAE,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAA;gBACvG,CAAC;gBACD,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC;gBACrD,IAAI,EAAE,KAAK,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAA,CAAC,CAAC;aACnE;SAC2B,CAAA;QAC9B,MAAM,MAAM,CAAC,OAAO,CAAC,wBAAwB,CAAC;YAC5C,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,EAAE,OAAO;YAC5F,wBAAwB,EAAE,KAAK,EAAC,SAAS,EAAC,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,OAAO;YAChH,qBAAqB,EAAE,KAAK,EAAC,SAAS,EAAC,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;YAC/F,MAAM,EAAE,KAAK,IAAI,EAAE,GAAG,OAAO,GAAG,IAAI,CAAA,CAAC,CAAC;SACvC,CAAC,EAAE,iDAAiD,CAAC,CAAA;QACtD,MAAM,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC,CAAC,CAAA;QAC1C,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IAC9B,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,0FAA0F,EAAE,KAAK,IAAI,EAAE;QACxG,MAAM,QAAQ,GAAG,gDAAgD,CAAA;QACjE,MAAM,UAAU,GAAG,GAAG,QAAQ,YAAY,CAAA;QAC1C,MAAM,SAAS,GAAG,KAAK,EAAE,SAAiB,EAAE,EAAE,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,oBAAoB;YAC1F,CAAC,CAAC,SAAS,KAAK,UAAU,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,SAAS,CAAA;QACxE,IAAI,WAAW,GAAG,2BAA2B,CAAA;QAC7C,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,IAAI,KAAK,GAAG,KAAK,CAAA;QACjB,MAAM,MAAM,GAAG;YACb,QAAQ,EAAE,EAAE,GAAG,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,EAAE;YAC3D,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,oBAAoB,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,EAAE;YAClG,OAAO,EAAE;gBACP,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;gBAC5C,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;gBACxB,IAAI,EAAE,KAAK,IAAI,EAAE,GAAG,KAAK,GAAG,IAAI,CAAA,CAAC,CAAC;aACnC;SAC2B,CAAA;QAC9B,MAAM,MAAM,GAAG;YACb,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,eAAe,EAAE,UAAU,EAAE,aAAa,EAAE,QAAQ;YACxF,wBAAwB,EAAE,KAAK,EAAE,SAAiB,EAAE,EAAE,CAAC,CAAC,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,YAAY;YAC3I,qBAAqB,EAAE,SAAS;YAChC,MAAM,EAAE,KAAK,IAAI,EAAE,GAAG,OAAO,GAAG,IAAI,CAAA,CAAC,CAAC;SACvC,CAAA;QACD,MAAM,MAAM,CAAC,OAAO,CAAC,wBAAwB,CAAC,MAAM,CAAC,EAAE,gCAAgC,CAAC,CAAA;QACxF,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAC5B,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;QAC1B,WAAW,GAAG,oBAAoB,CAAA;QAClC,MAAM,wBAAwB,CAAC,MAAM,CAAC,CAAA;QACtC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;IAC7B,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;QACvE,MAAM,MAAM,CAAC,OAAO,CAAC,wBAAwB,CAAC;YAC5C,MAAM,EAAE,EAAoB,EAAE,gBAAgB,EAAE,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,EAAE,OAAO;YAClH,wBAAwB,EAAE,KAAK,IAAI,EAAE,CAAC,YAAY;YAClD,qBAAqB,EAAE,KAAK,IAAI,EAAE,CAAC,SAAS;YAC5C,MAAM,EAAE,KAAK,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAA,CAAC,CAAC;SAClF,CAAC,EAAE,qCAAqC,CAAC,CAAA;IAC5C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wFAAwF,EAAE,KAAK,IAAI,EAAE;QACtG,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,CAAA;QAC/C,MAAM,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACtC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC,CAAA;QAChE,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;YAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;YACtC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;YAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;YAC1C,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;YAClF,MAAM,OAAO,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;YAClF,IAAI,WAAW,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,CAAA;YAC3C,IAAI,OAAO,GAAG,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;YACxC,MAAM,KAAK,GAAa,EAAE,CAAA;YAC1B,MAAM,MAAM,GAAG;gBACb,QAAQ,EAAE,EAAE,GAAG,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,EAAE;gBAC3D,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,EAAE;gBACzF,OAAO,EAAE;oBACP,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;oBACnD,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;oBACxB,IAAI,EAAE,KAAK,EAAE,EAAE,SAAS,EAAyB,EAAE,EAAE;wBACnD,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;wBACrB,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,EAAE,CAAA;oBACnD,CAAC;iBACF;aAC2B,CAAA;YAC9B,MAAM,MAAM,GAAG;gBACb,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK;gBAC9E,wBAAwB,EAAE,CAAC,SAAiB,EAAE,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC;gBACpE,qBAAqB,EAAE,CAAC,SAAiB,EAAE,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC;gBACjE,MAAM,EAAE,KAAK,IAAI,EAAE,GAAG,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAA,CAAC,CAAC;aACzE,CAAA;YACD,MAAM,MAAM,CAAC,OAAO,CAAC,wBAAwB,CAAC,MAAM,CAAC,EAAE,+BAA+B,CAAC,CAAA;YACvF,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAA;YAC9C,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;YAChD,KAAK,CAAC,MAAM,GAAG,CAAC,CAAA;YAChB,WAAW,GAAG,OAAO,CAAA;YACrB,MAAM,MAAM,CAAC,OAAO,CAAC,wBAAwB,CAAC,MAAM,CAAC,EAAE,wCAAwC,CAAC,CAAA;YAChG,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;YAC3B,MAAM,CAAC,OAAO,CAAC,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,CAAQ,CAAA;YACtF,MAAM,MAAM,CAAC,OAAO,CAAC,wBAAwB,CAAC,MAAM,CAAC,EAAE,gCAAgC,CAAC,CAAA;YACxF,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QAC7B,CAAC;gBAAS,CAAC;YAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;QAAC,CAAC;IAChE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;QAC5E,IAAI,OAAO,GAAG,OAAO,CAAC,QAAQ,EAAE,uBAAuB,CAAC,CAAA;QACxD,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,MAAM,MAAM,GAAG;YACb,QAAQ;YACR,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE;YACvI,OAAO,EAAE;gBACP,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;gBACnD,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;gBACxB,IAAI,EAAE,KAAK,EAAE,KAA4B,EAAE,EAAE,GAAG,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,EAAE,CAAA,CAAC,CAAC;aACrH;SAC2B,CAAA;QAE9B,MAAM,wBAAwB,CAAC;YAC7B,MAAM;YACN,gBAAgB,EAAE,OAAO;YACzB,eAAe,EAAE,gBAAgB;YACjC,aAAa,EAAE,OAAO;YACtB,wBAAwB,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,gBAAgB;YAC/H,MAAM,EAAE,KAAK,IAAI,EAAE,GAAG,OAAO,GAAG,IAAI,CAAA,CAAC,CAAC;SACvC,CAAC,CAAA;QAEF,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;QACjD,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;IAC7B,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAA;QACpD,MAAM,KAAK,GAAa,EAAE,CAAA;QAC1B,MAAM,MAAM,GAAG;YACb,QAAQ;YACR,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE;YACvI,OAAO,EAAE;gBACP,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;gBACnD,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;gBACxB,IAAI,EAAE,KAAK,EAAE,EAAE,SAAS,EAAyB,EAAE,EAAE;oBACnD,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;oBACrB,OAAO,CAAC,QAAQ,GAAG,EAAE,SAAS,EAAE,CAAA;gBAClC,CAAC;aACF;SAC2B,CAAA;QAE9B,MAAM,MAAM,CAAC,OAAO,CAAC,wBAAwB,CAAC;YAC5C,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,EAAE,OAAO;YAC5F,MAAM,EAAE,KAAK,IAAI,EAAE,GAAG,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAA,CAAC,CAAC;SAC9D,CAAC,EAAE,oBAAoB,CAAC,CAAA;QACzB,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAA;QACpD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAA;IAC5D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QACzE,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAA;QACpD,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAA;QACtD,IAAI,SAAS,GAAG,CAAC,CAAA;QACjB,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,MAAM,MAAM,GAAG;YACb,QAAQ;YACR,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE;YACvI,OAAO,EAAE;gBACP,IAAI,EAAE,KAAK,IAAI,EAAE;oBACf,SAAS,IAAI,CAAC,CAAA;oBACd,OAAO,EAAE,IAAI,EAAE,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAA;gBAC/E,CAAC;gBACD,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC;gBACvD,IAAI,EAAE,KAAK,EAAE,EAAE,SAAS,EAAyB,EAAE,EAAE,GAAG,OAAO,CAAC,QAAQ,GAAG,EAAE,SAAS,EAAE,CAAA,CAAC,CAAC;aAC3F;SAC2B,CAAA;QAE9B,MAAM,MAAM,CAAC,OAAO,CAAC,wBAAwB,CAAC;YAC5C,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,EAAE,OAAO;YAC5F,MAAM,EAAE,KAAK,IAAI,EAAE,GAAG,OAAO,GAAG,IAAI,CAAA,CAAC,CAAC;SACvC,CAAC,EAAE,mDAAmD,CAAC,CAAA;QAExD,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAC5B,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAA;IAC5D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;QAC5F,MAAM,QAAQ,GAAG,EAAE,SAAS,EAAE,gBAAgB,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAA;QAChF,MAAM,WAAW,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,CAAA;QACtE,MAAM,OAAO,GAAG,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAA;QACjF,MAAM,KAAK,GAAc,EAAE,CAAA;QAC3B,MAAM,MAAM,GAAG;YACb,QAAQ,EAAE,EAAE,GAAG,EAAE,KAAK,IAAI,EAAE,CAAC,WAAW,EAAE;YAC1C,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE;YACvG,OAAO,EAAE;gBACP,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;gBACnD,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;gBACxB,IAAI,EAAE,KAAK,EAAE,EAAE,SAAS,EAAyB,EAAE,OAA6C,EAAE,EAAE;oBAClG,MAAM,QAAQ,GAAG,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,sBAAsB,CAAC,EAAE,QAAQ,CAAE,CAAA;oBACzF,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;oBAC3C,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;oBACpB,OAAO,CAAC,QAAQ,GAAG,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAY,EAAE,CAAA;gBAC1F,CAAC;aACF;SAC2B,CAAA;QAC9B,MAAM,MAAM,CAAC,OAAO,CAAC,wBAAwB,CAAC;YAC5C,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,eAAe,EAAE,QAAQ,CAAC,SAAS,EAAE,aAAa,EAAE,WAAW,CAAC,SAAS;YAC5G,MAAM,EAAE,KAAK,IAAI,EAAE,GAAG,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAA,CAAC,CAAC;SAC9D,CAAC,EAAE,oBAAoB,CAAC,CAAA;QACzB,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAA;QAChD,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,uEAAuE,EAAE,KAAK,IAAI,EAAE;QACrF,MAAM,QAAQ,GAAG,EAAE,SAAS,EAAE,gBAAgB,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAA;QAChF,MAAM,OAAO,GAAG,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAA;QACjF,IAAI,KAAK,GAAG,CAAC,CAAA;QACb,MAAM,MAAM,GAAG;YACb,QAAQ;YACR,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE;YACvG,OAAO,EAAE;gBACP,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;gBACnD,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;gBACxB,IAAI,EAAE,KAAK,EAAE,EAAE,SAAS,EAAyB,EAAE,EAAE;oBACnD,KAAK,EAAE,CAAA;oBACP,OAAO,CAAC,QAAQ,GAAG,EAAE,SAAS,EAAE,WAAW,EAAE,SAAU,EAAE,CAAA;gBAC3D,CAAC;aACF;SAC2B,CAAA;QAC9B,MAAM,MAAM,CAAC,OAAO,CAAC,wBAAwB,CAAC;YAC5C,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,eAAe,EAAE,QAAQ,CAAC,SAAS,EAAE,aAAa,EAAE,OAAO;YAC9F,MAAM,EAAE,KAAK,IAAI,EAAE,GAAG,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAA,CAAC,CAAC;SAC9D,CAAC,EAAE,0BAA0B,CAAC,CAAA;QAC/B,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;IACxB,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import assert from \"node:assert/strict\"\nimport { describe, it } from \"node:test\"\nimport { mkdir, mkdtemp, realpath, rm, symlink } from \"node:fs/promises\"\nimport os from \"node:os\"\nimport path from \"node:path\"\nimport type { OpenCodeClient, SessionInfo } from \"@opencode/client\"\nimport { evacuateWorktreeSessions, WorktreeDeletionFence } from \"./worktree-session-evacuation\"\nimport { readLocationContext } from \"../opencode/compatibility/location\"\n\nfunction session(id: string, directory: string, parentID?: string): SessionInfo {\n return { id, parentID, projectID: \"project\", location: { directory }, cost: 0, tokens: {}, time: { created: 1, updated: 1 } } as SessionInfo\n}\n\nconst location = { get: async ({ location }: { location: { directory: string } }) => location }\n\ndescribe(\"evacuateWorktreeSessions\", () => {\n it(\"serializes deletion attempts for the same worktree\", async () => {\n const fence = new WorktreeDeletionFence()\n let release!: () => void\n const gate = new Promise<void>((resolve) => { release = resolve })\n const calls: string[] = []\n const first = fence.run(\"/repo/worktree\", [\"/repo/worktree\"], async () => {\n calls.push(\"first:start\")\n await gate\n calls.push(\"first:end\")\n })\n const second = fence.run(\"/repo/worktree\", [\"/repo/worktree\"], async () => { calls.push(\"second\") })\n\n await new Promise((resolve) => setImmediate(resolve))\n assert.deepEqual(calls, [\"first:start\"])\n assert.equal(fence.isBlocked(\"/repo/worktree/\"), true)\n release()\n await Promise.all([first, second])\n assert.deepEqual(calls, [\"first:start\", \"first:end\", \"second\"])\n assert.equal(fence.isBlocked(\"/repo/worktree\"), false)\n })\n\n it(\"waits for admitted mutations before deleting and rejects later admission\", async () => {\n const fence = new WorktreeDeletionFence()\n const releaseMutation = fence.enter([\"/repo/worktree\"])\n assert.ok(releaseMutation)\n const calls: string[] = []\n const deletion = fence.run(\"/repo/worktree\", [\"/repo/worktree\"], async () => { calls.push(\"delete\") })\n\n await new Promise((resolve) => setImmediate(resolve))\n assert.deepEqual(calls, [])\n assert.equal(fence.enter([\"/repo/worktree\"]), undefined)\n releaseMutation()\n await deletion\n assert.deepEqual(calls, [\"delete\"])\n })\n\n it(\"fails deletion closed when an admitted mutation does not finish\", async () => {\n const fence = new WorktreeDeletionFence(1)\n const releaseMutation = fence.enter([\"/repo/worktree\"])\n assert.ok(releaseMutation)\n\n await assert.rejects(\n fence.run(\"/repo/worktree\", [\"/repo/worktree\"], async () => {}),\n /Timed out waiting for worktree mutations/,\n )\n assert.equal(fence.isBlocked(\"/repo/worktree\"), false)\n releaseMutation()\n })\n\n it(\"finds later-page sessions and waits for their asynchronous moves\", async () => {\n const moves: Array<{ sessionID: string; directory: string }> = []\n const lists: unknown[] = []\n let listCall = 0\n const root = session(\"old-root\", \"/repo/worktree\")\n const child = session(\"old-child\", \"/repo/worktree\", root.id)\n const grandchild = session(\"old-grandchild\", \"/repo/worktree\", child.id)\n const state = new Map([root, child, grandchild].map((item) => [item.id, item]))\n let removed = false\n const client = {\n location,\n project: {\n list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }],\n },\n session: {\n list: async (input: unknown) => {\n lists.push(input)\n listCall += 1\n if (listCall === 1) return { data: [session(\"loaded\", \"/repo\")], cursor: { next: \"older\" } }\n if (listCall === 2) return { data: [root, child, grandchild], cursor: {} }\n return { data: [session(\"loaded\", \"/repo\"), ...state.values()], cursor: {} }\n },\n active: async () => ({}),\n move: async (input: { sessionID: string; directory: string }) => {\n moves.push(input)\n setImmediate(() => state.set(input.sessionID, { ...state.get(input.sessionID)!, location: { directory: input.directory } }))\n },\n },\n } as unknown as OpenCodeClient\n\n await evacuateWorktreeSessions({\n client, projectDirectory: \"/repo\", targetDirectory: \"/repo/worktree\", rootDirectory: \"/repo\",\n remove: async () => { removed = true },\n })\n\n assert.deepEqual(moves.map(({ sessionID }) => sessionID), [root.id, child.id, grandchild.id])\n assert.equal(removed, true)\n assert.ok(listCall > 3)\n assert.ok(lists.every((input: any) => input.cursor\n ? Object.keys(input).length === 1\n : input.project === \"project\" && input.directory === undefined))\n })\n\n it(\"evacuates sessions whose directory resolves to the target alias\", async () => {\n const aliased = session(\"aliased\", \"/repo/alias\")\n let current = aliased\n let removed = false\n const client = {\n location,\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }] },\n session: {\n list: async () => ({ data: [current], cursor: {} }),\n active: async () => ({}),\n move: async (input: { directory: string }) => { current = { ...current, location: { directory: input.directory } } },\n },\n } as unknown as OpenCodeClient\n\n await evacuateWorktreeSessions({\n client,\n projectDirectory: \"/repo\",\n targetDirectory: \"/repo/worktree\",\n rootDirectory: \"/repo\",\n resolveDirectoryIdentity: async (directory) => directory === \"/repo/alias\" ? \"/repo/worktree\" : directory,\n remove: async () => { removed = true },\n })\n\n assert.equal(current.location.directory, \"/repo\")\n assert.equal(removed, true)\n })\n\n it(\"resolves the project canonical path without collapsing containing-worktree identity\", async () => {\n const shortRoot = \"C:\\\\Users\\\\RUNNER~1\\\\Temp\\\\repo\"\n const longRoot = \"C:/Users/runneradmin/Temp/repo\"\n const target = `${shortRoot}\\\\worktree`\n const original = { directory: target, workspaceID: \"native-original\" }\n const current = { ...session(\"session\", target), location: original }\n const moves: unknown[] = []\n const client = {\n location: { get: async () => ({ directory: longRoot }) },\n project: { list: async () => [\n { id: \"foreign\", canonical: \"C:/Users/runneradmin/Temp/repo-other\", sandboxes: [] },\n { id: \"project\", canonical: longRoot, sandboxes: [] },\n ] },\n session: {\n list: async ({ project }: { project: string }) => {\n assert.equal(project, \"project\")\n return { data: [current], cursor: {} }\n },\n active: async () => ({}),\n move: async ({ directory }: { directory: string }, options?: { headers: Record<string, string> }) => {\n const location = readLocationContext(options?.headers[\"x-codenomad-location\"], \"legacy\") ?? { directory }\n moves.push(location)\n current.location = location as typeof original\n },\n },\n } as unknown as OpenCodeClient\n await assert.rejects(evacuateWorktreeSessions({\n client, projectDirectory: shortRoot, targetDirectory: target, rootDirectory: shortRoot,\n resolveDirectoryIdentity: async directory => directory === shortRoot || directory === longRoot\n ? \"owned:root\" : directory === target ? \"owned:worktree\" : undefined,\n resolveExactDirectory: async directory => directory === shortRoot || directory === longRoot\n ? longRoot : directory === target ? target : undefined,\n remove: async () => {\n assert.deepEqual(current.location, { directory: longRoot })\n throw new Error(\"Synthetic Git removal refusal\")\n },\n }), /Synthetic Git removal refusal/)\n assert.deepEqual(moves, [{ directory: longRoot }, original])\n assert.deepEqual(current.location, original)\n })\n\n it(\"inventories the exact project when nested projects and sandboxes share its worktree identity\", async () => {\n const inventories: string[] = []\n let removed = false\n const client = {\n location,\n project: { list: async () => [\n { id: \"nested\", canonical: \"/repo/nested\", sandboxes: [] },\n { id: \"nested-sandbox\", canonical: \"/other\", sandboxes: [\"/repo/worktree/nested\"] },\n { id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"] },\n ] },\n session: {\n list: async ({ project }: { project: string }) => {\n inventories.push(project)\n return { data: project === \"project\" ? [session(\"active\", \"/repo/worktree/child\")] : [], cursor: {} }\n },\n active: async () => ({ active: { type: \"running\" } }),\n move: async () => { assert.fail(\"Active sessions must not move\") },\n },\n } as unknown as OpenCodeClient\n await assert.rejects(evacuateWorktreeSessions({\n client, projectDirectory: \"/repo\", targetDirectory: \"/repo/worktree\", rootDirectory: \"/repo\",\n resolveDirectoryIdentity: async directory => directory.startsWith(\"/repo/worktree\") ? \"/repo/worktree\" : \"/repo\",\n resolveExactDirectory: async directory => directory.startsWith(\"/repo\") ? directory : undefined,\n remove: async () => { removed = true },\n }), /Active sessions block worktree deletion: active/)\n assert.deepEqual(inventories, [\"project\"])\n assert.equal(removed, false)\n })\n\n it(\"keeps WSL directory translation exact and refuses an owned nested evacuation destination\", async () => {\n const hostRoot = \"\\\\\\\\wsl.localhost\\\\Ubuntu\\\\home\\\\fixture\\\\repo\"\n const hostTarget = `${hostRoot}\\\\worktree`\n const translate = async (directory: string) => directory === hostRoot ? \"/home/fixture/repo\"\n : directory === hostTarget ? \"/home/fixture/repo/worktree\" : directory\n let destination = \"/home/fixture/repo/nested\"\n let removed = false\n let moved = false\n const client = {\n location: { get: async () => ({ directory: destination }) },\n project: { list: async () => [{ id: \"project\", canonical: \"/home/fixture/repo\", sandboxes: [] }] },\n session: {\n list: async () => ({ data: [], cursor: {} }),\n active: async () => ({}),\n move: async () => { moved = true },\n },\n } as unknown as OpenCodeClient\n const params = {\n client, projectDirectory: hostRoot, targetDirectory: hostTarget, rootDirectory: hostRoot,\n resolveDirectoryIdentity: async (directory: string) => (await translate(directory)).includes(\"/worktree\") ? \"owned:worktree\" : \"owned:root\",\n resolveExactDirectory: translate,\n remove: async () => { removed = true },\n }\n await assert.rejects(evacuateWorktreeSessions(params), /foreign evacuation destination/)\n assert.equal(removed, false)\n assert.equal(moved, false)\n destination = \"/home/fixture/repo\"\n await evacuateWorktreeSessions(params)\n assert.equal(removed, true)\n })\n\n it(\"fails closed when an exact directory cannot be resolved\", async () => {\n await assert.rejects(evacuateWorktreeSessions({\n client: {} as OpenCodeClient, projectDirectory: \"/repo\", targetDirectory: \"/repo/worktree\", rootDirectory: \"/repo\",\n resolveDirectoryIdentity: async () => \"owned:root\",\n resolveExactDirectory: async () => undefined,\n remove: async () => { assert.fail(\"Unresolved directories must not be deleted\") },\n }), /Unable to resolve owned directories/)\n })\n\n it(\"resolves a real junction/symlink project alias without admitting a foreign destination\", async () => {\n const temp = path.join(os.tmpdir(), \"opencode\")\n await mkdir(temp, { recursive: true })\n const root = await mkdtemp(path.join(temp, \"evacuation-alias-\"))\n try {\n const canonical = path.join(root, \"repository\")\n const alias = path.join(root, \"alias\")\n const target = path.join(root, \"worktree\")\n const foreign = path.join(root, \"foreign\")\n await Promise.all([canonical, target, foreign].map(directory => mkdir(directory)))\n await symlink(canonical, alias, process.platform === \"win32\" ? \"junction\" : \"dir\")\n let destination = await realpath(canonical)\n let current = session(\"session\", target)\n const moves: string[] = []\n const client = {\n location: { get: async () => ({ directory: destination }) },\n project: { list: async () => [{ id: \"project\", canonical: destination, sandboxes: [] }] },\n session: {\n list: async () => ({ data: [current], cursor: {} }),\n active: async () => ({}),\n move: async ({ directory }: { directory: string }) => {\n moves.push(directory)\n current = { ...current, location: { directory } }\n },\n },\n } as unknown as OpenCodeClient\n const params = {\n client, projectDirectory: alias, targetDirectory: target, rootDirectory: alias,\n resolveDirectoryIdentity: (directory: string) => realpath(directory),\n resolveExactDirectory: (directory: string) => realpath(directory),\n remove: async () => { throw new Error(\"Synthetic Git removal refusal\") },\n }\n await assert.rejects(evacuateWorktreeSessions(params), /Synthetic Git removal refusal/)\n assert.deepEqual(moves, [destination, target])\n assert.equal(current.location.directory, target)\n moves.length = 0\n destination = foreign\n await assert.rejects(evacuateWorktreeSessions(params), /Unable to resolve the OpenCode project/)\n assert.deepEqual(moves, [])\n client.project.list = async () => [{ id: \"project\", canonical, sandboxes: [] }] as any\n await assert.rejects(evacuateWorktreeSessions(params), /foreign evacuation destination/)\n assert.deepEqual(moves, [])\n } finally { await rm(root, { recursive: true, force: true }) }\n })\n\n it(\"evacuates sessions nested under the target worktree identity\", async () => {\n let current = session(\"nested\", \"/repo/worktree/nested\")\n let removed = false\n const client = {\n location,\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }] },\n session: {\n list: async () => ({ data: [current], cursor: {} }),\n active: async () => ({}),\n move: async (input: { directory: string }) => { current = { ...current, location: { directory: input.directory } } },\n },\n } as unknown as OpenCodeClient\n\n await evacuateWorktreeSessions({\n client,\n projectDirectory: \"/repo\",\n targetDirectory: \"/repo/worktree\",\n rootDirectory: \"/repo\",\n resolveDirectoryIdentity: async (directory) => directory.startsWith(\"/repo/worktree\") ? \"workspace:worktree\" : \"workspace:root\",\n remove: async () => { removed = true },\n })\n\n assert.equal(current.location.directory, \"/repo\")\n assert.equal(removed, true)\n })\n\n it(\"rolls sessions back when Git removal fails\", async () => {\n const current = session(\"session\", \"/repo/worktree\")\n const moves: string[] = []\n const client = {\n location,\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }] },\n session: {\n list: async () => ({ data: [current], cursor: {} }),\n active: async () => ({}),\n move: async ({ directory }: { directory: string }) => {\n moves.push(directory)\n current.location = { directory }\n },\n },\n } as unknown as OpenCodeClient\n\n await assert.rejects(evacuateWorktreeSessions({\n client, projectDirectory: \"/repo\", targetDirectory: \"/repo/worktree\", rootDirectory: \"/repo\",\n remove: async () => { throw new Error(\"Git removal failed\") },\n }), /Git removal failed/)\n assert.deepEqual(moves, [\"/repo\", \"/repo/worktree\"])\n assert.equal(current.location.directory, \"/repo/worktree\")\n })\n\n it(\"re-inventories active sessions immediately before removal\", async () => {\n const current = session(\"session\", \"/repo/worktree\")\n const intruder = session(\"intruder\", \"/repo/worktree\")\n let listCalls = 0\n let removed = false\n const client = {\n location,\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }] },\n session: {\n list: async () => {\n listCalls += 1\n return { data: listCalls >= 3 ? [current, intruder] : [current], cursor: {} }\n },\n active: async () => ({ intruder: { type: \"running\" } }),\n move: async ({ directory }: { directory: string }) => { current.location = { directory } },\n },\n } as unknown as OpenCodeClient\n\n await assert.rejects(evacuateWorktreeSessions({\n client, projectDirectory: \"/repo\", targetDirectory: \"/repo/worktree\", rootDirectory: \"/repo\",\n remove: async () => { removed = true },\n }), /Active sessions block worktree deletion: intruder/)\n\n assert.equal(removed, false)\n assert.equal(current.location.directory, \"/repo/worktree\")\n })\n\n it(\"uses the resolved destination and restores exact legacy identity on rollback\", async () => {\n const original = { directory: \"/repo/worktree\", workspaceID: \"native-original\" }\n const destination = { directory: \"/repo\", workspaceID: \"native-root\" }\n const current = { ...session(\"session\", original.directory), location: original }\n const moves: unknown[] = []\n const client = {\n location: { get: async () => destination },\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [original.directory] }] },\n session: {\n list: async () => ({ data: [current], cursor: {} }),\n active: async () => ({}),\n move: async ({ directory }: { directory: string }, options?: { headers: Record<string, string> }) => {\n const resolved = readLocationContext(options?.headers[\"x-codenomad-location\"], \"legacy\")!\n assert.equal(resolved.directory, directory)\n moves.push(resolved)\n current.location = { directory: resolved.directory, workspaceID: resolved.workspaceID! }\n },\n },\n } as unknown as OpenCodeClient\n await assert.rejects(evacuateWorktreeSessions({\n client, projectDirectory: \"/repo\", targetDirectory: original.directory, rootDirectory: destination.directory,\n remove: async () => { throw new Error(\"Git removal failed\") },\n }), /Git removal failed/)\n assert.deepEqual(moves, [destination, original])\n assert.deepEqual(current.location, original)\n })\n\n it(\"does not report rollback success when only the directory was restored\", async () => {\n const original = { directory: \"/repo/worktree\", workspaceID: \"native-original\" }\n const current = { ...session(\"session\", original.directory), location: original }\n let moves = 0\n const client = {\n location,\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [original.directory] }] },\n session: {\n list: async () => ({ data: [current], cursor: {} }),\n active: async () => ({}),\n move: async ({ directory }: { directory: string }) => {\n moves++\n current.location = { directory, workspaceID: undefined! }\n },\n },\n } as unknown as OpenCodeClient\n await assert.rejects(evacuateWorktreeSessions({\n client, projectDirectory: \"/repo\", targetDirectory: original.directory, rootDirectory: \"/repo\",\n remove: async () => { throw new Error(\"Git removal failed\") },\n }), /could not be rolled back/)\n assert.equal(moves, 2)\n })\n})\n"]}