zorya-classifier 0.1.0 → 0.2.0

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 (313) hide show
  1. package/NOTICE.md +2 -0
  2. package/README.md +18 -6
  3. package/dist/app/cli-arguments.d.ts +40 -0
  4. package/dist/app/cli-arguments.js +67 -0
  5. package/dist/app/create-classifier.d.ts +19 -0
  6. package/dist/app/create-classifier.js +226 -14
  7. package/dist/app/extraction-files.d.ts +9 -0
  8. package/dist/app/extraction-files.js +28 -0
  9. package/dist/cli.js +58 -14
  10. package/dist/entities/boundary-view.d.ts +29 -0
  11. package/dist/entities/boundary-view.js +1 -0
  12. package/dist/entities/contracts.d.ts +77 -1
  13. package/dist/entities/contracts.js +7 -0
  14. package/dist/entities/cutout-raster.d.ts +7 -0
  15. package/dist/entities/cutout-raster.js +1 -0
  16. package/dist/entities/object-proposals.d.ts +53 -0
  17. package/dist/entities/object-proposals.js +8 -0
  18. package/dist/entities/representation.d.ts +225 -0
  19. package/dist/entities/representation.js +33 -0
  20. package/dist/entities/segmentation-metadata.d.ts +11 -0
  21. package/dist/entities/segmentation-metadata.js +1 -0
  22. package/dist/entities/segmentation-verification.d.ts +30 -0
  23. package/dist/entities/segmentation-verification.js +1 -0
  24. package/dist/entities/segmentation.d.ts +159 -0
  25. package/dist/entities/segmentation.js +5 -0
  26. package/dist/entities/whole-object.d.ts +128 -0
  27. package/dist/entities/whole-object.js +7 -0
  28. package/dist/features/choice.d.ts +1 -1
  29. package/dist/features/choice.js +2 -2
  30. package/dist/features/segmentation/box-check.d.ts +10 -0
  31. package/dist/features/segmentation/box-check.js +299 -0
  32. package/dist/features/segmentation/mask.d.ts +6 -0
  33. package/dist/features/segmentation/mask.js +89 -0
  34. package/dist/features/segmentation/packed-mask.d.ts +5 -0
  35. package/dist/features/segmentation/packed-mask.js +53 -0
  36. package/dist/index.d.ts +6 -1
  37. package/dist/infrastructure/boundary-view.d.ts +2 -0
  38. package/dist/infrastructure/boundary-view.js +263 -0
  39. package/dist/infrastructure/openrouter-common.d.ts +8 -8
  40. package/dist/infrastructure/openrouter-vision.d.ts +3 -0
  41. package/dist/infrastructure/openrouter-vision.js +5 -0
  42. package/dist/infrastructure/raster.d.ts +10 -1
  43. package/dist/infrastructure/raster.js +99 -4
  44. package/dist/infrastructure/segmentation/box-presentation.d.ts +2 -0
  45. package/dist/infrastructure/segmentation/box-presentation.js +136 -0
  46. package/dist/infrastructure/segmentation/cutout-raster.d.ts +3 -0
  47. package/dist/infrastructure/segmentation/cutout-raster.js +79 -0
  48. package/dist/infrastructure/segmentation/grabcut/index.d.ts +13 -0
  49. package/dist/infrastructure/segmentation/grabcut/index.js +118 -0
  50. package/dist/infrastructure/segmentation/grabcut/worker-inference.d.ts +47 -0
  51. package/dist/infrastructure/segmentation/grabcut/worker-inference.js +260 -0
  52. package/dist/infrastructure/segmentation/index.d.ts +2 -0
  53. package/dist/infrastructure/segmentation/index.js +2 -0
  54. package/dist/infrastructure/segmentation/mobile-sam/index.d.ts +58 -0
  55. package/dist/infrastructure/segmentation/mobile-sam/index.js +472 -0
  56. package/dist/infrastructure/segmentation/mobile-sam/worker-inference.d.ts +149 -0
  57. package/dist/infrastructure/segmentation/mobile-sam/worker-inference.js +845 -0
  58. package/dist/infrastructure/segmentation/native-child.d.ts +23 -0
  59. package/dist/infrastructure/segmentation/native-child.js +297 -0
  60. package/dist/infrastructure/segmentation/native-protocol.d.ts +923 -0
  61. package/dist/infrastructure/segmentation/native-protocol.js +242 -0
  62. package/dist/infrastructure/segmentation/native-worker.d.ts +1 -0
  63. package/dist/infrastructure/segmentation/native-worker.js +93 -0
  64. package/dist/infrastructure/segmentation/probe-backend.d.ts +1 -0
  65. package/dist/infrastructure/segmentation/probe-backend.js +31 -0
  66. package/dist/infrastructure/segmentation/proposal-presentation.d.ts +3 -0
  67. package/dist/infrastructure/segmentation/proposal-presentation.js +371 -0
  68. package/dist/infrastructure/segmentation/verify-asset.d.ts +4 -0
  69. package/dist/infrastructure/segmentation/verify-asset.js +32 -0
  70. package/dist/infrastructure/text-representation.d.ts +23 -0
  71. package/dist/infrastructure/text-representation.js +226 -0
  72. package/dist/infrastructure/vision-chat.d.ts +1 -1
  73. package/dist/infrastructure/vision-chat.js +13 -4
  74. package/dist/openai-compatible.d.ts +3 -0
  75. package/dist/openai-compatible.js +4 -0
  76. package/dist/openrouter.d.ts +2 -2
  77. package/dist/openrouter.js +1 -1
  78. package/dist/processes/boundary-state.d.ts +12 -0
  79. package/dist/processes/boundary-state.js +196 -0
  80. package/dist/processes/classify.d.ts +2 -1
  81. package/dist/processes/classify.js +21 -3
  82. package/dist/processes/extract-from-region.d.ts +19 -0
  83. package/dist/processes/extract-from-region.js +415 -0
  84. package/dist/processes/extract.d.ts +25 -0
  85. package/dist/processes/extract.js +223 -0
  86. package/dist/processes/locate.d.ts +2 -1
  87. package/dist/processes/locate.js +84 -11
  88. package/dist/processes/proposal-location.d.ts +19 -0
  89. package/dist/processes/proposal-location.js +396 -0
  90. package/dist/processes/whole-object.d.ts +7 -0
  91. package/dist/processes/whole-object.js +272 -0
  92. package/dist/segmentation.d.ts +10 -0
  93. package/dist/segmentation.js +4 -0
  94. package/dist/shared/input-budget.d.ts +7 -0
  95. package/dist/shared/input-budget.js +21 -0
  96. package/docs/0.2.0-plan.md +190 -0
  97. package/docs/README.ru.md +18 -5
  98. package/docs/acceptance.md +116 -3
  99. package/docs/api.md +8 -4
  100. package/docs/architecture.md +20 -7
  101. package/docs/benchmarks.md +10 -0
  102. package/docs/categorical-presence-port.md +26 -0
  103. package/docs/evidence/v02/baseline.json +1043 -0
  104. package/docs/evidence/v02/boundary-variants.md +17 -0
  105. package/docs/evidence/v02/box-confirmation-dev40/README.md +15 -0
  106. package/docs/evidence/v02/box-confirmation-dev40/component-refinement.json +246 -0
  107. package/docs/evidence/v02/box-confirmation-dev40/evaluation.json +1663 -0
  108. package/docs/evidence/v02/box-confirmation-dev40/provenance.json +130 -0
  109. package/docs/evidence/v02/box-confirmation-dev40/report.json +127 -0
  110. package/docs/evidence/v02/box-confirmation-dev40-freeze-20261020-v1.json +230 -0
  111. package/docs/evidence/v02/box-confirmation-method-selection-20261020-v1.json +85 -0
  112. package/docs/evidence/v02/box-heldout-cancelled-before-dispatch-20261021-v1.json +32 -0
  113. package/docs/evidence/v02/box-heldout-combined-seal-20261021-v1.json +89 -0
  114. package/docs/evidence/v02/box-heldout-evaluator-truth-seal-20261021-v1.json +61 -0
  115. package/docs/evidence/v02/box-heldout-exclusion-audit-20261020-v1.json +81 -0
  116. package/docs/evidence/v02/box-heldout-map-seal-20261021-v1.json +48 -0
  117. package/docs/evidence/v02/box-heldout-photo-seal-20261020-v1.json +62 -0
  118. package/docs/evidence/v02/box-heldout-run-freeze-20261021-v1.json +61 -0
  119. package/docs/evidence/v02/box-heldout-source-review-20261020-v1.json +134 -0
  120. package/docs/evidence/v02/box-heldout-ui-seal-20261020-v1.json +44 -0
  121. package/docs/evidence/v02/calculator-proof-root-review-20261020-v1.json +63 -0
  122. package/docs/evidence/v02/categorical-dev40-freeze.json +94 -0
  123. package/docs/evidence/v02/categorical-dev40-preserve.json +460 -0
  124. package/docs/evidence/v02/categorical-dev40-target-only.json +460 -0
  125. package/docs/evidence/v02/categorical-heldout-admission-20261020-v3.json +215 -0
  126. package/docs/evidence/v02/categorical-heldout-admission-20261020-v4.json +270 -0
  127. package/docs/evidence/v02/categorical-heldout-dispatch-authorization-20261020-v4.json +79 -0
  128. package/docs/evidence/v02/categorical-heldout-predispatch-20261020-v3.json +57 -0
  129. package/docs/evidence/v02/categorical-heldout-producer-source-map-v4.json +262 -0
  130. package/docs/evidence/v02/categorical-heldout-producer-source-map.json +263 -0
  131. package/docs/evidence/v02/categorical-heldout-run-freeze-20261020-v3.json +35 -0
  132. package/docs/evidence/v02/categorical-heldout-run-freeze-20261020-v4.json +35 -0
  133. package/docs/evidence/v02/categorical-heldout-v4/README.md +35 -0
  134. package/docs/evidence/v02/categorical-heldout-v4/candidate-evaluation.json +2812 -0
  135. package/docs/evidence/v02/categorical-heldout-v4/candidate-physical-receipts.json +15261 -0
  136. package/docs/evidence/v02/categorical-heldout-v4/candidate-provenance.json +131 -0
  137. package/docs/evidence/v02/categorical-heldout-v4/control-evaluation.json +855 -0
  138. package/docs/evidence/v02/categorical-heldout-v4/control-physical-receipts.json +4402 -0
  139. package/docs/evidence/v02/categorical-heldout-v4/control-provenance.json +31 -0
  140. package/docs/evidence/v02/categorical-heldout-v4/mask-normalized-exploratory.json +682 -0
  141. package/docs/evidence/v02/categorical-heldout-v4/paired-qualification.json +21 -0
  142. package/docs/evidence/v02/categorical-heldout-v4/report.json +258 -0
  143. package/docs/evidence/v02/categorical-presence-dev6.json +144 -0
  144. package/docs/evidence/v02/categorical-presence-dev6.md +18 -0
  145. package/docs/evidence/v02/categorical-vision-method-selection.json +90 -0
  146. package/docs/evidence/v02/checked-region-component-v1/README.md +7 -0
  147. package/docs/evidence/v02/checked-region-component-v1/evidence.json +311 -0
  148. package/docs/evidence/v02/checked-region-resource-interrupted-2b0d09f.json +102 -0
  149. package/docs/evidence/v02/checked-region-segmentation-resource-4df544c-receipt.json +167 -0
  150. package/docs/evidence/v02/checked-region-segmentation-resource-4df544c-root-review.json +58 -0
  151. package/docs/evidence/v02/checked-region-segmentation-resource-987ac32-receipt.json +158 -0
  152. package/docs/evidence/v02/ci-012b935.json +17 -0
  153. package/docs/evidence/v02/ci-0c365cb.json +372 -0
  154. package/docs/evidence/v02/ci-185bde8.json +32 -0
  155. package/docs/evidence/v02/ci-24f849f.json +95 -0
  156. package/docs/evidence/v02/ci-25c19cd.json +375 -0
  157. package/docs/evidence/v02/ci-2922caf.json +375 -0
  158. package/docs/evidence/v02/ci-29357d7.json +29 -0
  159. package/docs/evidence/v02/ci-32aa90d.json +24 -0
  160. package/docs/evidence/v02/ci-35e3064.json +376 -0
  161. package/docs/evidence/v02/ci-4331b1b.json +384 -0
  162. package/docs/evidence/v02/ci-43b7426.json +376 -0
  163. package/docs/evidence/v02/ci-450439c.json +373 -0
  164. package/docs/evidence/v02/ci-46283fe.json +29 -0
  165. package/docs/evidence/v02/ci-4c9c09f.json +34 -0
  166. package/docs/evidence/v02/ci-5579646.json +27 -0
  167. package/docs/evidence/v02/ci-6f9cc15.json +127 -0
  168. package/docs/evidence/v02/ci-7c1051f.json +27 -0
  169. package/docs/evidence/v02/ci-9837888.json +34 -0
  170. package/docs/evidence/v02/ci-987ac32.json +29 -0
  171. package/docs/evidence/v02/ci-9d00fe5.json +25 -0
  172. package/docs/evidence/v02/ci-9f85acc.json +127 -0
  173. package/docs/evidence/v02/ci-be275a6.json +201 -0
  174. package/docs/evidence/v02/ci-c107ddf.json +372 -0
  175. package/docs/evidence/v02/ci-c35f67d.json +127 -0
  176. package/docs/evidence/v02/ci-c5eddd8.json +32 -0
  177. package/docs/evidence/v02/ci-d2042e0.json +372 -0
  178. package/docs/evidence/v02/ci-d684259.json +375 -0
  179. package/docs/evidence/v02/ci-e0c190b.json +127 -0
  180. package/docs/evidence/v02/ci-e551253.json +372 -0
  181. package/docs/evidence/v02/ci-f9d4633.json +29 -0
  182. package/docs/evidence/v02/dev1-proposal-evaluation.md +13 -0
  183. package/docs/evidence/v02/dev2-mobile-sam-proposal-oracle.md +21 -0
  184. package/docs/evidence/v02/dev2-proposal-evaluation.md +21 -0
  185. package/docs/evidence/v02/fresh-development.md +16 -0
  186. package/docs/evidence/v02/fresh-native-numeric/artifact-hashes.json +9 -0
  187. package/docs/evidence/v02/fresh-native-numeric/evaluation-root-v1.json +568 -0
  188. package/docs/evidence/v02/fresh-native-numeric/run-manifest.json +342 -0
  189. package/docs/evidence/v02/fresh-native-numeric/summary.json +5654 -0
  190. package/docs/evidence/v02/fresh-native-outline-axis/artifact-hashes.json +9 -0
  191. package/docs/evidence/v02/fresh-native-outline-axis/evaluation-root-v1.json +568 -0
  192. package/docs/evidence/v02/fresh-native-outline-axis/run-manifest.json +378 -0
  193. package/docs/evidence/v02/fresh-native-outline-axis/summary.json +5605 -0
  194. package/docs/evidence/v02/fresh-text/README.md +23 -0
  195. package/docs/evidence/v02/fresh-text/ascii-contours-v1/classify-followup-run-manifest.json +405 -0
  196. package/docs/evidence/v02/fresh-text/ascii-contours-v1/classify-followup-summary.json +1646 -0
  197. package/docs/evidence/v02/fresh-text/ascii-contours-v1/evaluation.json +568 -0
  198. package/docs/evidence/v02/fresh-text/ascii-contours-v1/original-run-manifest.json +404 -0
  199. package/docs/evidence/v02/fresh-text/ascii-contours-v1/original-summary.json +4416 -0
  200. package/docs/evidence/v02/fresh-text/ascii-luma-v1/classify-followup-run-manifest.json +405 -0
  201. package/docs/evidence/v02/fresh-text/ascii-luma-v1/classify-followup-summary.json +1646 -0
  202. package/docs/evidence/v02/fresh-text/ascii-luma-v1/evaluation.json +568 -0
  203. package/docs/evidence/v02/fresh-text/ascii-luma-v1/original-run-manifest.json +404 -0
  204. package/docs/evidence/v02/fresh-text/ascii-luma-v1/original-summary.json +4647 -0
  205. package/docs/evidence/v02/fresh-text/ascii-svg-layered-v1/classify-followup-run-manifest.json +405 -0
  206. package/docs/evidence/v02/fresh-text/ascii-svg-layered-v1/classify-followup-summary.json +846 -0
  207. package/docs/evidence/v02/fresh-text/ascii-svg-layered-v1/evaluation.json +568 -0
  208. package/docs/evidence/v02/fresh-text/ascii-svg-layered-v1/original-run-manifest.json +404 -0
  209. package/docs/evidence/v02/fresh-text/ascii-svg-layered-v1/original-summary.json +1789 -0
  210. package/docs/evidence/v02/fresh-text/no-image-control/evaluation.json +568 -0
  211. package/docs/evidence/v02/fresh-text/no-image-control/original-run-manifest.json +404 -0
  212. package/docs/evidence/v02/fresh-text/no-image-control/original-summary.json +3953 -0
  213. package/docs/evidence/v02/fresh-text/palette-grid-v1/classify-followup-run-manifest.json +405 -0
  214. package/docs/evidence/v02/fresh-text/palette-grid-v1/classify-followup-summary.json +1646 -0
  215. package/docs/evidence/v02/fresh-text/palette-grid-v1/evaluation.json +568 -0
  216. package/docs/evidence/v02/fresh-text/palette-grid-v1/original-run-manifest.json +404 -0
  217. package/docs/evidence/v02/fresh-text/palette-grid-v1/original-summary.json +3974 -0
  218. package/docs/evidence/v02/fresh-text/pixel-rle-v1/classify-followup-run-manifest.json +405 -0
  219. package/docs/evidence/v02/fresh-text/pixel-rle-v1/classify-followup-summary.json +1646 -0
  220. package/docs/evidence/v02/fresh-text/pixel-rle-v1/evaluation.json +568 -0
  221. package/docs/evidence/v02/fresh-text/pixel-rle-v1/original-run-manifest.json +404 -0
  222. package/docs/evidence/v02/fresh-text/pixel-rle-v1/original-summary.json +4945 -0
  223. package/docs/evidence/v02/fresh-text/report.json +284 -0
  224. package/docs/evidence/v02/fresh-text/svg-contours-v1/classify-followup-run-manifest.json +405 -0
  225. package/docs/evidence/v02/fresh-text/svg-contours-v1/classify-followup-summary.json +846 -0
  226. package/docs/evidence/v02/fresh-text/svg-contours-v1/evaluation.json +568 -0
  227. package/docs/evidence/v02/fresh-text/svg-contours-v1/original-run-manifest.json +404 -0
  228. package/docs/evidence/v02/fresh-text/svg-contours-v1/original-summary.json +2189 -0
  229. package/docs/evidence/v02/heldout-evaluation-20261020.md +37 -0
  230. package/docs/evidence/v02/heldout-input-derivation.md +18 -0
  231. package/docs/evidence/v02/integration-0c365cb.json +90 -0
  232. package/docs/evidence/v02/integration-25c19cd.json +52 -0
  233. package/docs/evidence/v02/integration-35e3064.json +41 -0
  234. package/docs/evidence/v02/integration-4331b1b.json +42 -0
  235. package/docs/evidence/v02/integration-4c9c09f.json +46 -0
  236. package/docs/evidence/v02/integration-8010aad.json +48 -0
  237. package/docs/evidence/v02/integration-c1aa640.json +41 -0
  238. package/docs/evidence/v02/integration-eea1790.json +46 -0
  239. package/docs/evidence/v02/integration-fa82e65.json +42 -0
  240. package/docs/evidence/v02/jpeg-transport-offline.json +268 -0
  241. package/docs/evidence/v02/jpeg-transport-offline.md +7 -0
  242. package/docs/evidence/v02/local-archive-cleanup.json +59 -0
  243. package/docs/evidence/v02/native-base64-after.json +30 -0
  244. package/docs/evidence/v02/native-base64-before.json +32 -0
  245. package/docs/evidence/v02/native-checked-region-windows-46283fe.json +248 -0
  246. package/docs/evidence/v02/native-checked-region-windows-987ac32.json +141 -0
  247. package/docs/evidence/v02/native-grabcut-windows-4c494a3-run2.json +148 -0
  248. package/docs/evidence/v02/native-grabcut-windows-4c494a3-run3.json +148 -0
  249. package/docs/evidence/v02/native-grabcut-windows-4c494a3.json +56 -0
  250. package/docs/evidence/v02/native-proposal-windows-0c365cb.json +148 -0
  251. package/docs/evidence/v02/native-proposal-windows-450439c.json +156 -0
  252. package/docs/evidence/v02/native-proposal-windows-4c9c09f-retry1.json +147 -0
  253. package/docs/evidence/v02/native-proposal-windows-4c9c09f.json +52 -0
  254. package/docs/evidence/v02/native-region-windows-efc9318.json +163 -0
  255. package/docs/evidence/v02/native-windows-eea1790-baseline.json +198 -0
  256. package/docs/evidence/v02/native-windows-eea1790.json +228 -0
  257. package/docs/evidence/v02/ordinary-luna-axis/run-manifest.json +99 -0
  258. package/docs/evidence/v02/ordinary-luna-axis/summary.json +811 -0
  259. package/docs/evidence/v02/owned-temp-cleanup-20261020-v1.json +28 -0
  260. package/docs/evidence/v02/presentation-paired-20261020.json +1438 -0
  261. package/docs/evidence/v02/proposal-resource-soak-261dc23.json +201 -0
  262. package/docs/evidence/v02/proposal-resource-soak-261dc23.md +15 -0
  263. package/docs/evidence/v02/proposal-resource-soak-450439c-receipt.md +13 -0
  264. package/docs/evidence/v02/reconstructed-c03-box.json +41 -0
  265. package/docs/evidence/v02/region-seeded-dev-v1/README.md +17 -0
  266. package/docs/evidence/v02/region-seeded-dev-v1/evaluation.json +260 -0
  267. package/docs/evidence/v02/region-seeded-dev-v2/README.md +15 -0
  268. package/docs/evidence/v02/region-seeded-dev-v2/evaluation.json +232 -0
  269. package/docs/evidence/v02/region-seeded-dev-v3-candidate-only/README.md +9 -0
  270. package/docs/evidence/v02/region-seeded-dev-v3-candidate-only/control-identity-failure.json +22 -0
  271. package/docs/evidence/v02/region-seeded-dev-v3-candidate-only/evaluation.json +160 -0
  272. package/docs/evidence/v02/region-seeded-dev-v3-control-r2/README.md +9 -0
  273. package/docs/evidence/v02/region-seeded-dev-v3-control-r2/attempt-accounting.json +55 -0
  274. package/docs/evidence/v02/region-seeded-dev-v3-control-r2/evaluation.json +136 -0
  275. package/docs/evidence/v02/region-seeded-development-control-r2-freeze-20261021.json +132 -0
  276. package/docs/evidence/v02/region-seeded-development-freeze-20261021-v1.json +167 -0
  277. package/docs/evidence/v02/region-seeded-development-freeze-20261021-v2.json +186 -0
  278. package/docs/evidence/v02/region-seeded-development-freeze-20261021-v3.json +205 -0
  279. package/docs/evidence/v02/region-seeded-heldout-cancelled-before-dispatch-20261021-v2.json +20 -0
  280. package/docs/evidence/v02/region-seeded-heldout-dispatch-authorization-20261021-v3.json +47 -0
  281. package/docs/evidence/v02/region-seeded-heldout-readiness-audit-20261021-v3.json +125 -0
  282. package/docs/evidence/v02/region-seeded-heldout-run-freeze-20261021-v2.json +234 -0
  283. package/docs/evidence/v02/region-seeded-heldout-run-freeze-20261021-v3.json +357 -0
  284. package/docs/evidence/v02/region-seeded-heldout-v3/README.md +19 -0
  285. package/docs/evidence/v02/region-seeded-heldout-v3/evaluation-summary.json +445 -0
  286. package/docs/evidence/v02/region-seeded-method-selection-20261021-v2.json +66 -0
  287. package/docs/evidence/v02/region-seeded-method-selection-20261021-v3.json +120 -0
  288. package/docs/evidence/v02/region-segmentation-resource-0ba4c87-receipt.json +156 -0
  289. package/docs/evidence/v02/resource-soak-linux-8010aad.json +125 -0
  290. package/docs/evidence/v02/screen-v2-terminal-evaluation.md +22 -0
  291. package/docs/evidence/v02/seed-outline/run-manifest.json +59 -0
  292. package/docs/evidence/v02/seed-outline/summary.json +487 -0
  293. package/docs/evidence/v02/seed-unbounded/run-manifest.json +80 -0
  294. package/docs/evidence/v02/seed-unbounded/summary.json +730 -0
  295. package/docs/evidence/v02/segmentation/README.md +21 -0
  296. package/docs/evidence/v02/segmentation/component-comparison-summary.json +240 -0
  297. package/docs/evidence/v02/segmentation/provenance/MOBILE-SAM-LICENSE.txt +201 -0
  298. package/docs/evidence/v02/segmentation/provenance/OPENCV-LICENSE.txt +202 -0
  299. package/docs/evidence/v02/segmentation/provenance/mobile-sam-download-receipt.json +29 -0
  300. package/docs/evidence/v02/segmentation/provenance/mobile-sam-graph-audit.json +214 -0
  301. package/docs/evidence/v02/segmentation/provenance/mobile-sam-model-metadata.json +166 -0
  302. package/docs/evidence/v02/split-choice-noul-dev6.json +72 -0
  303. package/docs/evidence/v02/split-choice-noul-dev6.md +11 -0
  304. package/docs/evidence/v02/supported-vision-screen-20261020.md +65 -0
  305. package/docs/evidence/v02/wesnoth-1.16.11-minimap-projection-source-audit.md +56 -0
  306. package/docs/evidence/v02/wesnoth-proof-root-review-20261020-v1.json +76 -0
  307. package/docs/operations.md +6 -0
  308. package/docs/plan.md +2 -0
  309. package/docs/publishing.md +10 -1
  310. package/docs/research/text-transport-audit.md +66 -0
  311. package/docs/v02-usage.md +162 -0
  312. package/docs/vision-results.md +29 -2
  313. package/package.json +30 -5
