@cerbur/clutch-dsh-worktree 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/README.md +135 -23
  2. package/README.zh.md +107 -13
  3. package/cordis.patch.yml +21 -0
  4. package/lib/client/WorktreeContext.d.ts.map +1 -1
  5. package/lib/client/WorktreeContext.js +2 -2
  6. package/lib/client/WorktreeContext.js.map +1 -1
  7. package/lib/client/WorktreeSurface.d.ts +2 -2
  8. package/lib/client/WorktreeSurface.d.ts.map +1 -1
  9. package/lib/client/WorktreeSurface.js +291 -70
  10. package/lib/client/WorktreeSurface.js.map +1 -1
  11. package/lib/client/entry.d.ts.map +1 -1
  12. package/lib/client/entry.js +84 -2
  13. package/lib/client/entry.js.map +1 -1
  14. package/lib/client/index.d.ts +3 -0
  15. package/lib/client/index.d.ts.map +1 -1
  16. package/lib/client/index.js +2 -0
  17. package/lib/client/index.js.map +1 -1
  18. package/lib/client/locales.d.ts +70 -0
  19. package/lib/client/locales.d.ts.map +1 -1
  20. package/lib/client/locales.js +70 -0
  21. package/lib/client/locales.js.map +1 -1
  22. package/lib/client/session-view.d.ts +34 -0
  23. package/lib/client/session-view.d.ts.map +1 -1
  24. package/lib/client/session-view.js +119 -1
  25. package/lib/client/session-view.js.map +1 -1
  26. package/lib/client/worktree-connection.d.ts +4 -2
  27. package/lib/client/worktree-connection.d.ts.map +1 -1
  28. package/lib/client/worktree-connection.js +4 -0
  29. package/lib/client/worktree-connection.js.map +1 -1
  30. package/lib/client/worktree-context.js +1 -1
  31. package/lib/client/worktree-context.js.map +1 -1
  32. package/lib/client/worktree-error-copy.d.ts +2 -0
  33. package/lib/client/worktree-error-copy.d.ts.map +1 -1
  34. package/lib/client/worktree-error-copy.js +24 -0
  35. package/lib/client/worktree-error-copy.js.map +1 -1
  36. package/lib/client/worktree-permission-icon.d.ts +7 -0
  37. package/lib/client/worktree-permission-icon.d.ts.map +1 -0
  38. package/lib/client/worktree-permission-icon.js +119 -0
  39. package/lib/client/worktree-permission-icon.js.map +1 -0
  40. package/lib/client/worktree-permission.d.ts +21 -0
  41. package/lib/client/worktree-permission.d.ts.map +1 -0
  42. package/lib/client/worktree-permission.js +70 -0
  43. package/lib/client/worktree-permission.js.map +1 -0
  44. package/lib/client/worktree-session-fork.d.ts +65 -0
  45. package/lib/client/worktree-session-fork.d.ts.map +1 -0
  46. package/lib/client/worktree-session-fork.js +174 -0
  47. package/lib/client/worktree-session-fork.js.map +1 -0
  48. package/lib/client/worktree-session-order.d.ts +36 -0
  49. package/lib/client/worktree-session-order.d.ts.map +1 -0
  50. package/lib/client/worktree-session-order.js +215 -0
  51. package/lib/client/worktree-session-order.js.map +1 -0
  52. package/lib/client/worktree-session.d.ts +34 -1
  53. package/lib/client/worktree-session.d.ts.map +1 -1
  54. package/lib/client/worktree-session.js +101 -3
  55. package/lib/client/worktree-session.js.map +1 -1
  56. package/lib/client/worktree-surface-rows.d.ts +4 -4
  57. package/lib/client/worktree-surface-rows.d.ts.map +1 -1
  58. package/lib/client/worktree-surface-rows.js +139 -47
  59. package/lib/client/worktree-surface-rows.js.map +1 -1
  60. package/lib/client/worktree-surface-selectors.d.ts +2 -0
  61. package/lib/client/worktree-surface-selectors.d.ts.map +1 -1
  62. package/lib/client/worktree-surface-selectors.js +8 -0
  63. package/lib/client/worktree-surface-selectors.js.map +1 -1
  64. package/lib/client/worktree-surface-types.d.ts +46 -3
  65. package/lib/client/worktree-surface-types.d.ts.map +1 -1
  66. package/lib/client/worktree-view-actions.d.ts +8 -3
  67. package/lib/client/worktree-view-actions.d.ts.map +1 -1
  68. package/lib/client/worktree-view-actions.js +28 -2
  69. package/lib/client/worktree-view-actions.js.map +1 -1
  70. package/lib/client/worktree-view-errors.d.ts +10 -0
  71. package/lib/client/worktree-view-errors.d.ts.map +1 -1
  72. package/lib/client/worktree-view-errors.js +22 -1
  73. package/lib/client/worktree-view-errors.js.map +1 -1
  74. package/lib/client.js +1515 -207
  75. package/lib/client.js.map +1 -1
  76. package/lib/contract/index.contract.js +2 -0
  77. package/lib/contract/index.contract.js.map +1 -1
  78. package/lib/contract/index.d.ts +11 -3
  79. package/lib/contract/index.d.ts.map +1 -1
  80. package/lib/contract/index.js +8 -0
  81. package/lib/contract/index.js.map +1 -1
  82. package/lib/contract/worktree-permission.d.ts +73 -0
  83. package/lib/contract/worktree-permission.d.ts.map +1 -0
  84. package/lib/contract/worktree-permission.js +32 -0
  85. package/lib/contract/worktree-permission.js.map +1 -0
  86. package/lib/host/dsh-read-adapter.d.ts +1 -0
  87. package/lib/host/dsh-read-adapter.d.ts.map +1 -1
  88. package/lib/host/dsh-read-adapter.js +6 -0
  89. package/lib/host/dsh-read-adapter.js.map +1 -1
  90. package/lib/host/index.d.ts +4 -0
  91. package/lib/host/index.d.ts.map +1 -1
  92. package/lib/host/index.js +2 -0
  93. package/lib/host/index.js.map +1 -1
  94. package/lib/host/remote.d.ts +2 -2
  95. package/lib/host/remote.d.ts.map +1 -1
  96. package/lib/host/remote.js +7 -1
  97. package/lib/host/remote.js.map +1 -1
  98. package/lib/host/service.d.ts +5 -1
  99. package/lib/host/service.d.ts.map +1 -1
  100. package/lib/host/service.js +42 -3
  101. package/lib/host/service.js.map +1 -1
  102. package/lib/host/worktree-permission-manager.d.ts +14 -0
  103. package/lib/host/worktree-permission-manager.d.ts.map +1 -0
  104. package/lib/host/worktree-permission-manager.js +113 -0
  105. package/lib/host/worktree-permission-manager.js.map +1 -0
  106. package/lib/host/worktree-permission.d.ts +38 -0
  107. package/lib/host/worktree-permission.d.ts.map +1 -0
  108. package/lib/host/worktree-permission.js +156 -0
  109. package/lib/host/worktree-permission.js.map +1 -0
  110. package/lib/index.d.ts +6 -4
  111. package/lib/index.d.ts.map +1 -1
  112. package/lib/index.js +3 -1
  113. package/lib/index.js.map +1 -1
  114. package/lib/manage/manager-context.d.ts +2 -0
  115. package/lib/manage/manager-context.d.ts.map +1 -1
  116. package/lib/manage/manager-worktrees.d.ts +5 -0
  117. package/lib/manage/manager-worktrees.d.ts.map +1 -1
  118. package/lib/manage/manager-worktrees.js +125 -37
  119. package/lib/manage/manager-worktrees.js.map +1 -1
  120. package/lib/manage/manager.d.ts +20 -0
  121. package/lib/manage/manager.d.ts.map +1 -1
  122. package/lib/manage/manager.js +91 -13
  123. package/lib/manage/manager.js.map +1 -1
  124. package/lib/manage/types.d.ts +9 -1
  125. package/lib/manage/types.d.ts.map +1 -1
  126. package/lib/manage/worktree-permission.d.ts +3 -0
  127. package/lib/manage/worktree-permission.d.ts.map +1 -0
  128. package/lib/manage/worktree-permission.js +3 -0
  129. package/lib/manage/worktree-permission.js.map +1 -0
  130. package/lib/provider/git.d.ts +37 -9
  131. package/lib/provider/git.d.ts.map +1 -1
  132. package/lib/provider/git.js +162 -61
  133. package/lib/provider/git.js.map +1 -1
  134. package/lib/provider/index.d.ts +5 -1
  135. package/lib/provider/index.d.ts.map +1 -1
  136. package/lib/provider/index.js +4 -0
  137. package/lib/provider/index.js.map +1 -1
  138. package/lib/provider/mutation-lock.d.ts +37 -0
  139. package/lib/provider/mutation-lock.d.ts.map +1 -0
  140. package/lib/provider/mutation-lock.js +270 -0
  141. package/lib/provider/mutation-lock.js.map +1 -0
  142. package/lib/provider/mutation-token.d.ts +8 -0
  143. package/lib/provider/mutation-token.d.ts.map +1 -0
  144. package/lib/provider/mutation-token.js +20 -0
  145. package/lib/provider/mutation-token.js.map +1 -0
  146. package/lib/provider/repository-fingerprint.d.ts +8 -0
  147. package/lib/provider/repository-fingerprint.d.ts.map +1 -0
  148. package/lib/provider/repository-fingerprint.js +13 -0
  149. package/lib/provider/repository-fingerprint.js.map +1 -0
  150. package/lib/provider/sidecar-persistence.d.ts +7 -3
  151. package/lib/provider/sidecar-persistence.d.ts.map +1 -1
  152. package/lib/provider/sidecar-persistence.js +118 -44
  153. package/lib/provider/sidecar-persistence.js.map +1 -1
  154. package/lib/provider/sidecar-schema.d.ts +1 -1
  155. package/lib/provider/sidecar-schema.d.ts.map +1 -1
  156. package/lib/provider/sidecar-schema.js +140 -8
  157. package/lib/provider/sidecar-schema.js.map +1 -1
  158. package/lib/provider/sidecar.d.ts +3 -1
  159. package/lib/provider/sidecar.d.ts.map +1 -1
  160. package/lib/provider/sidecar.js +6 -1
  161. package/lib/provider/sidecar.js.map +1 -1
  162. package/lib/provider/subprocess.d.ts +37 -0
  163. package/lib/provider/subprocess.d.ts.map +1 -0
  164. package/lib/provider/subprocess.js +319 -0
  165. package/lib/provider/subprocess.js.map +1 -0
  166. package/lib/provider/transaction.d.ts +65 -0
  167. package/lib/provider/transaction.d.ts.map +1 -0
  168. package/lib/provider/transaction.js +1011 -0
  169. package/lib/provider/transaction.js.map +1 -0
  170. package/lib/provider/types.d.ts +98 -7
  171. package/lib/provider/types.d.ts.map +1 -1
  172. package/lib/provider/types.js +1 -1
  173. package/lib/provider/types.js.map +1 -1
  174. package/lib/typert.host.js +133 -30
  175. package/lib/typert.remote-client.d.ts +7 -3
  176. package/lib/typert.remote-client.d.ts.map +1 -1
  177. package/lib/typert.remote-client.js +133 -30
  178. package/package.json +3 -1
