@tt-a1i/hive 1.6.0 → 2.0.1

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 (254) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.en.md +74 -11
  3. package/README.md +42 -8
  4. package/dist/src/cli/hive-remote.d.ts +46 -0
  5. package/dist/src/cli/hive-remote.js +257 -0
  6. package/dist/src/cli/hive-update.js +7 -2
  7. package/dist/src/cli/hive.d.ts +6 -0
  8. package/dist/src/cli/hive.js +64 -0
  9. package/dist/src/cli/team.d.ts +22 -0
  10. package/dist/src/cli/team.js +255 -5
  11. package/dist/src/server/agent-command-resolver.js +10 -3
  12. package/dist/src/server/agent-exit-classification.d.ts +6 -0
  13. package/dist/src/server/agent-exit-classification.js +6 -0
  14. package/dist/src/server/agent-manager-support.d.ts +2 -1
  15. package/dist/src/server/agent-manager-support.js +59 -15
  16. package/dist/src/server/agent-manager.d.ts +3 -0
  17. package/dist/src/server/agent-manager.js +22 -7
  18. package/dist/src/server/agent-run-bootstrap.d.ts +20 -1
  19. package/dist/src/server/agent-run-bootstrap.js +16 -6
  20. package/dist/src/server/agent-run-exit-handler.js +14 -8
  21. package/dist/src/server/agent-run-starter.d.ts +3 -1
  22. package/dist/src/server/agent-run-starter.js +37 -6
  23. package/dist/src/server/agent-run-sync.js +13 -5
  24. package/dist/src/server/agent-runtime-types.d.ts +1 -0
  25. package/dist/src/server/agent-runtime.d.ts +2 -1
  26. package/dist/src/server/agent-runtime.js +9 -2
  27. package/dist/src/server/agent-startup-instructions.d.ts +2 -1
  28. package/dist/src/server/agent-startup-instructions.js +8 -4
  29. package/dist/src/server/agent-stdin-dispatcher.d.ts +4 -2
  30. package/dist/src/server/agent-stdin-dispatcher.js +35 -3
  31. package/dist/src/server/command-preset-defaults.d.ts +6 -1
  32. package/dist/src/server/command-preset-defaults.js +68 -0
  33. package/dist/src/server/fs-browse.d.ts +2 -0
  34. package/dist/src/server/fs-browse.js +165 -31
  35. package/dist/src/server/fs-pick-folder.js +6 -69
  36. package/dist/src/server/fs-sandbox.d.ts +5 -3
  37. package/dist/src/server/fs-sandbox.js +5 -3
  38. package/dist/src/server/hive-team-guidance.js +18 -6
  39. package/dist/src/server/machine-name.d.ts +2 -0
  40. package/dist/src/server/machine-name.js +13 -0
  41. package/dist/src/server/open-target-commands.d.ts +1 -0
  42. package/dist/src/server/open-target-commands.js +4 -1
  43. package/dist/src/server/orchestrator-autostart.js +1 -1
  44. package/dist/src/server/platform-path.d.ts +1 -0
  45. package/dist/src/server/platform-path.js +14 -1
  46. package/dist/src/server/post-start-input-writer.js +50 -13
  47. package/dist/src/server/preset-launch-support.js +3 -1
  48. package/dist/src/server/recovery-summary.d.ts +2 -1
  49. package/dist/src/server/recovery-summary.js +2 -1
  50. package/dist/src/server/remote-audit-store.d.ts +51 -0
  51. package/dist/src/server/remote-audit-store.js +108 -0
  52. package/dist/src/server/remote-config-keys.d.ts +17 -0
  53. package/dist/src/server/remote-config-keys.js +27 -0
  54. package/dist/src/server/remote-control-constants.d.ts +30 -0
  55. package/dist/src/server/remote-control-constants.js +29 -0
  56. package/dist/src/server/remote-device-session.d.ts +40 -0
  57. package/dist/src/server/remote-device-session.js +22 -0
  58. package/dist/src/server/remote-device-store.d.ts +36 -0
  59. package/dist/src/server/remote-device-store.js +67 -0
  60. package/dist/src/server/remote-frame-bridge.d.ts +102 -0
  61. package/dist/src/server/remote-frame-bridge.js +791 -0
  62. package/dist/src/server/remote-gateway-client.d.ts +14 -0
  63. package/dist/src/server/remote-gateway-client.js +36 -0
  64. package/dist/src/server/remote-loopback-auth.d.ts +6 -0
  65. package/dist/src/server/remote-loopback-auth.js +112 -0
  66. package/dist/src/server/remote-pairing-tunnel.d.ts +59 -0
  67. package/dist/src/server/remote-pairing-tunnel.js +146 -0
  68. package/dist/src/server/remote-pairing.d.ts +58 -0
  69. package/dist/src/server/remote-pairing.js +237 -0
  70. package/dist/src/server/remote-tunnel.d.ts +113 -0
  71. package/dist/src/server/remote-tunnel.js +514 -0
  72. package/dist/src/server/restart-policy-support.d.ts +4 -1
  73. package/dist/src/server/restart-policy-support.js +3 -1
  74. package/dist/src/server/restart-policy.d.ts +1 -1
  75. package/dist/src/server/restart-policy.js +19 -3
  76. package/dist/src/server/route-types.d.ts +1 -1
  77. package/dist/src/server/routes-dispatches.js +1 -1
  78. package/dist/src/server/routes-fs.js +3 -3
  79. package/dist/src/server/routes-marketplace.js +2 -2
  80. package/dist/src/server/routes-open-workspace.js +1 -1
  81. package/dist/src/server/routes-remote.d.ts +2 -0
  82. package/dist/src/server/routes-remote.js +166 -0
  83. package/dist/src/server/routes-runtime.js +6 -6
  84. package/dist/src/server/routes-settings.js +16 -16
  85. package/dist/src/server/routes-tasks.js +2 -2
  86. package/dist/src/server/routes-team-memory.d.ts +2 -0
  87. package/dist/src/server/routes-team-memory.js +154 -0
  88. package/dist/src/server/routes-team-recall.d.ts +2 -0
  89. package/dist/src/server/routes-team-recall.js +119 -0
  90. package/dist/src/server/routes-team.js +31 -9
  91. package/dist/src/server/routes-ui.js +11 -1
  92. package/dist/src/server/routes-workflow-schedules.js +3 -3
  93. package/dist/src/server/routes-workflows.js +5 -5
  94. package/dist/src/server/routes-workspace-memory-dreams.d.ts +2 -0
  95. package/dist/src/server/routes-workspace-memory-dreams.js +105 -0
  96. package/dist/src/server/routes-workspace-memory.d.ts +2 -0
  97. package/dist/src/server/routes-workspace-memory.js +215 -0
  98. package/dist/src/server/routes-workspaces.js +9 -9
  99. package/dist/src/server/routes.js +10 -0
  100. package/dist/src/server/runtime-database.d.ts +1 -0
  101. package/dist/src/server/runtime-database.js +27 -2
  102. package/dist/src/server/runtime-restart-policy.d.ts +3 -1
  103. package/dist/src/server/runtime-restart-policy.js +2 -1
  104. package/dist/src/server/runtime-store-contract.d.ts +37 -0
  105. package/dist/src/server/runtime-store-dream.d.ts +23 -0
  106. package/dist/src/server/runtime-store-dream.js +16 -0
  107. package/dist/src/server/runtime-store-helpers.d.ts +20 -0
  108. package/dist/src/server/runtime-store-helpers.js +81 -7
  109. package/dist/src/server/runtime-store-memory.d.ts +33 -0
  110. package/dist/src/server/runtime-store-memory.js +37 -0
  111. package/dist/src/server/runtime-store-remote.d.ts +5 -0
  112. package/dist/src/server/runtime-store-remote.js +45 -0
  113. package/dist/src/server/runtime-store-workflows.js +2 -0
  114. package/dist/src/server/runtime-store.js +14 -3
  115. package/dist/src/server/session-capture-claude.d.ts +1 -1
  116. package/dist/src/server/session-capture-claude.js +7 -4
  117. package/dist/src/server/session-capture-codex.js +4 -5
  118. package/dist/src/server/session-capture-gemini.js +4 -5
  119. package/dist/src/server/session-capture-opencode.d.ts +4 -4
  120. package/dist/src/server/session-capture-opencode.js +20 -12
  121. package/dist/src/server/session-capture-qwen.d.ts +5 -0
  122. package/dist/src/server/session-capture-qwen.js +104 -0
  123. package/dist/src/server/session-capture.d.ts +23 -0
  124. package/dist/src/server/session-capture.js +48 -0
  125. package/dist/src/server/sqlite-schema-v22.d.ts +2 -0
  126. package/dist/src/server/sqlite-schema-v22.js +27 -0
  127. package/dist/src/server/sqlite-schema-v23.d.ts +2 -0
  128. package/dist/src/server/sqlite-schema-v23.js +43 -0
  129. package/dist/src/server/sqlite-schema-v24.d.ts +2 -0
  130. package/dist/src/server/sqlite-schema-v24.js +34 -0
  131. package/dist/src/server/sqlite-schema-v25.d.ts +2 -0
  132. package/dist/src/server/sqlite-schema-v25.js +127 -0
  133. package/dist/src/server/sqlite-schema-v26.d.ts +2 -0
  134. package/dist/src/server/sqlite-schema-v26.js +56 -0
  135. package/dist/src/server/sqlite-schema-v27.d.ts +6 -0
  136. package/dist/src/server/sqlite-schema-v27.js +92 -0
  137. package/dist/src/server/sqlite-schema-v28.d.ts +2 -0
  138. package/dist/src/server/sqlite-schema-v28.js +19 -0
  139. package/dist/src/server/sqlite-schema-v29.d.ts +2 -0
  140. package/dist/src/server/sqlite-schema-v29.js +27 -0
  141. package/dist/src/server/sqlite-schema-v30.d.ts +2 -0
  142. package/dist/src/server/sqlite-schema-v30.js +27 -0
  143. package/dist/src/server/sqlite-schema-v31.d.ts +2 -0
  144. package/dist/src/server/sqlite-schema-v31.js +30 -0
  145. package/dist/src/server/sqlite-schema.d.ts +1 -1
  146. package/dist/src/server/sqlite-schema.js +54 -1
  147. package/dist/src/server/startup-command-parser.js +5 -1
  148. package/dist/src/server/tasks-file-watcher.d.ts +2 -0
  149. package/dist/src/server/tasks-file-watcher.js +15 -6
  150. package/dist/src/server/tasks-file.js +30 -5
  151. package/dist/src/server/tasks-websocket-server.js +4 -0
  152. package/dist/src/server/team-authz.d.ts +1 -1
  153. package/dist/src/server/team-authz.js +13 -1
  154. package/dist/src/server/team-list-enrichment.js +3 -1
  155. package/dist/src/server/team-memory-digest.d.ts +52 -0
  156. package/dist/src/server/team-memory-digest.js +200 -0
  157. package/dist/src/server/team-memory-dream-applier.d.ts +5 -0
  158. package/dist/src/server/team-memory-dream-applier.js +234 -0
  159. package/dist/src/server/team-memory-dream-http-serializers.d.ts +13 -0
  160. package/dist/src/server/team-memory-dream-http-serializers.js +12 -0
  161. package/dist/src/server/team-memory-dream-ops.d.ts +40 -0
  162. package/dist/src/server/team-memory-dream-ops.js +153 -0
  163. package/dist/src/server/team-memory-dream-reverter.d.ts +22 -0
  164. package/dist/src/server/team-memory-dream-reverter.js +221 -0
  165. package/dist/src/server/team-memory-dream-run-store.d.ts +23 -0
  166. package/dist/src/server/team-memory-dream-run-store.js +211 -0
  167. package/dist/src/server/team-memory-dream-runner.d.ts +37 -0
  168. package/dist/src/server/team-memory-dream-runner.js +178 -0
  169. package/dist/src/server/team-memory-dream-scheduler.d.ts +32 -0
  170. package/dist/src/server/team-memory-dream-scheduler.js +115 -0
  171. package/dist/src/server/team-memory-dream-store.d.ts +19 -0
  172. package/dist/src/server/team-memory-dream-store.js +16 -0
  173. package/dist/src/server/team-memory-dream-types.d.ts +104 -0
  174. package/dist/src/server/team-memory-dream-types.js +23 -0
  175. package/dist/src/server/team-memory-export.d.ts +22 -0
  176. package/dist/src/server/team-memory-export.js +220 -0
  177. package/dist/src/server/team-memory-feature.d.ts +12 -0
  178. package/dist/src/server/team-memory-feature.js +12 -0
  179. package/dist/src/server/team-memory-http-serializers.d.ts +102 -0
  180. package/dist/src/server/team-memory-http-serializers.js +46 -0
  181. package/dist/src/server/team-memory-injection.d.ts +31 -0
  182. package/dist/src/server/team-memory-injection.js +49 -0
  183. package/dist/src/server/team-memory-store.d.ts +116 -0
  184. package/dist/src/server/team-memory-store.js +513 -0
  185. package/dist/src/server/team-operations.d.ts +5 -1
  186. package/dist/src/server/team-operations.js +46 -16
  187. package/dist/src/server/team-recall-store.d.ts +38 -0
  188. package/dist/src/server/team-recall-store.js +205 -0
  189. package/dist/src/server/terminal-input-profile.d.ts +1 -1
  190. package/dist/src/server/terminal-input-profile.js +8 -0
  191. package/dist/src/server/terminal-ws-server.js +6 -0
  192. package/dist/src/server/ui-auth-helpers.d.ts +1 -1
  193. package/dist/src/server/ui-auth-helpers.js +7 -1
  194. package/dist/src/server/ui-auth.d.ts +3 -0
  195. package/dist/src/server/ui-auth.js +21 -1
  196. package/dist/src/server/workflow-cli-policy.d.ts +2 -3
  197. package/dist/src/server/workflow-cli-policy.js +3 -3
  198. package/dist/src/server/workflow-runner.d.ts +1 -0
  199. package/dist/src/server/workflow-runner.js +9 -4
  200. package/dist/src/server/workspace-path-validation.js +6 -2
  201. package/dist/src/server/workspace-store.d.ts +1 -1
  202. package/dist/src/server/workspace-store.js +35 -9
  203. package/dist/src/shared/fs-browse.d.ts +1 -0
  204. package/dist/src/shared/fs-browse.js +1 -0
  205. package/dist/src/shared/path-input.d.ts +12 -0
  206. package/dist/src/shared/path-input.js +22 -0
  207. package/dist/src/shared/remote-bridge-routing.d.ts +19 -0
  208. package/dist/src/shared/remote-bridge-routing.js +141 -0
  209. package/dist/src/shared/remote-crypto.d.ts +138 -0
  210. package/dist/src/shared/remote-crypto.js +427 -0
  211. package/dist/src/shared/remote-pairing-code.d.ts +7 -0
  212. package/dist/src/shared/remote-pairing-code.js +47 -0
  213. package/dist/src/shared/remote-protocol.d.ts +160 -0
  214. package/dist/src/shared/remote-protocol.js +526 -0
  215. package/dist/src/shared/team-memory.d.ts +11 -0
  216. package/dist/src/shared/team-memory.js +10 -0
  217. package/dist/src/shared/team-recall.d.ts +1 -0
  218. package/dist/src/shared/team-recall.js +1 -0
  219. package/dist/src/shared/types.d.ts +5 -6
  220. package/package.json +12 -5
  221. package/scripts/postinstall-native-artifacts.mjs +113 -0
  222. package/web/dist/assets/AddWorkerDialog-C86CwNgQ.js +2 -0
  223. package/web/dist/assets/AddWorkspaceFlow-Bm2Jz34D.js +1 -0
  224. package/web/dist/assets/FirstRunWizard-XzBoEpA5.js +1 -0
  225. package/web/dist/assets/MarketplaceDrawer-BFfGT8hH.js +67 -0
  226. package/web/dist/assets/TaskGraphDrawer-_uVH_0C1.js +1 -0
  227. package/web/dist/assets/{WhatsNewDialog-CSGzk-2U.js → WhatsNewDialog-DkJHmkMs.js} +1 -1
  228. package/web/dist/assets/WorkerModal-BtMJEOG9.js +1 -0
  229. package/web/dist/assets/WorkflowsDrawer-CiIdHS6_.js +1 -0
  230. package/web/dist/assets/WorkspaceMemoryDrawer-C6sNocl_.js +1 -0
  231. package/web/dist/assets/WorkspaceTaskDrawer-CyhhEB1Z.js +1 -0
  232. package/web/dist/assets/index-BAiLYajK.css +1 -0
  233. package/web/dist/assets/index-K-GG8UwR.js +73 -0
  234. package/web/dist/assets/search-BtRkkEmS.js +1 -0
  235. package/web/dist/assets/square-terminal-lEeQUWb3.js +1 -0
  236. package/web/dist/cli-icons/agy.png +0 -0
  237. package/web/dist/cli-icons/cursor.ico +0 -0
  238. package/web/dist/cli-icons/grok.ico +0 -0
  239. package/web/dist/cli-icons/hermes.png +0 -0
  240. package/web/dist/cli-icons/qwen.png +0 -0
  241. package/web/dist/index.html +8 -3
  242. package/web/dist/sw.js +1 -1
  243. package/scripts/fix-runtime-artifacts.mjs +0 -33
  244. package/web/dist/assets/AddWorkerDialog-CGbaxu0T.js +0 -2
  245. package/web/dist/assets/AddWorkspaceDialog-CNgExu6b.js +0 -1
  246. package/web/dist/assets/FirstRunWizard-DxGApUNc.js +0 -1
  247. package/web/dist/assets/MarketplaceDrawer-Bk6cpukn.js +0 -76
  248. package/web/dist/assets/WorkerModal-i2F3n3nZ.js +0 -1
  249. package/web/dist/assets/WorkspaceTaskDrawer-C_Ta_K13.js +0 -1
  250. package/web/dist/assets/WorkspaceTerminalPanels-DDGTF8rc.css +0 -1
  251. package/web/dist/assets/WorkspaceTerminalPanels-VdDxtrQF.js +0 -1
  252. package/web/dist/assets/index-5zh61jMg.css +0 -1
  253. package/web/dist/assets/index-CAgGM6nb.js +0 -75
  254. package/web/dist/assets/path-join-7MR1s7b1.js +0 -1
