@tt-a1i/hive 1.7.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 (251) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.en.md +73 -11
  3. package/README.md +41 -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 +14 -0
  19. package/dist/src/server/agent-run-bootstrap.js +11 -4
  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 +22 -5
  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 +56 -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 +1 -0
  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 +17 -0
  124. package/dist/src/server/session-capture.js +16 -0
  125. package/dist/src/server/sqlite-schema-v23.d.ts +2 -0
  126. package/dist/src/server/sqlite-schema-v23.js +43 -0
  127. package/dist/src/server/sqlite-schema-v24.d.ts +2 -0
  128. package/dist/src/server/sqlite-schema-v24.js +34 -0
  129. package/dist/src/server/sqlite-schema-v25.d.ts +2 -0
  130. package/dist/src/server/sqlite-schema-v25.js +127 -0
  131. package/dist/src/server/sqlite-schema-v26.d.ts +2 -0
  132. package/dist/src/server/sqlite-schema-v26.js +56 -0
  133. package/dist/src/server/sqlite-schema-v27.d.ts +6 -0
  134. package/dist/src/server/sqlite-schema-v27.js +92 -0
  135. package/dist/src/server/sqlite-schema-v28.d.ts +2 -0
  136. package/dist/src/server/sqlite-schema-v28.js +19 -0
  137. package/dist/src/server/sqlite-schema-v29.d.ts +2 -0
  138. package/dist/src/server/sqlite-schema-v29.js +27 -0
  139. package/dist/src/server/sqlite-schema-v30.d.ts +2 -0
  140. package/dist/src/server/sqlite-schema-v30.js +27 -0
  141. package/dist/src/server/sqlite-schema-v31.d.ts +2 -0
  142. package/dist/src/server/sqlite-schema-v31.js +30 -0
  143. package/dist/src/server/sqlite-schema.d.ts +1 -1
  144. package/dist/src/server/sqlite-schema.js +49 -1
  145. package/dist/src/server/startup-command-parser.js +5 -1
  146. package/dist/src/server/tasks-file-watcher.d.ts +2 -0
  147. package/dist/src/server/tasks-file-watcher.js +15 -6
  148. package/dist/src/server/tasks-file.js +30 -5
  149. package/dist/src/server/tasks-websocket-server.js +4 -0
  150. package/dist/src/server/team-authz.d.ts +1 -1
  151. package/dist/src/server/team-authz.js +13 -1
  152. package/dist/src/server/team-list-enrichment.js +3 -1
  153. package/dist/src/server/team-memory-digest.d.ts +52 -0
  154. package/dist/src/server/team-memory-digest.js +200 -0
  155. package/dist/src/server/team-memory-dream-applier.d.ts +5 -0
  156. package/dist/src/server/team-memory-dream-applier.js +234 -0
  157. package/dist/src/server/team-memory-dream-http-serializers.d.ts +13 -0
  158. package/dist/src/server/team-memory-dream-http-serializers.js +12 -0
  159. package/dist/src/server/team-memory-dream-ops.d.ts +40 -0
  160. package/dist/src/server/team-memory-dream-ops.js +153 -0
  161. package/dist/src/server/team-memory-dream-reverter.d.ts +22 -0
  162. package/dist/src/server/team-memory-dream-reverter.js +221 -0
  163. package/dist/src/server/team-memory-dream-run-store.d.ts +23 -0
  164. package/dist/src/server/team-memory-dream-run-store.js +211 -0
  165. package/dist/src/server/team-memory-dream-runner.d.ts +37 -0
  166. package/dist/src/server/team-memory-dream-runner.js +178 -0
  167. package/dist/src/server/team-memory-dream-scheduler.d.ts +32 -0
  168. package/dist/src/server/team-memory-dream-scheduler.js +115 -0
  169. package/dist/src/server/team-memory-dream-store.d.ts +19 -0
  170. package/dist/src/server/team-memory-dream-store.js +16 -0
  171. package/dist/src/server/team-memory-dream-types.d.ts +104 -0
  172. package/dist/src/server/team-memory-dream-types.js +23 -0
  173. package/dist/src/server/team-memory-export.d.ts +22 -0
  174. package/dist/src/server/team-memory-export.js +220 -0
  175. package/dist/src/server/team-memory-feature.d.ts +12 -0
  176. package/dist/src/server/team-memory-feature.js +12 -0
  177. package/dist/src/server/team-memory-http-serializers.d.ts +102 -0
  178. package/dist/src/server/team-memory-http-serializers.js +46 -0
  179. package/dist/src/server/team-memory-injection.d.ts +31 -0
  180. package/dist/src/server/team-memory-injection.js +49 -0
  181. package/dist/src/server/team-memory-store.d.ts +116 -0
  182. package/dist/src/server/team-memory-store.js +513 -0
  183. package/dist/src/server/team-operations.d.ts +5 -1
  184. package/dist/src/server/team-operations.js +46 -16
  185. package/dist/src/server/team-recall-store.d.ts +38 -0
  186. package/dist/src/server/team-recall-store.js +205 -0
  187. package/dist/src/server/terminal-input-profile.d.ts +1 -1
  188. package/dist/src/server/terminal-input-profile.js +8 -0
  189. package/dist/src/server/terminal-ws-server.js +6 -0
  190. package/dist/src/server/ui-auth-helpers.d.ts +1 -1
  191. package/dist/src/server/ui-auth-helpers.js +7 -1
  192. package/dist/src/server/ui-auth.d.ts +3 -0
  193. package/dist/src/server/ui-auth.js +21 -1
  194. package/dist/src/server/workflow-cli-policy.d.ts +2 -3
  195. package/dist/src/server/workflow-cli-policy.js +3 -3
  196. package/dist/src/server/workflow-runner.d.ts +1 -0
  197. package/dist/src/server/workflow-runner.js +9 -4
  198. package/dist/src/server/workspace-path-validation.js +6 -2
  199. package/dist/src/server/workspace-store.d.ts +1 -1
  200. package/dist/src/server/workspace-store.js +35 -9
  201. package/dist/src/shared/fs-browse.d.ts +1 -0
  202. package/dist/src/shared/fs-browse.js +1 -0
  203. package/dist/src/shared/path-input.d.ts +12 -0
  204. package/dist/src/shared/path-input.js +22 -0
  205. package/dist/src/shared/remote-bridge-routing.d.ts +19 -0
  206. package/dist/src/shared/remote-bridge-routing.js +141 -0
  207. package/dist/src/shared/remote-crypto.d.ts +138 -0
  208. package/dist/src/shared/remote-crypto.js +427 -0
  209. package/dist/src/shared/remote-pairing-code.d.ts +7 -0
  210. package/dist/src/shared/remote-pairing-code.js +47 -0
  211. package/dist/src/shared/remote-protocol.d.ts +160 -0
  212. package/dist/src/shared/remote-protocol.js +526 -0
  213. package/dist/src/shared/team-memory.d.ts +11 -0
  214. package/dist/src/shared/team-memory.js +10 -0
  215. package/dist/src/shared/team-recall.d.ts +1 -0
  216. package/dist/src/shared/team-recall.js +1 -0
  217. package/dist/src/shared/types.d.ts +4 -5
  218. package/package.json +12 -5
  219. package/scripts/postinstall-native-artifacts.mjs +113 -0
  220. package/web/dist/assets/AddWorkerDialog-C86CwNgQ.js +2 -0
  221. package/web/dist/assets/AddWorkspaceFlow-Bm2Jz34D.js +1 -0
  222. package/web/dist/assets/FirstRunWizard-XzBoEpA5.js +1 -0
  223. package/web/dist/assets/MarketplaceDrawer-BFfGT8hH.js +67 -0
  224. package/web/dist/assets/TaskGraphDrawer-_uVH_0C1.js +1 -0
  225. package/web/dist/assets/{WhatsNewDialog-CHkZeINH.js → WhatsNewDialog-DkJHmkMs.js} +1 -1
  226. package/web/dist/assets/WorkerModal-BtMJEOG9.js +1 -0
  227. package/web/dist/assets/WorkflowsDrawer-CiIdHS6_.js +1 -0
  228. package/web/dist/assets/WorkspaceMemoryDrawer-C6sNocl_.js +1 -0
  229. package/web/dist/assets/WorkspaceTaskDrawer-CyhhEB1Z.js +1 -0
  230. package/web/dist/assets/index-BAiLYajK.css +1 -0
  231. package/web/dist/assets/index-K-GG8UwR.js +73 -0
  232. package/web/dist/assets/search-BtRkkEmS.js +1 -0
  233. package/web/dist/assets/square-terminal-lEeQUWb3.js +1 -0
  234. package/web/dist/cli-icons/agy.png +0 -0
  235. package/web/dist/cli-icons/cursor.ico +0 -0
  236. package/web/dist/cli-icons/grok.ico +0 -0
  237. package/web/dist/cli-icons/qwen.png +0 -0
  238. package/web/dist/index.html +8 -3
  239. package/web/dist/sw.js +1 -1
  240. package/scripts/fix-runtime-artifacts.mjs +0 -33
  241. package/web/dist/assets/AddWorkerDialog-BRUxpa3f.js +0 -2
  242. package/web/dist/assets/AddWorkspaceDialog-D56x5JCb.js +0 -1
  243. package/web/dist/assets/FirstRunWizard-BFVaMIsE.js +0 -1
  244. package/web/dist/assets/MarketplaceDrawer-DeEZ35dN.js +0 -76
  245. package/web/dist/assets/WorkerModal-BBCuMLIa.js +0 -1
  246. package/web/dist/assets/WorkspaceTaskDrawer-CpZHAcj1.js +0 -1
  247. package/web/dist/assets/WorkspaceTerminalPanels-7If2mDyp.js +0 -1
  248. package/web/dist/assets/WorkspaceTerminalPanels-DDGTF8rc.css +0 -1
  249. package/web/dist/assets/index-5zh61jMg.css +0 -1
  250. package/web/dist/assets/index-CxNL0O-C.js +0 -73
  251. 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,8 +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'];
