@jlongo78/agent-spaces 1.0.0 → 1.0.2

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 (224) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/server/app/(desktop)/admin/analytics/page.js.nft.json +1 -1
  5. package/.next/standalone/.next/server/app/(desktop)/admin/analytics/page_client-reference-manifest.js +1 -1
  6. package/.next/standalone/.next/server/app/(desktop)/admin/users/page.js.nft.json +1 -1
  7. package/.next/standalone/.next/server/app/(desktop)/admin/users/page_client-reference-manifest.js +1 -1
  8. package/.next/standalone/.next/server/app/(desktop)/analytics/page.js.nft.json +1 -1
  9. package/.next/standalone/.next/server/app/(desktop)/analytics/page_client-reference-manifest.js +1 -1
  10. package/.next/standalone/.next/server/app/(desktop)/network/page.js.nft.json +1 -1
  11. package/.next/standalone/.next/server/app/(desktop)/network/page_client-reference-manifest.js +1 -1
  12. package/.next/standalone/.next/server/app/(desktop)/page.js.nft.json +1 -1
  13. package/.next/standalone/.next/server/app/(desktop)/page_client-reference-manifest.js +1 -1
  14. package/.next/standalone/.next/server/app/(desktop)/projects/page.js.nft.json +1 -1
  15. package/.next/standalone/.next/server/app/(desktop)/projects/page_client-reference-manifest.js +1 -1
  16. package/.next/standalone/.next/server/app/(desktop)/sessions/[id]/page.js.nft.json +1 -1
  17. package/.next/standalone/.next/server/app/(desktop)/sessions/[id]/page_client-reference-manifest.js +1 -1
  18. package/.next/standalone/.next/server/app/(desktop)/sessions/page.js.nft.json +1 -1
  19. package/.next/standalone/.next/server/app/(desktop)/sessions/page_client-reference-manifest.js +1 -1
  20. package/.next/standalone/.next/server/app/(desktop)/settings/page.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/(desktop)/settings/page_client-reference-manifest.js +1 -1
  22. package/.next/standalone/.next/server/app/(desktop)/terminal/page.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/(desktop)/terminal/page_client-reference-manifest.js +1 -1
  24. package/.next/standalone/.next/server/app/(desktop)/terminal/pane/[id]/page.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/(desktop)/terminal/pane/[id]/page_client-reference-manifest.js +1 -1
  26. package/.next/standalone/.next/server/app/(desktop)/terminal/remote/[nodeId]/[workspaceId]/page.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/(desktop)/terminal/remote/[nodeId]/[workspaceId]/page_client-reference-manifest.js +1 -1
  28. package/.next/standalone/.next/server/app/(desktop)/workspaces/page.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/(desktop)/workspaces/page_client-reference-manifest.js +1 -1
  30. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  31. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  32. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  33. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  34. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  35. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  37. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  38. package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
  39. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  40. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  41. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  42. package/.next/standalone/.next/server/app/admin/analytics.html +1 -1
  43. package/.next/standalone/.next/server/app/admin/analytics.rsc +2 -2
  44. package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin/analytics/__PAGE__.segment.rsc +2 -2
  45. package/.next/standalone/.next/server/app/admin/analytics.segments/_full.segment.rsc +2 -2
  46. package/.next/standalone/.next/server/app/admin/analytics.segments/_tree.segment.rsc +2 -2
  47. package/.next/standalone/.next/server/app/admin/users.html +1 -1
  48. package/.next/standalone/.next/server/app/admin/users.rsc +2 -2
  49. package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin/users/__PAGE__.segment.rsc +2 -2
  50. package/.next/standalone/.next/server/app/admin/users.segments/_full.segment.rsc +2 -2
  51. package/.next/standalone/.next/server/app/admin/users.segments/_tree.segment.rsc +2 -2
  52. package/.next/standalone/.next/server/app/analytics.html +1 -1
  53. package/.next/standalone/.next/server/app/analytics.rsc +2 -2
  54. package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap/analytics/__PAGE__.segment.rsc +2 -2
  55. package/.next/standalone/.next/server/app/analytics.segments/_full.segment.rsc +2 -2
  56. package/.next/standalone/.next/server/app/analytics.segments/_tree.segment.rsc +2 -2
  57. package/.next/standalone/.next/server/app/api/admin/analytics/route.js.nft.json +1 -1
  58. package/.next/standalone/.next/server/app/api/admin/os-users/route.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/api/admin/users/[id]/route.js.nft.json +1 -1
  60. package/.next/standalone/.next/server/app/api/admin/users/route.js.nft.json +1 -1
  61. package/.next/standalone/.next/server/app/api/analytics/overview/route.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/api/auth/login/route.js.nft.json +1 -1
  63. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  64. package/.next/standalone/.next/server/app/api/auth/me/route.js.nft.json +1 -1
  65. package/.next/standalone/.next/server/app/api/auth/totp/setup/route.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/api/auth/totp/status/route.js.nft.json +1 -1
  67. package/.next/standalone/.next/server/app/api/auth/totp/verify/route.js.nft.json +1 -1
  68. package/.next/standalone/.next/server/app/api/bulk/route.js.nft.json +1 -1
  69. package/.next/standalone/.next/server/app/api/chat/route.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/api/claude/usage/route.js.nft.json +1 -1
  71. package/.next/standalone/.next/server/app/api/config/route.js.nft.json +1 -1
  72. package/.next/standalone/.next/server/app/api/events/route.js.nft.json +1 -1
  73. package/.next/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  74. package/.next/standalone/.next/server/app/api/folders/route.js.nft.json +1 -1
  75. package/.next/standalone/.next/server/app/api/network/discovered/route.js.nft.json +1 -1
  76. package/.next/standalone/.next/server/app/api/network/handshake/route.js.nft.json +1 -1
  77. package/.next/standalone/.next/server/app/api/network/health/route.js.nft.json +1 -1
  78. package/.next/standalone/.next/server/app/api/network/identity/route.js.nft.json +1 -1
  79. package/.next/standalone/.next/server/app/api/network/keys/[id]/route.js.nft.json +1 -1
  80. package/.next/standalone/.next/server/app/api/network/keys/route.js.nft.json +1 -1
  81. package/.next/standalone/.next/server/app/api/network/nodes/[id]/route.js.nft.json +1 -1
  82. package/.next/standalone/.next/server/app/api/network/nodes/check/route.js.nft.json +1 -1
  83. package/.next/standalone/.next/server/app/api/network/nodes/route.js.nft.json +1 -1
  84. package/.next/standalone/.next/server/app/api/network/panes/[id]/route.js.nft.json +1 -1
  85. package/.next/standalone/.next/server/app/api/network/panes/route.js.nft.json +1 -1
  86. package/.next/standalone/.next/server/app/api/network/projects/route.js.nft.json +1 -1
  87. package/.next/standalone/.next/server/app/api/network/proxy/[nodeId]/[...path]/route.js.nft.json +1 -1
  88. package/.next/standalone/.next/server/app/api/network/search/route.js.nft.json +1 -1
  89. package/.next/standalone/.next/server/app/api/network/sessions/[id]/messages/route.js.nft.json +1 -1
  90. package/.next/standalone/.next/server/app/api/network/sessions/[id]/route.js.nft.json +1 -1
  91. package/.next/standalone/.next/server/app/api/network/sessions/route.js.nft.json +1 -1
  92. package/.next/standalone/.next/server/app/api/network/workspaces/[id]/route.js.nft.json +1 -1
  93. package/.next/standalone/.next/server/app/api/network/workspaces/route.js.nft.json +1 -1
  94. package/.next/standalone/.next/server/app/api/panes/[id]/diff/route.js.nft.json +1 -1
  95. package/.next/standalone/.next/server/app/api/panes/[id]/route.js.nft.json +1 -1
  96. package/.next/standalone/.next/server/app/api/panes/route.js.nft.json +1 -1
  97. package/.next/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  98. package/.next/standalone/.next/server/app/api/proxy/models/[modelId]/[...path]/route.js.nft.json +1 -1
  99. package/.next/standalone/.next/server/app/api/proxy/models/[modelId]/status/route.js.nft.json +1 -1
  100. package/.next/standalone/.next/server/app/api/search/route.js.nft.json +1 -1
  101. package/.next/standalone/.next/server/app/api/server-config/route.js.nft.json +1 -1
  102. package/.next/standalone/.next/server/app/api/sessions/[id]/chat/route.js.nft.json +1 -1
  103. package/.next/standalone/.next/server/app/api/sessions/[id]/messages/route.js.nft.json +1 -1
  104. package/.next/standalone/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
  105. package/.next/standalone/.next/server/app/api/sessions/route.js.nft.json +1 -1
  106. package/.next/standalone/.next/server/app/api/sync/route.js.nft.json +1 -1
  107. package/.next/standalone/.next/server/app/api/tags/route.js.nft.json +1 -1
  108. package/.next/standalone/.next/server/app/api/tier/route.js.nft.json +1 -1
  109. package/.next/standalone/.next/server/app/api/updates/route.js.nft.json +1 -1
  110. package/.next/standalone/.next/server/app/api/whisper/route.js.nft.json +1 -1
  111. package/.next/standalone/.next/server/app/api/wizard/chat/route.js.nft.json +1 -1
  112. package/.next/standalone/.next/server/app/api/workspaces/[id]/context/[key]/route.js.nft.json +1 -1
  113. package/.next/standalone/.next/server/app/api/workspaces/[id]/context/route.js.nft.json +1 -1
  114. package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/[msgId]/route.js.nft.json +1 -1
  115. package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/route.js.nft.json +1 -1
  116. package/.next/standalone/.next/server/app/api/workspaces/[id]/route.js.nft.json +1 -1
  117. package/.next/standalone/.next/server/app/api/workspaces/[id]/sessions/route.js.nft.json +1 -1
  118. package/.next/standalone/.next/server/app/api/workspaces/[id]/todos/[todoId]/route.js.nft.json +1 -1
  119. package/.next/standalone/.next/server/app/api/workspaces/[id]/todos/route.js.nft.json +1 -1
  120. package/.next/standalone/.next/server/app/api/workspaces/route.js.nft.json +1 -1
  121. package/.next/standalone/.next/server/app/login/page.js.nft.json +1 -1
  122. package/.next/standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
  123. package/.next/standalone/.next/server/app/login.html +1 -1
  124. package/.next/standalone/.next/server/app/login.rsc +2 -2
  125. package/.next/standalone/.next/server/app/login.segments/_full.segment.rsc +2 -2
  126. package/.next/standalone/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  127. package/.next/standalone/.next/server/app/login.segments/login/__PAGE__.segment.rsc +2 -2
  128. package/.next/standalone/.next/server/app/m/page.js.nft.json +1 -1
  129. package/.next/standalone/.next/server/app/m/page_client-reference-manifest.js +1 -1
  130. package/.next/standalone/.next/server/app/m/projects/page.js.nft.json +1 -1
  131. package/.next/standalone/.next/server/app/m/projects/page_client-reference-manifest.js +1 -1
  132. package/.next/standalone/.next/server/app/m/projects.html +1 -1
  133. package/.next/standalone/.next/server/app/m/projects.rsc +2 -2
  134. package/.next/standalone/.next/server/app/m/projects.segments/_full.segment.rsc +2 -2
  135. package/.next/standalone/.next/server/app/m/projects.segments/_tree.segment.rsc +2 -2
  136. package/.next/standalone/.next/server/app/m/projects.segments/m/projects/__PAGE__.segment.rsc +2 -2
  137. package/.next/standalone/.next/server/app/m/sessions/[id]/page.js.nft.json +1 -1
  138. package/.next/standalone/.next/server/app/m/sessions/[id]/page_client-reference-manifest.js +1 -1
  139. package/.next/standalone/.next/server/app/m/sessions/page.js.nft.json +1 -1
  140. package/.next/standalone/.next/server/app/m/sessions/page_client-reference-manifest.js +1 -1
  141. package/.next/standalone/.next/server/app/m/sessions.html +1 -1
  142. package/.next/standalone/.next/server/app/m/sessions.rsc +2 -2
  143. package/.next/standalone/.next/server/app/m/sessions.segments/_full.segment.rsc +2 -2
  144. package/.next/standalone/.next/server/app/m/sessions.segments/_tree.segment.rsc +2 -2
  145. package/.next/standalone/.next/server/app/m/sessions.segments/m/sessions/__PAGE__.segment.rsc +2 -2
  146. package/.next/standalone/.next/server/app/m/settings/page.js.nft.json +1 -1
  147. package/.next/standalone/.next/server/app/m/settings/page_client-reference-manifest.js +1 -1
  148. package/.next/standalone/.next/server/app/m/settings.html +1 -1
  149. package/.next/standalone/.next/server/app/m/settings.rsc +2 -2
  150. package/.next/standalone/.next/server/app/m/settings.segments/_full.segment.rsc +2 -2
  151. package/.next/standalone/.next/server/app/m/settings.segments/_tree.segment.rsc +2 -2
  152. package/.next/standalone/.next/server/app/m/settings.segments/m/settings/__PAGE__.segment.rsc +2 -2
  153. package/.next/standalone/.next/server/app/m/terminal/page.js.nft.json +1 -1
  154. package/.next/standalone/.next/server/app/m/terminal/page_client-reference-manifest.js +1 -1
  155. package/.next/standalone/.next/server/app/m/terminal.html +1 -1
  156. package/.next/standalone/.next/server/app/m/terminal.rsc +2 -2
  157. package/.next/standalone/.next/server/app/m/terminal.segments/_full.segment.rsc +2 -2
  158. package/.next/standalone/.next/server/app/m/terminal.segments/_tree.segment.rsc +2 -2
  159. package/.next/standalone/.next/server/app/m/terminal.segments/m/terminal/__PAGE__.segment.rsc +2 -2
  160. package/.next/standalone/.next/server/app/m.html +1 -1
  161. package/.next/standalone/.next/server/app/m.rsc +2 -2
  162. package/.next/standalone/.next/server/app/m.segments/_full.segment.rsc +2 -2
  163. package/.next/standalone/.next/server/app/m.segments/_tree.segment.rsc +2 -2
  164. package/.next/standalone/.next/server/app/m.segments/m/__PAGE__.segment.rsc +2 -2
  165. package/.next/standalone/.next/server/app/network.html +1 -1
  166. package/.next/standalone/.next/server/app/network.rsc +2 -2
  167. package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap/network/__PAGE__.segment.rsc +2 -2
  168. package/.next/standalone/.next/server/app/network.segments/_full.segment.rsc +2 -2
  169. package/.next/standalone/.next/server/app/network.segments/_tree.segment.rsc +2 -2
  170. package/.next/standalone/.next/server/app/projects.html +1 -1
  171. package/.next/standalone/.next/server/app/projects.rsc +2 -2
  172. package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap/projects/__PAGE__.segment.rsc +2 -2
  173. package/.next/standalone/.next/server/app/projects.segments/_full.segment.rsc +2 -2
  174. package/.next/standalone/.next/server/app/projects.segments/_tree.segment.rsc +2 -2
  175. package/.next/standalone/.next/server/app/sessions.html +1 -1
  176. package/.next/standalone/.next/server/app/sessions.rsc +2 -2
  177. package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap/sessions/__PAGE__.segment.rsc +2 -2
  178. package/.next/standalone/.next/server/app/sessions.segments/_full.segment.rsc +2 -2
  179. package/.next/standalone/.next/server/app/sessions.segments/_tree.segment.rsc +2 -2
  180. package/.next/standalone/.next/server/app/settings.html +1 -1
  181. package/.next/standalone/.next/server/app/settings.rsc +2 -2
  182. package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap/settings/__PAGE__.segment.rsc +2 -2
  183. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +2 -2
  184. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  185. package/.next/standalone/.next/server/app/terminal.html +1 -1
  186. package/.next/standalone/.next/server/app/terminal.rsc +2 -2
  187. package/.next/standalone/.next/server/app/terminal.segments/!KGRlc2t0b3Ap/terminal/__PAGE__.segment.rsc +2 -2
  188. package/.next/standalone/.next/server/app/terminal.segments/_full.segment.rsc +2 -2
  189. package/.next/standalone/.next/server/app/terminal.segments/_tree.segment.rsc +2 -2
  190. package/.next/standalone/.next/server/app/vr/page.js.nft.json +1 -1
  191. package/.next/standalone/.next/server/app/vr/page_client-reference-manifest.js +1 -1
  192. package/.next/standalone/.next/server/app/vr.html +1 -1
  193. package/.next/standalone/.next/server/app/vr.rsc +2 -2
  194. package/.next/standalone/.next/server/app/vr.segments/_full.segment.rsc +2 -2
  195. package/.next/standalone/.next/server/app/vr.segments/_tree.segment.rsc +2 -2
  196. package/.next/standalone/.next/server/app/vr.segments/vr/__PAGE__.segment.rsc +2 -2
  197. package/.next/standalone/.next/server/app/workspaces.html +1 -1
  198. package/.next/standalone/.next/server/app/workspaces.rsc +2 -2
  199. package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap/workspaces/__PAGE__.segment.rsc +2 -2
  200. package/.next/standalone/.next/server/app/workspaces.segments/_full.segment.rsc +2 -2
  201. package/.next/standalone/.next/server/app/workspaces.segments/_tree.segment.rsc +2 -2
  202. package/.next/standalone/.next/server/chunks/_1-inpovedul0t._.js +1 -1
  203. package/.next/standalone/.next/server/chunks/ssr/_1yl1yt70hdi7k._.js +1 -1
  204. package/.next/standalone/.next/server/edge/chunks/[root-of-the-server]__00lhc9vpmf8oy._.js +1 -1
  205. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  206. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  207. package/.next/standalone/.next/server/pages/404.html +1 -1
  208. package/.next/standalone/.next/server/pages/500.html +1 -1
  209. package/.next/standalone/.next/server/preview-props.json +3 -3
  210. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  211. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  212. package/.next/standalone/.next/static/chunks/07t-bv_pwp3nh.css +3 -0
  213. package/.next/standalone/package.json +6 -6
  214. package/README.md +29 -3
  215. package/bin/lib/cli-flags.js +2 -0
  216. package/bin/lib/remote.js +124 -15
  217. package/bin/lib/runtime/claude-sessions.js +108 -13
  218. package/bin/lib/runtime/index.js +1 -1
  219. package/bin/spaces.js +330 -57
  220. package/package.json +6 -6
  221. package/.next/standalone/.next/static/chunks/3bk2w5qzafz7n.css +0 -3
  222. /package/.next/standalone/.next/static/{_RTyuAfH-40lIlwVs2azh → B50xUMpZMRp2r4CxAPPFu}/_buildManifest.js +0 -0
  223. /package/.next/standalone/.next/static/{_RTyuAfH-40lIlwVs2azh → B50xUMpZMRp2r4CxAPPFu}/_clientMiddlewareManifest.js +0 -0
  224. /package/.next/standalone/.next/static/{_RTyuAfH-40lIlwVs2azh → B50xUMpZMRp2r4CxAPPFu}/_ssgManifest.js +0 -0