@@ -2,6 +2,7 @@ import { FEATURE_FLAGS_ALL_OFF } from './feature-flags.js';
2
2
  import { buildOrchestratorReminderTail, buildWorkerReminderTail } from './hive-team-guidance.js';
3
3
  import { PtyInactiveError } from './http-errors.js';
4
4
  import { createPostStartInputWriter } from './post-start-input-writer.js';
5
+ import { buildDispatchMemoryDigestSafely, logMemoryDigestInjection, rollbackMemoryDigestInjection, } from './team-memory-injection.js';
5
6
  export const buildOrchestratorReportPayload = (workerName, text, artifacts, flags = FEATURE_FLAGS_ALL_OFF) => {
6
7
  const lines = [`<hive-message kind="report" from="@${workerName}">`, text];
7
8
  for (const artifact of artifacts)
@@ -17,7 +18,7 @@ export const buildOrchestratorStatusPayload = (workerName, text, artifacts, flag
17
18
  return lines.join('\n');
18
19
  };
19
20
  export const buildOrchestratorUserInputPayload = (text, flags = FEATURE_FLAGS_ALL_OFF) => [text, '', buildOrchestratorReminderTail(flags), ''].join('\n');
20
- export const buildWorkerDispatchPayload = (fromAgentName, workerDescription, dispatchId, text) => [
21
+ export const buildWorkerDispatchPayload = (fromAgentName, workerDescription, dispatchId, text, memoryDigest) => [
21
22
  `<hive-message kind="dispatch" from="@${fromAgentName}">`,
22
23
  '',
23
24
  `Your role: ${workerDescription}`,
@@ -28,6 +29,7 @@ export const buildWorkerDispatchPayload = (fromAgentName, workerDescription, dis
28
29
  '',
29
30
  `dispatch_id: ${dispatchId}`,
30
31
  '',
32
+ ...(memoryDigest ? [memoryDigest, ''] : []),
31
33
  'Task:',
32
34
  text,
33
35
  '</hive-message>',
@@ -45,7 +47,7 @@ export const buildWorkerCancelPayload = (dispatchId, reason) => [
45
47
  '</hive-message>',
46
48
  '',
47
49
  ].join('\n');
48
- export const createAgentStdinDispatcher = ({ agentManager, getLaunchConfig, getWorkspaceId, registry, syncRun, getFlags, }) => {
50
+ export const createAgentStdinDispatcher = ({ agentManager, getLaunchConfig, getWorkspaceId, registry, syncRun, memoryInjection, getFlags, }) => {
49
51
  const flags = () => getFlags?.() ?? FEATURE_FLAGS_ALL_OFF;
50
52
  const chains = new Map();
51
53
  const getChain = (agentId) => {
@@ -163,7 +165,37 @@ export const createAgentStdinDispatcher = ({ agentManager, getLaunchConfig, getW
163
165
  swallowQueuedFailure(writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorStatusPayload(workerName, text, artifacts, flags()), input));
164
166
  },
165
167
  writeSendPrompt(workspaceId, workerId, dispatchId, fromAgentName, workerDescription, text) {
166
- return writeToActiveAgentRun(workspaceId, workerId, buildWorkerDispatchPayload(fromAgentName, workerDescription, dispatchId, text), { requireActiveRun: true });
168
+ if (!resolveActiveRun(workspaceId, workerId)) {
169
+ throw new PtyInactiveError(`No active run for agent: ${workerId}`);
170
+ }
171
+ const memoryDigest = buildDispatchMemoryDigestSafely({
172
+ memoryInjection,
173
+ taskText: text,
174
+ workerDescription,
175
+ workspaceId,
176
+ });
177
+ const injectionIds = logMemoryDigestInjection({
178
+ agentId: workerId,
179
+ contextType: 'dispatch',
180
+ dispatchId,
181
+ memoryDigest,
182
+ memoryInjection,
183
+ workspaceId,
184
+ });
185
+ const rollback = () => rollbackMemoryDigestInjection({
186
+ injectionIds,
187
+ memoryInjection,
188
+ });
189
+ try {
190
+ return writeToActiveAgentRun(workspaceId, workerId, buildWorkerDispatchPayload(fromAgentName, workerDescription, dispatchId, text, injectionIds ? memoryDigest?.text : null), { requireActiveRun: true }).catch((error) => {
191
+ rollback();
192
+ throw error;
193
+ });
194
+ }
195
+ catch (error) {
196
+ rollback();
197
+ throw error;
198
+ }
167
199
  },
168
200
  writeCancelPrompt(workspaceId, workerId, dispatchId, reason, input = {}) {
169
201
  swallowQueuedFailure(writeToActiveAgentRun(workspaceId, workerId, buildWorkerCancelPayload(dispatchId, reason), input));
@@ -1,6 +1,8 @@
1
1
  import type { SessionIdCaptureConfig } from './session-capture.js';
2
+ export declare const BUILTIN_COMMAND_PRESET_IDS: readonly ["claude", "codex", "opencode", "gemini", "hermes", "qwen", "agy", "cursor", "grok"];
3
+ export type BuiltinCommandPresetId = (typeof BUILTIN_COMMAND_PRESET_IDS)[number];
2
4
  export interface BuiltinCommandPresetDefaults {
3
- id: string;
5
+ id: BuiltinCommandPresetId;
4
6
  displayName: string;
5
7
  command: string;
6
8
  resumeArgsTemplate: string | null;
@@ -9,3 +11,6 @@ export interface BuiltinCommandPresetDefaults {
9
11
  }
10
12
  export declare const BUILTIN_COMMAND_PRESETS: BuiltinCommandPresetDefaults[];
11
13
  export declare const getBuiltinCommandPreset: (id: string) => BuiltinCommandPresetDefaults | undefined;
14
+ export declare const getBuiltinCommandPresetByCommand: (command: string) => BuiltinCommandPresetDefaults | undefined;
15
+ export declare const BUILTIN_COMMAND_PRESET_CLI_LIST: string;
16
+ export declare const BUILTIN_INTERACTIVE_COMMANDS: Set<string>;
@@ -1,7 +1,23 @@
1
1
  import { CLAUDE_DEFAULT_YOLO_ARGS } from './claude-command-defaults.js';
2
+ export const BUILTIN_COMMAND_PRESET_IDS = [
3
+ 'claude',
4
+ 'codex',
5
+ 'opencode',
6
+ 'gemini',
7
+ 'hermes',
8
+ 'qwen',
9
+ 'agy',
10
+ 'cursor',
11
+ 'grok',
12
+ ];
2
13
  const CODEX_DEFAULT_YOLO_ARGS = ['--dangerously-bypass-approvals-and-sandbox'];
3
14
  const OPENCODE_DEFAULT_YOLO_ARGS = [];
4
15
  const GEMINI_DEFAULT_YOLO_ARGS = ['--yolo'];
16
+ const HERMES_DEFAULT_YOLO_ARGS = ['--yolo'];
17
+ const QWEN_DEFAULT_YOLO_ARGS = ['--approval-mode', 'yolo'];
18
+ const AGY_DEFAULT_YOLO_ARGS = ['--dangerously-skip-permissions'];
19
+ const CURSOR_DEFAULT_YOLO_ARGS = ['--force'];
20
+ const GROK_DEFAULT_YOLO_ARGS = ['--always-approve'];
5
21
  export const BUILTIN_COMMAND_PRESETS = [
6
22
  {
7
23
  command: 'claude',
@@ -47,5 +63,57 @@ export const BUILTIN_COMMAND_PRESETS = [
47
63
  },
48
64
  yoloArgsTemplate: GEMINI_DEFAULT_YOLO_ARGS,
49
65
  },
66
+ {
67
+ command: 'hermes',
68
+ displayName: 'Hermes',
69
+ id: 'hermes',
70
+ resumeArgsTemplate: '--resume {session_id}',
71
+ sessionIdCapture: {
72
+ pattern: String.raw `Session:\s*([A-Za-z0-9_-]+)`,
73
+ source: 'stdout_regex',
74
+ },
75
+ yoloArgsTemplate: HERMES_DEFAULT_YOLO_ARGS,
76
+ },
77
+ {
78
+ command: 'qwen',
79
+ displayName: 'Qwen Code',
80
+ id: 'qwen',
81
+ resumeArgsTemplate: '--resume {session_id}',
82
+ sessionIdCapture: {
83
+ pattern: '~/.qwen/sessions/**/*.json',
84
+ source: 'qwen_session_json_dir',
85
+ },
86
+ yoloArgsTemplate: QWEN_DEFAULT_YOLO_ARGS,
87
+ },
88
+ {
89
+ command: 'agy',
90
+ displayName: 'Antigravity CLI',
91
+ id: 'agy',
92
+ resumeArgsTemplate: '--conversation {session_id}',
93
+ sessionIdCapture: {
94
+ pattern: String.raw `(?:^|\s)(?:\S*[\\/])?agy(?:\.(?:cmd|exe))?\s+--conversation\s+([0-9a-fA-F-]{36})\b`,
95
+ source: 'stdout_regex',
96
+ },
97
+ yoloArgsTemplate: AGY_DEFAULT_YOLO_ARGS,
98
+ },
99
+ {
100
+ command: 'cursor-agent',
101
+ displayName: 'Cursor CLI',
102
+ id: 'cursor',
103
+ resumeArgsTemplate: null,
104
+ sessionIdCapture: null,
105
+ yoloArgsTemplate: CURSOR_DEFAULT_YOLO_ARGS,
106
+ },
107
+ {
108
+ command: 'grok',
109
+ displayName: 'Grok Build',
110
+ id: 'grok',
111
+ resumeArgsTemplate: null,
112
+ sessionIdCapture: null,
113
+ yoloArgsTemplate: GROK_DEFAULT_YOLO_ARGS,
114
+ },
50
115
  ];
51
116
  export const getBuiltinCommandPreset = (id) => BUILTIN_COMMAND_PRESETS.find((preset) => preset.id === id);
117
+ export const getBuiltinCommandPresetByCommand = (command) => BUILTIN_COMMAND_PRESETS.find((preset) => preset.command === command);
118
+ export const BUILTIN_COMMAND_PRESET_CLI_LIST = BUILTIN_COMMAND_PRESET_IDS.join('|');
119
+ export const BUILTIN_INTERACTIVE_COMMANDS = new Set(BUILTIN_COMMAND_PRESETS.flatMap((preset) => [preset.id, preset.command]));
@@ -21,6 +21,8 @@ export interface FsProbeResponse {
21
21
  path: string;
22
22
  suggested_name: string;
23
23
  }
24
+ export declare const getWindowsBrowseParentPath: (candidate: string) => string;
25
+ export declare const getSuggestedWorkspaceNameFromPath: (path: string) => string;
24
26
  export declare const browseDirectory: (requestedPath: string) => Promise<FsBrowseResponse>;
25
27
  export interface ProbeDirectoryOptions {
26
28
  /**
@@ -1,10 +1,9 @@
1
- import { execFile } from 'node:child_process';
2
- import { readdir, stat } from 'node:fs/promises';
3
- import { dirname, resolve } from 'node:path';
4
- import { promisify } from 'node:util';
5
- import { getFsBrowseRoot, isPathWithinRoot } from './fs-sandbox.js';
6
- const execFileP = promisify(execFile);
7
- const GIT_BRANCH_TIMEOUT_MS = 800;
1
+ import { readdir, readFile, stat } from 'node:fs/promises';
2
+ import { dirname, isAbsolute, resolve } from 'node:path';
3
+ import { dirname as dirnameWin32Path, parse as parseWin32Path, resolve as resolveWin32Path, } from 'node:path/win32';
4
+ import { WINDOWS_DRIVES_ROOT } from '../shared/fs-browse.js';
5
+ import { sanitizePastedPath } from '../shared/path-input.js';
6
+ import { getFsBrowseRoot, hasFsBrowseRootOverride, isPathWithinRoot } from './fs-sandbox.js';
8
7
  /**
9
8
  * Map a filesystem rejection (from `readdir`, `stat`, etc.) to a string
10
9
  * suitable for surfacing in the browse response. The common Windows
@@ -28,31 +27,94 @@ const formatFilesystemError = (error) => {
28
27
  }
29
28
  return error.message;
30
29
  };
31
- const detectGitRepository = async (entryPath) => {
30
+ const resolveGitDirPath = async (repoPath) => {
31
+ const dotGitPath = resolve(repoPath, '.git');
32
32
  try {
33
- const info = await stat(resolve(entryPath, '.git'));
34
- return info.isDirectory() || info.isFile();
33
+ const info = await stat(dotGitPath);
34
+ if (info.isDirectory())
35
+ return dotGitPath;
36
+ if (!info.isFile())
37
+ return null;
38
+ const text = await readFile(dotGitPath, 'utf8');
39
+ const firstLine = text.split(/\r?\n/u)[0]?.trim() ?? '';
40
+ const match = /^gitdir:\s*(?<path>.+)$/iu.exec(firstLine);
41
+ const gitDirPath = match?.groups?.path?.trim();
42
+ if (!gitDirPath)
43
+ return null;
44
+ return isAbsolute(gitDirPath) ? gitDirPath : resolve(repoPath, gitDirPath);
35
45
  }
36
46
  catch {
37
- return false;
47
+ return null;
38
48
  }
39
49
  };
50
+ const detectGitRepository = async (entryPath) => (await resolveGitDirPath(entryPath)) !== null;
40
51
  const readCurrentBranch = async (repoPath) => {
52
+ const gitDirPath = await resolveGitDirPath(repoPath);
53
+ if (!gitDirPath)
54
+ return null;
41
55
  try {
42
- const { stdout } = await execFileP('git', ['-C', repoPath, 'rev-parse', '--abbrev-ref', 'HEAD'], {
43
- timeout: GIT_BRANCH_TIMEOUT_MS,
44
- windowsHide: true,
45
- });
46
- const branch = stdout.trim();
47
- return branch.length > 0 ? branch : null;
56
+ const head = (await readFile(resolve(gitDirPath, 'HEAD'), 'utf8')).split(/\r?\n/u)[0]?.trim();
57
+ const branchRef = head?.startsWith('ref: refs/heads/')
58
+ ? head.slice('ref: refs/heads/'.length)
59
+ : '';
60
+ return branchRef.length > 0 ? branchRef : null;
48
61
  }
49
62
  catch {
50
63
  return null;
51
64
  }
52
65
  };
66
+ const isFullWindowsBrowseEnabled = () => process.platform === 'win32' && !hasFsBrowseRootOverride();
67
+ const trimTrailingWindowsSeparators = (path) => path.replace(/[\\/]+$/u, '');
68
+ const isWindowsRootPath = (path) => {
69
+ const resolved = resolveWin32Path(path);
70
+ const parsed = parseWin32Path(resolved);
71
+ return (parsed.root.length > 0 &&
72
+ trimTrailingWindowsSeparators(resolved).toLowerCase() ===
73
+ trimTrailingWindowsSeparators(parsed.root).toLowerCase());
74
+ };
75
+ const listWindowsDriveRoots = async () => {
76
+ const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
77
+ const roots = await Promise.all(letters.map(async (letter) => {
78
+ const root = `${letter}:\\`;
79
+ try {
80
+ const info = await stat(root);
81
+ if (!info.isDirectory())
82
+ return null;
83
+ return {
84
+ is_dir: true,
85
+ is_git_repository: false,
86
+ name: `${letter}:`,
87
+ path: root,
88
+ };
89
+ }
90
+ catch {
91
+ return null;
92
+ }
93
+ }));
94
+ return roots.filter((root) => root !== null);
95
+ };
96
+ const browseWindowsDrivesRoot = async () => ({
97
+ current_path: WINDOWS_DRIVES_ROOT,
98
+ entries: await listWindowsDriveRoots(),
99
+ error: null,
100
+ ok: true,
101
+ parent_path: null,
102
+ root_path: WINDOWS_DRIVES_ROOT,
103
+ });
104
+ const getSandboxParentPath = (rootPath, candidate) => {
105
+ if (candidate === rootPath)
106
+ return null;
107
+ const rawParent = dirname(candidate);
108
+ return isPathWithinRoot(rootPath, rawParent) ? rawParent : null;
109
+ };
110
+ export const getWindowsBrowseParentPath = (candidate) => isWindowsRootPath(candidate) ? WINDOWS_DRIVES_ROOT : dirnameWin32Path(candidate);
111
+ export const getSuggestedWorkspaceNameFromPath = (path) => (path.split(/[\\/]/).filter(Boolean).pop() ?? '').replace(/:$/u, '');
53
112
  export const browseDirectory = async (requestedPath) => {
113
+ if (isFullWindowsBrowseEnabled()) {
114
+ return browseWindowsDirectory(requestedPath);
115
+ }
54
116
  const rootPath = getFsBrowseRoot();
55
- const trimmed = requestedPath.trim();
117
+ const trimmed = sanitizePastedPath(requestedPath);
56
118
  const candidate = trimmed.length === 0 ? rootPath : resolve(rootPath, trimmed);
57
119
  if (!isPathWithinRoot(rootPath, candidate)) {
58
120
  return {
@@ -74,7 +136,7 @@ export const browseDirectory = async (requestedPath) => {
74
136
  entries: [],
75
137
  error: formatFilesystemError(error),
76
138
  ok: false,
77
- parent_path: null,
139
+ parent_path: getSandboxParentPath(rootPath, candidate),
78
140
  root_path: rootPath,
79
141
  };
80
142
  }
@@ -84,7 +146,7 @@ export const browseDirectory = async (requestedPath) => {
84
146
  entries: [],
85
147
  error: 'The specified path is not a directory.',
86
148
  ok: false,
87
- parent_path: null,
149
+ parent_path: getSandboxParentPath(rootPath, candidate),
88
150
  root_path: rootPath,
89
151
  };
90
152
  }
@@ -102,7 +164,7 @@ export const browseDirectory = async (requestedPath) => {
102
164
  entries: [],
103
165
  error: formatFilesystemError(error),
104
166
  ok: false,
105
- parent_path: null,
167
+ parent_path: getSandboxParentPath(rootPath, candidate),
106
168
  root_path: rootPath,
107
169
  };
108
170
  }
@@ -118,25 +180,97 @@ export const browseDirectory = async (requestedPath) => {
118
180
  path: entryPath,
119
181
  };
120
182
  }));
121
- const isAtRoot = candidate === rootPath;
122
- const rawParent = dirname(candidate);
123
- const parentIsWithinRoot = isPathWithinRoot(rootPath, rawParent);
124
- const parent_path = isAtRoot ? null : parentIsWithinRoot ? rawParent : null;
125
183
  return {
126
184
  current_path: candidate,
127
185
  entries,
128
186
  error: null,
129
187
  ok: true,
130
- parent_path,
188
+ parent_path: getSandboxParentPath(rootPath, candidate),
131
189
  root_path: rootPath,
132
190
  };
133
191
  };
192
+ const browseWindowsDirectory = async (requestedPath) => {
193
+ const trimmed = sanitizePastedPath(requestedPath);
194
+ if (trimmed.length === 0 || trimmed === WINDOWS_DRIVES_ROOT) {
195
+ return browseWindowsDrivesRoot();
196
+ }
197
+ const candidate = resolveWin32Path(trimmed);
198
+ let dirStat;
199
+ try {
200
+ dirStat = await stat(candidate);
201
+ }
202
+ catch (error) {
203
+ return {
204
+ current_path: candidate,
205
+ entries: [],
206
+ error: formatFilesystemError(error),
207
+ ok: false,
208
+ parent_path: getWindowsBrowseParentPath(candidate),
209
+ root_path: WINDOWS_DRIVES_ROOT,
210
+ };
211
+ }
212
+ if (!dirStat.isDirectory()) {
213
+ return {
214
+ current_path: candidate,
215
+ entries: [],
216
+ error: 'The specified path is not a directory.',
217
+ ok: false,
218
+ parent_path: getWindowsBrowseParentPath(candidate),
219
+ root_path: WINDOWS_DRIVES_ROOT,
220
+ };
221
+ }
222
+ let rawEntries;
223
+ try {
224
+ rawEntries = await readdir(candidate, { withFileTypes: true });
225
+ }
226
+ catch (error) {
227
+ return {
228
+ current_path: candidate,
229
+ entries: [],
230
+ error: formatFilesystemError(error),
231
+ ok: false,
232
+ parent_path: getWindowsBrowseParentPath(candidate),
233
+ root_path: WINDOWS_DRIVES_ROOT,
234
+ };
235
+ }
236
+ const directoryEntries = rawEntries
237
+ .filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'))
238
+ .sort((a, b) => a.name.localeCompare(b.name));
239
+ const entries = await Promise.all(directoryEntries.map(async (entry) => {
240
+ const entryPath = resolveWin32Path(candidate, entry.name);
241
+ return {
242
+ is_dir: true,
243
+ is_git_repository: await detectGitRepository(entryPath),
244
+ name: entry.name,
245
+ path: entryPath,
246
+ };
247
+ }));
248
+ return {
249
+ current_path: candidate,
250
+ entries,
251
+ error: null,
252
+ ok: true,
253
+ parent_path: getWindowsBrowseParentPath(candidate),
254
+ root_path: WINDOWS_DRIVES_ROOT,
255
+ };
256
+ };
134
257
  export const probeDirectory = async (requestedPath, options = {}) => {
135
258
  const enforceSandbox = options.enforceSandbox ?? true;
259
+ const fullWindowsBrowse = isFullWindowsBrowseEnabled();
260
+ const requested = sanitizePastedPath(requestedPath);
261
+ if (requested === WINDOWS_DRIVES_ROOT) {
262
+ return {
263
+ current_branch: null,
264
+ exists: false,
265
+ is_dir: false,
266
+ is_git_repository: false,
267
+ ok: false,
268
+ path: '',
269
+ suggested_name: '',
270
+ };
271
+ }
136
272
  const rootPath = getFsBrowseRoot();
137
- const candidate = enforceSandbox
138
- ? resolve(rootPath, requestedPath.trim())
139
- : resolve(requestedPath.trim());
273
+ const candidate = enforceSandbox && !fullWindowsBrowse ? resolve(rootPath, requested) : resolve(requested);
140
274
  const base = {
141
275
  current_branch: null,
142
276
  exists: false,
@@ -144,9 +278,9 @@ export const probeDirectory = async (requestedPath, options = {}) => {
144
278
  is_git_repository: false,
145
279
  ok: false,
146
280
  path: candidate,
147
- suggested_name: candidate.split(/[\\/]/).filter(Boolean).pop() ?? '',
281
+ suggested_name: getSuggestedWorkspaceNameFromPath(candidate),
148
282
  };
149
- if (enforceSandbox && !isPathWithinRoot(rootPath, candidate)) {
283
+ if (enforceSandbox && !fullWindowsBrowse && !isPathWithinRoot(rootPath, candidate)) {
150
284
  return base;
151
285
  }
152
286
  try {
@@ -95,73 +95,6 @@ const linuxPick = async (run) => {
95
95
  return emptyResponse({ canceled: true });
96
96
  return finalizeWithProbe(picked);
97
97
  };
98
- const windowsPick = async (run) => {
99
- /* Hive's PowerShell child has no visible main window, so a bare
100
- `$dialog.ShowDialog()` inherits the desktop as IWin32Window parent —
101
- and ends up below the foreground browser in z-order. To the user
102
- that looks like a hang ("Add Workspace pops up then nothing"); the
103
- picker is open, just occluded.
104
-
105
- Fix: build a TopMost invisible owner Form, `Show()` it so it has a
106
- real HWND (an unshown Form has none, and ShowDialog silently falls
107
- back to desktop-parent), then pass the owner to `ShowDialog($owner)`.
108
- The owner inherits TopMost z-order onto the dialog. The owner itself
109
- stays invisible — Opacity 0, parked at (-32000, -32000), 1x1 size,
110
- no taskbar entry — so the user only sees the picker. Dispose in
111
- `finally` to release the HWND each invocation.
112
-
113
- `Add-Type -AssemblyName System.Drawing` is required because Point /
114
- Size live in System.Drawing.dll, not System.Windows.Forms.dll. */
115
- // PS 5.1 on zh-CN Windows defaults [Console]::OutputEncoding to cp936/GBK;
116
- // Node decodes stdout as UTF-8 and CJK paths arrive mojibake'd. Force UTF-8
117
- // before any output. No-op on PS 7+ which already defaults to UTF-8.
118
- const script = [
119
- '[Console]::OutputEncoding = [System.Text.Encoding]::UTF8',
120
- 'Add-Type -AssemblyName System.Windows.Forms',
121
- 'Add-Type -AssemblyName System.Drawing',
122
- '$owner = New-Object System.Windows.Forms.Form',
123
- "$owner.FormBorderStyle = 'None'",
124
- '$owner.Opacity = 0',
125
- '$owner.ShowInTaskbar = $false',
126
- "$owner.StartPosition = 'Manual'",
127
- '$owner.Location = New-Object System.Drawing.Point(-32000, -32000)',
128
- '$owner.Size = New-Object System.Drawing.Size(1, 1)',
129
- '$owner.TopMost = $true',
130
- '$owner.Show()',
131
- 'try {',
132
- ' $dialog = New-Object System.Windows.Forms.FolderBrowserDialog',
133
- ' $dialog.Description = "Select Hive workspace"',
134
- ' $dialog.ShowNewFolderButton = $false',
135
- ' $result = $dialog.ShowDialog($owner)',
136
- ' if ($result -eq [System.Windows.Forms.DialogResult]::OK) { [Console]::Out.WriteLine($dialog.SelectedPath); exit 0 }',
137
- ' exit 1',
138
- '} finally {',
139
- ' $owner.Close()',
140
- ' $owner.Dispose()',
141
- '}',
142
- ].join('; ');
143
- const result = await run('powershell.exe', ['-NoProfile', '-STA', '-ExecutionPolicy', 'Bypass', '-Command', script], { timeout: PICKER_TIMEOUT_MS });
144
- if (result.spawnError?.code === 'ENOENT') {
145
- return emptyResponse({
146
- error: 'PowerShell is unavailable on this host. Use Advanced: paste path.',
147
- supported: false,
148
- });
149
- }
150
- if (result.timedOut) {
151
- return emptyResponse({ error: 'Folder picker timed out before a folder was selected.' });
152
- }
153
- if (result.status !== 0) {
154
- const stderr = result.stderr.trim();
155
- if (stderr.length > 0) {
156
- return emptyResponse({ error: `Folder picker failed: ${stderr}` });
157
- }
158
- return emptyResponse({ canceled: true });
159
- }
160
- const picked = result.stdout.trim();
161
- if (picked.length === 0)
162
- return emptyResponse({ canceled: true });
163
- return finalizeWithProbe(picked);
164
- };
165
98
  export const pickFolder = async (options = {}) => {
166
99
  const platform = options.platform ?? process.platform;
167
100
  const run = options.runCommand ?? defaultRunCommand;
@@ -169,8 +102,12 @@ export const pickFolder = async (options = {}) => {
169
102
  return macOsPick(run);
170
103
  if (platform === 'linux')
171
104
  return linuxPick(run);
172
- if (platform === 'win32')
173
- return windowsPick(run);
105
+ if (platform === 'win32') {
106
+ return emptyResponse({
107
+ error: 'Native folder picker is disabled on Windows. Use Browse server filesystem or paste path.',
108
+ supported: false,
109
+ });
110
+ }
174
111
  return emptyResponse({
175
112
  error: 'Native folder picker not supported on this platform. Use Advanced: paste path.',
176
113
  supported: false,
@@ -1,9 +1,11 @@
1
1
  /**
2
- * Root directory the FS-browse API is allowed to reveal. We sandbox to
3
- * `$HOME` (override via `HIVE_FS_BROWSE_ROOT` for tests). Anything outside
4
- * this prefix is rejected before readdir/stat even runs.
2
+ * Root directory the FS-browse API is allowed to reveal when sandboxing is
3
+ * enabled. Tests and remote/headless setups can set `HIVE_FS_BROWSE_ROOT`.
4
+ * Normal Windows runs skip this sandbox root and browse from the virtual
5
+ * "This PC" drive list; POSIX sandboxed browsing defaults to `$HOME`.
5
6
  */
6
7
  export declare const getFsBrowseRoot: () => string;
8
+ export declare const hasFsBrowseRootOverride: () => boolean;
7
9
  /**
8
10
  * True when `candidatePath` is `rootPath` itself or a descendant of it.
9
11
  * Uses `path.relative` + separator check so Windows back-slashes and drive
@@ -2,9 +2,10 @@ import { homedir } from 'node:os';
2
2
  import { isAbsolute, relative, resolve, sep } from 'node:path';
3
3
  import { realpathNative } from './path-canonicalization.js';
4
4
  /**
5
- * Root directory the FS-browse API is allowed to reveal. We sandbox to
6
- * `$HOME` (override via `HIVE_FS_BROWSE_ROOT` for tests). Anything outside
7
- * this prefix is rejected before readdir/stat even runs.
5
+ * Root directory the FS-browse API is allowed to reveal when sandboxing is
6
+ * enabled. Tests and remote/headless setups can set `HIVE_FS_BROWSE_ROOT`.
7
+ * Normal Windows runs skip this sandbox root and browse from the virtual
8
+ * "This PC" drive list; POSIX sandboxed browsing defaults to `$HOME`.
8
9
  */
9
10
  export const getFsBrowseRoot = () => {
10
11
  const override = process.env.HIVE_FS_BROWSE_ROOT;
@@ -16,6 +17,7 @@ export const getFsBrowseRoot = () => {
16
17
  return root;
17
18
  }
18
19
  };
20
+ export const hasFsBrowseRootOverride = () => (process.env.HIVE_FS_BROWSE_ROOT ?? '').length > 0;
19
21
  const isResolvedPathWithinRoot = (rootPath, candidatePath) => {
20
22
  if (candidatePath === rootPath)
21
23
  return true;