@@ -0,0 +1,58 @@
1
+ import { z } from 'zod';
2
+ import type { SegmentationPort } from '../../../entities/segmentation.js';
3
+ import type { ObjectProposalPort } from '../../../entities/object-proposals.js';
4
+ import { NativeChildPool } from '../native-child.js';
5
+ import { type NativeSegmentationRequest } from '../native-protocol.js';
6
+ declare const optionsSchema: z.ZodObject<{
7
+ encoderPath: z.ZodString;
8
+ decoderPath: z.ZodString;
9
+ executionProvider: z.ZodDefault<z.ZodEnum<["cpu", "dml"]>>;
10
+ deviceId: z.ZodOptional<z.ZodNumber>;
11
+ threads: z.ZodDefault<z.ZodNumber>;
12
+ }, "strict", z.ZodTypeAny, {
13
+ encoderPath: string;
14
+ decoderPath: string;
15
+ executionProvider: "cpu" | "dml";
16
+ threads: number;
17
+ deviceId?: number | undefined;
18
+ }, {
19
+ encoderPath: string;
20
+ decoderPath: string;
21
+ executionProvider?: "cpu" | "dml" | undefined;
22
+ deviceId?: number | undefined;
23
+ threads?: number | undefined;
24
+ }>;
25
+ export type MobileSamSegmenterOptions = z.input<typeof optionsSchema>;
26
+ declare const proposalOptionsSchema: z.ZodObject<{
27
+ encoderPath: z.ZodString;
28
+ decoderPath: z.ZodString;
29
+ executionProvider: z.ZodDefault<z.ZodLiteral<"cpu">>;
30
+ threads: z.ZodDefault<z.ZodNumber>;
31
+ }, "strict", z.ZodTypeAny, {
32
+ encoderPath: string;
33
+ decoderPath: string;
34
+ executionProvider: "cpu";
35
+ threads: number;
36
+ }, {
37
+ encoderPath: string;
38
+ decoderPath: string;
39
+ executionProvider?: "cpu" | undefined;
40
+ threads?: number | undefined;
41
+ }>;
42
+ export type MobileSamObjectProposalsOptions = z.input<typeof proposalOptionsSchema>;
43
+ type SerializedTask = (Omit<Extract<NativeSegmentationRequest, {
44
+ kind: 'mobile-sam';
45
+ }>, 'id' | 'rgba'> & {
46
+ rgba: string;
47
+ }) | (Omit<Extract<NativeSegmentationRequest, {
48
+ kind: 'mobile-sam-expand';
49
+ }>, 'id' | 'rgba'> & {
50
+ rgba: string;
51
+ });
52
+ /** Explicit pinned MobileSAM inference in a separately owned Node child process. */
53
+ export declare function createMobileSamSegmenter(options: MobileSamSegmenterOptions): Promise<SegmentationPort>;
54
+ /** Internal adapter seam: pool ownership stays with the caller. */
55
+ export declare function createMobileSamPortForPool(pool: Pick<NativeChildPool<SerializedTask>, 'runLazy' | 'settle' | 'dispose'>, parsed: z.output<typeof optionsSchema>): SegmentationPort & Required<Pick<SegmentationPort, 'expand'>>;
56
+ /** Explicit CPU-only, caller-owned one-shot 3x3-point / 36-mask proposal adapter. */
57
+ export declare function createMobileSamObjectProposals(options: MobileSamObjectProposalsOptions): Promise<ObjectProposalPort>;
58
+ export {};
@@ -0,0 +1,472 @@
1
+ import { performance } from 'node:perf_hooks';
2
+ import { ClassifierError, assertActive } from '../../../entities/errors.js';
3
+ import { z } from 'zod';
4
+ import { objectProposalLimits } from '../../../entities/object-proposals.js';
5
+ import { limits, pixelRectSchema } from '../../../entities/contracts.js';
6
+ import { NativeChildPool } from '../native-child.js';
7
+ import { verifyPinnedAsset } from '../verify-asset.js';
8
+ import { probeBackend } from '../probe-backend.js';
9
+ import { nativeErrorReplySchema, nativeMobileSamExpansionReplySchema, nativeObjectProposalReplySchema, nativeSegmentationReplySchema, } from '../native-protocol.js';
10
+ import { packFullSourceBinaryMask, unpackFullSourceBinaryMask, validateMobileSamPreviousMask, } from './worker-inference.js';
11
+ const optionsSchema = z
12
+ .object({
13
+ encoderPath: z.string().min(1).max(4096),
14
+ decoderPath: z.string().min(1).max(4096),
15
+ executionProvider: z.enum(['cpu', 'dml']).default('cpu'),
16
+ deviceId: z.number().int().min(0).max(32).optional(),
17
+ threads: z.number().int().min(1).max(8).default(2),
18
+ })
19
+ .strict();
20
+ const proposalOptionsSchema = z
21
+ .object({
22
+ encoderPath: z.string().min(1).max(4096),
23
+ decoderPath: z.string().min(1).max(4096),
24
+ executionProvider: z.literal('cpu').default('cpu'),
25
+ threads: z.number().int().min(1).max(8).default(2),
26
+ })
27
+ .strict();
28
+ /** Explicit pinned MobileSAM inference in a separately owned Node child process. */
29
+ export async function createMobileSamSegmenter(options) {
30
+ const checked = optionsSchema.safeParse(options);
31
+ if (!checked.success)
32
+ throw new ClassifierError('INVALID_INPUT', 'Invalid MobileSAM options');
33
+ const parsed = checked.data;
34
+ if (parsed.executionProvider === 'dml' && process.platform !== 'win32')
35
+ throw new ClassifierError('UNSUPPORTED', 'DirectML is supported only on Windows');
36
+ try {
37
+ import.meta.resolve('onnxruntime-node');
38
+ }
39
+ catch {
40
+ throw new ClassifierError('UNSUPPORTED', 'onnxruntime-node is not installed');
41
+ }
42
+ await verifyPinnedAsset(parsed.encoderPath, {
43
+ bytes: 28_157_093,
44
+ sha256: '580f5fb648ea1062c0aabc26217aed56921985f03f0cbbd852bba81d760cc749',
45
+ }, 'MobileSAM encoder');
46
+ await verifyPinnedAsset(parsed.decoderPath, {
47
+ bytes: 16_496_559,
48
+ sha256: '8976b90a87ba50a6a72217a5ff994f7d25ce16f2229fcc1ed259e1294c622ffe',
49
+ }, 'MobileSAM decoder');
50
+ const sourceWorker = import.meta.url.endsWith('.ts');
51
+ const workerUrl = new URL(sourceWorker ? '../native-worker.ts' : '../native-worker.js', import.meta.url);
52
+ await probeBackend(workerUrl, 'mobile-sam', { options: parsed });
53
+ const pool = new NativeChildPool({
54
+ workerUrl,
55
+ maxActive: 1,
56
+ maxWaiting: 2,
57
+ jobTimeoutMs: 10_000,
58
+ abortGraceMs: 250,
59
+ encode: (job) => Buffer.from(JSON.stringify({ id: job.id, ...job.payload })),
60
+ decode: (raw, id) => {
61
+ const error = nativeErrorReplySchema.safeParse(raw);
62
+ if (error.success) {
63
+ if (error.data.id !== id)
64
+ throw new ClassifierError('INVALID_RESPONSE', 'Native child error ID mismatch');
65
+ throw new ClassifierError(error.data.code, error.data.message);
66
+ }
67
+ const reply = nativeSegmentationReplySchema.safeParse(raw);
68
+ if (reply.success && reply.data.id === id)
69
+ return reply.data;
70
+ const expansion = nativeMobileSamExpansionReplySchema.safeParse(raw);
71
+ if (!expansion.success || expansion.data.id !== id)
72
+ throw new ClassifierError('INVALID_RESPONSE', 'Native child returned an invalid or mismatched reply');
73
+ return expansion.data;
74
+ },
75
+ });
76
+ return createMobileSamPortForPool(pool, parsed);
77
+ }
78
+ /** Internal adapter seam: pool ownership stays with the caller. */
79
+ export function createMobileSamPortForPool(pool, parsed) {
80
+ return {
81
+ async segment(input) {
82
+ const started = performance.now();
83
+ assertActive(input.signal);
84
+ let image;
85
+ let maskData;
86
+ try {
87
+ const raw = await pool.runLazy(input.signal, () => {
88
+ image = snapshotInput(input);
89
+ const rgba = Buffer.from(image.data);
90
+ let rgbaBase64;
91
+ try {
92
+ rgbaBase64 = rgba.toString('base64');
93
+ }
94
+ finally {
95
+ rgba.fill(0);
96
+ }
97
+ return {
98
+ kind: 'mobile-sam',
99
+ width: image.width,
100
+ height: image.height,
101
+ rgba: rgbaBase64,
102
+ seedRegion: image.seedRegion,
103
+ wholeRegion: image.wholeRegion,
104
+ roi: image.roi,
105
+ prompt: image.prompt,
106
+ options: parsed,
107
+ };
108
+ });
109
+ if (!image)
110
+ throw new ClassifierError('INVALID_RESPONSE', 'Native segmentation omitted its input snapshot');
111
+ const checkedReply = nativeSegmentationReplySchema.safeParse(raw);
112
+ if (!checkedReply.success ||
113
+ checkedReply.data.width !== image.width ||
114
+ checkedReply.data.height !== image.height)
115
+ throw new ClassifierError('INVALID_RESPONSE', 'Native child returned an invalid segmentation reply');
116
+ maskData = Buffer.from(checkedReply.data.mask, 'base64');
117
+ if (maskData.length !== image.width * image.height ||
118
+ maskData.toString('base64') !== checkedReply.data.mask)
119
+ throw new ClassifierError('INVALID_RESPONSE', 'Native child mask dimensions or encoding are invalid');
120
+ return {
121
+ status: 'segmented',
122
+ mask: {
123
+ data: new Uint8Array(maskData),
124
+ width: image.width,
125
+ height: image.height,
126
+ format: 'binary-u8',
127
+ },
128
+ metadata: {
129
+ method: checkedReply.data.method,
130
+ backend: 'mobile-sam',
131
+ durationMs: Math.max(0, performance.now() - started),
132
+ input: { width: image.width, height: image.height },
133
+ },
134
+ };
135
+ }
136
+ finally {
137
+ maskData?.fill(0);
138
+ image?.data.fill(0);
139
+ }
140
+ },
141
+ async expand(input, previousMask) {
142
+ const started = performance.now();
143
+ assertActive(input.signal);
144
+ let image;
145
+ let previous;
146
+ let resultData;
147
+ let transferred = false;
148
+ try {
149
+ const raw = await pool.runLazy(input.signal, () => {
150
+ image = snapshotInput(input);
151
+ previous = snapshotPreviousMask(input, previousMask, image);
152
+ const packedPrevious = packFullSourceBinaryMask(previous, image.width, image.height);
153
+ let previousMaskBase64;
154
+ try {
155
+ const packedBuffer = Buffer.from(packedPrevious);
156
+ try {
157
+ previousMaskBase64 = packedBuffer.toString('base64');
158
+ }
159
+ finally {
160
+ packedBuffer.fill(0);
161
+ }
162
+ }
163
+ finally {
164
+ packedPrevious.fill(0);
165
+ }
166
+ const rgbaBuffer = Buffer.from(image.data);
167
+ let rgbaBase64;
168
+ try {
169
+ rgbaBase64 = rgbaBuffer.toString('base64');
170
+ }
171
+ finally {
172
+ rgbaBuffer.fill(0);
173
+ }
174
+ return {
175
+ kind: 'mobile-sam-expand',
176
+ width: image.width,
177
+ height: image.height,
178
+ rgba: rgbaBase64,
179
+ previousMask: previousMaskBase64,
180
+ seedRegion: image.seedRegion,
181
+ wholeRegion: image.wholeRegion,
182
+ roi: image.roi,
183
+ prompt: image.prompt,
184
+ options: parsed,
185
+ };
186
+ });
187
+ if (!image || !previous)
188
+ throw new ClassifierError('INVALID_RESPONSE', 'Native expansion omitted its input snapshot');
189
+ const checkedReply = nativeMobileSamExpansionReplySchema.safeParse(raw);
190
+ if (!checkedReply.success ||
191
+ checkedReply.data.width !== image.width ||
192
+ checkedReply.data.height !== image.height)
193
+ throw new ClassifierError('INVALID_RESPONSE', 'Native child returned an invalid expansion reply');
194
+ if (checkedReply.data.status === 'unknown')
195
+ return {
196
+ status: 'unknown',
197
+ reason: 'no-enclosing-mask',
198
+ metadata: {
199
+ method: checkedReply.data.method,
200
+ backend: 'mobile-sam',
201
+ durationMs: Math.max(0, performance.now() - started),
202
+ input: { width: image.width, height: image.height },
203
+ },
204
+ };
205
+ const packedBytes = Buffer.from(checkedReply.data.mask, 'base64');
206
+ let data;
207
+ try {
208
+ if (packedBytes.toString('base64') !== checkedReply.data.mask)
209
+ throw new ClassifierError('INVALID_RESPONSE', 'Native expansion mask encoding is invalid');
210
+ try {
211
+ data = unpackFullSourceBinaryMask(packedBytes, image.width, image.height);
212
+ resultData = data;
213
+ }
214
+ catch {
215
+ throw new ClassifierError('INVALID_RESPONSE', 'Native expansion packed mask is invalid');
216
+ }
217
+ }
218
+ finally {
219
+ packedBytes.fill(0);
220
+ }
221
+ let oldStats;
222
+ let newStats;
223
+ try {
224
+ oldStats = validateMobileSamPreviousMask({
225
+ width: image.width,
226
+ height: image.height,
227
+ rgba: image.data,
228
+ previousMask: previous,
229
+ roi: image.roi,
230
+ });
231
+ newStats = validateMobileSamPreviousMask({
232
+ width: image.width,
233
+ height: image.height,
234
+ rgba: image.data,
235
+ previousMask: data,
236
+ roi: image.roi,
237
+ });
238
+ }
239
+ catch {
240
+ data.fill(0);
241
+ throw new ClassifierError('INVALID_RESPONSE', 'Native expanded mask is invalid');
242
+ }
243
+ let hasNewForeground = false;
244
+ for (let index = 0; index < data.length; index++) {
245
+ if (previous[index] === 1 && data[index] !== 1) {
246
+ data.fill(0);
247
+ throw new ClassifierError('INVALID_RESPONSE', 'Native expansion lost prior foreground');
248
+ }
249
+ if (data[index] === 1 && previous[index] === 0)
250
+ hasNewForeground = true;
251
+ }
252
+ if (!hasNewForeground ||
253
+ newStats.area <= oldStats.area ||
254
+ newStats.bounds.left > oldStats.bounds.left ||
255
+ newStats.bounds.top > oldStats.bounds.top ||
256
+ newStats.bounds.right < oldStats.bounds.right ||
257
+ newStats.bounds.bottom < oldStats.bounds.bottom ||
258
+ (newStats.bounds.left === oldStats.bounds.left &&
259
+ newStats.bounds.top === oldStats.bounds.top &&
260
+ newStats.bounds.right === oldStats.bounds.right &&
261
+ newStats.bounds.bottom === oldStats.bounds.bottom)) {
262
+ data.fill(0);
263
+ throw new ClassifierError('INVALID_RESPONSE', 'Native expansion is not a strict enclosing mask');
264
+ }
265
+ transferred = true;
266
+ return {
267
+ status: 'segmented',
268
+ mask: { data, width: image.width, height: image.height, format: 'binary-u8' },
269
+ metadata: {
270
+ method: checkedReply.data.method,
271
+ backend: 'mobile-sam',
272
+ durationMs: Math.max(0, performance.now() - started),
273
+ input: { width: image.width, height: image.height },
274
+ qualityScore: checkedReply.data.qualityScore,
275
+ },
276
+ };
277
+ }
278
+ finally {
279
+ image?.data.fill(0);
280
+ previous?.fill(0);
281
+ if (resultData && !transferred)
282
+ resultData.fill(0);
283
+ }
284
+ },
285
+ settle: () => pool.settle(),
286
+ dispose: () => pool.dispose(),
287
+ };
288
+ }
289
+ /** Explicit CPU-only, caller-owned one-shot 3x3-point / 36-mask proposal adapter. */
290
+ export async function createMobileSamObjectProposals(options) {
291
+ const checked = proposalOptionsSchema.safeParse(options);
292
+ if (!checked.success)
293
+ throw new ClassifierError('INVALID_INPUT', 'Invalid MobileSAM proposal options');
294
+ const parsed = checked.data;
295
+ try {
296
+ import.meta.resolve('onnxruntime-node');
297
+ }
298
+ catch {
299
+ throw new ClassifierError('UNSUPPORTED', 'onnxruntime-node is not installed');
300
+ }
301
+ await verifyPinnedAsset(parsed.encoderPath, {
302
+ bytes: 28_157_093,
303
+ sha256: '580f5fb648ea1062c0aabc26217aed56921985f03f0cbbd852bba81d760cc749',
304
+ }, 'MobileSAM encoder');
305
+ await verifyPinnedAsset(parsed.decoderPath, {
306
+ bytes: 16_496_559,
307
+ sha256: '8976b90a87ba50a6a72217a5ff994f7d25ce16f2229fcc1ed259e1294c622ffe',
308
+ }, 'MobileSAM decoder');
309
+ const sourceWorker = import.meta.url.endsWith('.ts');
310
+ const workerUrl = new URL(sourceWorker ? '../native-worker.ts' : '../native-worker.js', import.meta.url);
311
+ // Probe the explicitly requested CPU provider in an owned child, disposing it before return.
312
+ await probeBackend(workerUrl, 'mobile-sam', { options: { ...parsed, executionProvider: 'cpu' } });
313
+ const pool = new NativeChildPool({
314
+ workerUrl,
315
+ maxActive: 1,
316
+ maxWaiting: 2,
317
+ jobTimeoutMs: 10_000,
318
+ abortGraceMs: 250,
319
+ encode: (job) => Buffer.from(JSON.stringify({ id: job.id, ...job.payload })),
320
+ decode: (raw, id) => {
321
+ const error = nativeErrorReplySchema.safeParse(raw);
322
+ if (error.success) {
323
+ if (error.data.id !== id)
324
+ throw new ClassifierError('INVALID_RESPONSE', 'Native child error ID mismatch');
325
+ throw new ClassifierError(error.data.code, error.data.message);
326
+ }
327
+ const reply = nativeObjectProposalReplySchema.safeParse(raw);
328
+ if (!reply.success || reply.data.id !== id)
329
+ throw new ClassifierError('INVALID_RESPONSE', 'Native child returned invalid proposals');
330
+ return reply.data;
331
+ },
332
+ });
333
+ return {
334
+ async propose(input) {
335
+ const started = performance.now();
336
+ assertActive(input.signal);
337
+ let image;
338
+ const raw = await pool.runLazy(input.signal, () => {
339
+ const snapshot = snapshotProposalInput(input);
340
+ image = snapshot.image;
341
+ return {
342
+ kind: 'mobile-sam-proposals',
343
+ width: image.width,
344
+ height: image.height,
345
+ rgba: Buffer.from(image.data).toString('base64'),
346
+ seedRegion: snapshot.seedRegion,
347
+ roi: snapshot.roi,
348
+ options: { ...parsed, executionProvider: 'cpu' },
349
+ };
350
+ });
351
+ const reply = nativeObjectProposalReplySchema.parse(raw);
352
+ if (reply.width !== image.width || reply.height !== image.height)
353
+ throw new ClassifierError('INVALID_RESPONSE', 'Proposal dimensions do not match the input');
354
+ const expectedBytes = Math.ceil((image.width * image.height) / 8);
355
+ let aggregateBytes = 0;
356
+ const proposals = reply.proposals.map((proposal, index) => {
357
+ const expectedId = `C${String(index).padStart(2, '0')}`;
358
+ const bytes = Buffer.from(proposal.mask, 'base64');
359
+ aggregateBytes += bytes.byteLength;
360
+ if (proposal.id !== expectedId ||
361
+ bytes.byteLength !== expectedBytes ||
362
+ bytes.toString('base64') !== proposal.mask ||
363
+ aggregateBytes > objectProposalLimits.packedBytes)
364
+ throw new ClassifierError('INVALID_RESPONSE', 'Proposal masks are malformed or oversized');
365
+ const unusedBits = expectedBytes * 8 - image.width * image.height;
366
+ const allowed = unusedBits ? (1 << (8 - unusedBits)) - 1 : 255;
367
+ if ((bytes[expectedBytes - 1] & ~allowed) !== 0)
368
+ throw new ClassifierError('INVALID_RESPONSE', 'Proposal mask has nonzero padding bits');
369
+ return {
370
+ id: proposal.id,
371
+ mask: {
372
+ data: new Uint8Array(bytes),
373
+ width: image.width,
374
+ height: image.height,
375
+ format: 'binary-lsb0',
376
+ },
377
+ qualityScore: proposal.qualityScore,
378
+ };
379
+ });
380
+ return {
381
+ proposals,
382
+ metadata: {
383
+ method: 'mobile-sam-nine-point-proposals-v1',
384
+ backend: 'mobile-sam',
385
+ durationMs: Math.max(0, performance.now() - started),
386
+ input: { width: image.width, height: image.height },
387
+ },
388
+ };
389
+ },
390
+ settle: () => pool.settle(),
391
+ dispose: () => pool.dispose(),
392
+ };
393
+ }
394
+ function snapshotInput(input) {
395
+ const image = input.image;
396
+ const pixels = image.width * image.height;
397
+ const prompt = input.prompt ?? 'whole-region';
398
+ if (image.format !== 'rgba8' ||
399
+ !(image.data instanceof Uint8Array) ||
400
+ !Number.isSafeInteger(image.width) ||
401
+ !Number.isSafeInteger(image.height) ||
402
+ !Number.isSafeInteger(pixels) ||
403
+ image.width < 1 ||
404
+ image.height < 1 ||
405
+ pixels > limits.imagePixels ||
406
+ image.data.length !== pixels * 4)
407
+ throw new ClassifierError('INVALID_INPUT', 'Segmentation requires canonical RGBA8 within the image pixel limit');
408
+ if ((prompt !== 'whole-region' && prompt !== 'seed-region') ||
409
+ !pixelRectSchema.safeParse(input.seedRegion).success ||
410
+ !pixelRectSchema.safeParse(input.wholeRegion).success ||
411
+ !pixelRectSchema.safeParse(input.roi).success)
412
+ throw new ClassifierError('INVALID_INPUT', 'Segmentation regions must be positive integer rectangles');
413
+ for (const region of [input.seedRegion, input.wholeRegion, input.roi])
414
+ if (region.left + region.width > image.width || region.top + region.height > image.height)
415
+ throw new ClassifierError('INVALID_INPUT', 'Segmentation region lies outside the source image');
416
+ return {
417
+ data: new Uint8Array(image.data),
418
+ width: image.width,
419
+ height: image.height,
420
+ seedRegion: { ...input.seedRegion },
421
+ wholeRegion: { ...input.wholeRegion },
422
+ roi: { ...input.roi },
423
+ prompt,
424
+ };
425
+ }
426
+ function snapshotPreviousMask(input, previousMask, image) {
427
+ if (!previousMask ||
428
+ previousMask.format !== 'binary-u8' ||
429
+ !(previousMask.data instanceof Uint8Array) ||
430
+ previousMask.width !== image.width ||
431
+ previousMask.height !== image.height ||
432
+ previousMask.data.length !== image.width * image.height)
433
+ throw new ClassifierError('INVALID_INPUT', 'Expansion requires a full-source binary mask');
434
+ try {
435
+ validateMobileSamPreviousMask({
436
+ width: image.width,
437
+ height: image.height,
438
+ rgba: image.data,
439
+ previousMask: previousMask.data,
440
+ roi: image.roi,
441
+ });
442
+ }
443
+ catch {
444
+ throw new ClassifierError('INVALID_INPUT', 'Expansion mask must be nonempty binary foreground inside visible ROI pixels');
445
+ }
446
+ return new Uint8Array(previousMask.data);
447
+ }
448
+ function snapshotProposalInput(input) {
449
+ const { image } = input;
450
+ const pixels = image.width * image.height;
451
+ if (pixels > objectProposalLimits.imagePixels)
452
+ throw new ClassifierError('RESOURCE_LIMIT', 'Proposal image exceeds the two-megapixel limit');
453
+ if (image.format !== 'rgba8' ||
454
+ !(image.data instanceof Uint8Array) ||
455
+ !Number.isSafeInteger(image.width) ||
456
+ !Number.isSafeInteger(image.height) ||
457
+ !Number.isSafeInteger(pixels) ||
458
+ image.width < 1 ||
459
+ image.height < 1 ||
460
+ image.data.length !== pixels * 4)
461
+ throw new ClassifierError('INVALID_INPUT', 'Proposals require canonical RGBA8 pixels');
462
+ for (const region of [input.seedRegion, input.roi])
463
+ if (!pixelRectSchema.safeParse(region).success ||
464
+ region.left + region.width > image.width ||
465
+ region.top + region.height > image.height)
466
+ throw new ClassifierError('INVALID_INPUT', 'Proposal region lies outside the source image');
467
+ return {
468
+ image: { data: new Uint8Array(image.data), width: image.width, height: image.height },
469
+ seedRegion: { ...input.seedRegion },
470
+ roi: { ...input.roi },
471
+ };
472
+ }
@@ -0,0 +1,149 @@
1
+ import type { NativeMobileSamExpansionReply, NativeSegmentationReply, NativeSegmentationRequest } from '../native-protocol.js';
2
+ import type { NativeObjectProposalReply } from '../native-protocol.js';
3
+ export declare function probeMobileSam(options: Extract<NativeSegmentationRequest, {
4
+ kind: 'probe-mobile-sam';
5
+ }>['options']): Promise<void>;
6
+ interface MaskCandidate {
7
+ token: number;
8
+ score: number;
9
+ mask: Uint8Array;
10
+ }
11
+ /** Sort by model quality and use decoder token order as a deterministic tie break. */
12
+ export declare function selectBestMaskCandidate<T extends MaskCandidate>(candidates: readonly T[]): T | undefined;
13
+ export declare function mobileSamSegmentationPrompt(input: {
14
+ prompt: 'whole-region' | 'seed-region';
15
+ seedRegion: {
16
+ left: number;
17
+ top: number;
18
+ width: number;
19
+ height: number;
20
+ };
21
+ wholeRegion: {
22
+ left: number;
23
+ top: number;
24
+ width: number;
25
+ height: number;
26
+ };
27
+ xScale: number;
28
+ yScale: number;
29
+ }): {
30
+ coordinates: Float32Array<ArrayBuffer>;
31
+ labels: Float32Array<ArrayBuffer>;
32
+ };
33
+ export declare function collectMobileSamSegmentationCandidates(input: {
34
+ width: number;
35
+ height: number;
36
+ rgba: Uint8Array;
37
+ logits: Float32Array;
38
+ scores: Float32Array;
39
+ roi: {
40
+ left: number;
41
+ top: number;
42
+ width: number;
43
+ height: number;
44
+ };
45
+ seedRegion: {
46
+ left: number;
47
+ top: number;
48
+ width: number;
49
+ height: number;
50
+ };
51
+ prompt: 'whole-region' | 'seed-region';
52
+ }): MaskCandidate[];
53
+ export declare function selectMobileSamSegmentationMask(input: {
54
+ candidates: readonly MaskCandidate[];
55
+ width: number;
56
+ height: number;
57
+ prompt: 'whole-region' | 'seed-region';
58
+ }): Uint8Array;
59
+ interface EnclosingMobileSamCandidate {
60
+ token: number;
61
+ score: number;
62
+ mask: Uint8Array;
63
+ area: number;
64
+ bounds: {
65
+ left: number;
66
+ top: number;
67
+ right: number;
68
+ bottom: number;
69
+ };
70
+ }
71
+ /** Full-source segmentation masks use the segmentation pixel cap, not the proposal cap. */
72
+ export declare function packFullSourceBinaryMask(mask: Uint8Array, width: number, height: number): Uint8Array;
73
+ /** Strictly decode the LSB-first full-source mask representation used by expansion IPC. */
74
+ export declare function unpackFullSourceBinaryMask(data: Uint8Array, width: number, height: number): Uint8Array;
75
+ /** Validate a prior full-source mask and return its foreground area and bounds. */
76
+ export declare function validateMobileSamPreviousMask(input: {
77
+ width: number;
78
+ height: number;
79
+ rgba: Uint8Array;
80
+ previousMask: Uint8Array;
81
+ roi: {
82
+ left: number;
83
+ top: number;
84
+ width: number;
85
+ height: number;
86
+ };
87
+ }): {
88
+ area: number;
89
+ bounds: {
90
+ left: number;
91
+ top: number;
92
+ right: number;
93
+ bottom: number;
94
+ };
95
+ };
96
+ /** Select the smallest actual decoder mask that strictly encloses prior foreground. */
97
+ export declare function selectEnclosingMobileSamCandidate(input: {
98
+ width: number;
99
+ height: number;
100
+ rgba: Uint8Array;
101
+ logits: Float32Array;
102
+ scores: Float32Array;
103
+ previousMask: Uint8Array;
104
+ roi: {
105
+ left: number;
106
+ top: number;
107
+ width: number;
108
+ height: number;
109
+ };
110
+ }): EnclosingMobileSamCandidate | undefined;
111
+ export declare function runMobileSamTask(request: Extract<NativeSegmentationRequest, {
112
+ kind: 'mobile-sam';
113
+ }>): Promise<NativeSegmentationReply>;
114
+ type ExpansionRequest = Extract<NativeSegmentationRequest, {
115
+ kind: 'mobile-sam-expand';
116
+ }>;
117
+ /** Run one same-seed decode and return only the smallest strict enclosing mask. */
118
+ export declare function runMobileSamExpandTask(request: ExpansionRequest): Promise<NativeMobileSamExpansionReply>;
119
+ type ProposalRequest = Extract<NativeSegmentationRequest, {
120
+ kind: 'mobile-sam-proposals';
121
+ }>;
122
+ /** Produce the fixed row-major 3x3 prompt grid from the actual seed rectangle. */
123
+ export declare function mobileSamProposalPoints(rect: {
124
+ left: number;
125
+ top: number;
126
+ width: number;
127
+ height: number;
128
+ }): Array<{
129
+ x: number;
130
+ y: number;
131
+ }>;
132
+ /** Turn one decoder response into four owned packed candidates immediately. */
133
+ export declare function packMobileSamProposalCandidates(input: {
134
+ width: number;
135
+ height: number;
136
+ rgba: Uint8Array;
137
+ roi: {
138
+ left: number;
139
+ top: number;
140
+ width: number;
141
+ height: number;
142
+ };
143
+ pointIndex: number;
144
+ logits: Float32Array;
145
+ scores: Float32Array;
146
+ }): NativeObjectProposalReply['proposals'];
147
+ /** One encoder pass followed by nine independent positive-point decoder calls. */
148
+ export declare function runMobileSamObjectProposalTask(request: ProposalRequest): Promise<NativeObjectProposalReply>;
149
+ export {};