@harness-mix/cli 0.1.10 → 0.1.12

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 (306) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/NOTICE +16 -2
  3. package/README.md +265 -3
  4. package/docs/cross-platform.md +3 -3
  5. package/docs/multi-agent-collaboration.md +37 -23
  6. package/docs/native-acp.md +2 -2
  7. package/docs/native-codex.md +5 -5
  8. package/docs/{superpowers/specs → specs}/2026-09-13-native-hardening-design.md +2 -2
  9. package/docs/storage-verification-design.md +5 -5
  10. package/licenses/lobe-icons-MIT.txt +21 -0
  11. package/output/native-build/desktop-controller.mjs +24 -24
  12. package/output/native-build/harness-mix-secret.exe +0 -0
  13. package/output/native-build/harness-mix-shim.exe +0 -0
  14. package/output/native-build/renderer-extension.js +1325 -658
  15. package/package.json +8 -8
  16. package/scripts/adapters-test.cjs +9 -1
  17. package/scripts/antigravity-adapter-test.cjs +381 -284
  18. package/scripts/build-native.cjs +64 -46
  19. package/scripts/codex-accounts-test.cjs +1 -1
  20. package/scripts/codex-accounts-ui-smoke.cjs +1 -1
  21. package/scripts/collaboration-recovery-test.cjs +11 -2
  22. package/scripts/collaboration-test.cjs +114 -27
  23. package/scripts/collaboration-ui-smoke.cjs +45 -8
  24. package/scripts/concurrency-layers-test.cjs +5 -0
  25. package/scripts/diagnostics.cjs +1 -1
  26. package/scripts/e2e-native-host.cjs +13 -13
  27. package/scripts/error-kind-test.cjs +3 -3
  28. package/scripts/integrations-test.cjs +2 -2
  29. package/scripts/integrations-ui-smoke.cjs +6 -6
  30. package/scripts/native-config-test.cjs +6 -6
  31. package/scripts/native-job-object-test.cjs +1 -1
  32. package/scripts/native-protocol-test.cjs +44 -38
  33. package/scripts/native-rollback-test.cjs +1 -1
  34. package/scripts/native-secret-test.cjs +1 -1
  35. package/scripts/native-shim-test.cjs +1 -1
  36. package/scripts/native-sidebar-test.cjs +4 -4
  37. package/scripts/native-ui-smoke.cjs +12 -12
  38. package/scripts/pets-test.cjs +223 -3
  39. package/scripts/platform-test.cjs +6 -6
  40. package/scripts/publish-native-package.cjs +1 -1
  41. package/scripts/test-live-mentions.cjs +2 -2
  42. package/scripts/workspace-test.cjs +4 -2
  43. package/src/assets/icons/PROVENANCE.md +92 -0
  44. package/src/main/adapters/antigravity.js +1415 -1368
  45. package/src/main/adapters/claude.js +1 -1
  46. package/src/main/adapters/codex-app-server.js +1 -1
  47. package/src/main/adapters/dsh.js +1 -1
  48. package/src/main/adapters/index.js +1 -1
  49. package/src/main/adapters/pi-usage.js +2 -2
  50. package/src/main/harness-adapter/error-kind.js +1 -1
  51. package/src/main/harness-adapter/manifest.js +7 -1
  52. package/src/main/host/collaboration-tools.js +8 -1
  53. package/src/main/host/collaboration-worktree.js +27 -1
  54. package/src/main/host/collaboration.js +348 -15
  55. package/src/main/host/runtime.js +23 -15
  56. package/src/main/native/codex-accounts.js +1 -1
  57. package/src/main/native/config.js +9 -9
  58. package/src/main/native/host.js +1 -1
  59. package/src/main/native/launcher.js +10 -8
  60. package/src/main/native/pets.js +188 -49
  61. package/src/main/native/platform.js +19 -9
  62. package/src/main/native/protocol.js +83 -80
  63. package/src/main/native/rs/crates/appx/Cargo.toml +1 -1
  64. package/src/main/native/rs/crates/secret/Cargo.toml +1 -1
  65. package/src/main/native/rs/crates/secret/src/main.rs +3 -3
  66. package/src/main/native/rs/crates/shim/Cargo.toml +1 -1
  67. package/src/main/native/rs/crates/shim/src/main.rs +2 -2
  68. package/src/main/native/thread-list.js +1 -1
  69. package/src/main/shared-contracts/capability.js +3 -1
  70. package/src/main/workspace/core-review.js +4 -3
  71. package/src/native-ui/README.md +2 -2
  72. package/src/native-ui/desktop-control/dist/contract-audit.js +4 -4
  73. package/src/native-ui/desktop-control/dist/index.d.ts +1 -1
  74. package/src/native-ui/desktop-control/dist/index.js +2 -2
  75. package/src/native-ui/desktop-control/dist/index.js.map +1 -1
  76. package/src/native-ui/desktop-control/dist/main-process-title-policy.js +5 -5
  77. package/src/native-ui/desktop-control/dist/main-process-title-policy.js.map +1 -1
  78. package/src/native-ui/desktop-control/dist/production-controller.js +3 -3
  79. package/src/native-ui/desktop-control/dist/production-controller.js.map +1 -1
  80. package/src/native-ui/desktop-control/dist/release-main.js +1 -1
  81. package/src/native-ui/desktop-control/dist/release-main.js.map +1 -1
  82. package/src/native-ui/desktop-control/dist/renderer-cdp-control-session.js +1 -1
  83. package/src/native-ui/desktop-control/dist/renderer-cdp-control-session.js.map +1 -1
  84. package/src/native-ui/desktop-control/dist/renderer-control-session.js +3 -3
  85. package/src/native-ui/desktop-control/dist/renderer-control-session.js.map +1 -1
  86. package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-runtime.js +25 -25
  87. package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-runtime.js.map +1 -1
  88. package/src/native-ui/desktop-control/dist/tsconfig.tsbuildinfo +1 -1
  89. package/src/native-ui/desktop-control/package.json +2 -2
  90. package/src/native-ui/desktop-control/scripts/build-release.mjs +2 -2
  91. package/src/native-ui/desktop-control/src/contract-audit.ts +4 -4
  92. package/src/native-ui/desktop-control/src/index.ts +2 -2
  93. package/src/native-ui/desktop-control/src/main-process-title-policy.ts +5 -5
  94. package/src/native-ui/desktop-control/src/production-controller.ts +3 -3
  95. package/src/native-ui/desktop-control/src/release-main.ts +1 -1
  96. package/src/native-ui/desktop-control/src/renderer-cdp-control-session.ts +1 -1
  97. package/src/native-ui/desktop-control/src/renderer-control-session.ts +3 -3
  98. package/src/native-ui/desktop-control/src/renderer-draft-prewarm-runtime.ts +25 -25
  99. package/src/native-ui/desktop-control/test/index.test.ts +1 -1
  100. package/src/native-ui/desktop-control/test/production-controller.test.ts +1 -1
  101. package/src/native-ui/desktop-control/test/renderer-cdp-control-session.test.ts +1 -1
  102. package/src/native-ui/desktop-control/test/renderer-draft-prewarm-policy.test.ts +43 -43
  103. package/src/native-ui/renderer-extension/dist/types/agent-group-preference.d.ts +1 -1
  104. package/src/native-ui/renderer-extension/dist/types/agent-group-preference.d.ts.map +1 -1
  105. package/src/native-ui/renderer-extension/dist/types/agent-selection-state.d.ts +1 -1
  106. package/src/native-ui/renderer-extension/dist/types/agent-selection-state.d.ts.map +1 -1
  107. package/src/native-ui/renderer-extension/dist/types/contract-audit.d.ts +1 -1
  108. package/src/native-ui/renderer-extension/dist/types/contract-audit.d.ts.map +1 -1
  109. package/src/native-ui/renderer-extension/dist/types/index.d.ts +3 -1
  110. package/src/native-ui/renderer-extension/dist/types/index.d.ts.map +1 -1
  111. package/src/native-ui/renderer-extension/dist/types/production-entry.d.ts +1 -1
  112. package/src/native-ui/renderer-extension/dist/types/production-entry.d.ts.map +1 -1
  113. package/src/native-ui/renderer-extension/dist/types/renderer-agent-picker.d.ts +2 -2
  114. package/src/native-ui/renderer-extension/dist/types/renderer-agent-picker.d.ts.map +1 -1
  115. package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts +3 -3
  116. package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts.map +1 -1
  117. package/src/native-ui/renderer-extension/dist/types/renderer-codex-account-options.d.ts +1 -1
  118. package/src/native-ui/renderer-extension/dist/types/renderer-codex-account-options.d.ts.map +1 -1
  119. package/src/native-ui/renderer-extension/dist/types/renderer-codex-account-state.d.ts +1 -1
  120. package/src/native-ui/renderer-extension/dist/types/renderer-codex-account-state.d.ts.map +1 -1
  121. package/src/native-ui/renderer-extension/dist/types/renderer-collab-cards.d.ts +1 -1
  122. package/src/native-ui/renderer-extension/dist/types/renderer-collab-cards.d.ts.map +1 -1
  123. package/src/native-ui/renderer-extension/dist/types/renderer-composer-dom.d.ts +1 -1
  124. package/src/native-ui/renderer-extension/dist/types/renderer-composer-dom.d.ts.map +1 -1
  125. package/src/native-ui/renderer-extension/dist/types/renderer-credits-control.d.ts +1 -1
  126. package/src/native-ui/renderer-extension/dist/types/renderer-credits-control.d.ts.map +1 -1
  127. package/src/native-ui/renderer-extension/dist/types/renderer-fork-control.d.ts +1 -1
  128. package/src/native-ui/renderer-extension/dist/types/renderer-fork-control.d.ts.map +1 -1
  129. package/src/native-ui/renderer-extension/dist/types/renderer-harness-command-claim.d.ts +1 -1
  130. package/src/native-ui/renderer-extension/dist/types/renderer-harness-command-claim.d.ts.map +1 -1
  131. package/src/native-ui/renderer-extension/dist/types/renderer-harness-command-control.d.ts +1 -1
  132. package/src/native-ui/renderer-extension/dist/types/renderer-harness-command-control.d.ts.map +1 -1
  133. package/src/native-ui/renderer-extension/dist/types/renderer-harness-handoff.d.ts +1 -1
  134. package/src/native-ui/renderer-extension/dist/types/renderer-harness-handoff.d.ts.map +1 -1
  135. package/src/native-ui/renderer-extension/dist/types/renderer-harness-localization.d.ts +1 -1
  136. package/src/native-ui/renderer-extension/dist/types/renderer-harness-localization.d.ts.map +1 -1
  137. package/src/native-ui/renderer-extension/dist/types/renderer-harness-mentions.d.ts +1 -0
  138. package/src/native-ui/renderer-extension/dist/types/renderer-harness-mentions.d.ts.map +1 -1
  139. package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts +36 -31
  140. package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts.map +1 -1
  141. package/src/native-ui/renderer-extension/dist/types/renderer-model-picker.d.ts +1 -1
  142. package/src/native-ui/renderer-extension/dist/types/renderer-model-picker.d.ts.map +1 -1
  143. package/src/native-ui/renderer-extension/dist/types/renderer-new-thread-preference.d.ts +2 -2
  144. package/src/native-ui/renderer-extension/dist/types/renderer-new-thread-preference.d.ts.map +1 -1
  145. package/src/native-ui/renderer-extension/dist/types/renderer-permission-mode-picker.d.ts +1 -1
  146. package/src/native-ui/renderer-extension/dist/types/renderer-permission-mode-picker.d.ts.map +1 -1
  147. package/src/native-ui/renderer-extension/dist/types/renderer-permission-mode-preference.d.ts +2 -2
  148. package/src/native-ui/renderer-extension/dist/types/renderer-permission-mode-preference.d.ts.map +1 -1
  149. package/src/native-ui/renderer-extension/dist/types/renderer-request-sender.d.ts +18 -2
  150. package/src/native-ui/renderer-extension/dist/types/renderer-request-sender.d.ts.map +1 -1
  151. package/src/native-ui/renderer-extension/dist/types/renderer-session-import-client.d.ts +1 -1
  152. package/src/native-ui/renderer-extension/dist/types/renderer-session-import-client.d.ts.map +1 -1
  153. package/src/native-ui/renderer-extension/dist/types/renderer-sidebar-agent-icons.d.ts +2 -2
  154. package/src/native-ui/renderer-extension/dist/types/renderer-sidebar-agent-icons.d.ts.map +1 -1
  155. package/src/native-ui/renderer-extension/dist/types/renderer-team-cards.d.ts +58 -0
  156. package/src/native-ui/renderer-extension/dist/types/renderer-team-cards.d.ts.map +1 -0
  157. package/src/native-ui/renderer-extension/dist/types/renderer-transcript-dom.d.ts +1 -1
  158. package/src/native-ui/renderer-extension/dist/types/renderer-trigger-chip-style.d.ts +1 -1
  159. package/src/native-ui/renderer-extension/dist/types/renderer-trigger-chip-style.d.ts.map +1 -1
  160. package/src/native-ui/renderer-extension/dist/types/renderer-usage-control.d.ts +1 -1
  161. package/src/native-ui/renderer-extension/dist/types/renderer-usage-control.d.ts.map +1 -1
  162. package/src/native-ui/renderer-extension/dist/types/settings/accounts-list.d.ts +1 -1
  163. package/src/native-ui/renderer-extension/dist/types/settings/accounts-list.d.ts.map +1 -1
  164. package/src/native-ui/renderer-extension/dist/types/settings/accounts-page.d.ts +1 -1
  165. package/src/native-ui/renderer-extension/dist/types/settings/accounts-page.d.ts.map +1 -1
  166. package/src/native-ui/renderer-extension/dist/types/settings/accounts-usage.d.ts +1 -1
  167. package/src/native-ui/renderer-extension/dist/types/settings/accounts-usage.d.ts.map +1 -1
  168. package/src/native-ui/renderer-extension/dist/types/settings/connections-page.d.ts +3 -3
  169. package/src/native-ui/renderer-extension/dist/types/settings/connections-page.d.ts.map +1 -1
  170. package/src/native-ui/renderer-extension/dist/types/settings/harness-accounts.d.ts +1 -1
  171. package/src/native-ui/renderer-extension/dist/types/settings/harness-accounts.d.ts.map +1 -1
  172. package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts +4 -4
  173. package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts.map +1 -1
  174. package/src/native-ui/renderer-extension/dist/types/settings/pet-market.d.ts.map +1 -1
  175. package/src/native-ui/renderer-extension/dist/types/settings/session-import-list-controls.d.ts +1 -1
  176. package/src/native-ui/renderer-extension/dist/types/settings/session-import-list-controls.d.ts.map +1 -1
  177. package/src/native-ui/renderer-extension/dist/types/settings/session-import-page.d.ts +1 -1
  178. package/src/native-ui/renderer-extension/dist/types/settings/session-import-page.d.ts.map +1 -1
  179. package/src/native-ui/renderer-extension/dist/types/settings/shell.d.ts +2 -2
  180. package/src/native-ui/renderer-extension/dist/types/settings/shell.d.ts.map +1 -1
  181. package/src/native-ui/renderer-extension/dist/types/settings/trigger.d.ts +1 -1
  182. package/src/native-ui/renderer-extension/dist/types/settings/trigger.d.ts.map +1 -1
  183. package/src/native-ui/renderer-extension/dist/types/tsconfig.tsbuildinfo +1 -1
  184. package/src/native-ui/renderer-extension/dist/types/versioned-renderer-adapter.d.ts +18 -18
  185. package/src/native-ui/renderer-extension/dist/types/versioned-renderer-adapter.d.ts.map +1 -1
  186. package/src/native-ui/renderer-extension/package.json +2 -2
  187. package/src/native-ui/renderer-extension/src/agent-group-preference.ts +1 -1
  188. package/src/native-ui/renderer-extension/src/agent-selection-state.ts +31 -16
  189. package/src/native-ui/renderer-extension/src/audit-entry.ts +1 -1
  190. package/src/native-ui/renderer-extension/src/contract-audit.ts +4 -4
  191. package/src/native-ui/renderer-extension/src/index.ts +4 -2
  192. package/src/native-ui/renderer-extension/src/install-renderer-binding.ts +2 -2
  193. package/src/native-ui/renderer-extension/src/production-entry.ts +3 -3
  194. package/src/native-ui/renderer-extension/src/renderer-agent-picker.ts +7 -7
  195. package/src/native-ui/renderer-extension/src/renderer-binding-probe.ts +112 -89
  196. package/src/native-ui/renderer-extension/src/renderer-codex-account-options.ts +1 -1
  197. package/src/native-ui/renderer-extension/src/renderer-codex-account-state.ts +1 -1
  198. package/src/native-ui/renderer-extension/src/renderer-collab-cards.ts +1 -1
  199. package/src/native-ui/renderer-extension/src/renderer-composer-dom.ts +12 -12
  200. package/src/native-ui/renderer-extension/src/renderer-credits-control.ts +7 -7
  201. package/src/native-ui/renderer-extension/src/renderer-external-steering.ts +3 -3
  202. package/src/native-ui/renderer-extension/src/renderer-fork-control.ts +1 -1
  203. package/src/native-ui/renderer-extension/src/renderer-harness-command-claim.ts +1 -1
  204. package/src/native-ui/renderer-extension/src/renderer-harness-command-control.ts +3 -3
  205. package/src/native-ui/renderer-extension/src/renderer-harness-handoff.ts +1 -1
  206. package/src/native-ui/renderer-extension/src/renderer-harness-localization.ts +1 -1
  207. package/src/native-ui/renderer-extension/src/renderer-harness-mentions.ts +4 -0
  208. package/src/native-ui/renderer-extension/src/renderer-integrations-client.ts +5 -5
  209. package/src/native-ui/renderer-extension/src/renderer-model-client.ts +63 -42
  210. package/src/native-ui/renderer-extension/src/renderer-model-picker.ts +16 -16
  211. package/src/native-ui/renderer-extension/src/renderer-new-thread-preference.ts +2 -2
  212. package/src/native-ui/renderer-extension/src/renderer-permission-mode-picker.ts +3 -3
  213. package/src/native-ui/renderer-extension/src/renderer-permission-mode-preference.ts +2 -2
  214. package/src/native-ui/renderer-extension/src/renderer-request-sender.ts +46 -3
  215. package/src/native-ui/renderer-extension/src/renderer-session-import-client.ts +4 -4
  216. package/src/native-ui/renderer-extension/src/renderer-sidebar-agent-icons.ts +24 -20
  217. package/src/native-ui/renderer-extension/src/renderer-team-cards.ts +312 -0
  218. package/src/native-ui/renderer-extension/src/renderer-transcript-dom.ts +1 -1
  219. package/src/native-ui/renderer-extension/src/renderer-trigger-chip-style.ts +2 -2
  220. package/src/native-ui/renderer-extension/src/renderer-usage-control.ts +4 -4
  221. package/src/native-ui/renderer-extension/src/settings/accounts-list.ts +1 -1
  222. package/src/native-ui/renderer-extension/src/settings/accounts-page.ts +1 -1
  223. package/src/native-ui/renderer-extension/src/settings/accounts-usage.ts +1 -1
  224. package/src/native-ui/renderer-extension/src/settings/connections-page.ts +7 -7
  225. package/src/native-ui/renderer-extension/src/settings/harness-accounts.ts +1 -1
  226. package/src/native-ui/renderer-extension/src/settings/icons.ts +2 -2
  227. package/src/native-ui/renderer-extension/src/settings/pages.ts +11 -11
  228. package/src/native-ui/renderer-extension/src/settings/pet-market.css +33 -0
  229. package/src/native-ui/renderer-extension/src/settings/pet-market.ts +173 -91
  230. package/src/native-ui/renderer-extension/src/settings/pets-client.ts +5 -5
  231. package/src/native-ui/renderer-extension/src/settings/release-notes.ts +1 -1
  232. package/src/native-ui/renderer-extension/src/settings/session-import-list-controls.ts +1 -1
  233. package/src/native-ui/renderer-extension/src/settings/session-import-page.ts +1 -1
  234. package/src/native-ui/renderer-extension/src/settings/shell.css +15 -15
  235. package/src/native-ui/renderer-extension/src/settings/shell.ts +11 -11
  236. package/src/native-ui/renderer-extension/src/settings/skin-runtime.ts +17 -17
  237. package/src/native-ui/renderer-extension/src/settings/trigger.ts +1 -1
  238. package/src/native-ui/renderer-extension/src/versioned-renderer-adapter.ts +20 -20
  239. package/src/native-ui/renderer-extension/test/agent-selection-state.test.ts +60 -1
  240. package/src/native-ui/renderer-extension/test/codebuddy-migration.test.ts +1 -1
  241. package/src/native-ui/renderer-extension/test/renderer-agent-picker.test.ts +1 -1
  242. package/src/native-ui/renderer-extension/test/renderer-binding-probe-host-catalog.test.ts +15 -9
  243. package/src/native-ui/renderer-extension/test/renderer-binding-probe.test.ts +15 -15
  244. package/src/native-ui/renderer-extension/test/renderer-codex-account-state.test.ts +1 -1
  245. package/src/native-ui/renderer-extension/test/renderer-composer-permissions.test.ts +4 -4
  246. package/src/native-ui/renderer-extension/test/renderer-external-steering-rpc.test.ts +2 -2
  247. package/src/native-ui/renderer-extension/test/renderer-external-steering.test.ts +3 -3
  248. package/src/native-ui/renderer-extension/test/renderer-fork-control.test.ts +4 -4
  249. package/src/native-ui/renderer-extension/test/renderer-harness-command-claim.test.ts +1 -1
  250. package/src/native-ui/renderer-extension/test/renderer-harness-command-control.test.ts +1 -1
  251. package/src/native-ui/renderer-extension/test/renderer-model-client.test.ts +12 -11
  252. package/src/native-ui/renderer-extension/test/renderer-model-picker.test.ts +1 -1
  253. package/src/native-ui/renderer-extension/test/renderer-native-thread-ownership.test.ts +6 -6
  254. package/src/native-ui/renderer-extension/test/renderer-new-thread-preference.test.ts +1 -1
  255. package/src/native-ui/renderer-extension/test/renderer-permission-mode-picker.test.ts +1 -1
  256. package/src/native-ui/renderer-extension/test/renderer-permission-mode-preference.test.ts +1 -1
  257. package/src/native-ui/renderer-extension/test/renderer-request-timeout.test.ts +80 -0
  258. package/src/native-ui/renderer-extension/test/renderer-settings-lifecycle.test.ts +2 -2
  259. package/src/native-ui/renderer-extension/test/renderer-sidebar-agent-icons.test.ts +10 -3
  260. package/src/native-ui/renderer-extension/test/renderer-unsupported-methods.test.ts +1 -1
  261. package/src/native-ui/renderer-extension/test/settings/pages.test.ts +5 -5
  262. package/src/native-ui/renderer-extension/test/settings/release-notes.test.ts +9 -9
  263. package/src/native-ui/renderer-extension/test/versioned-renderer-adapter.test.ts +3 -3
  264. package/src/native-ui/shared-contracts/dist/errors.d.ts +2 -2
  265. package/src/native-ui/shared-contracts/dist/errors.d.ts.map +1 -1
  266. package/src/native-ui/shared-contracts/dist/errors.js +1 -1
  267. package/src/native-ui/shared-contracts/dist/errors.js.map +1 -1
  268. package/src/native-ui/shared-contracts/dist/harness-models.d.ts +40 -0
  269. package/src/native-ui/shared-contracts/dist/harness-models.d.ts.map +1 -1
  270. package/src/native-ui/shared-contracts/dist/harness-models.js +23 -2
  271. package/src/native-ui/shared-contracts/dist/harness-models.js.map +1 -1
  272. package/src/native-ui/shared-contracts/dist/harness-route.d.ts +1 -1
  273. package/src/native-ui/shared-contracts/dist/harness-route.d.ts.map +1 -1
  274. package/src/native-ui/shared-contracts/dist/harness-route.js +1 -1
  275. package/src/native-ui/shared-contracts/dist/harness-route.js.map +1 -1
  276. package/src/native-ui/shared-contracts/dist/harness-session-import.d.ts +1 -1
  277. package/src/native-ui/shared-contracts/dist/harness-session-import.d.ts.map +1 -1
  278. package/src/native-ui/shared-contracts/dist/harness-session-import.js +1 -1
  279. package/src/native-ui/shared-contracts/dist/harness-session-import.js.map +1 -1
  280. package/src/native-ui/shared-contracts/dist/index.d.ts +3 -3
  281. package/src/native-ui/shared-contracts/dist/index.d.ts.map +1 -1
  282. package/src/native-ui/shared-contracts/dist/index.js +2 -2
  283. package/src/native-ui/shared-contracts/dist/index.js.map +1 -1
  284. package/src/native-ui/shared-contracts/dist/tsconfig.tsbuildinfo +1 -1
  285. package/src/native-ui/shared-contracts/dist/updates.js +1 -1
  286. package/src/native-ui/shared-contracts/dist/updates.js.map +1 -1
  287. package/src/native-ui/shared-contracts/package.json +1 -1
  288. package/src/native-ui/shared-contracts/src/errors.ts +3 -3
  289. package/src/native-ui/shared-contracts/src/harness-models.ts +23 -2
  290. package/src/native-ui/shared-contracts/src/harness-route.ts +1 -1
  291. package/src/native-ui/shared-contracts/src/harness-session-import.ts +1 -1
  292. package/src/native-ui/shared-contracts/src/index.ts +3 -3
  293. package/src/native-ui/shared-contracts/src/updates.ts +1 -1
  294. package/src/native-ui/shared-contracts/test/browser-bundle.test.ts +3 -3
  295. package/src/native-ui/shared-contracts/test/deepseek-modern-sessions.test.ts +1 -1
  296. package/src/native-ui/shared-contracts/test/errors.test.ts +9 -9
  297. package/src/native-ui/shared-contracts/test/harness-commands.test.ts +1 -1
  298. package/src/native-ui/shared-contracts/test/harness-models.test.ts +3 -3
  299. package/src/native-ui/shared-contracts/test/harness-permission-modes.test.ts +1 -1
  300. package/src/native-ui/shared-contracts/test/harness-plugins.test.ts +1 -1
  301. package/src/native-ui/shared-contracts/test/harness-session-import.test.ts +1 -1
  302. package/src/native-ui/shared-contracts/test/index.test.ts +3 -3
  303. package/src/native-ui/shared-contracts/test/json-rpc.test.ts +1 -1
  304. package/src/native-ui/shared-contracts/test/thread-usage.test.ts +1 -1
  305. package/src/native-ui/shared-contracts/test/updates.test.ts +4 -4
  306. /package/licenses/{codex-host-MIT.txt → bytepioneer-MIT.txt} +0 -0