5
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'];
6
21
  export const BUILTIN_COMMAND_PRESETS = [
7
22
  {
8
23
  command: 'claude',
@@ -59,5 +74,46 @@ export const BUILTIN_COMMAND_PRESETS = [
59
74
  },
60
75
  yoloArgsTemplate: HERMES_DEFAULT_YOLO_ARGS,
61
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
+ },
62
115
  ];
63
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;
@@ -1,3 +1,4 @@
1
+ import { BUILTIN_COMMAND_PRESET_CLI_LIST } from './command-preset-defaults.js';
1
2
  import { FEATURE_FLAGS_ALL_OFF } from './feature-flags.js';
2
3
  import { DEFAULT_WORKFLOW_CLI_POLICY } from './workflow-cli-policy.js';
3
4
  /**
@@ -20,7 +21,7 @@ import { DEFAULT_WORKFLOW_CLI_POLICY } from './workflow-cli-policy.js';
20
21
  export const buildOrchestratorReminderTail = ({ workflowsEnabled }) => {
21
22
  const body = 'You are the Hive Orchestrator. Reply with one of: ' +
22
23
  '(a) `team send "<worker-name>" "<task>"` to dispatch — run `team list` first if the roster may have changed since your last view (Hive does not push membership changes; stale names fail). ' +
23
- 'If no worker fits or the roster is empty, `team spawn <role> [--cli claude|codex|opencode|gemini|hermes]` to create one (add `--ephemeral` for a one-shot), then dispatch; do not ask the user to add workers. ' +
24
+ `If no worker fits or the roster is empty, \`team spawn <role> [--cli <${BUILTIN_COMMAND_PRESET_CLI_LIST}>]\` to create one (add \`--ephemeral\` for a one-shot), then dispatch; do not ask the user to add workers. ` +
24
25
  '(b) `team cancel --dispatch <id> "<reason>"` to close an obsolete dispatch. ' +
25
26
  (workflowsEnabled
26
27
  ? '(c) `team workflow run --stdin` to fan out across 3+ workers or run a staged review→fix — never a loop of `team send` (no barrier, no UI group, no stop button). (d) plain text to the user. '
@@ -53,10 +54,11 @@ const CORE_ORCHESTRATOR_RULES = [
53
54
  'Small, low-risk tasks you can finish in a couple of minutes: do them yourself; do not dispatch for the sake of form. Reach for `team send` when the work needs parallelism, long execution, independent review/test, a dedicated role, or the user explicitly asked for a worker.',
54
55
  'If exactly one worker is available, dispatch to it directly with `team send <worker-name> "<task>"` — do not bounce the choice back to the user.',
55
56
  'When the user says "have a worker do X", dispatch it with `team send <worker-name> "<task>"`.',
56
- 'If the roster is empty or lacks the role the task needs, build the team yourself: `team spawn <role> [--name <n>] [--cli claude|codex|opencode|gemini|hermes]`, then immediately dispatch. Do not stop to ask the user to add members — that call is yours.',
57
+ `If the roster is empty or lacks the role the task needs, build the team yourself: \`team spawn <role> [--name <n>] [--cli <${BUILTIN_COMMAND_PRESET_CLI_LIST}>]\`, then immediately dispatch. Do not stop to ask the user to add members — that call is yours.`,
57
58
  '`team spawn` is PERSISTENT by default (a normal member that stays in the workspace); add `--ephemeral` for a one-shot worker that auto-dismisses after its first `team report`. Rule of thumb: will you reuse this role in the next 10 minutes? Yes → persistent; no → `--ephemeral`.',
58
59
  'When the user cancels or changes direction on an open dispatch, close it explicitly with `team cancel --dispatch <id> "<reason>"` — do not just say "cancel" in prose.',
59
60
  'Each `team send` opens a SEPARATE dispatch with its own pending count and its own required report — it is not a way to tack context onto a dispatch already in flight. Send the same worker twice and it owes you TWO reports; one report closes only one dispatch (the one its `--dispatch <id>` names, or the oldest open one if the flag is omitted), so the other stays open and the worker is pinned on `working`. To change or extend an in-flight task, `team cancel --dispatch <id> "<reason>"` the stale dispatch and re-send the whole task, or wait for the worker to report and dispatch the follow-up after.',
61
+ 'Only orchestrators can add memory directly; workers should report durable findings for the orchestrator or Dream to capture. Search memory before adding: use `team memory search "<query>"` to avoid duplicates. Use `team memory add "<body>"` only for rare, evidence-backed workspace facts, decisions, and pitfalls that should help future Hive agents across sessions. Nothing worth saving is a normal outcome; do not add memory just to prove you used it. Use `team memory forget <memory-id>` only to archive obsolete memory.',
60
62
  "Never substitute your CLI's own subagent / workflow tools (e.g. Task / Explore / Workflow / Agent) for Hive workers or Hive workflows — they run inside your CLI process, bypass Hive's PTY fleet, never appear in the UI or `team list`, and the stop button cannot reach them.",
61
63
  'In `team list`, `last_pty_line` is raw terminal output (stdout / help / control-sequence noise), NOT a worker\'s report. A real report arrives only as an injected "report from @<name>" / "status from @<name>" system message — treat only those as replies.',
62
64
  ];
@@ -96,6 +98,8 @@ const WORKER_RULES = [
96
98
  "Do not call `team send`, and do not launch your own CLI's subagent tools to do the work for you — finish it yourself.",
97
99
  'When an assigned task is done, blocked, or has failed, you MUST report to the Orchestrator with `team report`.',
98
100
  'When you have no active dispatch and only want to report readiness, environment, or status, use `team status "<state>"`.',
101
+ 'Use `team recall "<query>"` when prior team messages or reports may contain useful evidence.',
102
+ 'Use `team memory search "<query>"` to inspect active workspace memory. Include durable findings in `team report`; workers cannot add or forget memory.',
99
103
  '`team --help` only prints command syntax — it is NOT a way to report; its output never reaches the Orchestrator. You still owe a real `team report` / `team status` afterward.',
100
104
  'If `team report` / `team status` errors, it also prints USAGE — fix the arguments per USAGE and retry; do not use `team --help` as a stand-in for reporting.',
101
105
  ];
@@ -121,7 +125,7 @@ Host functions injected into the script: \`agent(prompt, opts)\`,
121
125
  \`parallel(thunks)\`, \`pipeline(items, ...stages)\`, \`phase(title)\`,
122
126
  \`log(msg)\`, plus the \`args\` global.
123
127
 
124
- \`agent(prompt, opts)\` opts: \`{ agentType?: "coder"|"reviewer"|"tester"|"custom"|<custom-role-name>, cli?: "claude"|"codex"|"opencode"|"gemini"|"hermes", model?: string, outputSchema?: object, label?: string, timeoutMs?: number }\` — other fields are silently ignored. \`agentType\` also accepts the name of a workspace custom role template (case-insensitive); a typo throws rather than silently falling back to coder. \`model\` is passed through to the worker launch config (\`--model <id>\`), so a 100-way fan-out can use a cheap model and the synthesizer a strong one. \`outputSchema\` makes \`agent()\` resolve to a parsed object instead of a string: the worker is told to end its report with a fenced \`\`\`json block whose keys match the schema. On a parse miss it falls back to \`{ text: "<raw report>" }\`, so ALWAYS treat a missing field as the SAFE default (never assume success). The worker auto-dismisses when its \`agent()\` call resolves — you never dismiss it.
128
+ \`agent(prompt, opts)\` opts: \`{ agentType?: "coder"|"reviewer"|"tester"|"custom"|<custom-role-name>, cli?: "${BUILTIN_COMMAND_PRESET_CLI_LIST.replace(/\|/gu, '"|"')}", model?: string, outputSchema?: object, label?: string, timeoutMs?: number }\` — other fields are silently ignored. \`agentType\` also accepts the name of a workspace custom role template (case-insensitive); a typo throws rather than silently falling back to coder. \`model\` is passed through to the worker launch config (\`--model <id>\`), so a 100-way fan-out can use a cheap model and the synthesizer a strong one. \`outputSchema\` makes \`agent()\` resolve to a parsed object instead of a string: the worker is told to end its report with a fenced \`\`\`json block whose keys match the schema. On a parse miss it falls back to \`{ text: "<raw report>" }\`, so ALWAYS treat a missing field as the SAFE default (never assume success). The worker auto-dismisses when its \`agent()\` call resolves — you never dismiss it.
125
129
 
126
130
  \`parallel()\` takes an array of THUNKS (\`() => agent(...)\`), NOT already-started promises: \`parallel([agent(...), agent(...)])\` degrades to unordered concurrency counted as a single step (a no-op grouping), because the promises already started at construction time. \`pipeline(items, ...stages)\` stages are also functions, shape \`(prev, item, i) => agent(...)\`.
127
131
 
@@ -240,7 +244,7 @@ export const buildProtocolDoc = (cliPolicy = DEFAULT_WORKFLOW_CLI_POLICY, flags
240
244
  '## You are running inside Hive',
241
245
  '',
242
246
  'Hive is a multi-CLI-agent workbench. Each agent in this workspace is a',
243
- 'real CLI process (Claude Code / Codex / OpenCode / Gemini). All',
247
+ 'real CLI process (Antigravity CLI / Claude Code / Codex / OpenCode / Gemini / Hermes / Qwen Code / Cursor CLI / Grok Build). All',
244
248
  'inter-agent communication goes through the `team` CLI binary on your',
245
249
  'PATH.',
246
250
  '',
@@ -252,8 +256,13 @@ export const buildProtocolDoc = (cliPolicy = DEFAULT_WORKFLOW_CLI_POLICY, flags
252
256
  '## `team` CLI — orchestrator',
253
257
  '',
254
258
  '- `team list` — show workspace members and their status',
259
+ '- `team recall "<query>" [--limit <n>] [--window <n>]` — search prior team messages/reports in this workspace',
260
+ '- `team memory add "<body>" [--kind fact|preference|decision|pitfall|procedure_ref] [--tag <tag>]` — save durable workspace memory (orchestrator entries become active)',
261
+ '- `team memory show <memory-id>` — inspect a memory entry and evidence snapshots',
262
+ '- `team memory search "<query>" [--limit <n>]` — search active workspace memory',
263
+ '- `team memory forget <memory-id>` — archive obsolete memory (orchestrator only; does not physically delete evidence)',
255
264
  '- `team send "<worker-name>" "<task>"` — dispatch to a worker by name (never id)',
256
- '- `team spawn <role> [--name <name>] [--cli claude|codex|opencode|gemini|hermes]` — create a PERSISTENT member when none fits (or when the roster is empty)',
265
+ `- \`team spawn <role> [--name <name>] [--cli <${BUILTIN_COMMAND_PRESET_CLI_LIST}>]\` — create a PERSISTENT member when none fits (or when the roster is empty)`,
257
266
  '- `team spawn <role> --ephemeral [other-flags]` — create a one-shot worker that auto-dismisses after its first `team report`',
258
267
  '- `team dismiss <worker-name>` — remove an ephemeral worker you spawned',
259
268
  '- `team cancel --dispatch <id> "<reason>"` — cancel an obsolete open dispatch',
@@ -261,8 +270,11 @@ export const buildProtocolDoc = (cliPolicy = DEFAULT_WORKFLOW_CLI_POLICY, flags
261
270
  '',
262
271
  '## `team` CLI — worker',
263
272
  '',
273
+ '- `team recall "<query>" [--limit <n>] [--window <n>]` — search prior team messages/reports in this workspace',
274
+ '- `team memory show <memory-id>` — inspect a memory entry and evidence snapshots',
275
+ '- `team memory search "<query>" [--limit <n>]` — search active workspace memory',
264
276
  '- `team report "<result>" --dispatch <id>` — report task outcome',
265
- '- `team report --stdin --dispatch <id>` — same, body from stdin (pipe content in via your shell — POSIX heredoc, `type file |`, or whatever your environment supports)',
277
+ '- `team report --stdin --dispatch <id>` — same, body from stdin (pipe content in via your shell — POSIX heredoc, Windows cmd `type file |`, PowerShell `Get-Content -Raw -Encoding utf8 file |`, or portable stdin redirection `< file`)',
266
278
  '- `team status "<state>"` — update orchestrator when no dispatch is active',
267
279
  '',
268
280
  '## Orchestrator rules',