package/lib/client.js CHANGED
@@ -36,10 +36,13 @@ window.__ModuleLoader__.load({
36
36
  "worktree.mainWithBranch": "本地({branch})",
37
37
  "worktree.detached": "已分离 Worktree",
38
38
  "worktree.repair": "Worktree 需要修复",
39
+ "worktree.recovery": "Worktree 需要恢复",
39
40
  "worktree.ready": "活动 Worktree 已就绪",
40
41
  "worktree.expand": "展开 {name}",
41
42
  "worktree.collapse": "收起 {name}",
42
43
  "worktree.options": "Worktree 选项:{name}",
44
+ "worktree.copyPath": "复制路径",
45
+ "worktree.createNew": "创建新的 Worktree",
43
46
  "worktree.remove": "移除 Worktree",
44
47
  "worktree.removeDescription": "移除“{name}”?Session 会保留在 DSH 中,并作为已分离关系继续可用。",
45
48
  "worktree.removeExternalDescription": "移除“{name}”?这会删除关联的外部 Worktree 目录。Session 会保留在 DSH 中,并作为已分离关系继续可用。",
@@ -70,9 +73,27 @@ window.__ModuleLoader__.load({
70
73
  "session.rename": "重命名会话",
71
74
  "session.fork": "Fork 会话",
72
75
  "session.archive": "归档会话",
76
+ "session.copyId": "复制 Session ID",
73
77
  "session.expandMore": "展开其余 {count} 个会话",
74
78
  "session.collapse": "收起",
75
79
  "session.name": "会话名称",
80
+ "session.status.running": "进行中",
81
+ "session.status.subagentsRunning.one": "{n} 个子代理运行中",
82
+ "session.status.subagentsRunning.other": "{n} 个子代理运行中",
83
+ "session.status.idle": "空闲",
84
+ "session.status.waitingApproval": "等待审批",
85
+ "session.status.planReview": "计划待审",
86
+ "session.status.waitingAnswer": "等待回答",
87
+ "session.status.completed": "已完成",
88
+ copy: "复制",
89
+ "hover.copied": "已复制",
90
+ "session.time.now": "刚刚",
91
+ "session.time.minutes": "{n}分钟",
92
+ "session.time.hours": "{n}小时",
93
+ "session.time.days": "{n}天",
94
+ "session.time.months": "{n}个月",
95
+ "session.time.years": "{n}年",
96
+ "session.time.ago": "{t}前",
76
97
  "dialog.close": "关闭",
77
98
  "dialog.closeWorkspaceRename": "关闭重命名工作区对话框",
78
99
  "dialog.closeWorkspaceDelete": "关闭删除工作区对话框",
@@ -84,6 +105,14 @@ window.__ModuleLoader__.load({
84
105
  "action.retry": "重试",
85
106
  "action.retryBinding": "重试绑定",
86
107
  "action.openCreatedSession": "打开已创建的 Session",
108
+ "permission.fallbackWorkspaceWrite": "无法使用 Worktree 完全访问预设,已降级为 workspace-write;保留审批提示。",
109
+ "permission.userRestricted": "已保留你在 DSH 原生权限界面选择的限制,未自动提升此 Worktree Session。",
110
+ "permission.unverified": "无法验证 Worktree Session 的权限服务;Session 已保留,但完全访问未得到确认。",
111
+ "permission.unavailable": "Worktree Session 权限状态暂时不可用,请重试。",
112
+ "permission.fullAccessTitle": "确认启用 Worktree Full access?",
113
+ "permission.fullAccessDescription": "启用 Worktree Full access 后,此 Session 可能访问 Session 目录之外的关联 Git 元数据和 Worktree 文件。完全访问会关闭此 Session 的文件系统沙箱,但会保留审批提示;网络和进程策略不变。目标 Worktree:{cwd}",
114
+ "permission.fullAccessAcknowledge": "我已了解风险,并愿意继续",
115
+ "permission.fullAccessEnable": "启用 Worktree Full access",
87
116
  "status.loading": "正在加载工作区…",
88
117
  "error.worktreeDataUnavailable": "Worktree 数据不可用,请重试。",
89
118
  "error.gitNotInstalled": "Git 未安装或不可用,请安装 Git 后重试。",
@@ -98,12 +127,18 @@ window.__ModuleLoader__.load({
98
127
  "error.worktreeRecordMissing": "Worktree 注册未返回 Worktree 记录,请重试请求。",
99
128
  "error.worktreeImportInvalid": "这个 Worktree 无法导入,请选择同一 Workspace 中有效的绝对路径。",
100
129
  "error.worktreeAlreadyManaged": "这个 Worktree 已由插件管理。",
130
+ "error.worktreeMutationBusy": "另一个 DSH 实例正在修改 Worktree 状态,请稍后重试。",
131
+ "error.worktreeStateConflict": "Worktree 状态已经变化,请刷新后重试。",
132
+ "error.worktreeRecoveryRequired": "Worktree 状态需要恢复;请先完成恢复后再重试破坏性操作。",
133
+ "error.worktreeIdentityChanged": "Worktree 或 repository 身份已经变化,需要恢复后再操作。",
101
134
  "error.worktreeRegistrationSessionUnavailable": "Worktree 已注册,但 Session 创建不可用,请重新连接后重试。",
102
135
  "error.workspaceRenameUnavailable": "工作区重命名不可用,请重新连接后重试。",
103
136
  "error.workspaceDeleteUnavailable": "工作区删除不可用,请重新连接后重试。",
104
137
  "error.sessionRenameUnavailable": "会话重命名不可用,请重新连接后重试。",
105
138
  "error.sessionBindingFailed": "Session {sessionId} 已创建,但 Worktree 绑定失败:{reason}",
106
139
  "error.sessionAlreadyBound": "Session 已绑定到其他 Worktree,未创建新的 Session。",
140
+ "error.worktreePermissionConfirmationRequired": "Session 和绑定已保留;确认完全访问后重试,未打开 Session。",
141
+ "error.worktreePermissionFailed": "Worktree Session 权限设置失败,可重试:{reason}",
107
142
  "error.sessionListNotReady": "Session 列表尚未就绪,请重试。",
108
143
  "error.sessionFactsIncomplete": "Session 信息尚未完整,请重试。",
109
144
  "error.worktreeSessionRepairRequired": "当前 Worktree 的 Session 绑定需要修复,请重试。",
@@ -138,10 +173,13 @@ window.__ModuleLoader__.load({
138
173
  "worktree.mainWithBranch": "Local ({branch})",
139
174
  "worktree.detached": "Detached Worktree",
140
175
  "worktree.repair": "Worktree needs repair",
176
+ "worktree.recovery": "Worktree requires recovery",
141
177
  "worktree.ready": "Active Worktree ready",
142
178
  "worktree.expand": "Expand {name}",
143
179
  "worktree.collapse": "Collapse {name}",
144
180
  "worktree.options": "Worktree options for {name}",
181
+ "worktree.copyPath": "Copy path",
182
+ "worktree.createNew": "Create new Worktree",
145
183
  "worktree.remove": "Remove Worktree",
146
184
  "worktree.removeDescription": "Remove {name}? Sessions stay in DSH and remain available as detached bindings.",
147
185
  "worktree.removeExternalDescription": "Remove {name}? This deletes the linked external Worktree directory. Sessions stay in DSH and remain available as detached bindings.",
@@ -172,9 +210,27 @@ window.__ModuleLoader__.load({
172
210
  "session.rename": "Rename session",
173
211
  "session.fork": "Fork session",
174
212
  "session.archive": "Archive session",
213
+ "session.copyId": "Copy session ID",
175
214
  "session.expandMore": "Expand {count} more",
176
215
  "session.collapse": "Collapse",
177
216
  "session.name": "Session name",
217
+ "session.status.running": "Running",
218
+ "session.status.subagentsRunning.one": "{n} subagent running",
219
+ "session.status.subagentsRunning.other": "{n} subagents running",
220
+ "session.status.idle": "Idle",
221
+ "session.status.waitingApproval": "Waiting for approval",
222
+ "session.status.planReview": "Plan awaiting review",
223
+ "session.status.waitingAnswer": "Waiting for answer",
224
+ "session.status.completed": "Completed",
225
+ copy: "Copy",
226
+ "hover.copied": "Copied",
227
+ "session.time.now": "now",
228
+ "session.time.minutes": "{n}min",
229
+ "session.time.hours": "{n}h",
230
+ "session.time.days": "{n}d",
231
+ "session.time.months": "{n}mo",
232
+ "session.time.years": "{n}y",
233
+ "session.time.ago": "{t} ago",
178
234
  "dialog.close": "Close",
179
235
  "dialog.closeWorkspaceRename": "Close Rename Workspace dialog",
180
236
  "dialog.closeWorkspaceDelete": "Close Delete Workspace dialog",
@@ -186,6 +242,14 @@ window.__ModuleLoader__.load({
186
242
  "action.retry": "Retry",
187
243
  "action.retryBinding": "Retry Binding",
188
244
  "action.openCreatedSession": "Open Created Session",
245
+ "permission.fallbackWorkspaceWrite": "The Worktree Full Access preset is unavailable; fell back to workspace-write with approval prompts enabled.",
246
+ "permission.userRestricted": "The restriction selected in DSH’s native permission UI was preserved; this Worktree Session was not elevated.",
247
+ "permission.unverified": "The Worktree Session permission service could not be verified; the Session was retained, but Full Access is not confirmed.",
248
+ "permission.unavailable": "The Worktree Session permission state is temporarily unavailable; retry the request.",
249
+ "permission.fullAccessTitle": "Enable Worktree Full access?",
250
+ "permission.fullAccessDescription": "Worktree Full access lets this Session access linked Git metadata and Worktree files outside the Session directory. It disables filesystem confinement for this Session while keeping approval prompts enabled; network and process policy are unchanged. Target Worktree: {cwd}",
251
+ "permission.fullAccessAcknowledge": "I understand the risks and want to continue",
252
+ "permission.fullAccessEnable": "Enable Worktree Full access",
189
253
  "status.loading": "Loading Workspaces…",
190
254
  "error.worktreeDataUnavailable": "Worktree data is unavailable. Retry the request.",
191
255
  "error.gitNotInstalled": "Git is not installed or unavailable. Install Git and retry.",
@@ -200,12 +264,18 @@ window.__ModuleLoader__.load({
200
264
  "error.worktreeRecordMissing": "Worktree registration returned no Worktree record; retry the request.",
201
265
  "error.worktreeImportInvalid": "This Worktree cannot be imported. Choose a valid absolute path in this Workspace.",
202
266
  "error.worktreeAlreadyManaged": "This Worktree is already managed by the plugin.",
267
+ "error.worktreeMutationBusy": "Another DSH instance is mutating Worktree state; retry shortly.",
268
+ "error.worktreeStateConflict": "The Worktree state changed; refresh and retry the request.",
269
+ "error.worktreeRecoveryRequired": "Worktree state requires recovery; resolve it before retrying destructive actions.",
270
+ "error.worktreeIdentityChanged": "The Worktree or repository identity changed; recovery is required before acting.",
203
271
  "error.worktreeRegistrationSessionUnavailable": "The Worktree was registered, but Session creation is unavailable; retry after reconnecting.",
204
272
  "error.workspaceRenameUnavailable": "Workspace rename is unavailable; retry after reconnecting.",
205
273
  "error.workspaceDeleteUnavailable": "Workspace delete is unavailable; retry after reconnecting.",
206
274
  "error.sessionRenameUnavailable": "Session rename is unavailable; retry after reconnecting.",
207
275
  "error.sessionBindingFailed": "Session {sessionId} was created, but Worktree binding failed: {reason}",
208
276
  "error.sessionAlreadyBound": "The Session is already bound to another Worktree; no new Session was created.",
277
+ "error.worktreePermissionConfirmationRequired": "The Session and binding were retained; confirm Full Access and retry. The Session was not opened.",
278
+ "error.worktreePermissionFailed": "Worktree Session permission setup failed; retryable error: {reason}",
209
279
  "error.sessionListNotReady": "The Session list is not ready yet; retry the request.",
210
280
  "error.sessionFactsIncomplete": "Session facts are incomplete; retry the request.",
211
281
  "error.worktreeSessionRepairRequired": "The current Worktree Session binding needs repair; retry the request.",
@@ -231,7 +301,9 @@ window.__ModuleLoader__.load({
231
301
  removeWorktree: "worktreeManager/removeWorktree",
232
302
  insertWorktreeBefore: "worktreeManager/insertWorktreeBefore",
233
303
  listBindings: "worktreeManager/listBindings",
234
- bindSession: "worktreeManager/bindSession"
304
+ bindSession: "worktreeManager/bindSession",
305
+ ensureWorktreePermission: "worktreeManager/ensureWorktreePermission",
306
+ normalizeDetachedWorktreePermissions: "worktreeManager/normalizeDetachedWorktreePermissions"
235
307
  });
236
308
  /** Browser-safe error shared by transport, Gateway, endpoint and domain failures. */
237
309
  var WorktreeConnectionError = class extends Error {
@@ -246,14 +318,14 @@ window.__ModuleLoader__.load({
246
318
  this.retryable = options.retryable;
247
319
  }
248
320
  };
249
- function isRecord$1(value) {
321
+ function isRecord$2(value) {
250
322
  return typeof value === "object" && value !== null && !Array.isArray(value);
251
323
  }
252
324
  function isWorktreeError(value) {
253
- return isRecord$1(value) && typeof value.code === "string" && typeof value.message === "string" && isRecord$1(value.details);
325
+ return isRecord$2(value) && typeof value.code === "string" && typeof value.message === "string" && isRecord$2(value.details);
254
326
  }
255
327
  function isWorktreeRemoteResult(value) {
256
- if (!isRecord$1(value) || typeof value.ok !== "boolean") return false;
328
+ if (!isRecord$2(value) || typeof value.ok !== "boolean") return false;
257
329
  return value.ok || isWorktreeError(value.error) && value.error.details !== void 0;
258
330
  }
259
331
  function disposedError() {
@@ -274,7 +346,7 @@ window.__ModuleLoader__.load({
274
346
  });
275
347
  }
276
348
  function gatewayFailure(endpoint, result) {
277
- const rawDetails = isRecord$1(result.error.details) ? result.error.details : {};
349
+ const rawDetails = isRecord$2(result.error.details) ? result.error.details : {};
278
350
  throw new WorktreeConnectionError({
279
351
  code: result.error.code,
280
352
  message: result.error.message,
@@ -343,6 +415,8 @@ window.__ModuleLoader__.load({
343
415
  },
344
416
  listBindings: (input) => invoke("listBindings", input),
345
417
  bindSession: (input) => invoke("bindSession", input),
418
+ ensureWorktreePermission: (input) => invoke("ensureWorktreePermission", input),
419
+ normalizeDetachedWorktreePermissions: (input) => invoke("normalizeDetachedWorktreePermissions", input),
346
420
  dispose() {
347
421
  if (disposed) return;
348
422
  disposed = true;
@@ -352,7 +426,7 @@ window.__ModuleLoader__.load({
352
426
  };
353
427
  }
354
428
  //#endregion
355
- //#region \0clutch-dsh-worktree-css:/Users/yuancheng/.dsh/clutch-dsh-worktree/worktree/wt_0c53e50f-f169-4c5e-9664-1c2d58ae1212/packages/clutch-dsh-worktree/src/client/worktree-context.css.mjs
429
+ //#region \0clutch-dsh-worktree-css:/Users/yuancheng/.dsh/clutch-dsh-worktree/worktree/wt_4ab905d8-c228-470f-9208-36c3552257a4/packages/clutch-dsh-worktree/src/client/worktree-context.css.mjs
356
430
  const css$1 = ".-cerbur-clutch-dsh-worktree-headerContext {\n display: inline-flex;\n min-width: 0;\n height: 22px;\n align-items: center;\n gap: 4px;\n max-width: 180px;\n overflow: hidden;\n border-radius: 6px;\n padding: 0 2px 0 0;\n background: var(--dsw-alias-fill-tsp-secondary);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n line-height: 22px;\n white-space: nowrap;\n}\n\n.-cerbur-clutch-dsh-worktree-contextIcon {\n flex: none;\n opacity: 0.7;\n}\n\n.-cerbur-clutch-dsh-worktree-contextLabel {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.-cerbur-clutch-dsh-worktree-contextHoverTitle {\n max-width: 360px;\n color: var(--dsw-static-neutral-bluish-00);\n font-size: 14px;\n line-height: 20px;\n overflow-wrap: anywhere;\n white-space: normal;\n}\n\n.-cerbur-clutch-dsh-worktree-heroContextHost {\n position: absolute;\n z-index: 1;\n display: block;\n width: max-content;\n max-width: 260px;\n min-height: 22px;\n}\n\n.-cerbur-clutch-dsh-worktree-heroContext {\n display: inline-flex;\n box-sizing: border-box;\n max-width: 260px;\n min-height: 22px;\n align-items: center;\n overflow: hidden;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n padding: 0 8px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n line-height: 20px;\n pointer-events: auto;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n";
357
431
  const styleId$1 = "@cerbur/clutch-dsh-worktree/worktree-context.css";
358
432
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"@cerbur/clutch-dsh-worktree/worktree-context.css\"]") === null) {
@@ -375,18 +449,25 @@ window.__ModuleLoader__.load({
375
449
  function WorktreeContextLabel({ value, t }) {
376
450
  if (value.kind === "none") return null;
377
451
  const ariaLabel = value.kind === "main" ? t("context.main", { name: value.label }) : t("context.worktree", { name: value.label });
378
- return (0, react_jsx_runtime.jsxs)("span", {
379
- className: worktree_context_css_default.headerContext,
380
- title: value.label,
381
- "aria-label": ariaLabel,
382
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, {
383
- size: 14,
384
- className: worktree_context_css_default.contextIcon,
385
- "aria-hidden": "true"
386
- }), (0, react_jsx_runtime.jsx)("span", {
387
- className: worktree_context_css_default.contextLabel,
452
+ return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.HoverCard, {
453
+ anchor: (0, react_jsx_runtime.jsxs)("span", {
454
+ className: worktree_context_css_default.headerContext,
455
+ title: value.label,
456
+ "aria-label": ariaLabel,
457
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, {
458
+ size: 14,
459
+ className: worktree_context_css_default.contextIcon,
460
+ "aria-hidden": "true"
461
+ }), (0, react_jsx_runtime.jsx)("span", {
462
+ className: worktree_context_css_default.contextLabel,
463
+ children: value.label
464
+ })]
465
+ }),
466
+ content: (0, react_jsx_runtime.jsx)("div", {
467
+ className: worktree_context_css_default.contextHoverTitle,
388
468
  children: value.label
389
- })]
469
+ }),
470
+ openDelayMs: 500
390
471
  });
391
472
  }
392
473
  /** Render the current branch or active Worktree branch beside the Session title. */
@@ -399,8 +480,8 @@ window.__ModuleLoader__.load({
399
480
  });
400
481
  }
401
482
  //#endregion
402
- //#region \0clutch-dsh-worktree-css:/Users/yuancheng/.dsh/clutch-dsh-worktree/worktree/wt_0c53e50f-f169-4c5e-9664-1c2d58ae1212/packages/clutch-dsh-worktree/src/client/worktree.css.mjs
403
- const css = ".-cerbur-clutch-dsh-worktree-action {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n gap: 8px;\n width: calc(100% + 4px);\n height: 42px;\n margin: 4px -2px;\n box-sizing: border-box;\n border: 0;\n border-radius: 12px;\n padding: 0 10px 0 8px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n line-height: 22px;\n text-align: left;\n overflow: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-action:hover,\n.-cerbur-clutch-dsh-worktree-action[data-active='true'] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-action[data-collapsed='true'] {\n width: 36px;\n height: 36px;\n margin: 8px 0 10px;\n justify-content: center;\n gap: 0;\n padding: 0;\n border-radius: 50%;\n}\n\n.-cerbur-clutch-dsh-worktree-actionLabel {\n display: inline-flex;\n min-width: 0;\n align-items: center;\n height: 22px;\n overflow: hidden;\n font-weight: 400;\n line-height: 22px;\n white-space: nowrap;\n}\n\n.-cerbur-clutch-dsh-worktree-surface {\n position: absolute;\n top: 0;\n left: 0;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n min-width: 56px;\n overflow: hidden;\n border-right: 1px solid var(--dsw-alias-border-l1);\n background: var(--dsw-specific-sidebar-fill);\n color: var(--dsw-alias-label-primary);\n pointer-events: auto;\n transition: width var(--ds-transition-duration-slow) var(--ds-ease-in-out);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .-cerbur-clutch-dsh-worktree-surface {\n transition: none;\n }\n}\n\n.-cerbur-clutch-dsh-worktree-wideContent {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex: 1;\n flex-direction: column;\n}\n\n.-cerbur-clutch-dsh-worktree-surface[data-collapsed='true'] .-cerbur-clutch-dsh-worktree-wideContent {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeModalTabs {\n display: flex;\n gap: 8px;\n margin-bottom: 12px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeModalTab {\n border: 0;\n border-bottom: 2px solid transparent;\n padding: 4px 0;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeModalTab[aria-selected='true'] {\n border-bottom-color: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeImportCandidates {\n display: grid;\n gap: 8px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeImportSelect {\n width: 100%;\n min-width: 0;\n padding: 7px 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 13px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeImportError {\n display: grid;\n gap: 8px;\n}\n\n.-cerbur-clutch-dsh-worktree-closeButton {\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: inherit;\n cursor: pointer;\n font: inherit;\n}\n\n.-cerbur-clutch-dsh-worktree-closeButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-header {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: none;\n min-height: 44px;\n padding: 6px 10px;\n box-sizing: border-box;\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n}\n\n.-cerbur-clutch-dsh-worktree-title {\n min-width: 0;\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n font-weight: 600;\n}\n\n.-cerbur-clutch-dsh-worktree-closeButton {\n flex: none;\n width: 28px;\n height: 28px;\n font-size: 18px;\n line-height: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-modeButton[data-active='true'] {\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-workspacePicker {\n display: block;\n width: calc(100% - 20px);\n margin: 6px 10px 8px;\n padding: 6px 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 7px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceLabel {\n display: block;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-content {\n min-height: 0;\n flex: 1;\n overflow: auto;\n padding: 0 10px 12px;\n}\n\n.-cerbur-clutch-dsh-worktree-content:focus-visible {\n outline: 2px solid var(--dsw-alias-border-l3);\n outline-offset: -2px;\n}\n\n.-cerbur-clutch-dsh-worktree-error {\n margin: 12px 0 0;\n padding: 8px;\n border: 1px solid var(--dsw-alias-state-error-primary);\n border-radius: 7px;\n}\n\n.-cerbur-clutch-dsh-worktree-retryButton,\n.-cerbur-clutch-dsh-worktree-actionButton {\n border: 0;\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n font: inherit;\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-retryButton,\n.-cerbur-clutch-dsh-worktree-actionButton {\n width: 100%;\n padding: 6px 8px;\n}\n\n.-cerbur-clutch-dsh-worktree-recoveryActions {\n display: grid;\n gap: 6px;\n margin-top: 6px;\n}\n\n.-cerbur-clutch-dsh-worktree-retryButton:disabled,\n.-cerbur-clutch-dsh-worktree-actionButton:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.-cerbur-clutch-dsh-worktree-actions {\n display: grid;\n gap: 6px;\n margin-top: 10px;\n padding: 8px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 7px;\n}\n\n.-cerbur-clutch-dsh-worktree-actionSelect {\n min-width: 0;\n padding: 5px 6px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-section {\n margin: 12px 0 0;\n}\n\n.-cerbur-clutch-dsh-worktree-sectionTitle {\n margin: 0 2px 4px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n}\n\n.-cerbur-clutch-dsh-worktree-empty,\n.-cerbur-clutch-dsh-worktree-message {\n margin: 0;\n padding: 8px 4px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.-cerbur-clutch-dsh-worktree-gitReadiness {\n display: grid;\n gap: 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-commandBlock {\n margin: 6px 0 0;\n padding: 8px;\n overflow-x: auto;\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-secondary);\n font: 11px/16px ui-monospace, SFMono-Regular, Menlo, monospace;\n white-space: pre-wrap;\n}\n\n.-cerbur-clutch-dsh-worktree-message[data-error='true'] {\n color: var(--dsw-alias-state-error-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-worktree,\n.-cerbur-clutch-dsh-worktree-session {\n display: flex;\n align-items: center;\n width: 100%;\n min-width: 0;\n box-sizing: border-box;\n border: 0;\n border-radius: 7px;\n padding: 6px 8px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-worktree {\n cursor: default;\n}\n\n.-cerbur-clutch-dsh-worktree-session {\n cursor: pointer;\n}\n\n.-cerbur-clutch-dsh-worktree-worktree:hover,\n.-cerbur-clutch-dsh-worktree-session:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeLabel,\n.-cerbur-clutch-dsh-worktree-sessionLabel {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.-cerbur-clutch-dsh-worktree-status {\n flex: none;\n margin-left: auto;\n padding-left: 8px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchRow {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px 6px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchIcon {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n color: var(--dsw-alias-label-secondary);\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput {\n min-width: 0;\n box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 7px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n outline: none;\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput {\n width: 100%;\n padding: 6px 8px;\n font-size: 13px;\n line-height: 18px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput:focus,\n.-cerbur-clutch-dsh-worktree-actionSelect:focus,\n.-cerbur-clutch-dsh-worktree-worktreeImportSelect:focus {\n border-color: #3b82f6;\n}\n\n.-cerbur-clutch-dsh-worktree-iconButton {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: 0;\n border-radius: 7px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 20px;\n line-height: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-iconButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceList {\n min-width: 0;\n min-height: 0;\n display: grid;\n gap: 4px;\n overflow-x: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceGroup {\n min-width: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow,\n.-cerbur-clutch-dsh-worktree-worktreeRow {\n position: relative;\n display: flex;\n align-items: center;\n min-width: 0;\n min-height: 36px;\n box-sizing: border-box;\n border-radius: 8px;\n padding: 3px 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover,\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow .-cerbur-clutch-dsh-worktree-workspaceDisclosure {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-workspaceDisclosure,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-workspaceDisclosure {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-workspaceIcon,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-workspaceIcon {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-worktreeDisclosure {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-worktreeDisclosure,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-worktreeDisclosure {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-worktreeIcon,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-worktreeIcon {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton {\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 28px;\n border: 0;\n border-radius: 6px;\n padding: 0;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 17px;\n line-height: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton > svg {\n display: block;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceIcon,\n.-cerbur-clutch-dsh-worktree-worktreeIcon {\n flex: none;\n width: 24px;\n color: var(--dsw-alias-label-secondary);\n font-size: 20px;\n line-height: 1;\n text-align: center;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeState {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 12px;\n margin-right: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceIcon {\n color: var(--dsw-alias-brand-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceTitle,\n.-cerbur-clutch-dsh-worktree-worktreeLabel {\n min-width: 0;\n overflow: hidden;\n flex: 1;\n padding: 0 6px;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-main-group='true'] .-cerbur-clutch-dsh-worktree-worktreeLabel {\n font-weight: 600;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeHoverTitle {\n color: var(--dsw-static-neutral-bluish-00);\n font-size: 14px;\n line-height: 20px;\n overflow-wrap: anywhere;\n}\n\n.-cerbur-clutch-dsh-worktree-treeActionSlot,\n.-cerbur-clutch-dsh-worktree-workspaceActions {\n position: relative;\n display: inline-flex;\n flex: 0 0 64px;\n align-items: center;\n justify-content: flex-end;\n width: 64px;\n height: 28px;\n margin-left: auto;\n}\n\n.-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-menuAction {\n position: absolute;\n top: 0;\n right: 32px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child {\n position: absolute;\n top: 0;\n right: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-menuAction {\n display: inline-flex;\n width: 28px;\n height: 28px;\n visibility: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-menuAction,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-menuAction,\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-menuAction,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-menuAction {\n visibility: visible;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropBefore::before,\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n position: absolute;\n right: 4px;\n left: 4px;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary);\n content: '';\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropBefore::before {\n top: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n bottom: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropBefore::before,\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n position: absolute;\n right: 4px;\n left: 4px;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary);\n content: '';\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropBefore::before {\n top: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n bottom: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeChildren {\n margin-left: 16px;\n padding: 2px 0 5px 12px;\n border-left: 1px solid var(--dsw-alias-border-l1);\n}\n\n.-cerbur-clutch-dsh-worktree-groupLabel {\n padding: 6px 8px 3px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n}\n\n.-cerbur-clutch-dsh-worktree-groupHeader {\n display: flex;\n align-items: center;\n min-height: 34px;\n padding-right: 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-groupHeader .-cerbur-clutch-dsh-worktree-groupLabel {\n flex: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeGroup {\n margin-top: 2px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow {\n min-height: 34px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-worktreeIcon,\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-disclosureButton {\n width: 22px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow {\n position: relative;\n display: flex;\n align-items: center;\n width: calc(100% - 22px);\n margin-left: 22px;\n min-width: 0;\n min-height: 31px;\n box-sizing: border-box;\n border-radius: 7px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow:hover,\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-menu-open='true'] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-session-current='true'] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-session-current='true'] .-cerbur-clutch-dsh-worktree-treeSessionContent {\n font-weight: 600;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropBefore::before,\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n position: absolute;\n right: 4px;\n left: 4px;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary);\n content: '';\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropBefore::before {\n top: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n bottom: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionContent {\n display: flex;\n align-items: center;\n flex: 1;\n width: 0;\n min-width: 0;\n min-height: 31px;\n box-sizing: border-box;\n border: 0;\n border-radius: 7px;\n padding: 4px 4px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionContent:hover {\n background: transparent;\n}\n\n.-cerbur-clutch-dsh-worktree-rowActions {\n display: none;\n flex: none;\n align-items: center;\n gap: 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionOverflowButton {\n display: block;\n width: calc(100% - 24px);\n margin: 2px 0 4px 24px;\n padding: 4px 8px;\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 12px;\n font-weight: 400;\n line-height: 20px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionOverflowButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow:hover .-cerbur-clutch-dsh-worktree-rowActions,\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-rowActions {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-renameInput {\n width: 100%;\n}\n\n.-cerbur-clutch-dsh-worktree-renameError {\n margin: 8px 0 0;\n color: var(--dsw-alias-state-error-primary);\n font-size: 13px;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-emptyNested {\n margin: 0;\n padding: 4px 8px 5px 28px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-modalField {\n display: grid;\n gap: 5px;\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n}\n";
483
+ //#region \0clutch-dsh-worktree-css:/Users/yuancheng/.dsh/clutch-dsh-worktree/worktree/wt_4ab905d8-c228-470f-9208-36c3552257a4/packages/clutch-dsh-worktree/src/client/worktree.css.mjs
484
+ const css = ".-cerbur-clutch-dsh-worktree-action {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n gap: 8px;\n width: calc(100% + 4px);\n height: 42px;\n margin: 4px -2px;\n box-sizing: border-box;\n border: 0;\n border-radius: 12px;\n padding: 0 10px 0 8px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n line-height: 22px;\n text-align: left;\n overflow: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-action:hover,\n.-cerbur-clutch-dsh-worktree-action[data-active='true'] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-action[data-collapsed='true'] {\n width: 36px;\n height: 36px;\n margin: 8px 0 10px;\n justify-content: center;\n gap: 0;\n padding: 0;\n border-radius: 50%;\n}\n\n.-cerbur-clutch-dsh-worktree-actionLabel {\n display: inline-flex;\n min-width: 0;\n align-items: center;\n height: 22px;\n overflow: hidden;\n font-weight: 400;\n line-height: 22px;\n white-space: nowrap;\n}\n\n.-cerbur-clutch-dsh-worktree-surface {\n position: absolute;\n top: 0;\n left: 0;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n min-width: 56px;\n overflow: hidden;\n border-right: 1px solid var(--dsw-alias-border-l1);\n background: var(--dsw-specific-sidebar-fill);\n color: var(--dsw-alias-label-primary);\n pointer-events: auto;\n transition: width var(--ds-transition-duration-slow) var(--ds-ease-in-out);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .-cerbur-clutch-dsh-worktree-surface {\n transition: none;\n }\n}\n\n.-cerbur-clutch-dsh-worktree-wideContent {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex: 1;\n flex-direction: column;\n}\n\n.-cerbur-clutch-dsh-worktree-surface[data-collapsed='true'] .-cerbur-clutch-dsh-worktree-wideContent {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeModalTabs {\n display: flex;\n gap: 8px;\n margin-bottom: 12px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeModalTab {\n border: 0;\n border-bottom: 2px solid transparent;\n padding: 4px 0;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeModalTab[aria-selected='true'] {\n border-bottom-color: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeImportCandidates {\n display: grid;\n gap: 8px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeImportSelect {\n width: 100%;\n min-width: 0;\n padding: 7px 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 13px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeImportError {\n display: grid;\n gap: 8px;\n}\n\n.-cerbur-clutch-dsh-worktree-closeButton {\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: inherit;\n cursor: pointer;\n font: inherit;\n}\n\n.-cerbur-clutch-dsh-worktree-closeButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-header {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: none;\n min-height: 44px;\n padding: 6px 10px;\n box-sizing: border-box;\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n}\n\n.-cerbur-clutch-dsh-worktree-title {\n min-width: 0;\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n font-weight: 600;\n}\n\n.-cerbur-clutch-dsh-worktree-closeButton {\n flex: none;\n width: 28px;\n height: 28px;\n font-size: 18px;\n line-height: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-modeButton[data-active='true'] {\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-workspacePicker {\n display: block;\n width: calc(100% - 20px);\n margin: 6px 10px 8px;\n padding: 6px 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 7px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceLabel {\n display: block;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-content {\n min-height: 0;\n flex: 1;\n overflow: auto;\n padding: 0 10px 12px;\n}\n\n.-cerbur-clutch-dsh-worktree-content:focus-visible {\n outline: 2px solid var(--dsw-alias-border-l3);\n outline-offset: -2px;\n}\n\n.-cerbur-clutch-dsh-worktree-error {\n margin: 12px 0 0;\n padding: 8px;\n border: 1px solid var(--dsw-alias-state-error-primary);\n border-radius: 7px;\n}\n\n.-cerbur-clutch-dsh-worktree-notice {\n margin: 12px 0 0;\n padding: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 7px;\n background: var(--dsw-alias-button-elevated-fill);\n}\n\n.-cerbur-clutch-dsh-worktree-retryButton,\n.-cerbur-clutch-dsh-worktree-actionButton {\n border: 0;\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n font: inherit;\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-retryButton,\n.-cerbur-clutch-dsh-worktree-actionButton {\n width: 100%;\n padding: 6px 8px;\n}\n\n.-cerbur-clutch-dsh-worktree-recoveryActions {\n display: grid;\n gap: 6px;\n margin-top: 6px;\n}\n\n.-cerbur-clutch-dsh-worktree-retryButton:disabled,\n.-cerbur-clutch-dsh-worktree-actionButton:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.-cerbur-clutch-dsh-worktree-actions {\n display: grid;\n gap: 6px;\n margin-top: 10px;\n padding: 8px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 7px;\n}\n\n.-cerbur-clutch-dsh-worktree-actionSelect {\n min-width: 0;\n padding: 5px 6px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-section {\n margin: 12px 0 0;\n}\n\n.-cerbur-clutch-dsh-worktree-sectionTitle {\n margin: 0 2px 4px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n}\n\n.-cerbur-clutch-dsh-worktree-empty,\n.-cerbur-clutch-dsh-worktree-message {\n margin: 0;\n padding: 8px 4px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.-cerbur-clutch-dsh-worktree-gitReadiness {\n display: grid;\n gap: 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-commandBlock {\n margin: 6px 0 0;\n padding: 8px;\n overflow-x: auto;\n border-radius: 6px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-secondary);\n font:\n 11px/16px ui-monospace,\n SFMono-Regular,\n Menlo,\n monospace;\n white-space: pre-wrap;\n}\n\n.-cerbur-clutch-dsh-worktree-message[data-error='true'] {\n color: var(--dsw-alias-state-error-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-worktree,\n.-cerbur-clutch-dsh-worktree-session {\n display: flex;\n align-items: center;\n width: 100%;\n min-width: 0;\n box-sizing: border-box;\n border: 0;\n border-radius: 7px;\n padding: 6px 8px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-worktree {\n cursor: default;\n}\n\n.-cerbur-clutch-dsh-worktree-session {\n cursor: pointer;\n}\n\n.-cerbur-clutch-dsh-worktree-worktree:hover,\n.-cerbur-clutch-dsh-worktree-session:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeLabel,\n.-cerbur-clutch-dsh-worktree-sessionLabel {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.-cerbur-clutch-dsh-worktree-status {\n flex: none;\n margin-left: auto;\n padding-left: 8px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchRow {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px 6px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchIcon {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n color: var(--dsw-alias-label-secondary);\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput {\n min-width: 0;\n box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 7px;\n background: var(--dsw-alias-button-elevated-fill);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n outline: none;\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput {\n width: 100%;\n padding: 6px 8px;\n font-size: 13px;\n line-height: 18px;\n}\n\n.-cerbur-clutch-dsh-worktree-searchInput:focus,\n.-cerbur-clutch-dsh-worktree-actionSelect:focus,\n.-cerbur-clutch-dsh-worktree-worktreeImportSelect:focus {\n border-color: #3b82f6;\n}\n\n.-cerbur-clutch-dsh-worktree-iconButton {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: 0;\n border-radius: 7px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 20px;\n line-height: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-iconButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceList {\n min-width: 0;\n min-height: 0;\n display: grid;\n gap: 4px;\n overflow-x: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceGroup {\n min-width: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow,\n.-cerbur-clutch-dsh-worktree-worktreeRow {\n position: relative;\n display: flex;\n align-items: center;\n min-width: 0;\n min-height: 36px;\n box-sizing: border-box;\n border-radius: 8px;\n padding: 3px 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover,\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow .-cerbur-clutch-dsh-worktree-workspaceDisclosure {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-workspaceDisclosure,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-workspaceDisclosure {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-workspaceIcon,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-workspaceIcon {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-worktreeDisclosure {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-worktreeDisclosure,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-worktreeDisclosure {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-worktreeIcon,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-worktreeIcon {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton {\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 28px;\n border: 0;\n border-radius: 6px;\n padding: 0;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 17px;\n line-height: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton > svg {\n display: block;\n}\n\n.-cerbur-clutch-dsh-worktree-disclosureButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceIcon,\n.-cerbur-clutch-dsh-worktree-worktreeIcon {\n flex: none;\n width: 24px;\n color: var(--dsw-alias-label-secondary);\n font-size: 20px;\n line-height: 1;\n text-align: center;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeState {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 12px;\n margin-right: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceIcon {\n color: var(--dsw-alias-brand-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceTitle,\n.-cerbur-clutch-dsh-worktree-worktreeLabel {\n min-width: 0;\n overflow: hidden;\n flex: 1;\n padding: 0 6px;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-main-group='true'] .-cerbur-clutch-dsh-worktree-worktreeLabel {\n font-weight: 600;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeHoverTitle {\n color: var(--dsw-static-neutral-bluish-00);\n font-size: 14px;\n line-height: 20px;\n overflow-wrap: anywhere;\n}\n\n/* Native Session hover card: full title, relative time, and current status. */\n.-cerbur-clutch-dsh-worktree-sessionHoverContent {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionHoverTitle {\n color: #FFFFFF;\n font-size: 14px;\n line-height: 20px;\n overflow-wrap: break-word;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionHoverTime {\n color: #CFD3D6;\n font-size: 12px;\n line-height: 16px;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionHoverStatus {\n display: flex;\n align-items: center;\n gap: 8px;\n color: #ADB2B8;\n font-size: 12px;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeActionSlot,\n.-cerbur-clutch-dsh-worktree-workspaceActions {\n position: relative;\n display: inline-flex;\n flex: 0 0 64px;\n align-items: center;\n justify-content: flex-end;\n width: 64px;\n height: 28px;\n margin-left: auto;\n}\n\n.-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-menuAction {\n position: absolute;\n top: 0;\n right: 32px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child {\n position: absolute;\n top: 0;\n right: 0;\n}\n\n.-cerbur-clutch-dsh-worktree-groupActivity {\n position: absolute;\n top: 0;\n right: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n visibility: hidden;\n pointer-events: none;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true'] .-cerbur-clutch-dsh-worktree-groupActivity,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true'] .-cerbur-clutch-dsh-worktree-groupActivity {\n visibility: visible;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true']:hover .-cerbur-clutch-dsh-worktree-groupActivity,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true']:hover .-cerbur-clutch-dsh-worktree-groupActivity,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true'][data-menu-open='true'] .-cerbur-clutch-dsh-worktree-groupActivity,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true'][data-menu-open='true'] .-cerbur-clutch-dsh-worktree-groupActivity,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true']:focus-within .-cerbur-clutch-dsh-worktree-groupActivity,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true']:focus-within .-cerbur-clutch-dsh-worktree-groupActivity {\n visibility: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true'] .-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true'] .-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child {\n visibility: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true']:hover .-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true']:hover .-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true'][data-menu-open='true']\n .-cerbur-clutch-dsh-worktree-treeActionSlot\n > .-cerbur-clutch-dsh-worktree-iconButton:last-child,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true'][data-menu-open='true']\n .-cerbur-clutch-dsh-worktree-treeActionSlot\n > .-cerbur-clutch-dsh-worktree-iconButton:last-child,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-group-activity='true']:focus-within .-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-group-activity='true']:focus-within .-cerbur-clutch-dsh-worktree-treeActionSlot > .-cerbur-clutch-dsh-worktree-iconButton:last-child {\n visibility: visible;\n}\n\n.-cerbur-clutch-dsh-worktree-menuAction {\n display: inline-flex;\n width: 28px;\n height: 28px;\n visibility: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow:hover .-cerbur-clutch-dsh-worktree-menuAction,\n.-cerbur-clutch-dsh-worktree-workspaceRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-menuAction,\n.-cerbur-clutch-dsh-worktree-worktreeRow:hover .-cerbur-clutch-dsh-worktree-menuAction,\n.-cerbur-clutch-dsh-worktree-worktreeRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-menuAction {\n visibility: visible;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropBefore::before,\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n position: absolute;\n right: 4px;\n left: 4px;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary);\n content: '';\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropBefore::before {\n top: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-workspaceRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n bottom: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropBefore::before,\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n position: absolute;\n right: 4px;\n left: 4px;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary);\n content: '';\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropBefore::before {\n top: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n bottom: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeChildren {\n margin-left: 16px;\n padding: 2px 0 5px 12px;\n border-left: 1px solid var(--dsw-alias-border-l1);\n}\n\n.-cerbur-clutch-dsh-worktree-groupLabel {\n padding: 6px 8px 3px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n}\n\n.-cerbur-clutch-dsh-worktree-groupHeader {\n display: flex;\n align-items: center;\n min-height: 34px;\n padding-right: 4px;\n}\n\n.-cerbur-clutch-dsh-worktree-groupHeader .-cerbur-clutch-dsh-worktree-groupLabel {\n flex: 1;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeGroup {\n margin-top: 2px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow {\n min-height: 34px;\n}\n\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-worktreeIcon,\n.-cerbur-clutch-dsh-worktree-worktreeRow .-cerbur-clutch-dsh-worktree-disclosureButton {\n width: 22px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow {\n position: relative;\n display: flex;\n align-items: center;\n width: calc(100% - 22px);\n margin-left: 22px;\n min-width: 0;\n min-height: 31px;\n box-sizing: border-box;\n border-radius: 7px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow:hover,\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-menu-open='true'] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-session-current='true'] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-session-current='true'] .-cerbur-clutch-dsh-worktree-treeSessionContent {\n font-weight: 600;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropBefore::before,\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n position: absolute;\n right: 4px;\n left: 4px;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary);\n content: '';\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropBefore::before {\n top: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow.-cerbur-clutch-dsh-worktree-dropAfter::after {\n bottom: -1px;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionContent {\n display: flex;\n align-items: center;\n flex: 1;\n width: 0;\n min-width: 0;\n min-height: 31px;\n box-sizing: border-box;\n border: 0;\n border-radius: 7px;\n padding: 4px 4px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionContent:hover {\n background: transparent;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionTrailing {\n position: relative;\n display: inline-flex;\n flex: 0 0 52px;\n align-items: center;\n justify-content: flex-end;\n width: 52px;\n height: 28px;\n margin-left: auto;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionMeta,\n.-cerbur-clutch-dsh-worktree-rowActions {\n position: absolute;\n inset: 0;\n display: inline-flex;\n align-items: center;\n justify-content: flex-end;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionMeta {\n color: var(--dsw-alias-label-tertiary);\n pointer-events: none;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionStatus {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionTime {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n margin-right: 4px;\n font-size: 12px;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionOverflowButton {\n display: block;\n width: calc(100% - 24px);\n margin: 2px 0 4px 24px;\n padding: 4px 8px;\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n font: inherit;\n font-size: 12px;\n font-weight: 400;\n line-height: 20px;\n text-align: left;\n}\n\n.-cerbur-clutch-dsh-worktree-sessionOverflowButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.-cerbur-clutch-dsh-worktree-rowActions {\n display: none;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow:hover .-cerbur-clutch-dsh-worktree-sessionMeta,\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-sessionMeta,\n.-cerbur-clutch-dsh-worktree-sessionTrailing:focus-within .-cerbur-clutch-dsh-worktree-sessionMeta {\n visibility: hidden;\n}\n\n.-cerbur-clutch-dsh-worktree-treeSessionRow:hover .-cerbur-clutch-dsh-worktree-rowActions,\n.-cerbur-clutch-dsh-worktree-treeSessionRow[data-menu-open='true'] .-cerbur-clutch-dsh-worktree-rowActions,\n.-cerbur-clutch-dsh-worktree-sessionTrailing:focus-within .-cerbur-clutch-dsh-worktree-rowActions {\n display: inline-flex;\n}\n\n.-cerbur-clutch-dsh-worktree-renameInput {\n width: 100%;\n}\n\n.-cerbur-clutch-dsh-worktree-renameError {\n margin: 8px 0 0;\n color: var(--dsw-alias-state-error-primary);\n font-size: 13px;\n line-height: 20px;\n}\n\n.-cerbur-clutch-dsh-worktree-emptyNested {\n margin: 0;\n padding: 4px 8px 5px 28px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.-cerbur-clutch-dsh-worktree-modalField {\n display: grid;\n gap: 5px;\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n}\n";
404
485
  const styleId = "@cerbur/clutch-dsh-worktree/worktree.css";
405
486
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"@cerbur/clutch-dsh-worktree/worktree.css\"]") === null) {
406
487
  const tag = document.createElement("style");
@@ -427,6 +508,7 @@ window.__ModuleLoader__.load({
427
508
  "workspaceLabel": "-cerbur-clutch-dsh-worktree-workspaceLabel",
428
509
  "content": "-cerbur-clutch-dsh-worktree-content",
429
510
  "error": "-cerbur-clutch-dsh-worktree-error",
511
+ "notice": "-cerbur-clutch-dsh-worktree-notice",
430
512
  "retryButton": "-cerbur-clutch-dsh-worktree-retryButton",
431
513
  "actionButton": "-cerbur-clutch-dsh-worktree-actionButton",
432
514
  "recoveryActions": "-cerbur-clutch-dsh-worktree-recoveryActions",
@@ -459,9 +541,14 @@ window.__ModuleLoader__.load({
459
541
  "worktreeState": "-cerbur-clutch-dsh-worktree-worktreeState",
460
542
  "workspaceTitle": "-cerbur-clutch-dsh-worktree-workspaceTitle",
461
543
  "worktreeHoverTitle": "-cerbur-clutch-dsh-worktree-worktreeHoverTitle",
544
+ "sessionHoverContent": "-cerbur-clutch-dsh-worktree-sessionHoverContent",
545
+ "sessionHoverTitle": "-cerbur-clutch-dsh-worktree-sessionHoverTitle",
546
+ "sessionHoverTime": "-cerbur-clutch-dsh-worktree-sessionHoverTime",
547
+ "sessionHoverStatus": "-cerbur-clutch-dsh-worktree-sessionHoverStatus",
462
548
  "treeActionSlot": "-cerbur-clutch-dsh-worktree-treeActionSlot",
463
549
  "workspaceActions": "-cerbur-clutch-dsh-worktree-workspaceActions",
464
550
  "menuAction": "-cerbur-clutch-dsh-worktree-menuAction",
551
+ "groupActivity": "-cerbur-clutch-dsh-worktree-groupActivity",
465
552
  "dropBefore": "-cerbur-clutch-dsh-worktree-dropBefore",
466
553
  "dropAfter": "-cerbur-clutch-dsh-worktree-dropAfter",
467
554
  "treeChildren": "-cerbur-clutch-dsh-worktree-treeChildren",
@@ -470,7 +557,11 @@ window.__ModuleLoader__.load({
470
557
  "worktreeGroup": "-cerbur-clutch-dsh-worktree-worktreeGroup",
471
558
  "treeSessionRow": "-cerbur-clutch-dsh-worktree-treeSessionRow",
472
559
  "treeSessionContent": "-cerbur-clutch-dsh-worktree-treeSessionContent",
560
+ "sessionTrailing": "-cerbur-clutch-dsh-worktree-sessionTrailing",
561
+ "sessionMeta": "-cerbur-clutch-dsh-worktree-sessionMeta",
473
562
  "rowActions": "-cerbur-clutch-dsh-worktree-rowActions",
563
+ "sessionStatus": "-cerbur-clutch-dsh-worktree-sessionStatus",
564
+ "sessionTime": "-cerbur-clutch-dsh-worktree-sessionTime",
474
565
  "sessionOverflowButton": "-cerbur-clutch-dsh-worktree-sessionOverflowButton",
475
566
  "renameInput": "-cerbur-clutch-dsh-worktree-renameInput",
476
567
  "renameError": "-cerbur-clutch-dsh-worktree-renameError",
@@ -746,27 +837,27 @@ window.__ModuleLoader__.load({
746
837
  //#region lib/client/worktree-expand-state.js
747
838
  /** Browser-local storage identity; never sent to DSH or the plugin sidecar. */
748
839
  const WORKTREE_EXPAND_STATE_STORAGE_KEY = "clutch-dsh-worktree.expand-state";
749
- function emptyState() {
840
+ function emptyState$1() {
750
841
  return {
751
842
  collapsedWorkspaceIds: {},
752
843
  collapsedMainWorkspaceIds: {},
753
844
  collapsedWorktreeIds: {}
754
845
  };
755
846
  }
756
- function isRecord(value) {
847
+ function isRecord$1(value) {
757
848
  return typeof value === "object" && value !== null && !Array.isArray(value);
758
849
  }
759
- function normalizeIds(value) {
760
- if (!isRecord(value)) return {};
850
+ function normalizeIds$1(value) {
851
+ if (!isRecord$1(value)) return {};
761
852
  return Object.fromEntries(Object.entries(value).filter(([id, collapsed]) => id.length > 0 && collapsed === true));
762
853
  }
763
854
  /** Turn any persisted JSON value into the current state shape. */
764
855
  function normalizeWorktreeExpandState(value) {
765
- const source = isRecord(value) ? value : {};
856
+ const source = isRecord$1(value) ? value : {};
766
857
  return {
767
- collapsedWorkspaceIds: normalizeIds(source.collapsedWorkspaceIds),
768
- collapsedMainWorkspaceIds: normalizeIds(source.collapsedMainWorkspaceIds),
769
- collapsedWorktreeIds: normalizeIds(source.collapsedWorktreeIds)
858
+ collapsedWorkspaceIds: normalizeIds$1(source.collapsedWorkspaceIds),
859
+ collapsedMainWorkspaceIds: normalizeIds$1(source.collapsedMainWorkspaceIds),
860
+ collapsedWorktreeIds: normalizeIds$1(source.collapsedWorktreeIds)
770
861
  };
771
862
  }
772
863
  function isWorkspaceExpanded(state, workspaceId) {
@@ -790,9 +881,9 @@ window.__ModuleLoader__.load({
790
881
  function createWorktreeExpandStateStore(snapshotStoreFactory) {
791
882
  let store;
792
883
  try {
793
- store = snapshotStoreFactory(emptyState(), { persist: { name: WORKTREE_EXPAND_STATE_STORAGE_KEY } });
884
+ store = snapshotStoreFactory(emptyState$1(), { persist: { name: WORKTREE_EXPAND_STATE_STORAGE_KEY } });
794
885
  } catch {
795
- store = snapshotStoreFactory(emptyState());
886
+ store = snapshotStoreFactory(emptyState$1());
796
887
  }
797
888
  store.set(normalizeWorktreeExpandState(store.getSnapshot()));
798
889
  return Object.assign(store, { actions: {
@@ -883,6 +974,14 @@ window.__ModuleLoader__.load({
883
974
  function reason(error, t) {
884
975
  return error.message.length > 0 ? error.message : t("error.worktreeDataUnavailable");
885
976
  }
977
+ function formatWorktreePermissionNotice(result, t) {
978
+ switch (result.status) {
979
+ case "fallback-workspace-write": return t("permission.fallbackWorkspaceWrite");
980
+ case "user-restricted": return t("permission.userRestricted");
981
+ case "unverified": return t("permission.unverified");
982
+ default: return t("permission.unavailable");
983
+ }
984
+ }
886
985
  function formatWorktreeViewError(error, t) {
887
986
  switch (error.code) {
888
987
  case "CLIENT_DISPOSED": return t("error.connectionDisposed");
@@ -899,6 +998,10 @@ window.__ModuleLoader__.load({
899
998
  case "WORKTREE_RECORD_MISSING": return t("error.worktreeRecordMissing");
900
999
  case "WORKTREE_IMPORT_INVALID": return t("error.worktreeImportInvalid");
901
1000
  case "WORKTREE_ALREADY_MANAGED": return t("error.worktreeAlreadyManaged");
1001
+ case "WORKTREE_MUTATION_BUSY": return t("error.worktreeMutationBusy");
1002
+ case "WORKTREE_STATE_CONFLICT": return t("error.worktreeStateConflict");
1003
+ case "WORKTREE_RECOVERY_REQUIRED": return t("error.worktreeRecoveryRequired");
1004
+ case "WORKTREE_IDENTITY_CHANGED": return t("error.worktreeIdentityChanged");
902
1005
  case "WORKTREE_REGISTRATION_SESSION_UNAVAILABLE": return t("error.worktreeRegistrationSessionUnavailable");
903
1006
  case "WORKSPACE_RENAME_UNAVAILABLE": return t("error.workspaceRenameUnavailable");
904
1007
  case "WORKSPACE_DELETE_UNAVAILABLE": return t("error.workspaceDeleteUnavailable");
@@ -908,6 +1011,8 @@ window.__ModuleLoader__.load({
908
1011
  reason: reason(error, t)
909
1012
  });
910
1013
  case "SESSION_ALREADY_BOUND": return t("error.sessionAlreadyBound");
1014
+ case "WORKTREE_PERMISSION_CONFIRMATION_REQUIRED": return t("error.worktreePermissionConfirmationRequired");
1015
+ case "WORKTREE_PERMISSION_FAILED": return t("error.worktreePermissionFailed", { reason: reason(error, t) });
911
1016
  case "SESSION_LIST_NOT_READY": return t("error.sessionListNotReady");
912
1017
  case "SESSION_FACTS_INCOMPLETE": return t("error.sessionFactsIncomplete");
913
1018
  case "WORKTREE_SESSION_REPAIR_REQUIRED": return t("error.worktreeSessionRepairRequired");
@@ -919,6 +1024,127 @@ window.__ModuleLoader__.load({
919
1024
  }
920
1025
  //#endregion
921
1026
  //#region lib/client/session-view.js
1027
+ function isPendingInteractionStatus(value) {
1028
+ return value === "approval" || value === "plan-review" || value === "question";
1029
+ }
1030
+ function validUpdatedAt(value) {
1031
+ return value !== void 0 && Number.isFinite(value);
1032
+ }
1033
+ /**
1034
+ * Native-compatible subagent lineage index. The upstream runtime's public client
1035
+ * entry is a browser module-loader bundle, so keeping this pure mirror local lets
1036
+ * the browser Consumer and its Node tests share the same status semantics.
1037
+ */
1038
+ function indexSubagentDescendants(summaries) {
1039
+ const indexed = /* @__PURE__ */ new Map();
1040
+ for (const descendant of Object.values(summaries)) {
1041
+ if (descendant.origin !== "subagent") continue;
1042
+ const seen = /* @__PURE__ */ new Set();
1043
+ let current = descendant;
1044
+ while (current?.origin === "subagent" && current.parentId !== void 0 && !seen.has(current.id)) {
1045
+ seen.add(current.id);
1046
+ const aggregate = indexed.get(current.parentId);
1047
+ if (aggregate === void 0) indexed.set(current.parentId, {
1048
+ count: 1,
1049
+ runningCount: descendant.running ? 1 : 0
1050
+ });
1051
+ else {
1052
+ aggregate.count += 1;
1053
+ if (descendant.running) aggregate.runningCount += 1;
1054
+ }
1055
+ current = summaries[current.parentId];
1056
+ }
1057
+ }
1058
+ return indexed;
1059
+ }
1060
+ /** Native Workspace relative-time buckets, kept pure for deterministic rendering/tests. */
1061
+ function relativeTime(updatedAt, now) {
1062
+ if (!validUpdatedAt(updatedAt) || !Number.isFinite(now)) return void 0;
1063
+ const MIN = 6e4;
1064
+ const HOUR = 36e5;
1065
+ const DAY = 864e5;
1066
+ const diff = Math.max(0, now - updatedAt);
1067
+ if (diff < MIN) return {
1068
+ unit: "now",
1069
+ n: 0
1070
+ };
1071
+ if (diff < HOUR) return {
1072
+ unit: "minutes",
1073
+ n: Math.floor(diff / MIN)
1074
+ };
1075
+ if (diff < DAY) return {
1076
+ unit: "hours",
1077
+ n: Math.floor(diff / HOUR)
1078
+ };
1079
+ if (diff < 30 * DAY) return {
1080
+ unit: "days",
1081
+ n: Math.floor(diff / DAY)
1082
+ };
1083
+ if (diff < 365 * DAY) return {
1084
+ unit: "months",
1085
+ n: Math.floor(diff / (30 * DAY))
1086
+ };
1087
+ return {
1088
+ unit: "years",
1089
+ n: Math.floor(diff / (365 * DAY))
1090
+ };
1091
+ }
1092
+ /** Match native Workspace status priority while retaining the aggregate subagent count. */
1093
+ function sessionStatus(session, runningSubagentCount) {
1094
+ const count = Number.isFinite(runningSubagentCount) && runningSubagentCount > 0 ? Math.floor(runningSubagentCount) : 0;
1095
+ if (isPendingInteractionStatus(session.pendingInteraction)) return {
1096
+ state: "warning",
1097
+ labelKey: session.pendingInteraction === "approval" ? "waitingApproval" : session.pendingInteraction === "plan-review" ? "planReview" : "waitingAnswer",
1098
+ runningSubagentCount: count
1099
+ };
1100
+ if (session.running === true) return {
1101
+ state: "ongoing",
1102
+ labelKey: "running",
1103
+ runningSubagentCount: count
1104
+ };
1105
+ if (count > 0) return {
1106
+ state: "ongoing",
1107
+ labelKey: "subagentsRunning",
1108
+ runningSubagentCount: count
1109
+ };
1110
+ if (session.completed === true) return {
1111
+ state: "done",
1112
+ labelKey: "completed",
1113
+ runningSubagentCount: 0
1114
+ };
1115
+ return {
1116
+ state: "done",
1117
+ labelKey: "idle",
1118
+ runningSubagentCount: 0
1119
+ };
1120
+ }
1121
+ function toLineageSummary(sessionId, summary) {
1122
+ return {
1123
+ id: sessionId,
1124
+ running: summary.running === true,
1125
+ ...summary.parentId === void 0 ? {} : { parentId: summary.parentId },
1126
+ ...summary.origin === "subagent" ? { origin: "subagent" } : {}
1127
+ };
1128
+ }
1129
+ /** Derive row and group activity facts from one retained DSH Session snapshot. */
1130
+ function deriveSessionPresentationIndex(sessions) {
1131
+ const descendants = indexSubagentDescendants(Object.fromEntries(Object.entries(sessions.byId).map(([sessionId, summary]) => [sessionId, toLineageSummary(sessionId, summary)])));
1132
+ return Object.fromEntries(Object.entries(sessions.byId).map(([sessionId, summary]) => {
1133
+ const runningSubagentCount = descendants.get(sessionId)?.runningCount ?? 0;
1134
+ return [sessionId, {
1135
+ status: sessionStatus(summary, runningSubagentCount),
1136
+ running: summary.running === true,
1137
+ ongoing: summary.running === true || runningSubagentCount > 0,
1138
+ runningSubagentCount,
1139
+ completed: summary.completed === true,
1140
+ ...validUpdatedAt(summary.updatedAt) ? { updatedAt: summary.updatedAt } : {}
1141
+ }];
1142
+ }));
1143
+ }
1144
+ /** Aggregate only live ongoing activity; callers decide which IDs are in the group. */
1145
+ function hasOngoingSession(sessionIds, presentations) {
1146
+ return sessionIds.some((sessionId) => presentations[sessionId]?.ongoing === true);
1147
+ }
922
1148
  /** Read the native blank flag without inferring blankness from title or id. */
923
1149
  function isBlankSession(sessionId, sessions) {
924
1150
  return sessions.byId[sessionId]?.blank === true;
@@ -943,6 +1169,193 @@ window.__ModuleLoader__.load({
943
1169
  return (summary?.displayTitle ?? sessionId).toLocaleLowerCase().includes(normalizedQuery);
944
1170
  }
945
1171
  //#endregion
1172
+ //#region lib/client/worktree-session-order.js
1173
+ /** Browser-only persistence key; this state never crosses the DSH or sidecar boundary. */
1174
+ const WORKTREE_SESSION_ORDER_STORAGE_KEY = "clutch-dsh-worktree.session-order";
1175
+ function emptyState() {
1176
+ return { accounts: {} };
1177
+ }
1178
+ function isRecord(value) {
1179
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1180
+ }
1181
+ function isValidTimestamp(value) {
1182
+ return typeof value === "number" && Number.isFinite(value);
1183
+ }
1184
+ function normalizeIds(value) {
1185
+ if (!Array.isArray(value)) return [];
1186
+ const seen = /* @__PURE__ */ new Set();
1187
+ const ids = [];
1188
+ for (const id of value) {
1189
+ if (typeof id !== "string" || id.length === 0 || seen.has(id)) continue;
1190
+ seen.add(id);
1191
+ ids.push(id);
1192
+ }
1193
+ return ids;
1194
+ }
1195
+ function normalizeAccount(value) {
1196
+ if (!isRecord(value)) return void 0;
1197
+ const order = normalizeIds(value.order);
1198
+ const allowed = new Set(order);
1199
+ const observedUpdatedAt = {};
1200
+ if (isRecord(value.observedUpdatedAt)) {
1201
+ for (const [id, timestamp] of Object.entries(value.observedUpdatedAt)) if (allowed.has(id) && isValidTimestamp(timestamp)) observedUpdatedAt[id] = timestamp;
1202
+ }
1203
+ return {
1204
+ order,
1205
+ observedUpdatedAt
1206
+ };
1207
+ }
1208
+ /** Normalize persisted state without trusting any browser-local JSON shape. */
1209
+ function normalizeWorktreeSessionOrderState(value) {
1210
+ if (!isRecord(value) || !isRecord(value.accounts)) return emptyState();
1211
+ const accounts = {};
1212
+ for (const [accountKey, account] of Object.entries(value.accounts)) {
1213
+ if (accountKey.length === 0) continue;
1214
+ const normalized = normalizeAccount(account);
1215
+ if (normalized !== void 0) accounts[accountKey] = normalized;
1216
+ }
1217
+ return { accounts };
1218
+ }
1219
+ function validIds(ids) {
1220
+ return normalizeIds([...ids]);
1221
+ }
1222
+ function sameRecord(left, right) {
1223
+ if (left === void 0) return false;
1224
+ const leftKeys = Object.keys(left);
1225
+ const rightKeys = Object.keys(right);
1226
+ if (leftKeys.length !== rightKeys.length) return false;
1227
+ return rightKeys.every((key) => left[key] === right[key]);
1228
+ }
1229
+ function sameAccount(left, right) {
1230
+ return left !== void 0 && left.order.length === right.order.length && left.order.every((id, index) => id === right.order[index]) && sameRecord(left.observedUpdatedAt, right.observedUpdatedAt);
1231
+ }
1232
+ /**
1233
+ * Native-style activity order transition. The first observation records timestamps
1234
+ * without turning an existing list into a recency sort.
1235
+ */
1236
+ function nextSessionOrderAccount(input) {
1237
+ const baseIds = validIds(input.baseIds);
1238
+ const previous = input.previous;
1239
+ if (previous === void 0) {
1240
+ const observedUpdatedAt = {};
1241
+ for (const id of baseIds) {
1242
+ const timestamp = input.updatedAtById[id];
1243
+ if (isValidTimestamp(timestamp)) observedUpdatedAt[id] = timestamp;
1244
+ }
1245
+ return {
1246
+ order: baseIds,
1247
+ observedUpdatedAt
1248
+ };
1249
+ }
1250
+ const available = new Set(baseIds);
1251
+ const order = [];
1252
+ const seen = /* @__PURE__ */ new Set();
1253
+ for (const id of previous.order) if (available.has(id) && !seen.has(id)) {
1254
+ seen.add(id);
1255
+ order.push(id);
1256
+ }
1257
+ for (const id of baseIds) if (!seen.has(id)) {
1258
+ seen.add(id);
1259
+ order.push(id);
1260
+ }
1261
+ const observedUpdatedAt = {};
1262
+ const previousIds = new Set(previous.order);
1263
+ const newlyObserved = [];
1264
+ const promoted = [];
1265
+ for (const [index, id] of order.entries()) {
1266
+ if (!previousIds.has(id)) newlyObserved.push(id);
1267
+ const previousTimestamp = previous.observedUpdatedAt[id];
1268
+ const timestamp = input.updatedAtById[id];
1269
+ if (isValidTimestamp(previousTimestamp)) observedUpdatedAt[id] = previousTimestamp;
1270
+ if (isValidTimestamp(timestamp)) {
1271
+ if (!isValidTimestamp(previousTimestamp) || timestamp > previousTimestamp) {
1272
+ if (isValidTimestamp(previousTimestamp)) promoted.push({
1273
+ id,
1274
+ timestamp,
1275
+ index
1276
+ });
1277
+ observedUpdatedAt[id] = Math.max(observedUpdatedAt[id] ?? timestamp, timestamp);
1278
+ }
1279
+ }
1280
+ }
1281
+ promoted.sort((left, right) => right.timestamp - left.timestamp || left.index - right.index);
1282
+ const promotedIds = /* @__PURE__ */ new Set([...newlyObserved, ...promoted.map(({ id }) => id)]);
1283
+ return {
1284
+ order: [
1285
+ ...newlyObserved,
1286
+ ...promoted.map(({ id }) => id),
1287
+ ...order.filter((id) => !promotedIds.has(id))
1288
+ ],
1289
+ observedUpdatedAt
1290
+ };
1291
+ }
1292
+ /** Apply a successful manual drag without dropping IDs hidden by search. */
1293
+ function reorderSessionIds(order, sessionId, overSessionId, half) {
1294
+ const next = order.filter((id) => id !== sessionId);
1295
+ const targetIndex = next.indexOf(overSessionId);
1296
+ if (targetIndex === -1) return [...order];
1297
+ next.splice(half === "before" ? targetIndex : targetIndex + 1, 0, sessionId);
1298
+ return next;
1299
+ }
1300
+ function cloneAccount(account) {
1301
+ return {
1302
+ order: [...account.order],
1303
+ observedUpdatedAt: { ...account.observedUpdatedAt }
1304
+ };
1305
+ }
1306
+ /** Create one apply-scoped, browser-local Session ordering store. */
1307
+ function createWorktreeSessionOrderStore(snapshotStoreFactory) {
1308
+ let store;
1309
+ try {
1310
+ store = snapshotStoreFactory(emptyState(), { persist: { name: WORKTREE_SESSION_ORDER_STORAGE_KEY } });
1311
+ } catch {
1312
+ store = snapshotStoreFactory(emptyState());
1313
+ }
1314
+ store.set(normalizeWorktreeSessionOrderState(store.getSnapshot()));
1315
+ let disposed = false;
1316
+ return Object.assign(store, {
1317
+ actions: {
1318
+ reconcile: (accountKey, baseIds, updatedAtById) => {
1319
+ if (disposed || accountKey.length === 0) return;
1320
+ store.update((draft) => {
1321
+ const next = nextSessionOrderAccount({
1322
+ baseIds,
1323
+ updatedAtById,
1324
+ previous: draft.accounts[accountKey]
1325
+ });
1326
+ if (sameAccount(draft.accounts[accountKey], next)) return;
1327
+ draft.accounts[accountKey] = cloneAccount(next);
1328
+ });
1329
+ },
1330
+ setOrder: (accountKey, order) => {
1331
+ if (disposed || accountKey.length === 0) return;
1332
+ store.update((draft) => {
1333
+ const current = draft.accounts[accountKey] ?? {
1334
+ order: [],
1335
+ observedUpdatedAt: {}
1336
+ };
1337
+ const next = {
1338
+ order: validIds(order),
1339
+ observedUpdatedAt: { ...current.observedUpdatedAt }
1340
+ };
1341
+ if (sameAccount(current, next)) return;
1342
+ draft.accounts[accountKey] = next;
1343
+ });
1344
+ },
1345
+ retain: (accountKeys) => {
1346
+ if (disposed) return;
1347
+ store.update((draft) => {
1348
+ const allowed = new Set(accountKeys);
1349
+ for (const accountKey of Object.keys(draft.accounts)) if (!allowed.has(accountKey)) delete draft.accounts[accountKey];
1350
+ });
1351
+ }
1352
+ },
1353
+ dispose: () => {
1354
+ disposed = true;
1355
+ }
1356
+ });
1357
+ }
1358
+ //#endregion
946
1359
  //#region lib/client/worktree-view-errors.js
947
1360
  /** A DSH Session exists even when the external Worktree binding needs repair. */
948
1361
  var WorktreeSessionBindingError = class extends Error {
@@ -964,6 +1377,25 @@ window.__ModuleLoader__.load({
964
1377
  this.cause = cause;
965
1378
  }
966
1379
  };
1380
+ /** A Session is retained after permission confirmation or permission setup needs retry. */
1381
+ var WorktreeSessionPermissionError = class extends Error {
1382
+ code;
1383
+ retryable = true;
1384
+ details;
1385
+ sessionId;
1386
+ createdSession;
1387
+ cause;
1388
+ constructor(sessionId, createdSession, cause) {
1389
+ const reason = cause instanceof Error ? cause.message : cause === void 0 ? "" : String(cause);
1390
+ super(reason);
1391
+ this.name = "WorktreeSessionPermissionError";
1392
+ this.code = cause === void 0 ? "WORKTREE_PERMISSION_CONFIRMATION_REQUIRED" : "WORKTREE_PERMISSION_FAILED";
1393
+ this.details = { sessionId };
1394
+ this.sessionId = sessionId;
1395
+ this.createdSession = createdSession;
1396
+ this.cause = cause;
1397
+ }
1398
+ };
967
1399
  function recordDetails(value) {
968
1400
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
969
1401
  const details = value.details;
@@ -974,7 +1406,7 @@ window.__ModuleLoader__.load({
974
1406
  function toWorktreeViewError(error) {
975
1407
  if (typeof error === "object" && error !== null) {
976
1408
  const candidate = error;
977
- const details = error instanceof WorktreeSessionBindingError ? {
1409
+ const details = error instanceof WorktreeSessionBindingError || error instanceof WorktreeSessionPermissionError ? {
978
1410
  ...recordDetails(error) ?? {},
979
1411
  sessionId: error.sessionId
980
1412
  } : recordDetails(error);
@@ -1016,6 +1448,21 @@ window.__ModuleLoader__.load({
1016
1448
  }
1017
1449
  throw new Error("Unable to generate an available default Worktree name.");
1018
1450
  }
1451
+ /** Generate the next available numeric sibling name for a selected Worktree. */
1452
+ function createNumberedWorktreeName(baseName, existingNames) {
1453
+ const normalizedBaseName = baseName.trim();
1454
+ const usedNames = new Set([...existingNames].map((name) => name.trim()));
1455
+ const numberedBase = normalizedBaseName.match(/^(.*)-(\d+)$/);
1456
+ const prefix = numberedBase?.[1] || normalizedBaseName || "worktree";
1457
+ const parsedNumber = numberedBase === null ? 1 : Number(numberedBase[2]);
1458
+ let number = Number.isSafeInteger(parsedNumber) ? parsedNumber + 1 : 2;
1459
+ for (let attempt = 0; attempt < 1e4; attempt += 1) {
1460
+ const candidate = `${prefix}-${number}`;
1461
+ if (!usedNames.has(candidate)) return candidate;
1462
+ number += 1;
1463
+ }
1464
+ throw new Error("Unable to generate an available numbered Worktree name.");
1465
+ }
1019
1466
  /** Resolve a row-half drop target to native-style optional before-anchor semantics. */
1020
1467
  function resolveWorktreeMove(worktreeIds, sourceWorktreeId, targetWorktreeId, half) {
1021
1468
  const sourceIndex = worktreeIds.indexOf(sourceWorktreeId);
@@ -1028,11 +1475,19 @@ window.__ModuleLoader__.load({
1028
1475
  return beforeWorktreeId === void 0 ? {} : { beforeWorktreeId };
1029
1476
  }
1030
1477
  /** Keep mutation routing in the browser Consumer while leaving wire details to the adapter. */
1031
- async function executeWorktreeAction(manager, action) {
1478
+ async function executeWorktreeAction(manager, action, permission, onPermissionResult) {
1032
1479
  if (action.type === "createWorktree") return manager.createWorktree(action.input);
1033
1480
  if (action.type === "importWorktree") return manager.importWorktree(action.input);
1034
1481
  if (action.type === "removeWorktree") {
1035
1482
  await manager.removeWorktree(action.input);
1483
+ const result = await permission?.normalizeDetachedWorktreePermissions({
1484
+ workspaceId: action.input.workspaceId,
1485
+ worktreeId: action.input.worktreeId
1486
+ });
1487
+ if (result !== void 0) onPermissionResult?.({
1488
+ workspaceId: action.input.workspaceId,
1489
+ worktreeId: action.input.worktreeId
1490
+ }, result);
1036
1491
  return;
1037
1492
  }
1038
1493
  }
@@ -1051,7 +1506,7 @@ window.__ModuleLoader__.load({
1051
1506
  } catch (error) {
1052
1507
  throw new WorktreeSessionBindingError(sessionId, error);
1053
1508
  }
1054
- input.beforeOpen?.(sessionId);
1509
+ await input.beforeOpen?.(sessionId);
1055
1510
  input.openSession(sessionId);
1056
1511
  return sessionId;
1057
1512
  }
@@ -1210,8 +1665,56 @@ window.__ModuleLoader__.load({
1210
1665
  this.details = details;
1211
1666
  }
1212
1667
  };
1668
+ /**
1669
+ * Keep the confirmation and second permission call in one place for new,
1670
+ * reused, and retried Sessions. The Host still validates the relation.
1671
+ */
1672
+ async function ensureWorktreeSessionPermission(input) {
1673
+ if (input.permission === void 0) return;
1674
+ const request = {
1675
+ workspaceId: input.workspaceId,
1676
+ worktreeId: input.worktreeId,
1677
+ sessionId: input.sessionId,
1678
+ binding: "active"
1679
+ };
1680
+ let result;
1681
+ try {
1682
+ result = await input.permission.ensureWorktreePermission(request);
1683
+ } catch (error) {
1684
+ throw new WorktreeSessionPermissionError(input.sessionId, input.createdSession, error);
1685
+ }
1686
+ if (result.status === "confirmation-required") {
1687
+ let confirmed;
1688
+ try {
1689
+ confirmed = input.confirmFullAccess === void 0 ? false : await input.confirmFullAccess({
1690
+ workspaceId: input.workspaceId,
1691
+ worktreeId: input.worktreeId,
1692
+ sessionId: input.sessionId,
1693
+ cwd: input.cwd
1694
+ });
1695
+ } catch (error) {
1696
+ throw new WorktreeSessionPermissionError(input.sessionId, input.createdSession, error);
1697
+ }
1698
+ if (!confirmed) throw new WorktreeSessionPermissionError(input.sessionId, input.createdSession);
1699
+ try {
1700
+ result = await input.permission.ensureWorktreePermission({
1701
+ ...request,
1702
+ confirmed: true
1703
+ });
1704
+ } catch (error) {
1705
+ throw new WorktreeSessionPermissionError(input.sessionId, input.createdSession, error);
1706
+ }
1707
+ }
1708
+ if (result.status === "confirmation-required") throw new WorktreeSessionPermissionError(input.sessionId, input.createdSession);
1709
+ input.onPermissionResult?.({
1710
+ workspaceId: input.workspaceId,
1711
+ worktreeId: input.worktreeId,
1712
+ sessionId: input.sessionId,
1713
+ cwd: input.cwd
1714
+ }, result);
1715
+ }
1213
1716
  function resolveWorktreeSessionAction(input) {
1214
- if (input.target.status !== "active" || input.target.health === "repair") return {
1717
+ if (input.target.status !== "active" || input.target.health === "repair" || input.target.health === "recovery-needed") return {
1215
1718
  kind: "reject",
1216
1719
  reason: "worktree-not-available"
1217
1720
  };
@@ -1273,6 +1776,16 @@ window.__ModuleLoader__.load({
1273
1776
  } catch (error) {
1274
1777
  throw new WorktreeSessionBindingError(input.pending.sessionId, error);
1275
1778
  }
1779
+ await ensureWorktreeSessionPermission({
1780
+ permission: input.permission,
1781
+ confirmFullAccess: input.confirmFullAccess,
1782
+ onPermissionResult: input.onPermissionResult,
1783
+ sessionId: input.pending.sessionId,
1784
+ workspaceId: input.pending.workspaceId,
1785
+ worktreeId: input.pending.worktreeId,
1786
+ cwd: input.pending.cwd,
1787
+ createdSession: true
1788
+ });
1276
1789
  input.ensureSessionWorkspace(input.pending.workspaceId, input.pending.sessionId);
1277
1790
  input.openSession(input.pending.sessionId);
1278
1791
  return input.pending.sessionId;
@@ -1296,6 +1809,16 @@ window.__ModuleLoader__.load({
1296
1809
  bindings
1297
1810
  });
1298
1811
  if (action.kind === "open-bound") {
1812
+ await ensureWorktreeSessionPermission({
1813
+ permission: options.permission,
1814
+ confirmFullAccess: options.confirmFullAccess,
1815
+ onPermissionResult: options.onPermissionResult,
1816
+ sessionId: action.sessionId,
1817
+ workspaceId: input.workspaceId,
1818
+ worktreeId: input.worktreeId,
1819
+ cwd: input.cwd,
1820
+ createdSession: false
1821
+ });
1299
1822
  options.ensureSessionWorkspace(input.workspaceId, action.sessionId);
1300
1823
  if (!disposed) options.openSession(action.sessionId);
1301
1824
  return action.sessionId;
@@ -1310,6 +1833,16 @@ window.__ModuleLoader__.load({
1310
1833
  } catch (error) {
1311
1834
  throw new WorktreeSessionBindingError(action.sessionId, error);
1312
1835
  }
1836
+ await ensureWorktreeSessionPermission({
1837
+ permission: options.permission,
1838
+ confirmFullAccess: options.confirmFullAccess,
1839
+ onPermissionResult: options.onPermissionResult,
1840
+ sessionId: action.sessionId,
1841
+ workspaceId: input.workspaceId,
1842
+ worktreeId: input.worktreeId,
1843
+ cwd: input.cwd,
1844
+ createdSession: false
1845
+ });
1313
1846
  if (!disposed) {
1314
1847
  options.ensureSessionWorkspace(input.workspaceId, action.sessionId);
1315
1848
  if (!disposed) options.openSession(action.sessionId);
@@ -1322,9 +1855,18 @@ window.__ModuleLoader__.load({
1322
1855
  ...input,
1323
1856
  createSession: options.createSession,
1324
1857
  manager: options.manager,
1325
- beforeOpen: (sessionId) => {
1858
+ beforeOpen: async (sessionId) => {
1326
1859
  if (disposed) throw new WorktreeSessionActionError("CLIENT_DISPOSED", "", false);
1327
- options.ensureSessionWorkspace(input.workspaceId, sessionId);
1860
+ await ensureWorktreeSessionPermission({
1861
+ permission: options.permission,
1862
+ confirmFullAccess: options.confirmFullAccess,
1863
+ onPermissionResult: options.onPermissionResult,
1864
+ sessionId,
1865
+ workspaceId: input.workspaceId,
1866
+ worktreeId: input.worktreeId,
1867
+ cwd: input.cwd,
1868
+ createdSession: true
1869
+ });
1328
1870
  },
1329
1871
  openSession: (sessionId) => {
1330
1872
  if (!disposed) options.openSession(sessionId);
@@ -1734,6 +2276,13 @@ window.__ModuleLoader__.load({
1734
2276
  "session-group:" + location.groupKey
1735
2277
  ];
1736
2278
  }
2279
+ function shouldRevealCurrentSessionGroup(sessionIds, currentSessionId) {
2280
+ if (currentSessionId === void 0 || sessionIds.length <= 5) return false;
2281
+ return sessionIds.indexOf(currentSessionId) >= 5;
2282
+ }
2283
+ function isSessionGroupAutoExpanded(sessionIds, currentSessionId, currentSessionRevealActive) {
2284
+ return currentSessionRevealActive && shouldRevealCurrentSessionGroup(sessionIds, currentSessionId);
2285
+ }
1737
2286
  //#endregion
1738
2287
  //#region lib/client/worktree-session-position.js
1739
2288
  function scrollCurrentSessionIntoView(root, sessionId) {
@@ -1749,8 +2298,65 @@ window.__ModuleLoader__.load({
1749
2298
  const rect = event.currentTarget.getBoundingClientRect();
1750
2299
  return event.clientY < rect.top + rect.height / 2 ? "before" : "after";
1751
2300
  }
2301
+ function sessionStatusLabel(t, status) {
2302
+ switch (status.labelKey) {
2303
+ case "running": return t("session.status.running");
2304
+ case "subagentsRunning": return t(status.runningSubagentCount === 1 ? "session.status.subagentsRunning.one" : "session.status.subagentsRunning.other", { n: status.runningSubagentCount });
2305
+ case "idle": return t("session.status.idle");
2306
+ case "waitingApproval": return t("session.status.waitingApproval");
2307
+ case "planReview": return t("session.status.planReview");
2308
+ case "waitingAnswer": return t("session.status.waitingAnswer");
2309
+ case "completed": return t("session.status.completed");
2310
+ }
2311
+ }
2312
+ function sessionTimeLabel(t, value) {
2313
+ switch (value.unit) {
2314
+ case "now": return t("session.time.now");
2315
+ case "minutes": return t("session.time.minutes", { n: value.n });
2316
+ case "hours": return t("session.time.hours", { n: value.n });
2317
+ case "days": return t("session.time.days", { n: value.n });
2318
+ case "months": return t("session.time.months", { n: value.n });
2319
+ case "years": return t("session.time.years", { n: value.n });
2320
+ }
2321
+ }
2322
+ /** Native Workspace hover-card time copy, including the trailing "ago" marker. */
2323
+ function sessionHoverTimeLabel(t, value) {
2324
+ const label = sessionTimeLabel(t, value);
2325
+ return value.unit === "now" ? label : t("session.time.ago", { t: label });
2326
+ }
2327
+ /** Native Workspace hover-card status list, including a secondary subagent status when relevant. */
2328
+ function sessionHoverStatuses(presentation) {
2329
+ if (presentation.runningSubagentCount === 0 || presentation.status.labelKey === "subagentsRunning") return [presentation.status];
2330
+ return [presentation.status, {
2331
+ state: "ongoing",
2332
+ labelKey: "subagentsRunning",
2333
+ runningSubagentCount: presentation.runningSubagentCount
2334
+ }];
2335
+ }
2336
+ /** Native Workspace Session hover-card details, adapted to the Worktree row projection. */
2337
+ function WorktreeSessionHoverContent({ label, blank, presentation, t }) {
2338
+ const statuses = presentation === void 0 ? [] : sessionHoverStatuses(presentation);
2339
+ const timeValue = !blank && presentation?.updatedAt !== void 0 ? relativeTime(presentation.updatedAt, Date.now()) : void 0;
2340
+ return (0, react_jsx_runtime.jsxs)("div", {
2341
+ className: worktree_css_default.sessionHoverContent,
2342
+ children: [
2343
+ (0, react_jsx_runtime.jsx)("div", {
2344
+ className: worktree_css_default.sessionHoverTitle,
2345
+ children: label
2346
+ }),
2347
+ !blank && timeValue !== void 0 && (0, react_jsx_runtime.jsx)("div", {
2348
+ className: worktree_css_default.sessionHoverTime,
2349
+ children: sessionHoverTimeLabel(t, timeValue)
2350
+ }),
2351
+ statuses.map((status) => (0, react_jsx_runtime.jsxs)("div", {
2352
+ className: worktree_css_default.sessionHoverStatus,
2353
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: status.state }), (0, react_jsx_runtime.jsx)("span", { children: sessionStatusLabel(t, status) })]
2354
+ }, `${status.labelKey}-${status.runningSubagentCount}`))
2355
+ ]
2356
+ });
2357
+ }
1752
2358
  /** Workspace row using the native DSH menu, fixed action column, and drag contract. */
1753
- function WorktreeWorkspaceRow({ t, workspace, expanded, actionPending, menuOpen, drag, onToggle, onCreateWorktree, onRename, onDelete, onMenuOpenChange }) {
2359
+ function WorktreeWorkspaceRow({ t, workspace, expanded, hasOngoingSession, actionPending, menuOpen, drag, onToggle, onCreateWorktree, onRename, onDelete, onMenuOpenChange }) {
1754
2360
  const workspaceMenuItems = [{
1755
2361
  id: "rename",
1756
2362
  label: t("workspace.rename"),
@@ -1767,6 +2373,7 @@ window.__ModuleLoader__.load({
1767
2373
  return (0, react_jsx_runtime.jsxs)("div", {
1768
2374
  className: `${worktree_css_default.workspaceRow} ${markerClass}`,
1769
2375
  "data-workspace-drag": drag.active ? "active" : void 0,
2376
+ "data-group-activity": hasOngoingSession && !expanded ? "true" : void 0,
1770
2377
  "data-menu-open": menuOpen || void 0,
1771
2378
  draggable: true,
1772
2379
  onClick: () => {
@@ -1812,50 +2419,61 @@ window.__ModuleLoader__.load({
1812
2419
  }),
1813
2420
  (0, react_jsx_runtime.jsxs)("span", {
1814
2421
  className: `${worktree_css_default.treeActionSlot} ${worktree_css_default.workspaceActions}`,
1815
- children: [(0, react_jsx_runtime.jsx)("span", {
1816
- className: worktree_css_default.menuAction,
1817
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
1818
- open: menuOpen,
1819
- onClose: () => {
1820
- onMenuOpenChange(false);
1821
- },
1822
- items: workspaceMenuItems,
1823
- onSelect: (id) => {
1824
- onMenuOpenChange(false);
1825
- if (id === "rename") onRename();
1826
- if (id === "delete") onDelete();
1827
- },
1828
- portal: true,
1829
- closeOnPointerLeave: true,
1830
- anchor: (0, react_jsx_runtime.jsx)("button", {
1831
- type: "button",
1832
- className: worktree_css_default.iconButton,
1833
- "data-workspace-menu": true,
1834
- "aria-label": t("workspace.options", { name: workspace.title }),
1835
- onClick: (event) => {
1836
- event.stopPropagation();
1837
- onMenuOpenChange(!menuOpen);
2422
+ children: [
2423
+ (0, react_jsx_runtime.jsx)("span", {
2424
+ className: worktree_css_default.groupActivity,
2425
+ "data-group-activity": hasOngoingSession && !expanded ? "true" : void 0,
2426
+ role: hasOngoingSession && !expanded ? "img" : void 0,
2427
+ "aria-label": hasOngoingSession && !expanded ? t("session.status.running") : void 0,
2428
+ title: hasOngoingSession && !expanded ? t("session.status.running") : void 0,
2429
+ children: hasOngoingSession && !expanded && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "ongoing" })
2430
+ }),
2431
+ (0, react_jsx_runtime.jsx)("span", {
2432
+ className: worktree_css_default.menuAction,
2433
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
2434
+ open: menuOpen,
2435
+ onClose: () => {
2436
+ onMenuOpenChange(false);
1838
2437
  },
1839
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, {})
2438
+ items: workspaceMenuItems,
2439
+ onSelect: (id) => {
2440
+ onMenuOpenChange(false);
2441
+ if (id === "rename") onRename();
2442
+ if (id === "delete") onDelete();
2443
+ },
2444
+ portal: true,
2445
+ closeOnPointerLeave: true,
2446
+ anchor: (0, react_jsx_runtime.jsx)("button", {
2447
+ type: "button",
2448
+ className: worktree_css_default.iconButton,
2449
+ "data-workspace-menu": true,
2450
+ "aria-label": t("workspace.options", { name: workspace.title }),
2451
+ onClick: (event) => {
2452
+ event.stopPropagation();
2453
+ onMenuOpenChange(!menuOpen);
2454
+ },
2455
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, {})
2456
+ })
1840
2457
  })
2458
+ }),
2459
+ (0, react_jsx_runtime.jsx)("button", {
2460
+ type: "button",
2461
+ className: worktree_css_default.iconButton,
2462
+ "data-add-worktree": true,
2463
+ "aria-label": t("workspace.addWorktree", { name: workspace.title }),
2464
+ onClick: (event) => {
2465
+ event.stopPropagation();
2466
+ onCreateWorktree();
2467
+ },
2468
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {})
1841
2469
  })
1842
- }), (0, react_jsx_runtime.jsx)("button", {
1843
- type: "button",
1844
- className: worktree_css_default.iconButton,
1845
- "data-add-worktree": true,
1846
- "aria-label": t("workspace.addWorktree", { name: workspace.title }),
1847
- onClick: (event) => {
1848
- event.stopPropagation();
1849
- onCreateWorktree();
1850
- },
1851
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {})
1852
- })]
2470
+ ]
1853
2471
  })
1854
2472
  ]
1855
2473
  });
1856
2474
  }
1857
2475
  /** Shared Main/Worktree group row with parameterized icon, state, and actions. */
1858
- function WorktreeGroupRow({ t, kind, label, worktreeId, expanded, icon, workspaceTitle, state, stateLabel, onToggle, onCreateSession, menu, drag }) {
2476
+ function WorktreeGroupRow({ t, kind, label, worktreeId, expanded, hasOngoingSession, icon, workspaceTitle, state, stateLabel, onToggle, onCreateSession, menu, drag }) {
1859
2477
  const main = kind === "main";
1860
2478
  const markerClass = drag?.marker === "before" ? worktree_css_default.dropBefore : drag?.marker === "after" ? worktree_css_default.dropAfter : "";
1861
2479
  const dragProps = drag === void 0 ? {} : {
@@ -1882,6 +2500,7 @@ window.__ModuleLoader__.load({
1882
2500
  className: `${worktree_css_default.worktreeRow} ${markerClass}`,
1883
2501
  "data-main-group": main ? "true" : void 0,
1884
2502
  "data-main-expanded": main ? String(expanded) : void 0,
2503
+ "data-group-activity": hasOngoingSession && !expanded ? "true" : void 0,
1885
2504
  "data-menu-open": menu?.open ? "true" : void 0,
1886
2505
  "data-worktree-drag": drag?.active ? "active" : void 0,
1887
2506
  ...dragProps,
@@ -1916,50 +2535,77 @@ window.__ModuleLoader__.load({
1916
2535
  }),
1917
2536
  (0, react_jsx_runtime.jsxs)("span", {
1918
2537
  className: worktree_css_default.treeActionSlot,
1919
- children: [menu !== void 0 && (0, react_jsx_runtime.jsx)("span", {
1920
- className: worktree_css_default.menuAction,
1921
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
1922
- open: menu.open,
1923
- onClose: () => {
1924
- menu.onOpenChange(false);
1925
- },
1926
- items: [{
1927
- id: "remove",
1928
- label: t("worktree.remove"),
1929
- icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}),
1930
- danger: true,
1931
- disabled: menu.disabled
1932
- }],
1933
- onSelect: (id) => {
1934
- menu.onOpenChange(false);
1935
- if (id === "remove") menu.onRemove();
1936
- },
1937
- portal: true,
1938
- closeOnPointerLeave: true,
1939
- anchor: (0, react_jsx_runtime.jsx)("button", {
1940
- type: "button",
1941
- className: worktree_css_default.iconButton,
1942
- "data-worktree-menu": true,
1943
- "aria-label": t("worktree.options", { name: menu.label }),
1944
- onClick: (event) => {
1945
- event.stopPropagation();
1946
- menu.onOpenChange(!menu.open);
2538
+ children: [
2539
+ (0, react_jsx_runtime.jsx)("span", {
2540
+ className: worktree_css_default.groupActivity,
2541
+ "data-group-activity": hasOngoingSession && !expanded ? "true" : void 0,
2542
+ role: hasOngoingSession && !expanded ? "img" : void 0,
2543
+ "aria-label": hasOngoingSession && !expanded ? t("session.status.running") : void 0,
2544
+ title: hasOngoingSession && !expanded ? t("session.status.running") : void 0,
2545
+ children: hasOngoingSession && !expanded && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "ongoing" })
2546
+ }),
2547
+ menu !== void 0 && (0, react_jsx_runtime.jsx)("span", {
2548
+ className: worktree_css_default.menuAction,
2549
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
2550
+ open: menu.open,
2551
+ onClose: () => {
2552
+ menu.onOpenChange(false);
1947
2553
  },
1948
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, {})
2554
+ items: [
2555
+ ...menu.showCreate ? [{
2556
+ id: "create",
2557
+ label: t("worktree.createNew"),
2558
+ icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {}),
2559
+ disabled: menu.disabled || menu.onCreateWorktree === void 0
2560
+ }] : [],
2561
+ {
2562
+ id: "copy-path",
2563
+ label: t("worktree.copyPath"),
2564
+ icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutline16, {}),
2565
+ disabled: menu.disabled
2566
+ },
2567
+ ...menu.showRemove ? [{
2568
+ id: "remove",
2569
+ label: t("worktree.remove"),
2570
+ icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}),
2571
+ danger: true,
2572
+ disabled: menu.disabled || menu.onRemove === void 0
2573
+ }] : []
2574
+ ],
2575
+ onSelect: (id) => {
2576
+ menu.onOpenChange(false);
2577
+ if (id === "create" && menu.showCreate) menu.onCreateWorktree?.();
2578
+ if (id === "copy-path") (0, _deepseek_ai_dsh_client_ui_primitives.writeClipboard)(menu.copyPath);
2579
+ if (id === "remove" && menu.showRemove) menu.onRemove?.();
2580
+ },
2581
+ portal: true,
2582
+ closeOnPointerLeave: true,
2583
+ anchor: (0, react_jsx_runtime.jsx)("button", {
2584
+ type: "button",
2585
+ className: worktree_css_default.iconButton,
2586
+ "data-worktree-menu": true,
2587
+ "aria-label": t("worktree.options", { name: menu.label }),
2588
+ onClick: (event) => {
2589
+ event.stopPropagation();
2590
+ menu.onOpenChange(!menu.open);
2591
+ },
2592
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, {})
2593
+ })
1949
2594
  })
2595
+ }),
2596
+ onCreateSession !== void 0 && (0, react_jsx_runtime.jsx)("button", {
2597
+ type: "button",
2598
+ className: worktree_css_default.iconButton,
2599
+ "data-add-main-session": main ? "true" : void 0,
2600
+ "data-add-session": main ? void 0 : "true",
2601
+ "aria-label": t("worktree.addSession", { name: workspaceTitle }),
2602
+ onClick: (event) => {
2603
+ event.stopPropagation();
2604
+ onCreateSession();
2605
+ },
2606
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {})
1950
2607
  })
1951
- }), onCreateSession !== void 0 && (0, react_jsx_runtime.jsx)("button", {
1952
- type: "button",
1953
- className: worktree_css_default.iconButton,
1954
- "data-add-main-session": main ? "true" : void 0,
1955
- "data-add-session": main ? void 0 : "true",
1956
- "aria-label": t("worktree.addSession", { name: workspaceTitle }),
1957
- onClick: (event) => {
1958
- event.stopPropagation();
1959
- onCreateSession();
1960
- },
1961
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {})
1962
- })]
2608
+ ]
1963
2609
  })
1964
2610
  ]
1965
2611
  });
@@ -1975,7 +2621,7 @@ window.__ModuleLoader__.load({
1975
2621
  });
1976
2622
  }
1977
2623
  /** Worktree-mode Session row with the same trailing options menu as native DSH rows. */
1978
- function WorktreeSessionRow({ t, sessionId, blank, current, label, drag, actionPending, onOpen, onRename, onFork, onArchive }) {
2624
+ function WorktreeSessionRow({ t, sessionId, blank, current, label, drag, actionPending, onOpen, onRename, onFork, onArchive, presentation }) {
1979
2625
  const [menuOpen, setMenuOpen] = (0, react.useState)(false);
1980
2626
  const markerClass = drag.marker === "before" ? worktree_css_default.dropBefore : drag.marker === "after" ? worktree_css_default.dropAfter : "";
1981
2627
  const sessionMenuItems = [
@@ -1996,75 +2642,115 @@ window.__ModuleLoader__.load({
1996
2642
  label: t("session.archive"),
1997
2643
  icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconArchiveOutline20, { size: 16 }),
1998
2644
  disabled: onArchive === void 0 || actionPending
2645
+ },
2646
+ {
2647
+ id: "copy-session-id",
2648
+ label: t("session.copyId"),
2649
+ icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutline16, {})
1999
2650
  }
2000
2651
  ];
2001
- return (0, react_jsx_runtime.jsxs)("div", {
2002
- className: `${worktree_css_default.treeSessionRow} ${markerClass}`,
2003
- "data-session-id": sessionId,
2004
- "data-session-blank": blank ? "true" : void 0,
2005
- "data-session-current": current ? "true" : void 0,
2006
- "data-session-drag": drag.active ? "active" : void 0,
2007
- "data-menu-open": menuOpen || void 0,
2008
- role: "treeitem",
2009
- "aria-current": current ? "true" : void 0,
2010
- draggable: true,
2011
- onDragStart: (event) => {
2012
- event.dataTransfer.effectAllowed = "move";
2013
- event.dataTransfer.setData("text/plain", sessionId);
2014
- drag.start();
2015
- },
2016
- onDragEnd: drag.end,
2017
- onDragOver: (event) => {
2018
- if (!drag.active) return;
2019
- event.preventDefault();
2020
- event.dataTransfer.dropEffect = "move";
2021
- drag.hover(rowHalf(event));
2022
- },
2023
- onDrop: (event) => {
2024
- if (!drag.active) return;
2025
- event.preventDefault();
2026
- drag.drop(rowHalf(event));
2027
- },
2028
- children: [(0, react_jsx_runtime.jsx)("button", {
2029
- type: "button",
2030
- className: worktree_css_default.treeSessionContent,
2031
- onClick: onOpen,
2032
- children: (0, react_jsx_runtime.jsx)("span", {
2033
- className: worktree_css_default.sessionLabel,
2034
- children: label
2035
- })
2036
- }), !blank && (0, react_jsx_runtime.jsx)("span", {
2037
- className: worktree_css_default.rowActions,
2038
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
2039
- open: menuOpen,
2040
- onClose: () => {
2041
- setMenuOpen(false);
2042
- },
2043
- items: sessionMenuItems,
2044
- onSelect: (id) => {
2045
- setMenuOpen(false);
2046
- if (id === "rename") onRename?.(sessionId, label);
2047
- if (id === "fork") onFork?.(sessionId);
2048
- if (id === "archive") onArchive?.(sessionId);
2049
- },
2050
- portal: true,
2051
- closeOnPointerLeave: true,
2052
- anchor: (0, react_jsx_runtime.jsx)("button", {
2053
- type: "button",
2054
- className: worktree_css_default.iconButton,
2055
- "data-session-menu": true,
2056
- "aria-label": t("session.options", { name: label }),
2057
- onClick: (event) => {
2058
- event.stopPropagation();
2059
- setMenuOpen((current) => !current);
2060
- },
2061
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, {})
2652
+ const statusLabel = presentation === void 0 ? void 0 : sessionStatusLabel(t, presentation.status);
2653
+ const showTrailingStatus = !blank && presentation !== void 0 && (presentation.status.state !== "done" || presentation.completed);
2654
+ const timeValue = !blank && !showTrailingStatus && presentation?.updatedAt !== void 0 ? relativeTime(presentation.updatedAt, Date.now()) : void 0;
2655
+ const timeLabel = timeValue === void 0 ? void 0 : sessionTimeLabel(t, timeValue);
2656
+ return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.HoverCard, {
2657
+ anchor: (0, react_jsx_runtime.jsxs)("div", {
2658
+ className: `${worktree_css_default.treeSessionRow} ${markerClass}`,
2659
+ "data-session-id": sessionId,
2660
+ "data-session-blank": blank ? "true" : void 0,
2661
+ "data-session-current": current ? "true" : void 0,
2662
+ "data-session-drag": drag.active ? "active" : void 0,
2663
+ "data-menu-open": menuOpen || void 0,
2664
+ role: "treeitem",
2665
+ "aria-current": current ? "true" : void 0,
2666
+ draggable: true,
2667
+ onDragStart: (event) => {
2668
+ event.dataTransfer.effectAllowed = "move";
2669
+ event.dataTransfer.setData("text/plain", sessionId);
2670
+ drag.start();
2671
+ },
2672
+ onDragEnd: drag.end,
2673
+ onDragOver: (event) => {
2674
+ if (!drag.active) return;
2675
+ event.preventDefault();
2676
+ event.dataTransfer.dropEffect = "move";
2677
+ drag.hover(rowHalf(event));
2678
+ },
2679
+ onDrop: (event) => {
2680
+ if (!drag.active) return;
2681
+ event.preventDefault();
2682
+ drag.drop(rowHalf(event));
2683
+ },
2684
+ children: [(0, react_jsx_runtime.jsx)("button", {
2685
+ type: "button",
2686
+ className: worktree_css_default.treeSessionContent,
2687
+ onClick: onOpen,
2688
+ children: (0, react_jsx_runtime.jsx)("span", {
2689
+ className: worktree_css_default.sessionLabel,
2690
+ children: label
2062
2691
  })
2063
- })
2064
- })]
2692
+ }), !blank && (0, react_jsx_runtime.jsxs)("span", {
2693
+ className: worktree_css_default.sessionTrailing,
2694
+ children: [(0, react_jsx_runtime.jsx)("span", {
2695
+ className: worktree_css_default.sessionMeta,
2696
+ children: showTrailingStatus && presentation !== void 0 ? (0, react_jsx_runtime.jsx)("span", {
2697
+ className: worktree_css_default.sessionStatus,
2698
+ "data-session-status": statusLabel,
2699
+ role: "img",
2700
+ "aria-label": statusLabel,
2701
+ title: statusLabel,
2702
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: presentation.status.state })
2703
+ }) : timeLabel !== void 0 ? (0, react_jsx_runtime.jsx)("span", {
2704
+ className: worktree_css_default.sessionTime,
2705
+ "data-session-time": true,
2706
+ children: timeLabel
2707
+ }) : null
2708
+ }), (0, react_jsx_runtime.jsx)("span", {
2709
+ className: worktree_css_default.rowActions,
2710
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
2711
+ open: menuOpen,
2712
+ onClose: () => {
2713
+ setMenuOpen(false);
2714
+ },
2715
+ items: sessionMenuItems,
2716
+ onSelect: (id) => {
2717
+ setMenuOpen(false);
2718
+ if (id === "rename") onRename?.(sessionId, label);
2719
+ if (id === "fork") onFork?.(sessionId);
2720
+ if (id === "archive") onArchive?.(sessionId);
2721
+ if (id === "copy-session-id") (0, _deepseek_ai_dsh_client_ui_primitives.writeClipboard)(sessionId);
2722
+ },
2723
+ portal: true,
2724
+ closeOnPointerLeave: true,
2725
+ anchor: (0, react_jsx_runtime.jsx)("button", {
2726
+ type: "button",
2727
+ className: worktree_css_default.iconButton,
2728
+ "data-session-menu": true,
2729
+ "aria-label": t("session.options", { name: label }),
2730
+ onClick: (event) => {
2731
+ event.stopPropagation();
2732
+ setMenuOpen((current) => !current);
2733
+ },
2734
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, {})
2735
+ })
2736
+ })
2737
+ })]
2738
+ })]
2739
+ }),
2740
+ content: (0, react_jsx_runtime.jsx)(WorktreeSessionHoverContent, {
2741
+ label,
2742
+ blank,
2743
+ presentation,
2744
+ t
2745
+ }),
2746
+ openDelayMs: 500,
2747
+ disabled: menuOpen || drag.active,
2748
+ copyText: blank ? void 0 : label,
2749
+ copyLabel: t("copy"),
2750
+ copiedLabel: t("hover.copied")
2065
2751
  });
2066
2752
  }
2067
- function WorktreeSessionGroup({ t, groupKey, sessionIds, workspaceId, currentSessionId, expanded, actionPending, sessions, dragState, onToggleExpanded, onStartDrag, onHoverDrag, onClearDrag, onFinishDrag, onCommitDrag, onOpen, onRename, onFork, onArchive }) {
2753
+ function WorktreeSessionGroup({ t, groupKey, sessionIds, workspaceId, currentSessionId, expanded, actionPending, sessions, sessionPresentations, dragState, onToggleExpanded, onStartDrag, onHoverDrag, onClearDrag, onFinishDrag, onCommitDrag, onOpen, onRename, onFork, onArchive }) {
2068
2754
  const visibleSessionIds = expanded ? sessionIds : sessionIds.slice(0, 5);
2069
2755
  const sameGroupDrag = dragState?.groupKey === groupKey;
2070
2756
  return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [visibleSessionIds.map((sessionId) => (0, react_jsx_runtime.jsx)(WorktreeSessionRow, {
@@ -2073,6 +2759,7 @@ window.__ModuleLoader__.load({
2073
2759
  blank: isBlankSession(sessionId, sessions),
2074
2760
  current: sessionId === currentSessionId,
2075
2761
  label: sessionLabel(sessionId, sessions, t),
2762
+ presentation: sessionPresentations[sessionId],
2076
2763
  drag: {
2077
2764
  active: sameGroupDrag,
2078
2765
  marker: sameGroupDrag && dragState.over?.sessionId === sessionId ? dragState.over.half : null,
@@ -2124,6 +2811,33 @@ window.__ModuleLoader__.load({
2124
2811
  status: "idle",
2125
2812
  views: []
2126
2813
  };
2814
+ const EMPTY_PERMISSION_NOTICE = void 0;
2815
+ const EMPTY_PERMISSION_NOTICE_SUBSCRIBE = () => () => {};
2816
+ const EMPTY_PERMISSION_NOTICE_SNAPSHOT = () => EMPTY_PERMISSION_NOTICE;
2817
+ const EMPTY_FULL_ACCESS_CONFIRMATION_SUBSCRIBE = () => () => {};
2818
+ const EMPTY_FULL_ACCESS_CONFIRMATION_SNAPSHOT = () => void 0;
2819
+ const EMPTY_FORK_RECOVERY_SNAPSHOT = {
2820
+ revision: 0,
2821
+ pending: []
2822
+ };
2823
+ const EMPTY_FORK_RECOVERY_STORE = {
2824
+ getSnapshot: () => EMPTY_FORK_RECOVERY_SNAPSHOT,
2825
+ subscribe: () => () => {}
2826
+ };
2827
+ function updatedAtById(sessionIds, sessions) {
2828
+ return Object.fromEntries(sessionIds.map((sessionId) => [sessionId, sessions.byId[sessionId]?.updatedAt]));
2829
+ }
2830
+ function forkRecoveryError(recovery) {
2831
+ const error = toNativeWorktreeViewError(recovery.error);
2832
+ return {
2833
+ ...error,
2834
+ code: error.code === "WORKTREE_VIEW_FAILED" ? "SESSION_BINDING_FAILED" : error.code,
2835
+ details: {
2836
+ ...error.details ?? {},
2837
+ sessionId: recovery.childSessionId
2838
+ }
2839
+ };
2840
+ }
2127
2841
  function useStableWorkspaceIds(workspaces) {
2128
2842
  const next = workspaces.map((workspace) => workspace.workspaceId);
2129
2843
  const previousRef = (0, react.useRef)([]);
@@ -2135,14 +2849,30 @@ window.__ModuleLoader__.load({
2135
2849
  * Peer Worktree navigation surface. It keeps the original DSH Workspace mode
2136
2850
  * intact and renders a browser-local Workspace → Worktree → Session projection.
2137
2851
  */
2138
- function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces, t, available, expandState, manager, invalidateWorktreeContext, createWorkspace, createSessionForWorktree: createSessionCallback, createMainSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, insertSessionBefore, insertWorktreeBefore, renameSession, forkSession, archiveSession, ensureSessionWorkspace, syncSessionWorkspaces, openSession }) {
2852
+ function WorktreeSurface({ useStore, actions, useSessions, useWorkspaces, t, available, expandState, sessionOrder, manager, permission, confirmFullAccess, fullAccessConfirmation, onPermissionResult, onPermissionNotice, permissionNotice, invalidateWorktreeContext, createWorkspace, createSessionForWorktree: createSessionCallback, createMainSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, insertSessionBefore, insertWorktreeBefore, renameSession, forkSession, archiveSession, forkRecovery, retryForkSession, ensureSessionWorkspace, syncSessionWorkspaces, openSession }) {
2139
2853
  const mode = effectiveViewMode(useStore((state) => state.viewMode), available && manager !== void 0);
2140
2854
  const sessions = useSessions((state) => state);
2141
2855
  const workspaces = useWorkspaces((state) => state);
2856
+ const forkRecoveryStore = forkRecovery ?? EMPTY_FORK_RECOVERY_STORE;
2857
+ const forkRecoverySnapshot = (0, react.useSyncExternalStore)(forkRecoveryStore.subscribe, forkRecoveryStore.getSnapshot, forkRecoveryStore.getSnapshot);
2142
2858
  const currentSessionId = sessions.current;
2143
2859
  const workspaceIds = useStableWorkspaceIds(workspaces.items);
2144
2860
  const [readState, setReadState] = (0, react.useState)(EMPTY_READ_STATE);
2145
2861
  const expandSnapshot = (0, react.useSyncExternalStore)(expandState.subscribe, expandState.getSnapshot, expandState.getSnapshot);
2862
+ const permissionNoticeSnapshot = (0, react.useSyncExternalStore)(permissionNotice?.subscribe ?? EMPTY_PERMISSION_NOTICE_SUBSCRIBE, permissionNotice?.getSnapshot ?? EMPTY_PERMISSION_NOTICE_SNAPSHOT, permissionNotice?.getSnapshot ?? EMPTY_PERMISSION_NOTICE_SNAPSHOT);
2863
+ const fullAccessConfirmationSnapshot = (0, react.useSyncExternalStore)(fullAccessConfirmation?.subscribe ?? EMPTY_FULL_ACCESS_CONFIRMATION_SUBSCRIBE, fullAccessConfirmation?.getSnapshot ?? EMPTY_FULL_ACCESS_CONFIRMATION_SNAPSHOT, fullAccessConfirmation?.getSnapshot ?? EMPTY_FULL_ACCESS_CONFIRMATION_SNAPSHOT);
2864
+ const fullAccessConfirmationKey = fullAccessConfirmationSnapshot === void 0 ? "" : [
2865
+ fullAccessConfirmationSnapshot.workspaceId,
2866
+ fullAccessConfirmationSnapshot.worktreeId,
2867
+ fullAccessConfirmationSnapshot.sessionId,
2868
+ fullAccessConfirmationSnapshot.cwd
2869
+ ].join("\0");
2870
+ const [fullAccessAcknowledged, setFullAccessAcknowledged] = (0, react.useState)(false);
2871
+ (0, react.useEffect)(() => {
2872
+ setFullAccessAcknowledged(false);
2873
+ }, [fullAccessConfirmationKey]);
2874
+ const sessionOrderSnapshot = (0, react.useSyncExternalStore)(sessionOrder.subscribe, sessionOrder.getSnapshot, sessionOrder.getSnapshot);
2875
+ const sessionPresentations = (0, react.useMemo)(() => deriveSessionPresentationIndex(sessions), [sessions.byId]);
2146
2876
  const readStateRef = (0, react.useRef)(readState);
2147
2877
  readStateRef.current = readState;
2148
2878
  const [searchQuery, setSearchQuery] = (0, react.useState)("");
@@ -2161,6 +2891,7 @@ window.__ModuleLoader__.load({
2161
2891
  });
2162
2892
  const [selectedImportPath, setSelectedImportPath] = (0, react.useState)();
2163
2893
  const [openWorkspaceMenuId, setOpenWorkspaceMenuId] = (0, react.useState)();
2894
+ const [openMainMenuId, setOpenMainMenuId] = (0, react.useState)();
2164
2895
  const [openWorktreeMenuId, setOpenWorktreeMenuId] = (0, react.useState)();
2165
2896
  const [worktreeRemoval, setWorktreeRemoval] = (0, react.useState)();
2166
2897
  const [selectedBranch, setSelectedBranch] = (0, react.useState)("");
@@ -2168,6 +2899,7 @@ window.__ModuleLoader__.load({
2168
2899
  const [pendingSessionBinding, setPendingSessionBinding] = (0, react.useState)();
2169
2900
  const [actionError, setActionError] = (0, react.useState)();
2170
2901
  const [actionPending, setActionPending] = (0, react.useState)(false);
2902
+ const [forkRetryKey, setForkRetryKey] = (0, react.useState)();
2171
2903
  const [sessionRenameTarget, setSessionRenameTarget] = (0, react.useState)();
2172
2904
  const [sessionRenameDraft, setSessionRenameDraft] = (0, react.useState)("");
2173
2905
  const [sessionRenamePending, setSessionRenamePending] = (0, react.useState)(false);
@@ -2187,6 +2919,7 @@ window.__ModuleLoader__.load({
2187
2919
  const [worktreeDrag, setWorktreeDrag] = (0, react.useState)();
2188
2920
  const worktreeDropCommitted = (0, react.useRef)(false);
2189
2921
  const refreshGuard = (0, react.useRef)(createWorktreeRefreshGuard());
2922
+ const forkRecoveryRevisionRef = (0, react.useRef)(forkRecoverySnapshot.revision);
2190
2923
  const modalReadLoader = (0, react.useRef)(createWorktreeModalViewLoader());
2191
2924
  const modalReadViewRef = (0, react.useRef)();
2192
2925
  const importCandidatesGuard = (0, react.useRef)(createWorktreeRefreshGuard());
@@ -2265,6 +2998,20 @@ window.__ModuleLoader__.load({
2265
2998
  setReadState(EMPTY_READ_STATE);
2266
2999
  }
2267
3000
  }, [mode, refresh]);
3001
+ (0, react.useEffect)(() => {
3002
+ if (forkRecoveryRevisionRef.current === forkRecoverySnapshot.revision) return;
3003
+ forkRecoveryRevisionRef.current = forkRecoverySnapshot.revision;
3004
+ if (mode !== "worktree" || manager === void 0) return;
3005
+ refresh({
3006
+ preserveCurrent: readStateRef.current.status === "ready",
3007
+ invalidateContext: false
3008
+ }).catch(() => {});
3009
+ }, [
3010
+ forkRecoverySnapshot.revision,
3011
+ manager,
3012
+ mode,
3013
+ refresh
3014
+ ]);
2268
3015
  (0, react.useEffect)(() => {
2269
3016
  if (readState.status !== "ready" || syncSessionWorkspaces === void 0) return;
2270
3017
  syncSessionWorkspaces(readState.views.flatMap((view) => view.bindings.map((binding) => ({
@@ -2277,6 +3024,60 @@ window.__ModuleLoader__.load({
2277
3024
  syncSessionWorkspaces
2278
3025
  ]);
2279
3026
  const viewByWorkspace = (0, react.useMemo)(() => new Map(readState.views.map((view) => [view.workspaceId, view])), [readState.views]);
3027
+ const sessionOrderInputs = (0, react.useMemo)(() => {
3028
+ if (readState.status !== "ready") return [];
3029
+ const inputs = [];
3030
+ for (const workspace of workspaces.items) {
3031
+ const view = viewByWorkspace.get(workspace.workspaceId);
3032
+ const allWorkspaceSessionIds = filterArchivedSessionIds(workspaceSessionIds(workspaces, workspace.workspaceId, sessions.ids), archivedSessionIds);
3033
+ const bindings = view?.bindings ?? [];
3034
+ const mainSessionIds = filterVisibleSessionIds(unboundSessionIds(allWorkspaceSessionIds, [...new Set(bindings.map((binding) => binding.sessionId))]), sessions);
3035
+ inputs.push({
3036
+ accountKey: `main:${workspace.workspaceId}`,
3037
+ sessionIds: mainSessionIds,
3038
+ updatedAtById: updatedAtById(mainSessionIds, sessions)
3039
+ });
3040
+ for (const record of view?.worktrees ?? []) {
3041
+ const worktreeSessionIds = filterVisibleSessionIds(filterArchivedSessionIds(bindingIdsFor(bindings, record.worktreeId).filter((sessionId) => sessions.ids.includes(sessionId)), archivedSessionIds), sessions);
3042
+ inputs.push({
3043
+ accountKey: `worktree:${record.worktreeId}`,
3044
+ sessionIds: worktreeSessionIds,
3045
+ updatedAtById: updatedAtById(worktreeSessionIds, sessions)
3046
+ });
3047
+ }
3048
+ }
3049
+ return inputs;
3050
+ }, [
3051
+ archivedSessionIds,
3052
+ readState.status,
3053
+ sessions.byId,
3054
+ sessions.ids,
3055
+ viewByWorkspace,
3056
+ workspaces,
3057
+ workspaces.items
3058
+ ]);
3059
+ const orderedSessionIdsByAccount = (0, react.useMemo)(() => {
3060
+ const ordered = /* @__PURE__ */ new Map();
3061
+ for (const input of sessionOrderInputs) {
3062
+ const previous = sessionOrderSnapshot.accounts[input.accountKey];
3063
+ ordered.set(input.accountKey, nextSessionOrderAccount({
3064
+ baseIds: input.sessionIds,
3065
+ updatedAtById: input.updatedAtById,
3066
+ previous
3067
+ }).order);
3068
+ }
3069
+ return ordered;
3070
+ }, [sessionOrderInputs, sessionOrderSnapshot]);
3071
+ (0, react.useEffect)(() => {
3072
+ if (mode !== "worktree" || readState.status !== "ready") return;
3073
+ for (const input of sessionOrderInputs) sessionOrder.actions.reconcile(input.accountKey, input.sessionIds, input.updatedAtById);
3074
+ sessionOrder.actions.retain(sessionOrderInputs.map((input) => input.accountKey));
3075
+ }, [
3076
+ mode,
3077
+ readState.status,
3078
+ sessionOrder,
3079
+ sessionOrderInputs
3080
+ ]);
2280
3081
  const currentSessionLocation = (0, react.useMemo)(() => readState.status === "ready" ? resolveCurrentSessionLocation(currentSessionId, workspaces.items, readState.views) : void 0, [
2281
3082
  currentSessionId,
2282
3083
  readState.status,
@@ -2496,7 +3297,10 @@ window.__ModuleLoader__.load({
2496
3297
  return;
2497
3298
  }
2498
3299
  setActionError(void 0);
2499
- insertSessionBefore(workspaceId, activeDrag.sessionId, beforeSessionId).catch((error) => {
3300
+ const currentOrder = sessionOrderSnapshot.accounts[activeDrag.groupKey]?.order ?? sessionIds;
3301
+ insertSessionBefore(workspaceId, activeDrag.sessionId, beforeSessionId).then(() => {
3302
+ sessionOrder.actions.setOrder(activeDrag.groupKey, reorderSessionIds(currentOrder, activeDrag.sessionId, over.sessionId, over.half));
3303
+ }).catch((error) => {
2500
3304
  setActionError(toWorktreeViewError(error));
2501
3305
  });
2502
3306
  };
@@ -2617,8 +3421,7 @@ window.__ModuleLoader__.load({
2617
3421
  expandState.actions.toggleWorktree(worktreeId);
2618
3422
  if (visuallyExpanded) clearSessionGroups(["worktree:" + worktreeId]);
2619
3423
  };
2620
- const toggleSessionGroup = (groupKey) => {
2621
- const autoExpanded = isCurrentSessionReveal("session-group:" + groupKey);
3424
+ const toggleSessionGroup = (groupKey, autoExpanded) => {
2622
3425
  const transientExpanded = expandedSessionGroups[groupKey] === true;
2623
3426
  if (autoExpanded) {
2624
3427
  suppressCurrentSessionReveal("session-group:" + groupKey);
@@ -2643,7 +3446,7 @@ window.__ModuleLoader__.load({
2643
3446
  setModalReadError(void 0);
2644
3447
  setModalReadLoading(false);
2645
3448
  };
2646
- const openWorktreeCreator = (workspace) => {
3449
+ const openWorktreeCreator = (workspace, defaults = {}) => {
2647
3450
  const view = viewByWorkspace.get(workspace.workspaceId);
2648
3451
  modalReadLoader.current.invalidate();
2649
3452
  modalReadViewRef.current = void 0;
@@ -2659,13 +3462,13 @@ window.__ModuleLoader__.load({
2659
3462
  setModalReadError(void 0);
2660
3463
  setModalReadLoading(false);
2661
3464
  if (view === void 0) {
2662
- setSelectedBranch("");
2663
- setNewBranch("");
3465
+ setSelectedBranch(defaults.baseBranch ?? "");
3466
+ setNewBranch(defaults.newBranch ?? "");
2664
3467
  loadModalWorktreeView(workspace.workspaceId);
2665
3468
  return;
2666
3469
  }
2667
- setSelectedBranch(selectDefaultBaseBranch(view.branches));
2668
- setNewBranch(createDefaultWorktreeName([...view.branches.map((branch) => branch.name), ...view.worktrees.map((worktree) => worktree.branch)]));
3470
+ setSelectedBranch(defaults.baseBranch ?? selectDefaultBaseBranch(view.branches));
3471
+ setNewBranch(defaults.newBranch ?? createDefaultWorktreeName([...view.branches.map((branch) => branch.name), ...view.worktrees.map((worktree) => worktree.branch)]));
2669
3472
  };
2670
3473
  const changeWorktreeModalMode = (mode) => {
2671
3474
  setWorktreeModalMode(mode);
@@ -2695,6 +3498,11 @@ window.__ModuleLoader__.load({
2695
3498
  ...sessionInput,
2696
3499
  sessionId: error.sessionId
2697
3500
  });
3501
+ if (error instanceof WorktreeSessionPermissionError && error.retryable) setPendingSessionBinding({
3502
+ ...sessionInput,
3503
+ sessionId: error.sessionId,
3504
+ permissionRequired: true
3505
+ });
2698
3506
  throw error;
2699
3507
  }
2700
3508
  await refresh({ preserveCurrent: true });
@@ -2756,6 +3564,11 @@ window.__ModuleLoader__.load({
2756
3564
  ...input,
2757
3565
  sessionId: error.sessionId
2758
3566
  });
3567
+ if (error instanceof WorktreeSessionPermissionError && error.retryable) setPendingSessionBinding({
3568
+ ...input,
3569
+ sessionId: error.sessionId,
3570
+ permissionRequired: true
3571
+ });
2759
3572
  setActionError(toWorktreeViewError(error));
2760
3573
  } finally {
2761
3574
  setActionPending(false);
@@ -2774,6 +3587,9 @@ window.__ModuleLoader__.load({
2774
3587
  ensureSessionWorkspace: (workspaceId, sessionId) => {
2775
3588
  ensureSessionWorkspace?.(workspaceId, sessionId);
2776
3589
  },
3590
+ permission,
3591
+ confirmFullAccess,
3592
+ onPermissionResult,
2777
3593
  openSession
2778
3594
  });
2779
3595
  setPendingSessionBinding(void 0);
@@ -2786,6 +3602,17 @@ window.__ModuleLoader__.load({
2786
3602
  setActionPending(false);
2787
3603
  }
2788
3604
  };
3605
+ const retryForkBinding = async (key) => {
3606
+ if (retryForkSession === void 0 || forkRetryKey !== void 0) return;
3607
+ setForkRetryKey(key);
3608
+ try {
3609
+ await retryForkSession(key);
3610
+ } catch (error) {
3611
+ setActionError(toNativeWorktreeViewError(error));
3612
+ } finally {
3613
+ setForkRetryKey(void 0);
3614
+ }
3615
+ };
2789
3616
  const openWorkspaceSession = (workspaceId, sessionId) => {
2790
3617
  ensureSessionWorkspace?.(workspaceId, sessionId);
2791
3618
  openWorktreeSession({ open: openSession }, sessionId);
@@ -2851,7 +3678,7 @@ window.__ModuleLoader__.load({
2851
3678
  onClick: () => {
2852
3679
  if (createWorkspace !== void 0) runMutation(createWorkspace);
2853
3680
  },
2854
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {})
3681
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconProjectAddOutline16, {})
2855
3682
  })
2856
3683
  ]
2857
3684
  }),
@@ -2859,6 +3686,47 @@ window.__ModuleLoader__.load({
2859
3686
  className: worktree_css_default.content,
2860
3687
  tabIndex: 0,
2861
3688
  children: [
3689
+ permissionNoticeSnapshot !== void 0 && (0, react_jsx_runtime.jsx)("div", {
3690
+ className: worktree_css_default.notice,
3691
+ role: "status",
3692
+ "data-worktree-permission-notice": true,
3693
+ children: (0, react_jsx_runtime.jsx)("p", {
3694
+ className: worktree_css_default.message,
3695
+ children: formatWorktreePermissionNotice(permissionNoticeSnapshot.result, t)
3696
+ })
3697
+ }),
3698
+ forkRecoverySnapshot.pending.map((recovery) => {
3699
+ const recoveryViewError = forkRecoveryError(recovery);
3700
+ return (0, react_jsx_runtime.jsxs)("div", {
3701
+ className: worktree_css_default.error,
3702
+ role: "alert",
3703
+ "data-fork-recovery": recovery.key,
3704
+ children: [(0, react_jsx_runtime.jsx)("p", {
3705
+ className: worktree_css_default.message,
3706
+ "data-error": "true",
3707
+ children: formatWorktreeViewError(recoveryViewError, t)
3708
+ }), (0, react_jsx_runtime.jsxs)("div", {
3709
+ className: worktree_css_default.recoveryActions,
3710
+ children: [retryForkSession !== void 0 && recoveryViewError.retryable && (0, react_jsx_runtime.jsx)("button", {
3711
+ type: "button",
3712
+ className: worktree_css_default.actionButton,
3713
+ disabled: forkRetryKey !== void 0,
3714
+ onClick: () => {
3715
+ retryForkBinding(recovery.key);
3716
+ },
3717
+ children: t("action.retryBinding")
3718
+ }), (0, react_jsx_runtime.jsx)("button", {
3719
+ type: "button",
3720
+ className: worktree_css_default.actionButton,
3721
+ disabled: forkRetryKey !== void 0,
3722
+ onClick: () => {
3723
+ openSession(recovery.childSessionId);
3724
+ },
3725
+ children: t("action.openCreatedSession")
3726
+ })]
3727
+ })]
3728
+ }, recovery.key);
3729
+ }),
2862
3730
  actionError !== void 0 && (0, react_jsx_runtime.jsxs)("div", {
2863
3731
  className: worktree_css_default.error,
2864
3732
  role: "alert",
@@ -2879,7 +3747,7 @@ window.__ModuleLoader__.load({
2879
3747
  retrySessionBinding();
2880
3748
  },
2881
3749
  children: t("action.retryBinding")
2882
- }), (0, react_jsx_runtime.jsx)("button", {
3750
+ }), !pendingSessionBinding.permissionRequired && (0, react_jsx_runtime.jsx)("button", {
2883
3751
  type: "button",
2884
3752
  className: worktree_css_default.actionButton,
2885
3753
  disabled: actionPending,
@@ -2935,13 +3803,19 @@ window.__ModuleLoader__.load({
2935
3803
  const expanded = isWorkspaceExpanded(expandSnapshot, workspace.workspaceId) || isCurrentSessionReveal("workspace:" + workspace.workspaceId);
2936
3804
  const workspaceMatchesQuery = includesText(workspace.title, query);
2937
3805
  const allWorkspaceSessionIds = filterArchivedSessionIds(workspaceSessionIds(workspaces, workspace.workspaceId, sessions.ids), archivedSessionIds);
3806
+ const workspaceHasOngoingSession = hasOngoingSession(filterVisibleSessionIds(allWorkspaceSessionIds, sessions), sessionPresentations);
2938
3807
  const bindings = view?.bindings ?? [];
2939
- const visibleMainSessionIds = filterVisibleSessionIds(unboundSessionIds(allWorkspaceSessionIds, [...new Set(bindings.map((binding) => binding.sessionId))]), sessions).filter((sessionId) => workspaceMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
2940
- const mainExpanded = isMainExpanded(expandSnapshot, workspace.workspaceId) || isCurrentSessionReveal("main:" + workspace.workspaceId);
3808
+ const mainSessionIds = filterVisibleSessionIds(unboundSessionIds(allWorkspaceSessionIds, [...new Set(bindings.map((binding) => binding.sessionId))]), sessions);
2941
3809
  const mainGroupKey = `main:${workspace.workspaceId}`;
2942
- const sessionIds = visibleMainSessionIds;
2943
- const mainSessionGroupExpanded = expandedSessionGroups[mainGroupKey] === true || sessionIds.length > 5 && isCurrentSessionReveal("session-group:" + mainGroupKey);
3810
+ const orderedMainSessionIds = orderedSessionIdsByAccount.get(mainGroupKey) ?? mainSessionIds;
3811
+ const visibleMainSessionIds = mainSessionIds.filter((sessionId) => workspaceMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
3812
+ const orderedVisibleMainSessionIds = orderedMainSessionIds.filter((sessionId) => workspaceMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
3813
+ const mainExpanded = isMainExpanded(expandSnapshot, workspace.workspaceId) || isCurrentSessionReveal("main:" + workspace.workspaceId);
3814
+ const sessionIds = orderedVisibleMainSessionIds.length === visibleMainSessionIds.length ? orderedVisibleMainSessionIds : visibleMainSessionIds;
3815
+ const mainSessionGroupAutoExpanded = isSessionGroupAutoExpanded(sessionIds, currentSessionId, isCurrentSessionReveal("session-group:" + mainGroupKey));
3816
+ const mainSessionGroupExpanded = expandedSessionGroups[mainGroupKey] === true || mainSessionGroupAutoExpanded;
2944
3817
  const worktrees = view?.worktrees ?? [];
3818
+ const workspaceWorktreeNames = [...view?.branches.map((branch) => branch.name) ?? [], ...view?.worktrees.map((worktree) => worktree.branch) ?? []];
2945
3819
  const sameWorkspaceWorktreeDrag = worktreeDrag?.workspaceId === workspace.workspaceId;
2946
3820
  return (0, react_jsx_runtime.jsxs)("section", {
2947
3821
  className: worktree_css_default.workspaceGroup,
@@ -2950,6 +3824,7 @@ window.__ModuleLoader__.load({
2950
3824
  t,
2951
3825
  workspace,
2952
3826
  expanded,
3827
+ hasOngoingSession: workspaceHasOngoingSession,
2953
3828
  actionPending,
2954
3829
  menuOpen: openWorkspaceMenuId === workspace.workspaceId,
2955
3830
  drag: {
@@ -3007,11 +3882,29 @@ window.__ModuleLoader__.load({
3007
3882
  kind: "main",
3008
3883
  label: mainLabel,
3009
3884
  expanded: mainExpanded,
3885
+ hasOngoingSession: hasOngoingSession(mainSessionIds, sessionPresentations),
3010
3886
  icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, {}),
3011
3887
  workspaceTitle: workspace.title,
3012
3888
  onToggle: () => {
3013
3889
  toggleMain(workspace.workspaceId);
3014
3890
  },
3891
+ menu: {
3892
+ open: openMainMenuId === workspace.workspaceId,
3893
+ label: mainLabel,
3894
+ copyPath: workspace.path,
3895
+ showCreate: currentBranch !== void 0,
3896
+ showRemove: false,
3897
+ disabled: actionPending,
3898
+ onOpenChange: (open) => {
3899
+ setOpenMainMenuId(open ? workspace.workspaceId : void 0);
3900
+ },
3901
+ onCreateWorktree: currentBranch === void 0 ? void 0 : () => {
3902
+ openWorktreeCreator(workspace, {
3903
+ baseBranch: currentBranch,
3904
+ newBranch: createNumberedWorktreeName(currentBranch, workspaceWorktreeNames)
3905
+ });
3906
+ }
3907
+ },
3015
3908
  onCreateSession: createMainSession === void 0 ? void 0 : () => {
3016
3909
  createMainSession(workspace.workspaceId);
3017
3910
  }
@@ -3025,9 +3918,10 @@ window.__ModuleLoader__.load({
3025
3918
  expanded: mainSessionGroupExpanded,
3026
3919
  actionPending,
3027
3920
  sessions,
3921
+ sessionPresentations,
3028
3922
  dragState: sessionDrag,
3029
3923
  onToggleExpanded: () => {
3030
- toggleSessionGroup(mainGroupKey);
3924
+ toggleSessionGroup(mainGroupKey, mainSessionGroupAutoExpanded);
3031
3925
  },
3032
3926
  onStartDrag: (groupKey, sessionId) => {
3033
3927
  sessionDropCommitted.current = false;
@@ -3070,10 +3964,12 @@ window.__ModuleLoader__.load({
3070
3964
  if (query.length > 0 && !worktreeMatchesQuery && !worktreeSessionIds.some((sessionId) => sessionMatchesQuery(sessionId, sessions, query))) return null;
3071
3965
  const visibleWorktreeSessionIds = worktreeSessionIds.filter((sessionId) => worktreeMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
3072
3966
  const worktreeGroupKey = `worktree:${record.worktreeId}`;
3073
- const sessionIds = visibleWorktreeSessionIds;
3074
- const sessionGroupExpanded = expandedSessionGroups[worktreeGroupKey] === true || sessionIds.length > 5 && isCurrentSessionReveal("session-group:" + worktreeGroupKey);
3075
- const state = record.status === "removed" ? "warning" : record.health === "repair" ? "error" : "done";
3076
- const stateLabel = record.status === "removed" ? t("worktree.detached") : record.health === "repair" ? t("worktree.repair") : t("worktree.ready");
3967
+ const orderedVisibleWorktreeSessionIds = (orderedSessionIdsByAccount.get(worktreeGroupKey) ?? worktreeSessionIds).filter((sessionId) => worktreeMatchesQuery || sessionMatchesQuery(sessionId, sessions, query));
3968
+ const sessionIds = orderedVisibleWorktreeSessionIds.length === visibleWorktreeSessionIds.length ? orderedVisibleWorktreeSessionIds : visibleWorktreeSessionIds;
3969
+ const sessionGroupAutoExpanded = isSessionGroupAutoExpanded(sessionIds, currentSessionId, isCurrentSessionReveal("session-group:" + worktreeGroupKey));
3970
+ const sessionGroupExpanded = expandedSessionGroups[worktreeGroupKey] === true || sessionGroupAutoExpanded;
3971
+ const state = record.status === "removed" ? "warning" : record.health === "repair" || record.health === "recovery-needed" ? "error" : "done";
3972
+ const stateLabel = record.status === "removed" ? t("worktree.detached") : record.health === "recovery-needed" ? t("worktree.recovery") : record.health === "repair" ? t("worktree.repair") : t("worktree.ready");
3077
3973
  const worktreeExpanded = isWorktreeExpanded(expandSnapshot, record.worktreeId) || isCurrentSessionReveal("worktree:" + record.worktreeId);
3078
3974
  return (0, react_jsx_runtime.jsxs)("div", {
3079
3975
  className: worktree_css_default.worktreeGroup,
@@ -3084,6 +3980,7 @@ window.__ModuleLoader__.load({
3084
3980
  label: record.branch,
3085
3981
  worktreeId: record.worktreeId,
3086
3982
  expanded: worktreeExpanded,
3983
+ hasOngoingSession: hasOngoingSession(worktreeSessionIds, sessionPresentations),
3087
3984
  icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, {}),
3088
3985
  workspaceTitle: workspace.title,
3089
3986
  state,
@@ -3091,25 +3988,34 @@ window.__ModuleLoader__.load({
3091
3988
  onToggle: () => {
3092
3989
  toggleWorktree(record.worktreeId);
3093
3990
  },
3094
- onCreateSession: record.status === "active" && record.health !== "repair" ? () => {
3991
+ onCreateSession: record.status === "active" && record.health !== "repair" && record.health !== "recovery-needed" ? () => {
3095
3992
  createSession({
3096
3993
  workspaceId: record.workspaceId,
3097
3994
  worktreeId: record.worktreeId,
3098
3995
  cwd: record.absolutePath
3099
3996
  });
3100
3997
  } : void 0,
3101
- menu: record.status === "active" ? {
3998
+ menu: {
3102
3999
  open: openWorktreeMenuId === record.worktreeId,
3103
4000
  label: record.branch,
4001
+ copyPath: record.absolutePath,
4002
+ showCreate: record.status === "active" && record.health !== "repair" && record.health !== "recovery-needed",
4003
+ showRemove: record.status === "active" && record.health !== "repair" && record.health !== "recovery-needed",
3104
4004
  disabled: actionPending,
3105
4005
  onOpenChange: (open) => {
3106
4006
  setOpenWorktreeMenuId(open ? record.worktreeId : void 0);
3107
4007
  },
3108
- onRemove: () => {
4008
+ onCreateWorktree: record.status === "active" && record.health !== "repair" && record.health !== "recovery-needed" ? () => {
4009
+ openWorktreeCreator(workspace, {
4010
+ baseBranch: record.branch,
4011
+ newBranch: createNumberedWorktreeName(record.branch, workspaceWorktreeNames)
4012
+ });
4013
+ } : void 0,
4014
+ onRemove: record.status === "active" ? () => {
3109
4015
  setWorktreeRemoval(record);
3110
4016
  setActionError(void 0);
3111
- }
3112
- } : void 0,
4017
+ } : void 0
4018
+ },
3113
4019
  drag: {
3114
4020
  active: sameWorkspaceWorktreeDrag,
3115
4021
  marker: worktreeDrag?.over?.worktreeId === record.worktreeId ? worktreeDrag.over.half : null,
@@ -3151,9 +4057,10 @@ window.__ModuleLoader__.load({
3151
4057
  expanded: sessionGroupExpanded,
3152
4058
  actionPending,
3153
4059
  sessions,
4060
+ sessionPresentations,
3154
4061
  dragState: sessionDrag,
3155
4062
  onToggleExpanded: () => {
3156
- toggleSessionGroup(worktreeGroupKey);
4063
+ toggleSessionGroup(worktreeGroupKey, sessionGroupAutoExpanded);
3157
4064
  },
3158
4065
  onStartDrag: (groupKey, sessionId) => {
3159
4066
  sessionDropCommitted.current = false;
@@ -3272,18 +4179,50 @@ window.__ModuleLoader__.load({
3272
4179
  onSubmit: () => {
3273
4180
  if (manager === void 0 || worktreeRemoval === void 0) return;
3274
4181
  const target = worktreeRemoval;
4182
+ const mutationToken = target.mutationToken;
4183
+ if (mutationToken === void 0) {
4184
+ setActionError({
4185
+ code: "WORKTREE_STATE_CONFLICT",
4186
+ message: "",
4187
+ retryable: true,
4188
+ details: {
4189
+ workspaceId: target.workspaceId,
4190
+ worktreeId: target.worktreeId
4191
+ }
4192
+ });
4193
+ return;
4194
+ }
3275
4195
  runMutation(async () => {
3276
4196
  await executeWorktreeAction(manager, {
3277
4197
  type: "removeWorktree",
3278
4198
  input: {
3279
4199
  workspaceId: target.workspaceId,
3280
- worktreeId: target.worktreeId
4200
+ worktreeId: target.worktreeId,
4201
+ mutationToken
3281
4202
  }
3282
- });
4203
+ }, permission, onPermissionNotice);
3283
4204
  await invalidateWorktreeContext?.(target.workspaceId);
3284
4205
  setWorktreeRemoval(void 0);
3285
4206
  });
3286
4207
  }
4208
+ }),
4209
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.RiskConfirmation, {
4210
+ open: fullAccessConfirmationSnapshot !== void 0,
4211
+ title: t("permission.fullAccessTitle"),
4212
+ description: fullAccessConfirmationSnapshot === void 0 ? "" : t("permission.fullAccessDescription", { cwd: fullAccessConfirmationSnapshot.cwd }),
4213
+ acknowledgeLabel: t("permission.fullAccessAcknowledge"),
4214
+ cancelLabel: t("dialog.cancel"),
4215
+ confirmLabel: t("permission.fullAccessEnable"),
4216
+ acknowledged: fullAccessAcknowledged,
4217
+ onAcknowledgedChange: setFullAccessAcknowledged,
4218
+ onCancel: () => {
4219
+ setFullAccessAcknowledged(false);
4220
+ fullAccessConfirmation?.resolve(false);
4221
+ },
4222
+ onConfirm: () => {
4223
+ setFullAccessAcknowledged(false);
4224
+ fullAccessConfirmation?.resolve(true);
4225
+ }
3287
4226
  })
3288
4227
  ]
3289
4228
  });
@@ -3313,7 +4252,7 @@ window.__ModuleLoader__.load({
3313
4252
  const record = input.worktrees.find((candidate) => candidate.worktreeId === binding.worktreeId);
3314
4253
  if (record === void 0 || record.workspaceId !== workspace.workspaceId) return none(record === void 0 ? "stale" : "workspace-mismatch");
3315
4254
  if (record.status !== "active") return none("stale");
3316
- if (record.health === "repair") return none("repair");
4255
+ if (record.health === "repair" || record.health === "recovery-needed") return none("repair");
3317
4256
  if (record.branch.length === 0) return none("not-ready");
3318
4257
  return {
3319
4258
  kind: "worktree",
@@ -3649,6 +4588,313 @@ window.__ModuleLoader__.load({
3649
4588
  };
3650
4589
  }
3651
4590
  //#endregion
4591
+ //#region lib/client/worktree-permission.js
4592
+ /**
4593
+ * Coordinate async permission requests with the one in-page confirmation
4594
+ * rendered by the Worktree surface. Requests are serialized so concurrent
4595
+ * Worktree Session creations cannot replace one another's dialog.
4596
+ */
4597
+ function createWorktreeFullAccessConfirmationController() {
4598
+ let active;
4599
+ const queue = [];
4600
+ const listeners = /* @__PURE__ */ new Set();
4601
+ let disposed = false;
4602
+ const notify = () => {
4603
+ for (const listener of listeners) listener();
4604
+ };
4605
+ const showNext = () => {
4606
+ if (active !== void 0) return;
4607
+ active = queue.shift();
4608
+ notify();
4609
+ };
4610
+ return {
4611
+ getSnapshot: () => active?.input,
4612
+ subscribe: (listener) => {
4613
+ listeners.add(listener);
4614
+ return () => {
4615
+ listeners.delete(listener);
4616
+ };
4617
+ },
4618
+ request: (input) => {
4619
+ if (disposed) return Promise.resolve(false);
4620
+ return new Promise((resolve) => {
4621
+ queue.push({
4622
+ input,
4623
+ resolve
4624
+ });
4625
+ showNext();
4626
+ });
4627
+ },
4628
+ resolve: (confirmed) => {
4629
+ if (active === void 0) return;
4630
+ const current = active;
4631
+ active = void 0;
4632
+ current.resolve(confirmed);
4633
+ showNext();
4634
+ },
4635
+ dispose: () => {
4636
+ if (disposed) return;
4637
+ disposed = true;
4638
+ active?.resolve(false);
4639
+ active = void 0;
4640
+ for (const pending of queue.splice(0)) pending.resolve(false);
4641
+ notify();
4642
+ }
4643
+ };
4644
+ }
4645
+ //#endregion
4646
+ //#region lib/client/worktree-permission-icon.js
4647
+ const PLUGIN_ID = "@cerbur/clutch-dsh-worktree";
4648
+ const STYLE_ID = `${PLUGIN_ID}/permission-icon`;
4649
+ const WORKTREE_PERMISSION_LABEL = "Worktree Full Access";
4650
+ /** DOM marker owned only by this plugin's permission icon decorator. */
4651
+ const WORKTREE_PERMISSION_ICON_ATTRIBUTE = "data-clutch-dsh-worktree-permission-icon";
4652
+ const ICON_SVG = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path d=\"M13.0762 1.37207C14.0846 1.37228 14.9021 2.19077 14.9023 3.19922C14.9022 4.20772 14.0847 5.02518 13.0762 5.02539C12.2967 5.02539 11.6325 4.53691 11.3701 3.84961H4.35547C4.79397 4.26458 5.15861 4.7644 5.41699 5.33496L7.10645 9.06738C7.88526 10.7875 9.55104 11.9228 11.4189 12.0371C11.7085 11.4109 12.3411 10.9756 13.0762 10.9756C14.0843 10.9759 14.9023 11.7936 14.9023 12.8018C14.9023 13.81 14.0843 14.6277 13.0762 14.6279C12.2534 14.6279 11.5574 14.0832 11.3291 13.335C8.9868 13.1879 6.89981 11.7612 5.92285 9.60352L4.23242 5.87109C3.67503 4.64033 2.44878 3.84961 1.09766 3.84961V2.54883C1.10665 2.54883 1.11601 2.54975 1.125 2.5498L11.3701 2.54883C11.6326 1.86151 12.2969 1.37207 13.0762 1.37207ZM13.0762 12.2764C12.7858 12.2764 12.5508 12.5114 12.5508 12.8018C12.5508 13.0921 12.7858 13.3281 13.0762 13.3281C13.3664 13.3279 13.6025 13.092 13.6025 12.8018C13.6025 12.5115 13.3664 12.2766 13.0762 12.2764ZM13.0762 2.67285C12.7855 2.67285 12.55 2.90861 12.5498 3.19922C12.5499 3.48987 12.7855 3.72559 13.0762 3.72559C13.3667 3.72538 13.6024 3.48975 13.6025 3.19922C13.6023 2.90874 13.3666 2.67306 13.0762 2.67285Z\" fill=\"black\"/></svg>";
4653
+ function iconStyles() {
4654
+ const mask = `url("data:image/svg+xml,${encodeURIComponent(ICON_SVG)}")`;
4655
+ return `
4656
+ [${WORKTREE_PERMISSION_ICON_ATTRIBUTE}]::before {
4657
+ content: "";
4658
+ display: inline-block;
4659
+ flex: 0 0 auto;
4660
+ width: 16px;
4661
+ height: 16px;
4662
+ background-color: var(--dsw-alias-label-tertiary, currentColor);
4663
+ -webkit-mask-image: ${mask};
4664
+ mask-image: ${mask};
4665
+ -webkit-mask-position: center;
4666
+ mask-position: center;
4667
+ -webkit-mask-repeat: no-repeat;
4668
+ mask-repeat: no-repeat;
4669
+ -webkit-mask-size: contain;
4670
+ mask-size: contain;
4671
+ }
4672
+ [${WORKTREE_PERMISSION_ICON_ATTRIBUTE}="trigger"]::before {
4673
+ width: 14px;
4674
+ height: 14px;
4675
+ }
4676
+ @container (max-width: 460px) {
4677
+ [${WORKTREE_PERMISSION_ICON_ATTRIBUTE}="trigger"] > span:first-of-type {
4678
+ display: none;
4679
+ }
4680
+ }
4681
+ `;
4682
+ }
4683
+ function normalizedText(element) {
4684
+ return (element.textContent ?? "").replace(/\s+/g, " ").trim();
4685
+ }
4686
+ function isWorktreeMenuItem(element) {
4687
+ return element.matches("button[role=\"menuitem\"]") && normalizedText(element) === WORKTREE_PERMISSION_LABEL && element.closest("[role=\"menu\"]") !== null;
4688
+ }
4689
+ function isWorktreeTrigger(element) {
4690
+ if (!element.matches("button[aria-label]")) return false;
4691
+ const label = element.getAttribute("aria-label") ?? "";
4692
+ return /^(?:access mode,\s*current:\s*|访问模式,当前:\s*)worktree full access$/iu.test(label.trim());
4693
+ }
4694
+ /** Mark the native custom permission row and trigger for CSS decoration. */
4695
+ function decorateWorktreePermissionIcons(document) {
4696
+ for (const marked of document.querySelectorAll(`[${WORKTREE_PERMISSION_ICON_ATTRIBUTE}]`)) {
4697
+ const kind = marked.getAttribute(WORKTREE_PERMISSION_ICON_ATTRIBUTE);
4698
+ if (kind === "menu" && !isWorktreeMenuItem(marked) || kind === "trigger" && !isWorktreeTrigger(marked)) marked.removeAttribute(WORKTREE_PERMISSION_ICON_ATTRIBUTE);
4699
+ }
4700
+ for (const item of document.querySelectorAll("button[role=\"menuitem\"]")) if (isWorktreeMenuItem(item)) item.setAttribute(WORKTREE_PERMISSION_ICON_ATTRIBUTE, "menu");
4701
+ for (const button of document.querySelectorAll("button[aria-label]")) if (isWorktreeTrigger(button)) button.setAttribute(WORKTREE_PERMISSION_ICON_ATTRIBUTE, "trigger");
4702
+ }
4703
+ /** Install the Worktree permission icon and return a plugin-local disposer. */
4704
+ function installWorktreePermissionIcon(document) {
4705
+ for (const existing of document.querySelectorAll("style[data-plugin-css]")) if (existing.getAttribute("data-plugin-css") === STYLE_ID) existing.remove();
4706
+ const style = document.createElement("style");
4707
+ style.setAttribute("data-plugin", PLUGIN_ID);
4708
+ style.setAttribute("data-plugin-css", STYLE_ID);
4709
+ style.textContent = iconStyles();
4710
+ document.head.appendChild(style);
4711
+ let active = true;
4712
+ let queued = false;
4713
+ const scan = () => {
4714
+ if (!active || queued) return;
4715
+ queued = true;
4716
+ queueMicrotask(() => {
4717
+ queued = false;
4718
+ if (active) decorateWorktreePermissionIcons(document);
4719
+ });
4720
+ };
4721
+ decorateWorktreePermissionIcons(document);
4722
+ const observer = typeof MutationObserver === "function" ? new MutationObserver(scan) : void 0;
4723
+ observer?.observe(document.documentElement, {
4724
+ attributes: true,
4725
+ attributeFilter: ["aria-label"],
4726
+ characterData: true,
4727
+ childList: true,
4728
+ subtree: true
4729
+ });
4730
+ return () => {
4731
+ if (!active) return;
4732
+ active = false;
4733
+ observer?.disconnect();
4734
+ style.remove();
4735
+ for (const marked of document.querySelectorAll(`[${WORKTREE_PERMISSION_ICON_ATTRIBUTE}]`)) marked.removeAttribute(WORKTREE_PERMISSION_ICON_ATTRIBUTE);
4736
+ };
4737
+ }
4738
+ //#endregion
4739
+ //#region lib/client/worktree-session-fork.js
4740
+ function recoveryKey(sourceSessionId, childSessionId) {
4741
+ return `${sourceSessionId}\u0000${childSessionId}`;
4742
+ }
4743
+ /**
4744
+ * Coordinate native Session fork with the plugin-owned Worktree relation.
4745
+ * Native Session creation always wins: plugin lookup/bind failures are kept as
4746
+ * recovery state and never turn an already-created child into a failed fork.
4747
+ */
4748
+ function createWorktreeSessionForkCoordinator(options) {
4749
+ let disposed = false;
4750
+ let revision = 0;
4751
+ let pending = /* @__PURE__ */ new Map();
4752
+ let recoverySnapshot = {
4753
+ revision: 0,
4754
+ pending: []
4755
+ };
4756
+ const subscribers = /* @__PURE__ */ new Set();
4757
+ const boundChildren = /* @__PURE__ */ new Set();
4758
+ const bindingInFlight = /* @__PURE__ */ new Map();
4759
+ let reconciliationInFlight;
4760
+ let reconciliationAgain = false;
4761
+ const publish = () => {
4762
+ revision += 1;
4763
+ recoverySnapshot = {
4764
+ revision,
4765
+ pending: [...pending.values()]
4766
+ };
4767
+ for (const subscriber of subscribers) subscriber();
4768
+ };
4769
+ const setRecovery = (sourceSessionId, childSessionId, error, target) => {
4770
+ if (disposed) return;
4771
+ const key = recoveryKey(sourceSessionId, childSessionId);
4772
+ pending = new Map(pending).set(key, {
4773
+ key,
4774
+ sourceSessionId,
4775
+ childSessionId,
4776
+ ...target === void 0 ? {} : { binding: target },
4777
+ error
4778
+ });
4779
+ publish();
4780
+ };
4781
+ const clearRecovery = (sourceSessionId, childSessionId) => {
4782
+ const key = recoveryKey(sourceSessionId, childSessionId);
4783
+ if (!pending.has(key)) return;
4784
+ const next = new Map(pending);
4785
+ next.delete(key);
4786
+ pending = next;
4787
+ publish();
4788
+ };
4789
+ const attemptBinding = async (sourceSessionId, childSessionId) => {
4790
+ if (disposed || boundChildren.has(childSessionId)) return { bound: false };
4791
+ let target;
4792
+ try {
4793
+ target = await options.findBinding(sourceSessionId);
4794
+ } catch (error) {
4795
+ setRecovery(sourceSessionId, childSessionId, error);
4796
+ return { bound: false };
4797
+ }
4798
+ if (disposed) return { bound: false };
4799
+ if (target === void 0 || target.status !== "active") {
4800
+ clearRecovery(sourceSessionId, childSessionId);
4801
+ return { bound: false };
4802
+ }
4803
+ try {
4804
+ await options.bindSession({
4805
+ workspaceId: target.workspaceId,
4806
+ worktreeId: target.worktreeId,
4807
+ sessionId: childSessionId
4808
+ });
4809
+ if (disposed) return { bound: false };
4810
+ const childBinding = {
4811
+ workspaceId: target.workspaceId,
4812
+ worktreeId: target.worktreeId,
4813
+ sessionId: childSessionId,
4814
+ status: "active"
4815
+ };
4816
+ boundChildren.add(childSessionId);
4817
+ clearRecovery(sourceSessionId, childSessionId);
4818
+ options.onBound?.(childBinding);
4819
+ publish();
4820
+ return { bound: true };
4821
+ } catch (error) {
4822
+ setRecovery(sourceSessionId, childSessionId, error, target);
4823
+ return { bound: false };
4824
+ }
4825
+ };
4826
+ const bindChild = (sourceSessionId, childSessionId) => {
4827
+ if (disposed || boundChildren.has(childSessionId)) return Promise.resolve({ bound: false });
4828
+ const key = recoveryKey(sourceSessionId, childSessionId);
4829
+ const current = bindingInFlight.get(key);
4830
+ if (current !== void 0) return current;
4831
+ const promise = attemptBinding(sourceSessionId, childSessionId).finally(() => {
4832
+ if (bindingInFlight.get(key) === promise) bindingInFlight.delete(key);
4833
+ });
4834
+ bindingInFlight.set(key, promise);
4835
+ return promise;
4836
+ };
4837
+ const reconcileNow = async () => {
4838
+ if (disposed || options.sessions === void 0) return;
4839
+ const snapshot = options.sessions.getSnapshot();
4840
+ if (snapshot.phase === "pending") return;
4841
+ if (!Array.isArray(snapshot.ids) || typeof snapshot.byId !== "object" || snapshot.byId === null) return;
4842
+ const candidates = snapshot.ids.flatMap((childSessionId) => {
4843
+ const summary = snapshot.byId[childSessionId];
4844
+ if (summary?.parentId === void 0 || summary.parentId === childSessionId || summary.blank === true || summary.origin === "subagent" || snapshot.byId[summary.parentId] === void 0) return [];
4845
+ return [{
4846
+ childSessionId,
4847
+ sourceSessionId: summary.parentId
4848
+ }];
4849
+ });
4850
+ await Promise.all(candidates.map(({ childSessionId, sourceSessionId }) => bindChild(sourceSessionId, childSessionId).then(() => void 0)));
4851
+ };
4852
+ const reconcile = () => {
4853
+ if (reconciliationInFlight !== void 0) {
4854
+ reconciliationAgain = true;
4855
+ return reconciliationInFlight;
4856
+ }
4857
+ const run = async () => {
4858
+ do {
4859
+ reconciliationAgain = false;
4860
+ await reconcileNow();
4861
+ } while (!disposed && reconciliationAgain);
4862
+ };
4863
+ const promise = run().finally(() => {
4864
+ if (reconciliationInFlight === promise) reconciliationInFlight = void 0;
4865
+ });
4866
+ reconciliationInFlight = promise;
4867
+ return promise;
4868
+ };
4869
+ return {
4870
+ recovery: {
4871
+ getSnapshot() {
4872
+ return recoverySnapshot;
4873
+ },
4874
+ subscribe(listener) {
4875
+ subscribers.add(listener);
4876
+ return () => subscribers.delete(listener);
4877
+ }
4878
+ },
4879
+ async fork(input) {
4880
+ const childSessionId = await options.fork(input);
4881
+ if (!disposed) await bindChild(input.sessionId, childSessionId);
4882
+ return childSessionId;
4883
+ },
4884
+ reconcile,
4885
+ async retry(key) {
4886
+ const item = pending.get(key);
4887
+ if (item === void 0 || disposed) return false;
4888
+ return (await bindChild(item.sourceSessionId, item.childSessionId)).bound;
4889
+ },
4890
+ dispose() {
4891
+ disposed = true;
4892
+ bindingInFlight.clear();
4893
+ subscribers.clear();
4894
+ }
4895
+ };
4896
+ }
4897
+ //#endregion
3652
4898
  //#region lib/client/entry.js
3653
4899
  /** Required DSH Client services; Connection is the sole Worktree wire dependency. */
3654
4900
  const inject = [
@@ -3663,11 +4909,22 @@ window.__ModuleLoader__.load({
3663
4909
  * with that fiber, so slot consumers share one manager and one request lifetime.
3664
4910
  */
3665
4911
  function apply(ctx) {
4912
+ if (typeof document !== "undefined") ctx.effect(() => installWorktreePermissionIcon(document), "clutch-dsh-worktree: permission icon cleanup");
3666
4913
  ctx.effect(() => ctx.locale.register(WORKTREE_NS, {
3667
4914
  zh,
3668
4915
  en
3669
4916
  }), "clutch-dsh-worktree: locale dictionaries");
3670
4917
  const manager = createWorktreeConnectionAdapter(ctx.connection.rpc);
4918
+ const fullAccessConfirmation = createWorktreeFullAccessConfirmationController();
4919
+ const permissionNotice = (0, _deepseek_ai_dsh_client_runtime_client.createSnapshotStore)(void 0);
4920
+ const permissionManager = typeof document !== "undefined" ? manager : void 0;
4921
+ const reportPermissionNotice = (input, result) => {
4922
+ const visible = result.status === "fallback-workspace-write" || result.status === "user-restricted" || result.status === "unverified";
4923
+ permissionNotice.set(visible ? {
4924
+ ...input,
4925
+ result
4926
+ } : void 0);
4927
+ };
3671
4928
  const sessions = ctx.sessions;
3672
4929
  const virtualWorkspaceMembership = createVirtualWorkspaceMembership(ctx.workspaces.list);
3673
4930
  const contextProjection = createWorktreeContextProjection({
@@ -3677,11 +4934,14 @@ window.__ModuleLoader__.load({
3677
4934
  storeFactory: _deepseek_ai_dsh_client_runtime_client.createSnapshotStore
3678
4935
  });
3679
4936
  ctx.effect(() => () => manager.dispose(), "clutch-dsh-worktree: connection cleanup");
4937
+ ctx.effect(() => () => fullAccessConfirmation.dispose(), "clutch-dsh-worktree: Full Access confirmation cleanup");
3680
4938
  ctx.effect(() => () => virtualWorkspaceMembership.dispose(), "clutch-dsh-worktree: Workspace membership cleanup");
3681
4939
  ctx.effect(() => () => contextProjection.dispose(), "clutch-dsh-worktree: Session context cleanup");
3682
4940
  contextProjection.refresh();
3683
4941
  const viewStore = createWorktreeViewStore();
3684
4942
  const expandState = createWorktreeExpandStateStore(_deepseek_ai_dsh_client_runtime_client.createSnapshotStore);
4943
+ const sessionOrder = createWorktreeSessionOrderStore(_deepseek_ai_dsh_client_runtime_client.createSnapshotStore);
4944
+ ctx.effect(() => () => sessionOrder.dispose(), "clutch-dsh-worktree: Session order cleanup");
3685
4945
  const ensureSessionWorkspace = (workspaceId, sessionId) => {
3686
4946
  virtualWorkspaceMembership.ensure({
3687
4947
  workspaceId,
@@ -3691,12 +4951,48 @@ window.__ModuleLoader__.load({
3691
4951
  const syncSessionWorkspaces = (bindings) => {
3692
4952
  virtualWorkspaceMembership.sync(bindings);
3693
4953
  };
4954
+ const forkableSessions = ctx.sessions;
4955
+ const nativeFork = forkableSessions.fork;
4956
+ const findWorktreeSessionBinding = async (sessionId) => {
4957
+ const workspaceIds = ctx.workspaces.list.getSnapshot().items.map((workspace) => workspace.workspaceId);
4958
+ const results = await Promise.allSettled(workspaceIds.map(async (workspaceId) => {
4959
+ return (await manager.listBindings({ workspaceId })).find((binding) => binding.sessionId === sessionId && binding.status === "active");
4960
+ }));
4961
+ const found = results.find((result) => result.status === "fulfilled" && result.value !== void 0);
4962
+ if (found !== void 0) return found.value;
4963
+ const failed = results.find((result) => result.status === "rejected");
4964
+ if (failed !== void 0) throw failed.reason;
4965
+ };
4966
+ const forkCoordinator = typeof nativeFork !== "function" ? void 0 : createWorktreeSessionForkCoordinator({
4967
+ fork: (input) => nativeFork.call(ctx.sessions, input),
4968
+ findBinding: findWorktreeSessionBinding,
4969
+ bindSession: (input) => manager.bindSession(input),
4970
+ sessions: ctx.sessions.list
4971
+ });
4972
+ if (forkCoordinator !== void 0) {
4973
+ forkableSessions.fork = (input) => forkCoordinator.fork(input);
4974
+ const reconcileForkChildren = () => {
4975
+ forkCoordinator.reconcile();
4976
+ };
4977
+ const unsubscribeSessionList = ctx.sessions.list.subscribe(reconcileForkChildren);
4978
+ const unsubscribeWorkspaceList = ctx.workspaces.list.subscribe(reconcileForkChildren);
4979
+ ctx.effect(() => () => {
4980
+ unsubscribeSessionList();
4981
+ unsubscribeWorkspaceList();
4982
+ forkCoordinator.dispose();
4983
+ forkableSessions.fork = nativeFork;
4984
+ }, "clutch-dsh-worktree: Session fork cleanup");
4985
+ forkCoordinator.reconcile();
4986
+ }
3694
4987
  const worktreeSessionConnector = createWorktreeSessionConnector({
3695
4988
  manager,
3696
4989
  sessions: ctx.sessions.list,
3697
4990
  archivedSessionIds: () => ctx.workspaces.list.getSnapshot().archivedSessionIds ?? [],
3698
4991
  createSession: async (input) => String(await sessions.create(input)),
3699
4992
  ensureSessionWorkspace,
4993
+ permission: permissionManager,
4994
+ confirmFullAccess: permissionManager === void 0 ? void 0 : fullAccessConfirmation.request,
4995
+ onPermissionResult: reportPermissionNotice,
3700
4996
  openSession: (sessionId) => {
3701
4997
  ctx.sessions.open(sessionId);
3702
4998
  }
@@ -3724,8 +5020,15 @@ window.__ModuleLoader__.load({
3724
5020
  inject: () => ({
3725
5021
  available: true,
3726
5022
  expandState,
5023
+ sessionOrder,
3727
5024
  hooks: { worktreeContext: contextProjection.store },
3728
5025
  manager,
5026
+ permission: permissionManager,
5027
+ confirmFullAccess: permissionManager === void 0 ? void 0 : fullAccessConfirmation.request,
5028
+ fullAccessConfirmation: typeof document === "undefined" ? void 0 : fullAccessConfirmation,
5029
+ onPermissionResult: reportPermissionNotice,
5030
+ onPermissionNotice: reportPermissionNotice,
5031
+ permissionNotice,
3729
5032
  createWorkspace: async () => {
3730
5033
  const workspacePath = await ctx.workspaces.pickDirectory();
3731
5034
  if (workspacePath !== null) await ctx.workspaces.create({ path: workspacePath });
@@ -3759,6 +5062,7 @@ window.__ModuleLoader__.load({
3759
5062
  if (!result.ok) throw new Error(result.error.message);
3760
5063
  },
3761
5064
  forkSession: (sessionId) => {
5065
+ if (forkableSessions.fork === void 0) return;
3762
5066
  ctx.sessions.fork({
3763
5067
  sessionId,
3764
5068
  increaseTitle: true
@@ -3769,6 +5073,10 @@ window.__ModuleLoader__.load({
3769
5073
  archiveSession: (sessionId) => ctx.workspaces.archiveSession(sessionId),
3770
5074
  ensureSessionWorkspace,
3771
5075
  syncSessionWorkspaces,
5076
+ forkRecovery: forkCoordinator?.recovery,
5077
+ retryForkSession: forkCoordinator === void 0 ? void 0 : async (key) => {
5078
+ await forkCoordinator.retry(key);
5079
+ },
3772
5080
  openSession: (sessionId) => {
3773
5081
  ctx.sessions.open(sessionId);
3774
5082
  }