@@ -8,7 +8,7 @@ const manifest = {
8
8
  name: "Claude Code",
9
9
  icon: "claude-color.svg",
10
10
  aliases: ['claude', 'claude-code'],
11
- // 完整接入(对齐 codex-host):官方 Agent SDK query() 常驻双向 stream-json 会话,
11
+ // 完整接入:官方 Agent SDK query() 常驻双向 stream-json 会话,
12
12
  // 审批/提问经 canUseTool 桥接回 Host,中断/模型/权限模式走原生控制协议。
13
13
  capabilities: { collaborationTools: true, streaming: true, thinking: true, tools: true, approvals: true, questions: true, models: true, thinkingLevels: true, permissionModes: true, resume: true, fork: true, forkFromMessage: true, compaction: true, usage: true, contextUsage: true, attachments: true },
14
14
  };
@@ -16,7 +16,7 @@ class CodexAppServer {
16
16
  this.closed = false;
17
17
  this.codexHome = codexHome ? require('node:path').resolve(codexHome) : null;
18
18
  // The Desktop-bundled CLI and the PATH CLI can be different versions.
19
- const executable = process.env.HARNESS_MIX_CODEX_EXECUTABLE || process.env.CODEXHOST_STOCK_CODEX_PATH;
19
+ const executable = process.env.HARNESS_MIX_CODEX_EXECUTABLE || process.env.HARNESSMIX_STOCK_CODEX_PATH;
20
20
  const { command, args } = executable ? { command: executable, args: ['app-server', '--listen', 'stdio://'] } : cliSpawn('codex', ['app-server', '--stdio']);
21
21
  const env = { ...process.env, ...(this.codexHome ? { CODEX_HOME: this.codexHome } : {}) };
22
22
  this.process = new JsonlProcess(command, args, { env }, {
@@ -11,7 +11,7 @@ const manifest = {
11
11
  name: "DeepSeek Harness",
12
12
  icon: "deepseek-color.svg",
13
13
  aliases: ["dsh", "deepseek-harness"],
14
- // 完整接入(对齐 codex-host):官方 Web Remote 协议(Typert RPC + remote.mux 流),
14
+ // 完整接入:官方 Web Remote 协议(Typert RPC + remote.mux 流),
15
15
  // 审批/提问走 $events waterfall 原生应答;ACP 自动化面(无 fork/提问/plan)已弃用。
16
16
  capabilities: { collaborationTools: true, plan: true, streaming: true, thinking: true, tools: true, approvals: true, questions: true, models: true, thinkingLevels: true, permissionModes: false, resume: true, fork: true, forkFromMessage: true, compaction: true, usage: true, contextUsage: true, attachments: true },
17
17
  };
@@ -17,7 +17,7 @@ const cursor = require('./cursor');
17
17
  const cline = require('./cline');
18
18
 
19
19
  /**
20
- * Adapter 注册表(借鉴 codex-host 插件结构:Manifest + 工厂 + Adapter + Session)。
20
+ * Adapter 注册表:Manifest + 工厂 + Adapter + Session
21
21
  * 新增 Harness 时:实现同形状模块并加入此列表,Renderer 无需改动协议。
22
22
  */
23
23
  const REGISTRY = [antigravity, pi, omp, dsh, claude, codex, opencode, grok, openclaw, hermes, qoder, codebuddy, zcode, trae, kiro, cursor, cline];
@@ -1,8 +1,8 @@
1
1
  const valid = n => typeof n === 'number' && Number.isFinite(n) && n >= 0;
2
2
  const clean = obj => Object.fromEntries(Object.entries(obj).filter(([, v]) => valid(v)));
3
3
 
4
- // Same native accounting as codex-host/pi-usage: cache tokens are separate
5
- // from uncached input. Session totals and latest-request cache rate must not mix.
4
+ // Cache tokens are accounted separately from uncached input, matching the native
5
+ // accounting. Session totals and latest-request cache rate must not mix.
6
6
  function sessionUsage(data = {}) {
7
7
  const t = data.tokens ?? {}, c = data.contextUsage ?? {};
8
8
  return { ...clean({ input: t.input, output: t.output, cacheRead: t.cacheRead,
@@ -1,4 +1,4 @@
1
- // 失败回合错误分类(借鉴 codeg 的失败分类 UX):
1
+ // 失败回合错误分类:
2
2
  // 诚实优先——适配器从原生结构化错误显式给出 errorKind / codexErrorInfo 时直接使用;
3
3
  // 否则按 HTTP 状态码与消息特征归类;都不中则 'unknown',绝不虚构原因。
4
4
  const ERROR_KINDS = ['network', 'auth', 'quota', 'refused', 'server', 'unknown'];
@@ -49,7 +49,13 @@ function normalizeCapabilities(caps = {}) {
49
49
  return createCapabilities({
50
50
  conversation: { streaming: caps.streaming === true, reasoning: caps.thinking === true, plan: caps.plan === true, compaction: caps.compaction === true, attachments: caps.attachments === true },
51
51
  interaction: { approval: caps.approvals === true, question: caps.questions === true, permissionMode: caps.permissionModes === true },
52
- workspace: { nativeDiff: caps.nativeDiff === true, nativePatch: caps.nativePatch === true },
52
+ workspace: {
53
+ git: true,
54
+ worktree: true,
55
+ finalDiff: true,
56
+ nativeDiff: caps.nativeDiff === true,
57
+ nativePatch: caps.nativePatch === true,
58
+ },
53
59
  session: { resume: caps.resume === true, fork: caps.fork === true, forkFromMessage: caps.fork === true && caps.forkFromMessage === true },
54
60
  model: { selection: caps.models === true, thinkingLevel: caps.thinkingLevels === true },
55
61
  usage: { tokens: caps.usage === true, context: caps.contextUsage === true, cost: caps.cost === true },
@@ -1,9 +1,16 @@
1
1
  const object = (properties, required = []) => ({ type: 'object', properties, required, additionalProperties: false });
2
2
  const taskId = { type: 'string', minLength: 1 };
3
3
  const task = { type: 'string', minLength: 1, maxLength: 16000 };
4
+ const teamId = { type: 'string', minLength: 1 };
5
+ const memberId = { type: 'string', minLength: 1 };
4
6
  const tools = [
5
7
  { name: 'list_agents', description: 'List native Harnesses available for delegation and their IDs.', inputSchema: object({}) },
6
- { name: 'delegate_to_agent', description: 'Start an independent native Harness session asynchronously. An @Agent mention explicitly assigns that agent its associated work. Include all necessary context. By default workers share the lead working directory, so later reviewers see earlier edits. Wait for development before starting dependent review; do not edit overlapping files concurrently. Use isolation=worktree for independent isolated experiments (auto also isolates Git projects). Returns task_id; collect results before finishing.', inputSchema: object({ agent_type: taskId, task, isolation: { type: 'string', enum: ['auto', 'worktree', 'shared'] } }, ['agent_type', 'task']) },
8
+ { name: 'delegate_to_agent', description: 'Start an independent native Harness session asynchronously. For Agent Team work, pass team_id, member_id and team_task_id so the native session becomes that durable teammate and can use the shared task graph and mailbox. An @Agent mention explicitly assigns that agent its associated work. Include all necessary context. By default workers share the lead working directory, so later reviewers see earlier edits. Wait for development before starting dependent review; do not edit overlapping files concurrently. Use isolation=worktree for independent isolated experiments (auto also isolates Git projects). Returns task_id; collect results before finishing.', inputSchema: object({ agent_type: taskId, task, isolation: { type: 'string', enum: ['auto', 'worktree', 'shared'] }, team_id: teamId, member_id: memberId, team_task_id: taskId }, ['agent_type', 'task']) },
9
+ { name: 'create_agent_team', description: 'Create a durable multi-Harness Agent Team with one lead and up to six named members with explicit roles. This creates shared team state, not worker sessions; assign tasks and delegate them to members next.', inputSchema: object({ name: { type: 'string', minLength: 1, maxLength: 120 }, goal: task, members: { type: 'array', minItems: 1, maxItems: 6, items: object({ name: { type: 'string', minLength: 1, maxLength: 80 }, role: { type: 'string', minLength: 1, maxLength: 240 }, agent_type: taskId }, ['name', 'role', 'agent_type']) } }, ['name', 'goal', 'members']) },
10
+ { name: 'assign_team_task', description: 'Add a task to the shared team task graph. Dependencies must already exist. The assignee is a team member id or unique member name.', inputSchema: object({ team_id: teamId, title: { type: 'string', minLength: 1, maxLength: 160 }, description: task, assignee: memberId, depends_on: { type: 'array', maxItems: 16, items: taskId } }, ['team_id', 'title', 'description', 'assignee']) },
11
+ { name: 'get_team_state', description: 'Read the current team roster, shared task graph and recent mailbox messages. Available to the lead and native teammate sessions. The projection includes a coarse phase (forming/running/waiting/completed), per-status progress counters, and each task\'s dependency depth so parallel lanes and pipeline stages are visible without walking depends_on by hand.', inputSchema: object({ team_id: teamId }, ['team_id']) },
12
+ { name: 'update_team_task', description: 'Update task status/result in the shared graph. Teammates may update only tasks assigned to themselves; the lead may update any task.', inputSchema: object({ team_id: teamId, task_id: taskId, status: { type: 'string', enum: ['pending', 'blocked', 'in_progress', 'completed', 'failed'] }, result: { type: 'string', maxLength: 48000 } }, ['team_id', 'task_id', 'status']) },
13
+ { name: 'send_team_message', description: 'Send a durable teammate-to-teammate mailbox message. Use a member id/name, lead, or * for broadcast. An idle native recipient is notified directly in its own session. Set kind to classify the hand-off: handoff (work passes to another member), review-request, or review-result; pair it with task_id so the recipient can locate the shared task. Defaults to text.', inputSchema: object({ team_id: teamId, to: { type: 'string', minLength: 1 }, message: task, task_id: taskId, kind: { type: 'string', enum: ['text', 'handoff', 'review-request', 'review-result'] } }, ['team_id', 'to', 'message']) },
7
14
  { name: 'update_agent_plan', description: 'Publish or update the lead collaboration plan shown in the conversation. Include development, review, fixes and final verification as appropriate. Mark steps completed only after collecting and verifying real results.', inputSchema: object({ steps: { type: 'array', minItems: 1, maxItems: 16, items: object({ text: task, status: { type: 'string', enum: ['pending', 'in_progress', 'completed'] } }, ['text', 'status']) } }, ['steps']) },
8
15
  { name: 'list_delegations', description: 'List this lead session\'s durable child tasks, including interrupted tasks from earlier Host runs.', inputSchema: object({}) },
9
16
  { name: 'resume_delegation', description: 'Continue an interrupted child in its recorded native session and workspace. Inspect existing progress; do not replay completed side effects.', inputSchema: object({ task_id: taskId }, ['task_id']) },
@@ -13,6 +13,32 @@ async function git(cwd, args, env = {}) {
13
13
  })).stdout;
14
14
  }
15
15
 
16
+ async function inspectWorkspace(cwd, workspace = null) {
17
+ const result = {
18
+ hostManaged: true,
19
+ git: { available: false },
20
+ worktree: { available: false, active: workspace?.mode === 'worktree' },
21
+ finalDiff: { available: true, source: 'snapshot' },
22
+ };
23
+ try {
24
+ const root = (await git(cwd, ['rev-parse', '--show-toplevel'])).trim();
25
+ const head = (await git(cwd, ['rev-parse', '--verify', 'HEAD'])).trim();
26
+ let branch = null;
27
+ try { branch = (await git(cwd, ['symbolic-ref', '--quiet', '--short', 'HEAD'])).trim() || null; } catch {}
28
+ const dirty = Boolean((await git(cwd, ['status', '--porcelain', '--untracked-files=normal'])).trim());
29
+ result.git = { available: true, root, head, branch, dirty };
30
+ result.worktree = {
31
+ available: true,
32
+ active: workspace?.mode === 'worktree',
33
+ ...(workspace?.branch ? { branch: workspace.branch } : {}),
34
+ ...(workspace?.root ? { root: workspace.root } : {}),
35
+ };
36
+ } catch (error) {
37
+ result.git.reason = /not a git repository/i.test(error.stderr || '') ? 'not-a-git-repository' : 'git-unavailable';
38
+ }
39
+ return result;
40
+ }
41
+
16
42
  // A private index snapshots tracked and non-ignored files without staging the user's index.
17
43
  async function tree(cwd) {
18
44
  const index = path.join(os.tmpdir(), `harness-mix-index-${randomUUID()}`);
@@ -148,4 +174,4 @@ async function pushWorkspace(workspace, remote = 'origin', remoteBranch = worksp
148
174
  return { remote, branch: remoteBranch, commit: newCommit, pushed: true };
149
175
  }
150
176
 
151
- module.exports = { createWorkspace, reviewWorkspace, applyWorkspace, removeWorkspace, discardWorkspace, pushWorkspace, git, tree };
177
+ module.exports = { createWorkspace, inspectWorkspace, reviewWorkspace, applyWorkspace, removeWorkspace, discardWorkspace, pushWorkspace, git, tree };
@@ -7,6 +7,50 @@ const { Store } = require('./store');
7
7
  const { createWorkspace, reviewWorkspace, applyWorkspace, discardWorkspace, pushWorkspace } = require('./collaboration-worktree');
8
8
  const validators = new Map(tools.map(tool => [tool.name, z.fromJSONSchema(tool.inputSchema)]));
9
9
  const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
10
+ const MAX_CONCURRENT_SUBTASKS = 6;
11
+ const MAX_SUBTASKS_PER_TURN = 16;
12
+
13
+ // Dependency depth of each task: the longest chain of prerequisites, used to
14
+ // lay the team board out in lanes.
15
+ // Missing dependency ids are ignored (the runtime already rejects unknown ids at
16
+ // assign time); the cycle back-edge returns 0 so corrupted graphs terminate.
17
+ function teamTaskDepths(tasks) {
18
+ const byId = new Map(tasks.map(task => [task.id, task]));
19
+ const depths = new Map();
20
+ const visiting = new Set();
21
+ const depthOf = id => {
22
+ if (depths.has(id)) return depths.get(id);
23
+ if (visiting.has(id)) return 0;
24
+ const task = byId.get(id);
25
+ if (!task) return 0;
26
+ visiting.add(id);
27
+ const deps = (task.dependsOn || []).filter(dep => byId.has(dep));
28
+ const depth = deps.length ? 1 + Math.max(...deps.map(depthOf)) : 0;
29
+ visiting.delete(id);
30
+ depths.set(id, depth);
31
+ return depth;
32
+ };
33
+ for (const task of tasks) depthOf(task.id);
34
+ return depths;
35
+ }
36
+
37
+ // Coarse team phase: forming (no tasks yet),
38
+ // running (at least one task in flight), waiting (unfinished work but nothing
39
+ // running — the lead still has to delegate or unblock it), completed.
40
+ function teamPhase(team) {
41
+ if (!team.tasks.length) return 'forming';
42
+ if (team.tasks.every(task => task.status === 'completed')) return 'completed';
43
+ return team.tasks.some(task => task.status === 'in_progress') ? 'running' : 'waiting';
44
+ }
45
+
46
+ // Per-status task counters plus completion percent, so a reader can reconstruct
47
+ // the workbench progress bar without walking the task list itself.
48
+ function teamProgress(tasks) {
49
+ const progress = { total: tasks.length, pending: 0, blocked: 0, in_progress: 0, completed: 0, failed: 0, interrupted: 0 };
50
+ for (const task of tasks) if (progress[task.status] !== undefined) progress[task.status] += 1;
51
+ progress.percent = progress.total ? Math.round((progress.completed / progress.total) * 100) : 0;
52
+ return progress;
53
+ }
10
54
 
11
55
  function defaultWorkerPermissionMode(agent) {
12
56
  switch (agent) {
@@ -33,15 +77,32 @@ class Collaboration {
33
77
  this.cancelling = new Set();
34
78
  this.closing = false;
35
79
  this.store = new Store(path.join(runtime.store.directory, 'collaboration'));
80
+ this.teamStore = new Store(path.join(runtime.store.directory, 'collaboration'), 'teams.json');
81
+ this.teams = new Map();
36
82
  }
37
83
 
38
84
  async initialize() {
39
- if (!this.loading) this.loading = this.store.load().then(async rows => {
85
+ if (!this.loading) this.loading = Promise.all([this.store.load(), this.teamStore.load()]).then(async ([rows, teams]) => {
40
86
  for (const row of rows) {
41
87
  if (!row.id || !row.owner || !row.agent) throw new Error('Invalid collaboration history');
42
88
  this.jobs.set(row.id, { ...row, ...(row.status === 'running' ? { status: 'interrupted', error: 'Host restarted; resume this native session explicitly.' } : {}) });
43
89
  }
90
+ for (const team of teams) {
91
+ if (!team.id || !team.owner || !Array.isArray(team.members) || !Array.isArray(team.tasks) || !Array.isArray(team.messages)) throw new Error('Invalid Agent Team history');
92
+ if (!Array.isArray(team.history)) team.history = [];
93
+ this.teams.set(team.id, team);
94
+ }
95
+ for (const job of this.jobs.values()) {
96
+ if (job.status !== 'interrupted' || !job.teamId) continue;
97
+ const team = this.teams.get(job.teamId);
98
+ const member = team?.members.find(entry => entry.id === job.memberId);
99
+ const task = team?.tasks.find(entry => entry.id === job.teamTaskId);
100
+ if (member) member.status = 'interrupted';
101
+ if (task?.status === 'in_progress') task.status = 'interrupted';
102
+ if (team) this.refreshTeamStatus(team);
103
+ }
44
104
  await this.save();
105
+ await this.saveTeams();
45
106
  });
46
107
  return this.loading;
47
108
  }
@@ -50,8 +111,92 @@ class Collaboration {
50
111
  return this.store.save([...this.jobs.values()].map(({ done, cancelling, followupPending, applying, ...job }) => job));
51
112
  }
52
113
 
114
+ saveTeams() { return this.teamStore.save([...this.teams.values()]); }
115
+
116
+ recordTeamSnapshot(team, action) {
117
+ if (!Array.isArray(team.history)) team.history = [];
118
+ team.history.push({ id: randomUUID(), action, at: Date.now(), team: this.teamView(team) });
119
+ if (team.history.length > 200) team.history.splice(0, team.history.length - 200);
120
+ }
121
+
122
+ async publishTeam(team, action) {
123
+ this.recordTeamSnapshot(team, action);
124
+ await this.saveTeams();
125
+ this.emitTeam(team, action);
126
+ }
127
+
128
+ async inspectTeam(threadId, teamId) {
129
+ await this.initialize();
130
+ const participant = this.participant(threadId, teamId);
131
+ if (!participant) {
132
+ // An explicit teamId is an ownership check: outsiders are denied. Without
133
+ // one the caller only asks "does this thread belong to a team?" — the
134
+ // renderer polls exactly that for every active thread, so answer benignly
135
+ // instead of failing an internal error on every poll.
136
+ if (teamId) throw new Error('Unknown team or caller is not a team participant');
137
+ return { team: null, snapshots: [] };
138
+ }
139
+ return {
140
+ team: this.teamView(participant.team),
141
+ snapshots: (participant.team.history || []).map(snapshot => ({ ...snapshot, team: { ...snapshot.team } })),
142
+ };
143
+ }
144
+
145
+ refreshTeamStatus(team) {
146
+ team.status = team.tasks.length && team.tasks.every(task => task.status === 'completed') ? 'completed' : 'active';
147
+ team.updatedAt = Date.now();
148
+ }
149
+
53
150
  list(owner) { return [...this.jobs.values()].filter(j => !owner || j.owner === owner).map(j => this.view(j)); }
54
151
 
152
+ participant(threadId, teamId) {
153
+ for (const team of this.teams.values()) {
154
+ if (teamId && team.id !== teamId) continue;
155
+ if (team.owner === threadId) return { team, kind: 'lead', id: 'lead', name: 'Lead' };
156
+ const member = team.members.find(entry => entry.childId === threadId);
157
+ if (member) return { team, kind: 'member', id: member.id, name: member.name, member };
158
+ }
159
+ return null;
160
+ }
161
+
162
+ isTeamParticipantThread(threadId) { return !!this.participant(threadId); }
163
+
164
+ teamFor(principal, teamId) {
165
+ const participant = this.participant(principal, teamId);
166
+ if (!participant) throw new Error('Unknown team or caller is not a team participant');
167
+ return participant;
168
+ }
169
+
170
+ resolveMember(team, value) {
171
+ const normalized = String(value).toLowerCase();
172
+ const matches = team.members.filter(member => member.id === value || member.name.toLowerCase() === normalized);
173
+ if (matches.length !== 1) throw new Error(matches.length ? 'Team member name is ambiguous; use member_id' : 'Unknown team member');
174
+ return matches[0];
175
+ }
176
+
177
+ teamView(team) {
178
+ const leadThread = this.runtime.threads.find(thread => thread.id === team.owner);
179
+ const leadAgent = leadThread?.harnessId ?? 'codex';
180
+ const leadName = this.runtime.adapters.get(leadAgent)?.manifest?.name ?? leadAgent;
181
+ const depths = teamTaskDepths(team.tasks);
182
+ return {
183
+ team_id: team.id, name: team.name, goal: team.goal, status: team.status, lead_thread_id: team.owner,
184
+ phase: teamPhase(team), progress: teamProgress(team.tasks),
185
+ lead: { id: 'lead', name: 'Team Lead', role: `${leadName} · 协调与验收`, agent: leadAgent, display_status: this.runtime.execution.isRunning(team.owner) ? 'working' : 'ready' },
186
+ members: team.members.map(member => ({ ...member, display_status: member.childId && this.runtime.execution.isRunning(member.childId) ? 'working' : member.status })),
187
+ tasks: team.tasks.map(task => ({ ...task, depth: depths.get(task.id) ?? 0 })), messages: team.messages.slice(-40).map(message => ({ ...message })),
188
+ updated_at: team.updatedAt,
189
+ };
190
+ }
191
+
192
+ emitTeam(team, action) {
193
+ if (!this.runtime.execution.isRunning(team.owner)) return;
194
+ this.runtime.emitCollaboration(team.owner, {
195
+ kind: 'tool', toolCallId: `agent-team:${team.id}`, title: `Agent Team · ${team.name}`,
196
+ state: 'running', input: team.goal, output: JSON.stringify({ action, ...this.teamView(team) }),
197
+ });
198
+ }
199
+
55
200
  async review(id) {
56
201
  const job = this.jobs.get(id);
57
202
  if (!job || job.status === 'running') throw new Error('子任务尚未完成');
@@ -126,24 +271,108 @@ class Collaboration {
126
271
  return job;
127
272
  }
128
273
 
274
+ async teamCall(principal, name, args) {
275
+ const rt = this.runtime;
276
+ if (name === 'create_agent_team') {
277
+ const lead = rt.threads.find(thread => thread.id === principal);
278
+ if (!lead || lead.parentThreadId) throw new Error('Only a lead task can create an Agent Team');
279
+ const names = new Set();
280
+ const members = args.members.map(entry => {
281
+ const agent = rt.resolveHarnessId(entry.agent_type);
282
+ if (!agent || !rt.status[agent]?.available) throw new Error(`Target Harness unavailable: ${entry.agent_type}`);
283
+ if (!rt.adapters.get(agent)?.manifest?.capabilities?.collaborationTools) throw new Error(`Harness cannot participate in Agent Team messaging: ${agent}`);
284
+ if (!lead.activeMentions?.includes(agent)) throw new Error(`Agent Team member ${entry.name} uses unselected Harness "${agent}"`);
285
+ const key = entry.name.trim().toLowerCase();
286
+ if (names.has(key)) throw new Error('Agent Team member names must be unique');
287
+ names.add(key);
288
+ return { id: randomUUID(), name: entry.name.trim(), role: entry.role.trim(), agent, status: 'ready' };
289
+ });
290
+ const team = { id: randomUUID(), owner: principal, name: args.name.trim(), goal: args.goal, status: 'active', members, tasks: [], messages: [], history: [], createdAt: Date.now(), updatedAt: Date.now() };
291
+ this.teams.set(team.id, team);
292
+ await this.publishTeam(team, 'team_created');
293
+ return this.teamView(team);
294
+ }
295
+
296
+ const participant = this.teamFor(principal, args.team_id);
297
+ const { team } = participant;
298
+ if (name === 'get_team_state') return this.teamView(team);
299
+ if (name === 'assign_team_task') {
300
+ if (participant.kind !== 'lead') throw new Error('Only the Team Lead can assign team tasks');
301
+ const assignee = this.resolveMember(team, args.assignee);
302
+ const dependencies = [...new Set(args.depends_on ?? [])];
303
+ if (dependencies.some(id => !team.tasks.some(task => task.id === id))) throw new Error('Unknown dependency task');
304
+ const taskEntry = { id: randomUUID(), title: args.title.trim(), description: args.description, assignee: assignee.id, dependsOn: dependencies, status: dependencies.length ? 'blocked' : 'pending', createdAt: Date.now(), updatedAt: Date.now() };
305
+ team.tasks.push(taskEntry);
306
+ this.refreshTeamStatus(team);
307
+ await this.publishTeam(team, 'task_assigned');
308
+ return { task: taskEntry, team: this.teamView(team) };
309
+ }
310
+ if (name === 'update_team_task') {
311
+ const taskEntry = team.tasks.find(task => task.id === args.task_id);
312
+ if (!taskEntry) throw new Error('Unknown team task');
313
+ if (participant.kind === 'member' && taskEntry.assignee !== participant.id) throw new Error('A teammate can update only its assigned tasks');
314
+ if (participant.kind === 'member' && !['in_progress', 'completed', 'failed'].includes(args.status)) throw new Error('A teammate can only start, complete, or fail its assigned task');
315
+ if (participant.kind === 'member' && taskEntry.status === 'completed') throw new Error('A completed task can only be reopened by the Team Lead');
316
+ if (['in_progress', 'completed'].includes(args.status) && taskEntry.dependsOn.some(id => team.tasks.find(task => task.id === id)?.status !== 'completed')) throw new Error('Task dependencies are not complete');
317
+ taskEntry.status = args.status;
318
+ taskEntry.updatedAt = Date.now();
319
+ if (args.result !== undefined) taskEntry.result = args.result;
320
+ for (const candidate of team.tasks) {
321
+ if (candidate.status === 'blocked' && candidate.dependsOn.every(id => team.tasks.find(task => task.id === id)?.status === 'completed')) candidate.status = 'pending';
322
+ }
323
+ this.refreshTeamStatus(team);
324
+ await this.publishTeam(team, 'task_updated');
325
+ return { task: { ...taskEntry }, team: this.teamView(team) };
326
+ }
327
+ if (name === 'send_team_message') {
328
+ const target = args.to === '*' || args.to.toLowerCase() === 'lead' ? args.to.toLowerCase() : this.resolveMember(team, args.to).id;
329
+ if (args.task_id && !team.tasks.some(task => task.id === args.task_id)) throw new Error('Unknown team task');
330
+ const message = { id: randomUUID(), from: participant.id, fromName: participant.name, to: target, kind: args.kind || 'text', body: args.message, ...(args.task_id ? { taskId: args.task_id } : {}), at: Date.now(), delivery: 'mailbox' };
331
+ team.messages.push(message);
332
+ if (team.messages.length > 200) team.messages.splice(0, team.messages.length - 200);
333
+ const recipients = target === '*' ? team.members.filter(member => member.id !== participant.id) : team.members.filter(member => member.id === target);
334
+ for (const recipient of recipients) {
335
+ if (!recipient.childId || rt.execution.isRunning(recipient.childId)) continue;
336
+ const envelope = `[Harness Mix Agent Team message]\nTeam: ${team.name} (${team.id})\nFrom: ${participant.name}\nType: ${message.kind}\n${args.task_id ? `Task: ${args.task_id}\n` : ''}Message: ${args.message}\n\nTreat this as teammate input. Inspect shared team state with get_team_state, coordinate through send_team_message, and update only your assigned tasks.`;
337
+ message.delivery = 'native_session';
338
+ const recipientJob = [...this.jobs.values()].reverse().find(job => job.teamId === team.id && job.memberId === recipient.id && job.childId === recipient.childId);
339
+ void rt.send(recipient.childId, envelope, { collaborationOf: team.owner, isolated: recipientJob?.workspace?.mode === 'worktree' }).catch(error => {
340
+ message.delivery = 'mailbox'; message.deliveryError = error.message; void this.saveTeams();
341
+ });
342
+ }
343
+ team.updatedAt = Date.now();
344
+ await this.publishTeam(team, 'message_sent');
345
+ return { message, team: this.teamView(team) };
346
+ }
347
+ throw new Error('Unknown Agent Team operation');
348
+ }
349
+
129
350
  view(job) {
130
351
  const pending = job.childId ? this.runtime.core.interactions?.pending(job.childId)?.[0] : null;
131
352
  return { task_id: job.id, parent_thread_id: job.owner, child_thread_id: job.childId, agent_type: job.agent, status: job.status,
353
+ team_id: job.teamId, member_id: job.memberId, team_task_id: job.teamTaskId,
132
354
  display_status: pending ? 'waiting_approval' : job.status, attention: pending ? { type: pending.type, title: pending.title, message: pending.message } : undefined,
133
355
  task: job.task, workspace: job.workspace, applied: !!job.appliedDigest, result: job.result, error: job.error,
134
356
  diff: job.diff, digest: job.digest, branch: job.workspace?.branch };
135
357
  }
136
358
 
137
- async call(owner, name, args) {
359
+ async call(principal, name, args) {
138
360
  await this.initialize();
139
361
  if (this.closing) throw new Error('Host is closing');
140
362
  if (!validators.has(name)) throw new Error('Unknown collaboration tool');
141
363
  args = validators.get(name).parse(args);
142
364
  const rt = this.runtime;
365
+ const teamTools = new Set(['create_agent_team', 'assign_team_task', 'get_team_state', 'update_team_task', 'send_team_message']);
366
+ const participant = this.participant(principal, args.team_id);
367
+ const owner = participant?.team.owner ?? principal;
143
368
  const parent = rt.threads.find(t => t.id === owner);
144
- if (!parent || parent.parentThreadId) throw new Error('Only lead tasks can delegate');
145
- if (!rt.execution.isRunning(owner) || this.cancelling.has(owner)) throw new Error('Lead turn is no longer active');
146
- if (name === 'list_agents') return [...rt.adapters.values()].map(a => ({ agent_type: a.manifest.id, name: a.manifest.name, available: !!rt.status[a.manifest.id]?.available }));
369
+ if (teamTools.has(name)) {
370
+ if (!rt.execution.isRunning(principal) || (principal === owner && this.cancelling.has(owner))) throw new Error('Collaboration turn is no longer active');
371
+ return this.teamCall(principal, name, args);
372
+ }
373
+ if (!parent || principal !== owner || parent.parentThreadId) throw new Error('Only lead tasks can delegate');
374
+ if (!rt.execution.isRunning(principal) || this.cancelling.has(owner)) throw new Error('Collaboration turn is no longer active');
375
+ if (name === 'list_agents') return [...rt.adapters.values()].map(a => ({ agent_type: a.manifest.id, name: a.manifest.name, available: !!rt.status[a.manifest.id]?.available, team_capable: !!a.manifest.capabilities?.collaborationTools }));
147
376
  if (name === 'list_delegations') return this.list(owner);
148
377
  if (name === 'update_agent_plan') {
149
378
  rt.emitCollaboration(owner, { kind: 'plan', entries: args.steps });
@@ -154,18 +383,43 @@ class Collaboration {
154
383
  if (!agent || !rt.status[agent]?.available) throw new Error('Target Harness unavailable');
155
384
  // Server-side enforcement: multi-agent collaboration can ONLY start when the user explicitly selected/mentioned agents.
156
385
  if (!parent.activeMentions || !parent.activeMentions.length) {
157
- throw new Error('跨 Harness 协作仅在用户显式使用 #agent @agent(如 #pi、#claude)指定时允许启动。用户本轮未显式委派,不能由大模型自行决定启动跨 Harness 协作。请直接使用当前 Harness 的原生工具完成任务。');
386
+ throw new Error('跨 Harness 协作仅在用户显式选择 Agent,或在团队/委派语境中明确写出 Harness 名称时允许启动(例如 #pi #claude,或“用 Pi 开发、Claude 审查组成团队”)。用户本轮未显式委派,不能由大模型自行决定启动跨 Harness 协作。');
158
387
  }
159
388
  if (!parent.activeMentions.includes(agent)) {
160
389
  throw new Error(`用户仅显式指定了 [${parent.activeMentions.join(', ')}],不能委派给未指定的 "${agent}"。请向用户确认是否需要委派给其他 Harness。`);
161
390
  }
391
+ let team, member, teamTask, previousMemberJob;
392
+ const teamFields = [args.team_id, args.member_id, args.team_task_id].filter(Boolean).length;
393
+ if (teamFields && teamFields !== 3) throw new Error('Agent Team delegation requires team_id, member_id and team_task_id together');
394
+ if (teamFields) {
395
+ ({ team } = this.teamFor(owner, args.team_id));
396
+ member = this.resolveMember(team, args.member_id);
397
+ teamTask = team.tasks.find(entry => entry.id === args.team_task_id);
398
+ if (!teamTask || teamTask.assignee !== member.id) throw new Error('Team task is not assigned to this member');
399
+ if (member.agent !== agent) throw new Error('Delegated Harness does not match the team member');
400
+ if (member.childId && !rt.threads.some(thread => thread.id === member.childId)) delete member.childId;
401
+ previousMemberJob = member.childId ? [...this.jobs.values()].reverse().find(job => job.teamId === team.id && job.memberId === member.id && job.childId === member.childId) : null;
402
+ if (teamTask.dependsOn.some(id => team.tasks.find(entry => entry.id === id)?.status !== 'completed')) throw new Error('Team task dependencies are not complete');
403
+ if (teamTask.status === 'completed') throw new Error('Team task is already completed');
404
+ if (teamTask.status === 'in_progress') throw new Error('Team task is already running');
405
+ if ([...this.jobs.values()].some(job => job.teamId === team.id && job.memberId === member.id && job.status === 'running')) throw new Error('Team member is already working on another task');
406
+ }
162
407
  const jobs = [...this.jobs.values()].filter(j => j.owner === owner);
163
- if (jobs.filter(j => j.status === 'running').length >= 4) throw new Error('At most four concurrent subtasks; collect existing results first');
164
- if (jobs.filter(j => j.turnId === rt.execution.lastTurn(owner)?.id).length >= 16) throw new Error('At most sixteen subtasks per lead turn');
165
- const job = { id: randomUUID(), owner, agent, turnId: rt.execution.lastTurn(owner).id, status: 'running', task: args.task, isolation: args.isolation || 'shared' };
408
+ if (jobs.filter(j => j.status === 'running').length >= MAX_CONCURRENT_SUBTASKS) throw new Error('At most six concurrent subtasks; collect existing results first');
409
+ if (jobs.filter(j => j.turnId === rt.execution.lastTurn(owner)?.id).length >= MAX_SUBTASKS_PER_TURN) throw new Error('At most sixteen subtasks per lead turn');
410
+ const job = { id: randomUUID(), owner, agent, turnId: rt.execution.lastTurn(owner).id, status: 'running', task: args.task,
411
+ isolation: previousMemberJob?.isolation ?? args.isolation ?? 'shared',
412
+ ...(previousMemberJob?.workspace ? { workspace: previousMemberJob.workspace } : {}),
413
+ ...(team ? { teamId: team.id, memberId: member.id, teamTaskId: teamTask.id, ...(member.childId ? { childId: member.childId } : {}) } : {}) };
166
414
  this.jobs.set(job.id, job);
415
+ if (team) {
416
+ teamTask.status = 'in_progress'; teamTask.jobId = job.id; teamTask.updatedAt = Date.now();
417
+ member.status = 'working'; this.refreshTeamStatus(team);
418
+ await this.publishTeam(team, 'task_started');
419
+ }
167
420
  await this.save();
168
- job.done = this.run(parent, job, args.task);
421
+ const teamPrompt = team ? `${args.task}\n\n[Harness Mix Agent Team]\nTeam: ${team.name} (${team.id})\nShared goal: ${team.goal}\nYou are ${member.name}. Role: ${member.role}\nAssigned task: ${teamTask.title} (${teamTask.id})\nYou are a persistent teammate, not a one-shot subagent. Read shared state with get_team_state, update your assigned task with update_team_task, and coordinate directly with teammates through send_team_message. Do not create or assign team members.` : args.task;
422
+ job.done = this.run(parent, job, teamPrompt);
169
423
  return this.view(job);
170
424
  }
171
425
  if (name === 'get_delegation_status') {
@@ -185,11 +439,19 @@ class Collaboration {
185
439
  job.followupPending = true;
186
440
  try { await job.done; } finally { job.followupPending = false; }
187
441
  if (this.closing || !rt.execution.isRunning(owner) || this.cancelling.has(owner)) throw new Error('Lead turn is no longer active');
188
- if ([...this.jobs.values()].filter(j => j.owner === owner && j.status === 'running').length >= 4) throw new Error('At most four concurrent subtasks');
442
+ if ([...this.jobs.values()].filter(j => j.owner === owner && j.status === 'running').length >= MAX_CONCURRENT_SUBTASKS) throw new Error('At most six concurrent subtasks');
189
443
  job.status = 'running'; job.result = undefined; job.error = undefined;
190
444
  job.turnId = rt.execution.lastTurn(owner).id;
191
445
  const task = name === 'resume_delegation' ? `Continue the interrupted task in this existing workspace. Inspect existing progress before acting; do not repeat completed side effects. Original task:\n${job.task}` : args.task;
192
446
  if (name !== 'resume_delegation') job.task = task;
447
+ if (job.teamId) {
448
+ const team = this.teams.get(job.teamId);
449
+ const member = team?.members.find(entry => entry.id === job.memberId);
450
+ const teamTask = team?.tasks.find(entry => entry.id === job.teamTaskId);
451
+ if (member) member.status = 'working';
452
+ if (teamTask) { teamTask.status = 'in_progress'; teamTask.updatedAt = Date.now(); }
453
+ if (team) { this.refreshTeamStatus(team); await this.publishTeam(team, name === 'resume_delegation' ? 'task_resumed' : 'task_followup'); }
454
+ }
193
455
  await this.save();
194
456
  job.done = this.run(parent, job, task);
195
457
  return this.view(job);
@@ -213,7 +475,13 @@ class Collaboration {
213
475
  const child = job.childId ? rt.threads.find(t => t.id === job.childId) : await rt.createThread({
214
476
  harnessId: job.agent, cwd: job.workspace.cwd, title: `${parent.title} › ${task.slice(0, 40)}`, parentThreadId: parent.id,
215
477
  options: { ...(workerPermMode ? { permissionMode: workerPermMode } : {}) },
216
- onCreated: async thread => { job.childId = thread.id; await this.save(); }
478
+ onCreated: async thread => {
479
+ job.childId = thread.id;
480
+ const team = job.teamId ? this.teams.get(job.teamId) : null;
481
+ const member = team?.members.find(entry => entry.id === job.memberId);
482
+ if (member) { member.childId = thread.id; member.status = 'working'; team.updatedAt = Date.now(); await this.publishTeam(team, 'member_session_ready'); }
483
+ await this.save();
484
+ }
217
485
  });
218
486
  if (!child) throw new Error('Native child history is missing; no replacement session was created');
219
487
  job.childId = child.id;
@@ -248,6 +516,21 @@ class Collaboration {
248
516
  const messages = rt.core.getItemsForTurn(turn.id).filter(i => i.type === 'agent_message');
249
517
  const finals = messages.filter(i => i.phase === 'final');
250
518
  job.result = (finals.length ? finals : messages).map(i => i.content || '').join('\n').slice(0, 48000);
519
+ if (job.teamId) {
520
+ const team = this.teams.get(job.teamId);
521
+ const member = team?.members.find(entry => entry.id === job.memberId);
522
+ const teamTask = team?.tasks.find(entry => entry.id === job.teamTaskId);
523
+ if (member) member.status = 'ready';
524
+ if (teamTask && teamTask.status === 'in_progress') {
525
+ teamTask.status = job.status === 'completed' ? 'completed' : job.status === 'cancelled' ? 'pending' : 'failed';
526
+ teamTask.result = job.result;
527
+ teamTask.updatedAt = Date.now();
528
+ for (const candidate of team.tasks) {
529
+ if (candidate.status === 'blocked' && candidate.dependsOn.every(id => team.tasks.find(entry => entry.id === id)?.status === 'completed')) candidate.status = 'pending';
530
+ }
531
+ }
532
+ if (team) { this.refreshTeamStatus(team); await this.publishTeam(team, 'task_settled'); }
533
+ }
251
534
  if (job.workspace?.mode === 'worktree' && job.status === 'completed') {
252
535
  try {
253
536
  const rev = await reviewWorkspace(job.workspace);
@@ -255,7 +538,17 @@ class Collaboration {
255
538
  job.digest = rev.digest;
256
539
  } catch {}
257
540
  }
258
- } catch (error) { if (job.status === 'running') { job.status = 'failed'; job.error = error.message; } }
541
+ } catch (error) {
542
+ if (job.status === 'running') { job.status = 'failed'; job.error = error.message; }
543
+ if (job.teamId) {
544
+ const team = this.teams.get(job.teamId);
545
+ const member = team?.members.find(entry => entry.id === job.memberId);
546
+ const teamTask = team?.tasks.find(entry => entry.id === job.teamTaskId);
547
+ if (member) member.status = 'ready';
548
+ if (teamTask) { teamTask.status = 'failed'; teamTask.result = error.message; teamTask.updatedAt = Date.now(); }
549
+ if (team) { this.refreshTeamStatus(team); await this.publishTeam(team, 'task_failed'); }
550
+ }
551
+ }
259
552
  finally { await this.save(); emit({ kind: 'tool', toolCallId, state: job.status === 'completed' ? 'done' : 'error', output: JSON.stringify(this.view(job)) }); }
260
553
  }
261
554
 
@@ -270,7 +563,18 @@ class Collaboration {
270
563
  new Promise(r => setTimeout(r, 3_000)),
271
564
  ]).catch(() => {});
272
565
  }
273
- } finally { job.cancelling = false; await this.save(); }
566
+ } finally {
567
+ job.cancelling = false;
568
+ if (job.teamId) {
569
+ const team = this.teams.get(job.teamId);
570
+ const member = team?.members.find(entry => entry.id === job.memberId);
571
+ const teamTask = team?.tasks.find(entry => entry.id === job.teamTaskId);
572
+ if (member) member.status = this.closing ? 'interrupted' : 'ready';
573
+ if (teamTask?.status === 'in_progress') { teamTask.status = this.closing ? 'interrupted' : 'pending'; teamTask.updatedAt = Date.now(); }
574
+ if (team) { this.refreshTeamStatus(team); await this.publishTeam(team, this.closing ? 'task_interrupted' : 'task_cancelled'); }
575
+ }
576
+ await this.save();
577
+ }
274
578
  }
275
579
 
276
580
  async cancelOwner(owner) {
@@ -292,6 +596,7 @@ class Collaboration {
292
596
  await Promise.all([...this.jobs.values()].map(j => j.done));
293
597
  this.keys.clear();
294
598
  await this.save();
599
+ await this.saveTeams();
295
600
  if (this.server) await new Promise(resolve => this.server.close(resolve));
296
601
  }
297
602
  }
@@ -306,7 +611,35 @@ function mentionedAgents(text, runtime) {
306
611
  const id = runtime.resolveHarnessId(match[1] || match[2]);
307
612
  if (id) ids.add(id);
308
613
  }
614
+ // Natural-language team requests may name Harnesses without using the picker.
615
+ // Require an explicit collaboration intent so ordinary product discussion such
616
+ // as "Codex UI" does not silently authorize cross-Harness delegation.
617
+ const teamIntent = /agent\s*team|团队|组队|协作|委派|调度|分工|成员|队长|主导者|\b(?:team|delegate|delegation|assign|member|teammate)\b/i.test(prose);
618
+ if (teamIntent) {
619
+ for (const adapter of runtime.adapters.values()) {
620
+ const labels = [adapter.manifest.id, adapter.manifest.name, ...(adapter.manifest.aliases ?? [])]
621
+ .filter(label => String(label ?? '').trim().length >= 2)
622
+ .sort((a, b) => String(b).length - String(a).length);
623
+ if (labels.some(label => containsPlainAgentName(prose, label))) ids.add(adapter.manifest.id);
624
+ }
625
+ }
309
626
  return [...ids];
310
627
  }
311
628
 
312
- module.exports = { Collaboration, mentionedAgents, defaultWorkerPermissionMode };
629
+ function containsPlainAgentName(text, label) {
630
+ const haystack = String(text).toLowerCase();
631
+ const needle = String(label).trim().toLowerCase();
632
+ for (let offset = haystack.indexOf(needle); offset !== -1; offset = haystack.indexOf(needle, offset + 1)) {
633
+ const before = offset === 0 ? '' : haystack[offset - 1];
634
+ const afterIndex = offset + needle.length;
635
+ const after = afterIndex === haystack.length ? '' : haystack[afterIndex];
636
+ if (plainNameBoundary(before) && plainNameBoundary(after)) return true;
637
+ }
638
+ return false;
639
+ }
640
+
641
+ function plainNameBoundary(char) {
642
+ return !char || /[\s\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff,;:!?()[\]{}"',。;:、!?“”‘’]/u.test(char);
643
+ }
644
+
645
+ module.exports = { Collaboration, mentionedAgents, defaultWorkerPermissionMode, teamTaskDepths, teamPhase, teamProgress };