@@ -9,22 +9,33 @@
9
9
  // conversation exactly. Guessing from the newest transcript in the pane's
10
10
  // folder instead picks up any other Claude working in the same folder.
11
11
  //
12
+ // Windows has no `ps`, and ancestry breaks there anyway: the `claude` shim
13
+ // is a shell script that `exec`s the real program, and Git Bash leaves a
14
+ // dead pid in the chain when it does. What every process in a pane does
15
+ // share is the pane's console, so on Windows the pane is found by asking
16
+ // which processes are attached to its console instead.
17
+ //
12
18
  // Recorded so that a pane stopped by a server restart or a reboot resumes
13
19
  // its conversation (pane.restart) rather than starting a new one.
14
20
 
15
21
  const fs = require('fs');
16
22
  const os = require('os');
17
23
  const path = require('path');
18
- const { execFile } = require('child_process');
24
+ const { execFile, fork } = require('child_process');
19
25
 
20
26
  const POLL_MS = 5000;
21
27
  // Deeper than any shell → wrapper → node → claude chain, and a bound on a
22
28
  // corrupt table that loops.
23
29
  const MAX_DEPTH = 32;
30
+ // A console-list helper that hangs is given up on; the pane stays unplaced
31
+ // and is looked at again next tick.
32
+ const CONSOLE_LIST_MS = 5000;
24
33
 
