@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
@@ -5,164 +5,164 @@ const os = require('node:os');
5
5
  const path = require('node:path');
6
6
  const { PassThrough } = require('node:stream');
7
7
  const antigravity = require('../src/main/adapters/antigravity');
8
-
9
- (async () => {
8
+
9
+ (async () => {
10
10
  const { manifest, create, parseModelsOutput, parseUsage, formatPrompt, prepareImageAttachments, formatAntigravityResultError, modelSupportsEffort, ANTIGRAVITY_PERMISSION_MODES } = antigravity;
11
-
12
- // 1. Manifest
13
- assert.equal(manifest.id, 'antigravity');
14
- assert.equal(manifest.name, 'Antigravity');
15
- assert.equal(manifest.icon, 'antigravity-color.svg');
16
- assert.equal(manifest.capabilities.streaming, true);
17
- assert.equal(manifest.capabilities.thinking, true);
18
- assert.equal(manifest.capabilities.tools, true);
19
- assert.equal(manifest.capabilities.approvals, true);
20
- assert.equal(manifest.capabilities.questions, true);
21
- assert.equal(manifest.capabilities.models, true);
22
- assert.equal(manifest.capabilities.thinkingLevels, true);
23
- assert.equal(manifest.capabilities.permissionModes, true);
24
- assert.equal(manifest.capabilities.resume, true);
25
- assert.equal(manifest.capabilities.fork, true);
26
- assert.equal(manifest.capabilities.forkFromMessage, true);
27
- assert.equal(manifest.capabilities.attachments, true);
28
-
29
- // 2. parseModelsOutput
30
- const sampleModelsOutput = `
31
- Fetching available models...
32
- gemini-3.8-flash-high\tGemini 3.8 Flash (High)
33
- gemini-3.8-flash-medium\tGemini 3.8 Flash (Medium)
34
- gemini-3.8-flash-low\tGemini 3.8 Flash (Low)
35
- gemini-3.1-pro-high\tGemini 3.1 Pro (High)
36
- gemini-3.1-pro-low\tGemini 3.1 Pro (Low)
37
- claude-sonnet-4-6\tClaude Sonnet 4.6 (Thinking)
38
- gpt-oss-120b-medium\tGPT-OSS 120B (Medium)
39
- `;
40
- const models = parseModelsOutput(sampleModelsOutput);
41
- assert.ok(models.length >= 4);
42
-
43
- const flash = models.find(m => m.id === 'gemini-3.8-flash');
44
- assert.ok(flash);
45
- assert.equal(flash.name, 'Gemini 3.8 Flash');
46
- assert.equal(flash.provider, 'google');
47
- assert.equal(flash.efforts.length, 3);
48
- assert.equal(flash.efforts[0].id, 'low');
49
- assert.equal(flash.efforts[2].id, 'high');
11
+
12
+ // 1. Manifest
13
+ assert.equal(manifest.id, 'antigravity');
14
+ assert.equal(manifest.name, 'Antigravity');
15
+ assert.equal(manifest.icon, 'antigravity-color.svg');
16
+ assert.equal(manifest.capabilities.streaming, true);
17
+ assert.equal(manifest.capabilities.thinking, true);
18
+ assert.equal(manifest.capabilities.tools, true);
19
+ assert.equal(manifest.capabilities.approvals, true);
20
+ assert.equal(manifest.capabilities.questions, true);
21
+ assert.equal(manifest.capabilities.models, true);
22
+ assert.equal(manifest.capabilities.thinkingLevels, true);
23
+ assert.equal(manifest.capabilities.permissionModes, true);
24
+ assert.equal(manifest.capabilities.resume, true);
25
+ assert.equal(manifest.capabilities.fork, true);
26
+ assert.equal(manifest.capabilities.forkFromMessage, true);
27
+ assert.equal(manifest.capabilities.attachments, true);
28
+
29
+ // 2. parseModelsOutput
30
+ const sampleModelsOutput = `
31
+ Fetching available models...
32
+ gemini-3.8-flash-high\tGemini 3.8 Flash (High)
33
+ gemini-3.8-flash-medium\tGemini 3.8 Flash (Medium)
34
+ gemini-3.8-flash-low\tGemini 3.8 Flash (Low)
35
+ gemini-3.1-pro-high\tGemini 3.1 Pro (High)
36
+ gemini-3.1-pro-low\tGemini 3.1 Pro (Low)
37
+ claude-sonnet-4-6\tClaude Sonnet 4.6 (Thinking)
38
+ gpt-oss-120b-medium\tGPT-OSS 120B (Medium)
39
+ `;
40
+ const models = parseModelsOutput(sampleModelsOutput);
41
+ assert.ok(models.length >= 4);
42
+
43
+ const flash = models.find(m => m.id === 'gemini-3.8-flash');
44
+ assert.ok(flash);
45
+ assert.equal(flash.name, 'Gemini 3.8 Flash');
46
+ assert.equal(flash.provider, 'google');
47
+ assert.equal(flash.efforts.length, 3);
48
+ assert.equal(flash.efforts[0].id, 'low');
49
+ assert.equal(flash.efforts[2].id, 'high');
50
50
  assert.equal(flash.defaultEffort, 'high');
51
51
  assert.equal(flash.contextWindow, 1_048_576);
52
52
  assert.equal(modelSupportsEffort(flash), true);
53
-
54
- const claude = models.find(m => m.id === 'claude-sonnet-4-6');
53
+
54
+ const claude = models.find(m => m.id === 'claude-sonnet-4-6');
55
55
  assert.ok(claude);
56
56
  assert.equal(claude.provider, 'anthropic');
57
57
  assert.equal(claude.contextWindow, 200_000);
58
58
  assert.equal(modelSupportsEffort(claude), false);
59
59
  assert.equal(modelSupportsEffort({ id: 'claude-sonnet-4-6' }), false);
60
-
61
- // 3. parseUsage
62
- const usage = parseUsage({
63
- input_tokens: 1200,
64
- output_tokens: 300,
65
- thinking_tokens: 150,
66
- total_tokens: 1650,
67
- context_used_tokens: 25000,
68
- }, 'gemini-3.8-flash');
69
- assert.equal(usage.inputTokens, 1200);
70
- assert.equal(usage.outputTokens, 300);
71
- assert.equal(usage.reasoningOutputTokens, 150);
72
- assert.equal(usage.contextWindow, 1_048_576);
73
- assert.equal(usage.tokens, 25000);
74
- assert.ok(usage.contextPercent > 2.3 && usage.contextPercent < 2.5);
75
-
76
- // 4. formatPrompt
77
- const rawPrompt = '帮我修改 app.js';
78
- const formatted = formatPrompt(rawPrompt);
79
- assert.ok(formatted.includes('write_to_file'));
80
- assert.ok(formatted.includes('replace_file_content'));
81
- assert.ok(formatted.includes(rawPrompt));
82
-
83
- // Slash commands / already instruction formatted should not duplicate
84
- assert.equal(formatPrompt('/usage'), '/usage');
85
- assert.equal(formatPrompt(formatted), formatted);
86
-
87
- // 5. Adapter lifecycle and Session
88
- const adapter = create(() => {});
89
- assert.equal(typeof adapter.open, 'function');
90
- assert.equal(typeof adapter.send, 'function');
91
- assert.equal(typeof adapter.cancel, 'function');
92
- assert.equal(typeof adapter.close, 'function');
93
- assert.equal(typeof adapter.respond, 'function');
94
- assert.equal(typeof adapter.fork, 'function');
95
- assert.equal(typeof adapter.listModelsFor, 'function');
96
- assert.equal(typeof adapter.setModel, 'function');
97
- assert.equal(typeof adapter.setThinkingLevel, 'function');
98
- assert.equal(typeof adapter.setPermissionMode, 'function');
99
-
100
- // 6. Inspect
101
- const inspection = await adapter.inspect();
102
- assert.ok(typeof inspection.available === 'boolean');
103
- assert.ok(typeof inspection.detail === 'string');
104
-
105
- // 7. Open session
106
- const openEvents = [];
107
- const session = await adapter.open({
108
- thread: {
109
- id: 'thread-1',
110
- nativeSessionId: 'conv-12345',
111
- cwd: 'E:\\harness-mix',
112
- restore: true,
113
- options: {
114
- model: { id: 'gemini-3.8-flash', name: 'Gemini 3.8 Flash' },
115
- thinking: 'medium',
116
- permissionMode: 'desktop',
117
- },
118
- },
119
- emit: (event) => openEvents.push(event),
120
- diagnostic: () => {},
121
- });
122
- assert.equal(session.nativeSessionId, 'conv-12345');
123
- assert.equal(session.thinkingLevel, 'medium');
124
- assert.equal(session.permissionMode, 'desktop');
125
- assert.ok(openEvents.some(e => e.kind === 'session' && e.nativeSessionId === 'conv-12345'));
126
-
127
- // 8. setModel, setThinkingLevel, setPermissionMode
128
- await adapter.setModel(session, { id: 'gemini-3.1-pro', name: 'Gemini 3.1 Pro' });
129
- assert.equal(session.model.id, 'gemini-3.1-pro');
130
- await adapter.setThinkingLevel(session, 'high');
131
- assert.equal(session.thinkingLevel, 'high');
132
- await adapter.setPermissionMode(session, 'skip');
133
- assert.equal(session.permissionMode, 'skip');
134
-
135
- // 9. Describe
136
- const desc = await adapter.describe();
137
- assert.ok(Array.isArray(desc.models));
138
- assert.equal(desc.thinkingLevels.length, 3);
139
- assert.equal(desc.permissionModes.length, 3);
140
-
141
- // 10. Fork session
142
- const forkEvents = [];
143
- const forked = await adapter.fork({
144
- id: 'thread-1',
145
- nativeSessionId: 'conv-12345',
146
- cwd: 'E:\\harness-mix',
147
- model: { id: 'gemini-3.8-flash', name: 'Gemini 3.8 Flash' },
148
- options: { thinking: 'high', permissionMode: 'default' },
149
- }, {
150
- emit: (e) => forkEvents.push(e),
151
- diagnostic: () => {},
152
- });
153
- assert.ok(forked.nativeSessionId);
154
- assert.notEqual(forked.nativeSessionId, 'conv-12345');
155
- assert.equal(forked.session.nativeSessionId, forked.nativeSessionId);
156
- assert.ok(forkEvents.some(e => e.kind === 'session' && e.nativeSessionId === forked.nativeSessionId));
157
-
158
- // 11. Image attachments
159
- const tmpRoot = path.join(os.tmpdir(), `agy-attach-test-${Date.now()}`);
160
- await fs.promises.mkdir(tmpRoot, { recursive: true });
161
- const localImg = path.join(tmpRoot, 'test.png');
162
- await fs.promises.writeFile(localImg, Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+jRZkAAAAASUVORK5CYII=', 'base64'));
163
- const prepared = await prepareImageAttachments([
164
- { name: 'test.png', path: localImg },
165
- { name: 'inline.png', mime: 'image/png', data: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+jRZkAAAAASUVORK5CYII=' },
60
+
61
+ // 3. parseUsage
62
+ const usage = parseUsage({
63
+ input_tokens: 1200,
64
+ output_tokens: 300,
65
+ thinking_tokens: 150,
66
+ total_tokens: 1650,
67
+ context_used_tokens: 25000,
68
+ }, 'gemini-3.8-flash');
69
+ assert.equal(usage.inputTokens, 1200);
70
+ assert.equal(usage.outputTokens, 300);
71
+ assert.equal(usage.reasoningOutputTokens, 150);
72
+ assert.equal(usage.contextWindow, 1_048_576);
73
+ assert.equal(usage.tokens, 25000);
74
+ assert.ok(usage.contextPercent > 2.3 && usage.contextPercent < 2.5);
75
+
76
+ // 4. formatPrompt
77
+ const rawPrompt = '帮我修改 app.js';
78
+ const formatted = formatPrompt(rawPrompt);
79
+ assert.ok(formatted.includes('write_to_file'));
80
+ assert.ok(formatted.includes('replace_file_content'));
81
+ assert.ok(formatted.includes(rawPrompt));
82
+
83
+ // Slash commands / already instruction formatted should not duplicate
84
+ assert.equal(formatPrompt('/usage'), '/usage');
85
+ assert.equal(formatPrompt(formatted), formatted);
86
+
87
+ // 5. Adapter lifecycle and Session
88
+ const adapter = create(() => {});
89
+ assert.equal(typeof adapter.open, 'function');
90
+ assert.equal(typeof adapter.send, 'function');
91
+ assert.equal(typeof adapter.cancel, 'function');
92
+ assert.equal(typeof adapter.close, 'function');
93
+ assert.equal(typeof adapter.respond, 'function');
94
+ assert.equal(typeof adapter.fork, 'function');
95
+ assert.equal(typeof adapter.listModelsFor, 'function');
96
+ assert.equal(typeof adapter.setModel, 'function');
97
+ assert.equal(typeof adapter.setThinkingLevel, 'function');
98
+ assert.equal(typeof adapter.setPermissionMode, 'function');
99
+
100
+ // 6. Inspect
101
+ const inspection = await adapter.inspect();
102
+ assert.ok(typeof inspection.available === 'boolean');
103
+ assert.ok(typeof inspection.detail === 'string');
104
+
105
+ // 7. Open session
106
+ const openEvents = [];
107
+ const session = await adapter.open({
108
+ thread: {
109
+ id: 'thread-1',
110
+ nativeSessionId: 'conv-12345',
111
+ cwd: 'E:\\harness-mix',
112
+ restore: true,
113
+ options: {
114
+ model: { id: 'gemini-3.8-flash', name: 'Gemini 3.8 Flash' },
115
+ thinking: 'medium',
116
+ permissionMode: 'desktop',
117
+ },
118
+ },
119
+ emit: (event) => openEvents.push(event),
120
+ diagnostic: () => {},
121
+ });
122
+ assert.equal(session.nativeSessionId, 'conv-12345');
123
+ assert.equal(session.thinkingLevel, 'medium');
124
+ assert.equal(session.permissionMode, 'desktop');
125
+ assert.ok(openEvents.some(e => e.kind === 'session' && e.nativeSessionId === 'conv-12345'));
126
+
127
+ // 8. setModel, setThinkingLevel, setPermissionMode
128
+ await adapter.setModel(session, { id: 'gemini-3.1-pro', name: 'Gemini 3.1 Pro' });
129
+ assert.equal(session.model.id, 'gemini-3.1-pro');
130
+ await adapter.setThinkingLevel(session, 'high');
131
+ assert.equal(session.thinkingLevel, 'high');
132
+ await adapter.setPermissionMode(session, 'skip');
133
+ assert.equal(session.permissionMode, 'skip');
134
+
135
+ // 9. Describe
136
+ const desc = await adapter.describe();
137
+ assert.ok(Array.isArray(desc.models));
138
+ assert.equal(desc.thinkingLevels.length, 3);
139
+ assert.equal(desc.permissionModes.length, 3);
140
+
141
+ // 10. Fork session
142
+ const forkEvents = [];
143
+ const forked = await adapter.fork({
144
+ id: 'thread-1',
145
+ nativeSessionId: 'conv-12345',
146
+ cwd: 'E:\\harness-mix',
147
+ model: { id: 'gemini-3.8-flash', name: 'Gemini 3.8 Flash' },
148
+ options: { thinking: 'high', permissionMode: 'default' },
149
+ }, {
150
+ emit: (e) => forkEvents.push(e),
151
+ diagnostic: () => {},
152
+ });
153
+ assert.ok(forked.nativeSessionId);
154
+ assert.notEqual(forked.nativeSessionId, 'conv-12345');
155
+ assert.equal(forked.session.nativeSessionId, forked.nativeSessionId);
156
+ assert.ok(forkEvents.some(e => e.kind === 'session' && e.nativeSessionId === forked.nativeSessionId));
157
+
158
+ // 11. Image attachments
159
+ const tmpRoot = path.join(os.tmpdir(), `agy-attach-test-${Date.now()}`);
160
+ await fs.promises.mkdir(tmpRoot, { recursive: true });
161
+ const localImg = path.join(tmpRoot, 'test.png');
162
+ await fs.promises.writeFile(localImg, Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+jRZkAAAAASUVORK5CYII=', 'base64'));
163
+ const prepared = await prepareImageAttachments([
164
+ { name: 'test.png', path: localImg },
165
+ { name: 'inline.png', mime: 'image/png', data: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+jRZkAAAAASUVORK5CYII=' },
166
166
  ], tmpRoot);
167
167
  assert.equal(prepared.imageEntries.length, 2);
168
168
  assert.equal(prepared.imageEntries[0].name, 'test.png');
@@ -189,105 +189,105 @@ gpt-oss-120b-medium\tGPT-OSS 120B (Medium)
189
189
  await adapter.close(legacySession);
190
190
  await fs.promises.unlink(legacyPath).catch(() => {});
191
191
  await fs.promises.rm(tmpRoot, { recursive: true, force: true }).catch(() => {});
192
-
193
- // 12. mergePendingStep
194
- const { mergePendingStep, cloneDatabase } = antigravity;
195
- const activeStep = {
196
- step_index: 2,
197
- state: 'ACTIVE',
198
- step_type: 'tool',
199
- tool_name: 'write_to_file',
200
- tool_info: {
201
- name: 'write_to_file',
202
- parameters: { TargetFile: 'test.txt', Description: 'create file' },
203
- },
204
- };
205
- const doneStep = {
206
- step_index: 2,
207
- state: 'DONE',
208
- step_type: 'tool',
209
- tool_info: {
210
- output: 'File written successfully',
211
- },
212
- };
213
- const merged = mergePendingStep(activeStep, doneStep);
214
- assert.equal(merged.state, 'DONE');
215
- assert.equal(merged.tool_name, 'write_to_file');
216
- assert.equal(merged.tool_info.name, 'write_to_file');
217
- assert.equal(merged.tool_info.parameters.TargetFile, 'test.txt');
218
- assert.equal(merged.tool_info.output, 'File written successfully');
219
-
220
- // 13. cloneDatabase with message turn pruning and summaries.db
221
- const mockHome = path.join(os.tmpdir(), `agy-mock-home-${Date.now()}`);
222
- const convDir = path.join(mockHome, '.gemini', 'antigravity-cli', 'conversations');
223
- await fs.promises.mkdir(convDir, { recursive: true });
224
- const { DatabaseSync } = require('node:sqlite');
225
- const sourceDbPath = path.join(convDir, 'source-conv.db');
226
- const db = new DatabaseSync(sourceDbPath);
227
- db.exec(`
228
- CREATE TABLE trajectory_meta (cascade_id TEXT);
229
- INSERT INTO trajectory_meta VALUES ('source-conv');
230
- CREATE TABLE steps (idx INTEGER PRIMARY KEY, step_type INTEGER);
231
- INSERT INTO steps VALUES (1, 14), (2, 1), (3, 14), (4, 1);
232
- CREATE TABLE gen_metadata (idx INTEGER PRIMARY KEY);
233
- INSERT INTO gen_metadata VALUES (0), (1);
234
- CREATE TABLE executor_metadata (idx INTEGER PRIMARY KEY);
235
- INSERT INTO executor_metadata VALUES (0), (1);
236
- CREATE TABLE parent_references (idx INTEGER PRIMARY KEY);
237
- INSERT INTO parent_references VALUES (0), (1);
238
- CREATE TABLE battle_mode_infos (idx INTEGER PRIMARY KEY);
239
- INSERT INTO battle_mode_infos VALUES (0), (1);
240
- `);
241
- db.close();
242
-
243
- const sumDbPath = path.join(mockHome, '.gemini', 'antigravity-cli', 'conversation_summaries.db');
244
- const sumDb = new DatabaseSync(sumDbPath);
245
- sumDb.exec(`
246
- CREATE TABLE conversation_summaries (conversation_id TEXT PRIMARY KEY, title TEXT, step_count INTEGER, last_modified_time TEXT);
247
- INSERT INTO conversation_summaries VALUES ('source-conv', 'Test Session', 4, '2026-09-01T00:00:00.000Z');
248
- `);
249
- sumDb.close();
250
-
251
- const clonedOk = await cloneDatabase('source-conv', 'derived-conv', 1, mockHome);
252
- assert.equal(clonedOk, true);
253
-
254
- const derivedDb = new DatabaseSync(path.join(convDir, 'derived-conv.db'));
255
- const meta = derivedDb.prepare('SELECT cascade_id FROM trajectory_meta').get();
256
- assert.equal(meta.cascade_id, 'derived-conv');
257
- const remainingSteps = derivedDb.prepare('SELECT count(*) as c FROM steps').get();
258
- assert.equal(remainingSteps.c, 2); // only first turn retained
259
- derivedDb.close();
260
-
261
- const sumDbCheck = new DatabaseSync(sumDbPath);
262
- const sumRow = sumDbCheck.prepare('SELECT * FROM conversation_summaries WHERE conversation_id = ?').get('derived-conv');
263
- assert.ok(sumRow);
264
- assert.equal(sumRow.title, 'Test Session');
265
- assert.equal(sumRow.step_count, 2);
266
- sumDbCheck.close();
267
-
268
- await fs.promises.rm(mockHome, { recursive: true, force: true }).catch(() => {});
269
-
270
- // 14. Quota and Credits parsing
271
- const mockUsageCommand = {
272
- name: 'usage',
273
- data: {
274
- groups: [
275
- {
276
- name: 'Gemini Models',
277
- buckets: [
278
- { id: 'gemini-weekly', name: 'Weekly Limit', window: 'weekly', remaining_fraction: 0.45, reset_time: '2026-09-16T01:12:46Z' },
279
- { id: 'gemini-5h', name: '5-Hour Limit', window: '5h', remaining_fraction: 0.80, reset_time: '2026-09-12T14:45:59Z' },
280
- ],
281
- },
282
- ],
283
- },
284
- };
285
- const { parseAntigravityUsageCommand } = antigravity;
286
- const quotaSnapshot = parseAntigravityUsageCommand(mockUsageCommand);
287
- assert.ok(quotaSnapshot);
288
- assert.equal(quotaSnapshot.periodType, 'weekly');
289
- assert.equal(quotaSnapshot.usedPercent, 55); // (1 - 0.45) * 100
290
- assert.equal(quotaSnapshot.resetsAt, '2026-09-16T01:12:46Z');
192
+
193
+ // 12. mergePendingStep
194
+ const { mergePendingStep, cloneDatabase } = antigravity;
195
+ const activeStep = {
196
+ step_index: 2,
197
+ state: 'ACTIVE',
198
+ step_type: 'tool',
199
+ tool_name: 'write_to_file',
200
+ tool_info: {
201
+ name: 'write_to_file',
202
+ parameters: { TargetFile: 'test.txt', Description: 'create file' },
203
+ },
204
+ };
205
+ const doneStep = {
206
+ step_index: 2,
207
+ state: 'DONE',
208
+ step_type: 'tool',
209
+ tool_info: {
210
+ output: 'File written successfully',
211
+ },
212
+ };
213
+ const merged = mergePendingStep(activeStep, doneStep);
214
+ assert.equal(merged.state, 'DONE');
215
+ assert.equal(merged.tool_name, 'write_to_file');
216
+ assert.equal(merged.tool_info.name, 'write_to_file');
217
+ assert.equal(merged.tool_info.parameters.TargetFile, 'test.txt');
218
+ assert.equal(merged.tool_info.output, 'File written successfully');
219
+
220
+ // 13. cloneDatabase with message turn pruning and summaries.db
221
+ const mockHome = path.join(os.tmpdir(), `agy-mock-home-${Date.now()}`);
222
+ const convDir = path.join(mockHome, '.gemini', 'antigravity-cli', 'conversations');
223
+ await fs.promises.mkdir(convDir, { recursive: true });
224
+ const { DatabaseSync } = require('node:sqlite');
225
+ const sourceDbPath = path.join(convDir, 'source-conv.db');
226
+ const db = new DatabaseSync(sourceDbPath);
227
+ db.exec(`
228
+ CREATE TABLE trajectory_meta (cascade_id TEXT);
229
+ INSERT INTO trajectory_meta VALUES ('source-conv');
230
+ CREATE TABLE steps (idx INTEGER PRIMARY KEY, step_type INTEGER);
231
+ INSERT INTO steps VALUES (1, 14), (2, 1), (3, 14), (4, 1);
232
+ CREATE TABLE gen_metadata (idx INTEGER PRIMARY KEY);
233
+ INSERT INTO gen_metadata VALUES (0), (1);
234
+ CREATE TABLE executor_metadata (idx INTEGER PRIMARY KEY);
235
+ INSERT INTO executor_metadata VALUES (0), (1);
236
+ CREATE TABLE parent_references (idx INTEGER PRIMARY KEY);
237
+ INSERT INTO parent_references VALUES (0), (1);
238
+ CREATE TABLE battle_mode_infos (idx INTEGER PRIMARY KEY);
239
+ INSERT INTO battle_mode_infos VALUES (0), (1);
240
+ `);
241
+ db.close();
242
+
243
+ const sumDbPath = path.join(mockHome, '.gemini', 'antigravity-cli', 'conversation_summaries.db');
244
+ const sumDb = new DatabaseSync(sumDbPath);
245
+ sumDb.exec(`
246
+ CREATE TABLE conversation_summaries (conversation_id TEXT PRIMARY KEY, title TEXT, step_count INTEGER, last_modified_time TEXT);
247
+ INSERT INTO conversation_summaries VALUES ('source-conv', 'Test Session', 4, '2026-09-01T00:00:00.000Z');
248
+ `);
249
+ sumDb.close();
250
+
251
+ const clonedOk = await cloneDatabase('source-conv', 'derived-conv', 1, mockHome);
252
+ assert.equal(clonedOk, true);
253
+
254
+ const derivedDb = new DatabaseSync(path.join(convDir, 'derived-conv.db'));
255
+ const meta = derivedDb.prepare('SELECT cascade_id FROM trajectory_meta').get();
256
+ assert.equal(meta.cascade_id, 'derived-conv');
257
+ const remainingSteps = derivedDb.prepare('SELECT count(*) as c FROM steps').get();
258
+ assert.equal(remainingSteps.c, 2); // only first turn retained
259
+ derivedDb.close();
260
+
261
+ const sumDbCheck = new DatabaseSync(sumDbPath);
262
+ const sumRow = sumDbCheck.prepare('SELECT * FROM conversation_summaries WHERE conversation_id = ?').get('derived-conv');
263
+ assert.ok(sumRow);
264
+ assert.equal(sumRow.title, 'Test Session');
265
+ assert.equal(sumRow.step_count, 2);
266
+ sumDbCheck.close();
267
+
268
+ await fs.promises.rm(mockHome, { recursive: true, force: true }).catch(() => {});
269
+
270
+ // 14. Quota and Credits parsing
271
+ const mockUsageCommand = {
272
+ name: 'usage',
273
+ data: {
274
+ groups: [
275
+ {
276
+ name: 'Gemini Models',
277
+ buckets: [
278
+ { id: 'gemini-weekly', name: 'Weekly Limit', window: 'weekly', remaining_fraction: 0.45, reset_time: '2026-09-16T01:12:46Z' },
279
+ { id: 'gemini-5h', name: '5-Hour Limit', window: '5h', remaining_fraction: 0.80, reset_time: '2026-09-12T14:45:59Z' },
280
+ ],
281
+ },
282
+ ],
283
+ },
284
+ };
285
+ const { parseAntigravityUsageCommand } = antigravity;
286
+ const quotaSnapshot = parseAntigravityUsageCommand(mockUsageCommand);
287
+ assert.ok(quotaSnapshot);
288
+ assert.equal(quotaSnapshot.periodType, 'weekly');
289
+ assert.equal(quotaSnapshot.usedPercent, 55); // (1 - 0.45) * 100
290
+ assert.equal(quotaSnapshot.resetsAt, '2026-09-16T01:12:46Z');
291
291
  assert.ok(Array.isArray(quotaSnapshot.productUsage));
292
292
  assert.equal(quotaSnapshot.productUsage[0].usagePercent, 20); // (1 - 0.8) * 100
293
293
 
@@ -374,43 +374,140 @@ gpt-oss-120b-medium\tGPT-OSS 120B (Medium)
374
374
  assert.equal(noResultSession.activeTurn, null);
375
375
  await noResultAdapter.close(noResultSession);
376
376
 
377
- // 17. A SUCCESS result with no assistant text must not be reported as a
378
- // successful empty turn.
379
- const emptySuccessChild = new EventEmitter();
380
- emptySuccessChild.stdin = new PassThrough();
381
- emptySuccessChild.stdout = new PassThrough();
382
- emptySuccessChild.stderr = new PassThrough();
383
- let emptySuccessClosed = false;
384
- const closeEmptySuccessChild = () => {
385
- if (emptySuccessClosed) return;
386
- emptySuccessClosed = true;
387
- emptySuccessChild.stdout.end();
388
- emptySuccessChild.stderr.end();
389
- emptySuccessChild.emit('close', 0);
377
+ // 17. A SUCCESS result with no assistant text triggers exactly one automatic
378
+ // nudge retry in the same native conversation; only a repeated empty result
379
+ // surfaces as an error.
380
+ const makeEmptySuccessChild = (onPrompt) => {
381
+ const child = new EventEmitter();
382
+ child.stdin = new PassThrough();
383
+ child.stdout = new PassThrough();
384
+ child.stderr = new PassThrough();
385
+ let closed = false;
386
+ const closeChild = () => {
387
+ if (closed) return;
388
+ closed = true;
389
+ child.stdout.end();
390
+ child.stderr.end();
391
+ child.emit('close', 0);
392
+ };
393
+ child.isClosed = () => closed;
394
+ child.stdin.once('data', (chunk) => {
395
+ const content = JSON.parse(String(chunk).trim()).message?.content || '';
396
+ setTimeout(() => onPrompt(child, content, closeChild), 1);
397
+ });
398
+ child.stdin.once('finish', () => setTimeout(closeChild, 0));
399
+ child.kill = closeChild;
400
+ return child;
390
401
  };
391
- emptySuccessChild.stdin.once('data', () => setTimeout(() => {
392
- if (!emptySuccessClosed) emptySuccessChild.stdout.write(`${JSON.stringify({
393
- event: 'result',
394
- result: { conversation_id: 'fake-empty-success', status: 'SUCCESS', response: '' },
395
- })}\n`);
396
- }, 1));
397
- emptySuccessChild.stdin.once('finish', () => setTimeout(closeEmptySuccessChild, 0));
398
- emptySuccessChild.kill = closeEmptySuccessChild;
399
- const emptySuccessAdapter = create(() => {}, { spawnProcess: () => emptySuccessChild, resultDrainMaxMs: 20 });
400
- const emptySuccessSession = await emptySuccessAdapter.open({
401
- thread: { id: 'fake-empty-success-thread', cwd: os.tmpdir(), options: { permissionMode: 'skip' } },
402
+ const writeStream = (child, event) => {
403
+ if (!child.isClosed()) child.stdout.write(`${JSON.stringify(event)}\n`);
404
+ };
405
+
406
+ // 17a. First attempt empty, nudge retry answers → turn completes, no error.
407
+ const retryChildren = [];
408
+ const retryArgs = [];
409
+ const retryAdapter = create(() => {}, {
410
+ spawnProcess: (_bin, args) => {
411
+ retryArgs.push(args);
412
+ const attempt = retryChildren.length;
413
+ const child = makeEmptySuccessChild((c, content) => {
414
+ if (attempt === 0) {
415
+ writeStream(c, { event: 'init', conversation_id: 'fake-empty-success' });
416
+ writeStream(c, { event: 'result', result: { conversation_id: 'fake-empty-success', status: 'SUCCESS', response: '' } });
417
+ } else {
418
+ c.nudgePrompt = content;
419
+ writeStream(c, { event: 'init', conversation_id: 'fake-empty-success' });
420
+ writeStream(c, {
421
+ event: 'step_update',
422
+ step_update: { conversation_id: 'fake-empty-success', step_index: 1, state: 'DONE', step_type: 'agent_response', text_delta: 'late recovered answer' },
423
+ });
424
+ writeStream(c, { event: 'result', result: { conversation_id: 'fake-empty-success', status: 'SUCCESS', num_turns: 2, response: 'late recovered answer' } });
425
+ }
426
+ });
427
+ retryChildren.push(child);
428
+ return child;
429
+ },
430
+ resultDrainMs: 10,
431
+ resultDrainMaxMs: 20,
432
+ });
433
+ const retrySession = await retryAdapter.open({
434
+ thread: { id: 'fake-empty-retry-thread', cwd: os.tmpdir(), options: { permissionMode: 'skip' } },
402
435
  emit: () => {},
403
436
  diagnostic: () => {},
404
437
  });
405
- const emptySuccessEvents = [];
438
+ const retryEvents = [];
439
+ await retryAdapter.send(retrySession, 'return an empty success', { emit: event => retryEvents.push(event) });
440
+ assert.equal(retryChildren.length, 2);
441
+ assert.match(retryChildren[1].nudgePrompt || '', /previous turn ended WITHOUT any visible assistant response/);
442
+ const resumeIdx = retryArgs[1].indexOf('--conversation');
443
+ assert.ok(resumeIdx >= 0);
444
+ assert.equal(retryArgs[1][resumeIdx + 1], 'fake-empty-success');
445
+ assert.equal(retryEvents.filter(event => event.kind === 'error').length, 0);
446
+ assert.ok(retryEvents.some(event => event.kind === 'status' && /自动补问/.test(event.text || '')));
447
+ assert.equal(retryEvents.filter(event => event.kind === 'text-delta').map(event => event.text).join(''), 'late recovered answer');
448
+ assert.ok(retryEvents.some(event => event.kind === 'completed'));
449
+ assert.equal(retrySession.activeTurn, null);
450
+ await retryAdapter.close(retrySession);
451
+
452
+ // 17b. Every attempt empty → rejects, error emitted exactly once (final attempt).
453
+ let alwaysEmptySpawns = 0;
454
+ const alwaysEmptyAdapter = create(() => {}, {
455
+ spawnProcess: () => {
456
+ alwaysEmptySpawns += 1;
457
+ return makeEmptySuccessChild((c) => {
458
+ writeStream(c, { event: 'init', conversation_id: 'fake-empty-success' });
459
+ writeStream(c, { event: 'result', result: { conversation_id: 'fake-empty-success', status: 'SUCCESS', response: '' } });
460
+ });
461
+ },
462
+ resultDrainMaxMs: 20,
463
+ });
464
+ const alwaysEmptySession = await alwaysEmptyAdapter.open({
465
+ thread: { id: 'fake-always-empty-thread', cwd: os.tmpdir(), options: { permissionMode: 'skip' } },
466
+ emit: () => {},
467
+ diagnostic: () => {},
468
+ });
469
+ const alwaysEmptyEvents = [];
406
470
  await assert.rejects(
407
- emptySuccessAdapter.send(emptySuccessSession, 'return an empty success', { emit: event => emptySuccessEvents.push(event) }),
471
+ alwaysEmptyAdapter.send(alwaysEmptySession, 'return an empty success', { emit: event => alwaysEmptyEvents.push(event) }),
408
472
  /没有 assistant 文本/,
409
473
  );
410
- assert.match(emptySuccessEvents.find(event => event.kind === 'error')?.message || '', /没有 assistant 文本/);
411
- assert.equal(emptySuccessEvents.some(event => event.kind === 'completed'), false);
412
- assert.equal(emptySuccessSession.activeTurn, null);
413
- await emptySuccessAdapter.close(emptySuccessSession);
474
+ assert.equal(alwaysEmptySpawns, 2);
475
+ assert.equal(alwaysEmptyEvents.filter(event => event.kind === 'error').length, 1);
476
+ assert.match(alwaysEmptyEvents.find(event => event.kind === 'error')?.message || '', /没有 assistant 文本/);
477
+ assert.equal(alwaysEmptyEvents.some(event => event.kind === 'completed'), false);
478
+ assert.equal(alwaysEmptySession.activeTurn, null);
479
+ await alwaysEmptyAdapter.close(alwaysEmptySession);
480
+
481
+ // 17c. Automatic retry disabled via env → single attempt, immediate error.
482
+ process.env.HARNESSMIX_ANTIGRAVITY_EMPTY_RESULT_RETRIES = '0';
483
+ try {
484
+ let noRetrySpawns = 0;
485
+ const noRetryAdapter = create(() => {}, {
486
+ spawnProcess: () => {
487
+ noRetrySpawns += 1;
488
+ return makeEmptySuccessChild((c) => {
489
+ writeStream(c, { event: 'init', conversation_id: 'fake-empty-success' });
490
+ writeStream(c, { event: 'result', result: { conversation_id: 'fake-empty-success', status: 'SUCCESS', response: '' } });
491
+ });
492
+ },
493
+ resultDrainMaxMs: 20,
494
+ });
495
+ const noRetrySession = await noRetryAdapter.open({
496
+ thread: { id: 'fake-no-retry-thread', cwd: os.tmpdir(), options: { permissionMode: 'skip' } },
497
+ emit: () => {},
498
+ diagnostic: () => {},
499
+ });
500
+ const noRetryEvents = [];
501
+ await assert.rejects(
502
+ noRetryAdapter.send(noRetrySession, 'return an empty success', { emit: event => noRetryEvents.push(event) }),
503
+ /没有 assistant 文本/,
504
+ );
505
+ assert.equal(noRetrySpawns, 1);
506
+ assert.equal(noRetryEvents.filter(event => event.kind === 'error').length, 1);
507
+ await noRetryAdapter.close(noRetrySession);
508
+ } finally {
509
+ delete process.env.HARNESSMIX_ANTIGRAVITY_EMPTY_RESULT_RETRIES;
510
+ }
414
511
 
415
512
  // 18. An empty view_file result must not close the Core turn before a late
416
513
  // assistant response. This is the stream shape produced for image files by
@@ -521,9 +618,9 @@ gpt-oss-120b-medium\tGPT-OSS 120B (Medium)
521
618
 
522
619
  // 19. Close session
523
620
  await adapter.close(session);
524
-
525
- console.log('antigravity adapter: manifest, models catalog, usage projection, quota/credits, prompt formatting, image attachments, session lifecycle, model switching, describe, fork, step merging, and turn pruning passed');
526
- })().catch((err) => {
527
- console.error(err);
528
- process.exitCode = 1;
529
- });
621
+
622
+ console.log('antigravity adapter: manifest, models catalog, usage projection, quota/credits, prompt formatting, image attachments, session lifecycle, model switching, describe, fork, step merging, and turn pruning passed');
623
+ })().catch((err) => {
624
+ console.error(err);
625
+ process.exitCode = 1;
626
+ });