25
- /** pid → ppid from `ps`, or null where that is unavailable (Windows). */
26
- function readProcessTree(callback, run = execFile) {
27
- if (process.platform === 'win32') return callback(null);
34
+ /**
35
+ * pid → ppid from `ps`, or null when it cannot be read. `panes` is unused
36
+ * here; the Windows reader takes the same arguments.
37
+ */
38
+ function readProcessTree(panes, callback, run = execFile) {
28
39
  run('ps', ['-A', '-o', 'pid=,ppid='], { timeout: 3000 }, (error, stdout) => {
29
40
  if (error) return callback(null);
30
41
  const tree = new Map();
@@ -32,10 +43,59 @@ function readProcessTree(callback, run = execFile) {
32
43
  const [pid, ppid] = line.trim().split(/\s+/).map(Number);
33
44
  if (Number.isInteger(pid) && Number.isInteger(ppid)) tree.set(pid, ppid);
34
45
  }
35
- callback(tree);
46
+ callback(tree.size ? tree : null);
36
47
  });
37
48
  }
38
49
 
50
+ // node-pty ships this helper for its own kill(): it must run in a separate
51
+ // process, since a process can attach to only one console at a time.
52
+ function consoleListAgent() {
53
+ try { return require.resolve('node-pty/lib/conpty_console_list_agent'); } catch { return null; }
54
+ }
55
+
56
+ /** The pids attached to the console of `pid`, or [] when that cannot be read. */
57
+ function readConsoleProcesses(pid, callback, forkProcess = fork) {
58
+ const agentPath = consoleListAgent();
59
+ if (!agentPath) return callback([]);
60
+ let agent;
61
+ try {
62
+ agent = forkProcess(agentPath, [String(pid)], { stdio: ['ignore', 'ignore', 'ignore', 'ipc'], windowsHide: true });
63
+ } catch {
64
+ return callback([]);
65
+ }
66
+ let done = false;
67
+ const finish = (list) => {
68
+ if (done) return;
69
+ done = true;
70
+ clearTimeout(timer);
71
+ callback(list);
72
+ };
73
+ const timer = setTimeout(() => { try { agent.kill(); } catch { /* gone */ } finish([]); }, CONSOLE_LIST_MS);
74
+ agent.on('message', (m) => finish(Array.isArray(m?.consoleProcessList) ? m.consoleProcessList.filter(Number.isInteger) : []));
75
+ agent.on('error', () => finish([]));
76
+ agent.on('exit', () => finish([]));
77
+ }
78
+
79
+ /**
80
+ * The Windows reader: a tree in which every process attached to a pane's
81
+ * console hangs directly off that pane's pid, so matchSessions needs one
82
+ * step. Null when no console could be read, so the watcher tries again.
83
+ */
84
+ function readConsoleTree(panes, callback, readConsole = readConsoleProcesses) {
85
+ const shells = panes.filter(p => Number.isInteger(p.pid));
86
+ if (!shells.length) return callback(null);
87
+ const tree = new Map();
88
+ let pending = shells.length;
89
+ let anyRead = false;
90
+ for (const { pid } of shells) {
91
+ readConsole(pid, (list) => {
92
+ if (list.length) anyRead = true;
93
+ for (const member of list) if (member !== pid) tree.set(member, pid);
94
+ if (--pending === 0) callback(anyRead ? tree : null);
95
+ });
96
+ }
97
+ }
98
+
39
99
  /** `{ pid, sessionId }` for every interactive Claude session file. */
40
100
  function readClaudeSessions(home, fsMod = fs) {
41
101
  const dir = path.join(home, '.claude', 'sessions');
@@ -86,23 +146,55 @@ function matchSessions(panes, sessions, tree) {
86
146
  */
87
147
  function createClaudeSessionWatcher({
88
148
  listPanes, onSession, home = os.homedir(), intervalMs = POLL_MS,
89
- readTree = readProcessTree, readSessions = readClaudeSessions,
149
+ readTree = process.platform === 'win32' ? readConsoleTree : readProcessTree,
150
+ readSessions = readClaudeSessions,
90
151
  }) {
91
152
  let busy = false;
153
+ // A process's parents are fixed when it starts, so a session's pid, once
154
+ // placed in a pane (or in none), needs no second look at the process
155
+ // table: /resume and /clear change the sessionId under the same pid.
156
+ // Forgotten when the set of panes changes, since a pane listed late may be
157
+ // the one a session already looked at belongs to.
158
+ const placed = new Map(); // pid → paneId | null
159
+ let placedFor = '';
160
+
161
+ function report(panes, found) {
162
+ for (const [paneId, sessionId] of found) {
163
+ const pane = panes.find(p => p.paneId === paneId);
164
+ if (pane && pane.sessionId !== sessionId) {
165
+ try { onSession(paneId, sessionId); } catch { /* bookkeeping only */ }
166
+ }
167
+ }
168
+ }
169
+
92
170
  function tick() {
93
171
  if (busy) return;
94
172
  const panes = listPanes();
95
173
  if (!panes.length) return;
174
+ const key = panes.map(p => `${p.paneId}:${p.pid}`).sort().join(',');
175
+ if (key !== placedFor) { placed.clear(); placedFor = key; }
176
+ const sessions = readSessions(home);
177
+ const current = new Set(sessions.map(s => s.pid));
178
+ for (const pid of placed.keys()) if (!current.has(pid)) placed.delete(pid);
179
+
180
+ const known = new Map();
181
+ for (const { pid, sessionId } of sessions) {
182
+ const paneId = placed.get(pid);
183
+ if (paneId) known.set(paneId, sessionId);
184
+ }
185
+ report(panes, known);
186
+ const unplaced = sessions.filter(s => !placed.has(s.pid));
187
+ if (!unplaced.length) return;
188
+
96
189
  busy = true;
97
- readTree((tree) => {
190
+ readTree(panes, (tree) => {
98
191
  busy = false;
99
192
  if (!tree) return;
100
- for (const [paneId, sessionId] of matchSessions(panes, readSessions(home), tree)) {
101
- const pane = panes.find(p => p.paneId === paneId);
102
- if (pane && pane.sessionId !== sessionId) {
103
- try { onSession(paneId, sessionId); } catch { /* bookkeeping only */ }
104
- }
193
+ for (const s of unplaced) {
194
+ const [paneId] = matchSessions(panes, [s], tree).keys();
195
+ placed.set(s.pid, paneId ?? null);
105
196
  }
197
+ report(panes, matchSessions(panes, unplaced, tree));
106
198
  });
107
199
  }
108
200
  const timer = setInterval(tick, intervalMs);
@@ -110,4 +202,7 @@ function createClaudeSessionWatcher({
110
202
  return { tick, stop: () => clearInterval(timer) };
111
203
  }
112
204
 
113
- module.exports = { createClaudeSessionWatcher, matchSessions, readClaudeSessions, readProcessTree };
205
+ module.exports = {
206
+ createClaudeSessionWatcher, matchSessions, readClaudeSessions,
207
+ readProcessTree, readConsoleTree, readConsoleProcesses,
208
+ };
@@ -1000,7 +1000,7 @@ function createRuntime({ nodeId = 'local', spacesDbPath, getApiPort, federationD
1000
1000
  // spawnPane starts only the shell. A fresh pane gets its agent from
1001
1001
  // the client's follow-up agent.start; a restart types it here, so the
1002
1002
  // pane comes back running what it ran before with no second call.
1003
- if (spawned.launchCommand) writePane(params.paneId, `${spawned.launchCommand}
1003
+ if (spawned.launchCommand) writePane(params.paneId, `${spawned.launchCommand}\r`);
1004
1004
  return ok({ paneId: params.paneId, workspaceId, agentType, cwd, resumed: Boolean(agentSession), launchCommand: spawned.launchCommand });
1005
1005
  }
1006
1006