@wenathlan/saddle 1.8.17 → 2.0.2

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 (1005) hide show
  1. package/Dockerfile +1339 -0
  2. package/README.md +68 -22
  3. package/alternatives.ts +1218 -0
  4. package/biome.json +153 -0
  5. package/boards.json +1517 -0
  6. package/compute.ts +3144 -0
  7. package/cores.json +1801 -0
  8. package/dist/acquisition.d.ts +359 -0
  9. package/dist/acquisition.d.ts.map +1 -0
  10. package/dist/acquisition.js +629 -0
  11. package/dist/acquisition.js.map +1 -0
  12. package/dist/alternatives.d.ts +509 -0
  13. package/dist/alternatives.d.ts.map +1 -0
  14. package/dist/alternatives.js +951 -0
  15. package/dist/alternatives.js.map +1 -0
  16. package/dist/automation.d.ts +426 -0
  17. package/dist/automation.d.ts.map +1 -0
  18. package/dist/automation.js +561 -0
  19. package/dist/automation.js.map +1 -0
  20. package/dist/browser.d.ts +562 -0
  21. package/dist/browser.d.ts.map +1 -0
  22. package/dist/browser.js +1057 -0
  23. package/dist/browser.js.map +1 -0
  24. package/dist/capacitor.config.d.ts +13 -0
  25. package/dist/capacitor.config.d.ts.map +1 -0
  26. package/dist/capacitor.config.js +26 -0
  27. package/dist/capacitor.config.js.map +1 -0
  28. package/dist/cli.d.ts +17 -0
  29. package/dist/cli.d.ts.map +1 -0
  30. package/dist/cli.js +111 -0
  31. package/dist/cli.js.map +1 -0
  32. package/dist/communication.d.ts +192 -0
  33. package/dist/communication.d.ts.map +1 -0
  34. package/dist/communication.js +418 -0
  35. package/dist/communication.js.map +1 -0
  36. package/dist/compute.d.ts +1161 -0
  37. package/dist/compute.d.ts.map +1 -0
  38. package/dist/compute.js +2273 -0
  39. package/dist/compute.js.map +1 -0
  40. package/dist/distribution.d.ts +529 -0
  41. package/dist/distribution.d.ts.map +1 -0
  42. package/dist/distribution.js +811 -0
  43. package/dist/distribution.js.map +1 -0
  44. package/dist/execution.d.ts +530 -0
  45. package/dist/execution.d.ts.map +1 -0
  46. package/dist/execution.js +761 -0
  47. package/dist/execution.js.map +1 -0
  48. package/dist/format.d.ts +19 -0
  49. package/dist/format.d.ts.map +1 -0
  50. package/dist/format.js +60 -0
  51. package/dist/format.js.map +1 -0
  52. package/dist/foundation.d.ts +176 -0
  53. package/dist/foundation.d.ts.map +1 -0
  54. package/dist/foundation.js +309 -0
  55. package/dist/foundation.js.map +1 -0
  56. package/dist/index.d.ts +733 -122
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +1018 -122
  59. package/dist/index.js.map +1 -1
  60. package/dist/integration.d.ts +236 -0
  61. package/dist/integration.d.ts.map +1 -0
  62. package/dist/integration.js +236 -0
  63. package/dist/integration.js.map +1 -0
  64. package/dist/intelligence.d.ts +94 -0
  65. package/dist/intelligence.d.ts.map +1 -0
  66. package/dist/intelligence.js +145 -0
  67. package/dist/intelligence.js.map +1 -0
  68. package/dist/isolation.d.ts +129 -0
  69. package/dist/isolation.d.ts.map +1 -0
  70. package/dist/isolation.js +96 -0
  71. package/dist/isolation.js.map +1 -0
  72. package/dist/media.d.ts +344 -0
  73. package/dist/media.d.ts.map +1 -0
  74. package/dist/media.js +986 -0
  75. package/dist/media.js.map +1 -0
  76. package/dist/modes.d.ts +359 -0
  77. package/dist/modes.d.ts.map +1 -0
  78. package/dist/modes.js +134 -0
  79. package/dist/modes.js.map +1 -0
  80. package/dist/{library/public.d.ts → operations.d.ts} +15 -1
  81. package/dist/operations.d.ts.map +1 -0
  82. package/dist/{library/public.js → operations.js} +18 -7
  83. package/dist/operations.js.map +1 -0
  84. package/dist/orchestrator.d.ts +1339 -0
  85. package/dist/orchestrator.d.ts.map +1 -0
  86. package/dist/orchestrator.js +4308 -0
  87. package/dist/orchestrator.js.map +1 -0
  88. package/dist/performance.d.ts +416 -0
  89. package/dist/performance.d.ts.map +1 -0
  90. package/dist/performance.js +827 -0
  91. package/dist/performance.js.map +1 -0
  92. package/dist/quantum.d.ts +810 -0
  93. package/dist/quantum.d.ts.map +1 -0
  94. package/dist/quantum.js +2073 -0
  95. package/dist/quantum.js.map +1 -0
  96. package/dist/render.d.ts +381 -0
  97. package/dist/render.d.ts.map +1 -0
  98. package/dist/render.js +1073 -0
  99. package/dist/render.js.map +1 -0
  100. package/dist/scheduler.d.ts +596 -0
  101. package/dist/scheduler.d.ts.map +1 -0
  102. package/dist/scheduler.js +1259 -0
  103. package/dist/scheduler.js.map +1 -0
  104. package/dist/security.d.ts +501 -0
  105. package/dist/security.d.ts.map +1 -0
  106. package/dist/security.js +1170 -0
  107. package/dist/security.js.map +1 -0
  108. package/dist/server.d.ts +21 -0
  109. package/dist/server.d.ts.map +1 -0
  110. package/dist/{server/node.js → server.js} +16 -2
  111. package/dist/server.js.map +1 -0
  112. package/dist/tiers.d.ts +1002 -0
  113. package/dist/tiers.d.ts.map +1 -0
  114. package/dist/tiers.js +2165 -0
  115. package/dist/tiers.js.map +1 -0
  116. package/dist/virtual.d.ts +641 -0
  117. package/dist/virtual.d.ts.map +1 -0
  118. package/dist/virtual.js +1285 -0
  119. package/dist/virtual.js.map +1 -0
  120. package/dist/virtualcpu.d.ts +316 -0
  121. package/dist/virtualcpu.d.ts.map +1 -0
  122. package/dist/virtualcpu.js +1180 -0
  123. package/dist/virtualcpu.js.map +1 -0
  124. package/dist/virtualgpu.d.ts +345 -0
  125. package/dist/virtualgpu.d.ts.map +1 -0
  126. package/dist/virtualgpu.js +1000 -0
  127. package/dist/virtualgpu.js.map +1 -0
  128. package/dist/virtualization.d.ts +607 -0
  129. package/dist/virtualization.d.ts.map +1 -0
  130. package/dist/virtualization.js +1187 -0
  131. package/dist/virtualization.js.map +1 -0
  132. package/dist/virtualmemory.d.ts +818 -0
  133. package/dist/virtualmemory.d.ts.map +1 -0
  134. package/dist/virtualmemory.js +1796 -0
  135. package/dist/virtualmemory.js.map +1 -0
  136. package/dist/webscrape.d.ts +720 -0
  137. package/dist/webscrape.d.ts.map +1 -0
  138. package/dist/webscrape.js +2697 -0
  139. package/dist/webscrape.js.map +1 -0
  140. package/docker.config +769 -0
  141. package/docs/CONVERSA.txt +1523 -0
  142. package/docs/alternatives.md +499 -0
  143. package/docs/architecture-1.8.19.md +76 -0
  144. package/docs/architecture-1.8.19.mmd +33 -0
  145. package/docs/architecture-1.8.19.png +0 -0
  146. package/docs/architecture.md +355 -0
  147. package/docs/artifactavailability.md +6 -0
  148. package/docs/brancharchive-2026-08-18.md +18 -0
  149. package/docs/browser.md +7 -0
  150. package/docs/consolidation.md +70 -0
  151. package/docs/e2ugh-engine.md +528 -0
  152. package/{dist/examples/localjob.js → docs/example-localjob.ts} +3 -3
  153. package/{dist/examples/publicapi.js → docs/example-publicapi.ts} +2 -2
  154. package/docs/hardware.md +342 -0
  155. package/docs/optimization.md +126 -0
  156. package/docs/performance.md +499 -0
  157. package/docs/planning.1.8.18.md +94 -0
  158. package/docs/planning.1.8.19.md +49 -0
  159. package/docs/releasenotes-1.8.17.md +6 -0
  160. package/docs/releasenotes-1.8.18.md +32 -0
  161. package/docs/releasenotes-1.8.19.md +35 -0
  162. package/docs/research-1.8.18-isolation.md +43 -0
  163. package/docs/research-1.8.19-virtual-browser.md +27 -0
  164. package/docs/saddle.archive.1.8.17.tar.gz.gpg +0 -0
  165. package/docs/security.md +454 -0
  166. package/docs/todo-1.8.16.md +56 -1
  167. package/docs/todo-1.8.18.md +164 -0
  168. package/docs/todo-1.8.19.md +241 -0
  169. package/docs/viability.md +777 -0
  170. package/docs/virtualization.md +335 -0
  171. package/docs/web-duplicatecleanup.md +9 -0
  172. package/docs/web-ideas.md +106 -0
  173. package/docs/workflowimprovements-2026-08-19.md +70 -0
  174. package/gpumonitor.cpp +1543 -0
  175. package/gpus.json +2408 -0
  176. package/index.ts +1478 -0
  177. package/media.ts +1337 -0
  178. package/mttg.config +1549 -0
  179. package/orchestrator.ts +5462 -0
  180. package/package.json +304 -55
  181. package/passage.config +1331 -0
  182. package/performance.ts +1049 -0
  183. package/processors.json +2217 -0
  184. package/qemu.config +1062 -0
  185. package/qemubridge.py +1340 -0
  186. package/quantum.ts +2393 -0
  187. package/render.ts +1325 -0
  188. package/scheduler.ts +1600 -0
  189. package/security.ts +1449 -0
  190. package/tiers.ts +2871 -0
  191. package/tsconfig.json +29 -0
  192. package/virtualcpu.ts +1273 -0
  193. package/virtualgpu.ts +1181 -0
  194. package/virtualhardware.c +1011 -0
  195. package/virtualhardware.json +739 -0
  196. package/virtualization.ts +1628 -0
  197. package/virtualizationcore.cpp +4635 -0
  198. package/virtualmemory.ts +2303 -0
  199. package/vm.config.json +1910 -0
  200. package/{extension → web/extension}/manifest.json +1 -1
  201. package/web/readme.md +382 -0
  202. package/web/tsconfig.json +25 -0
  203. package/dist/adapters/forge.d.ts +0 -17
  204. package/dist/adapters/forge.d.ts.map +0 -1
  205. package/dist/adapters/forge.js +0 -19
  206. package/dist/adapters/forge.js.map +0 -1
  207. package/dist/adapters/forgejo.d.ts +0 -49
  208. package/dist/adapters/forgejo.d.ts.map +0 -1
  209. package/dist/adapters/forgejo.js +0 -8
  210. package/dist/adapters/forgejo.js.map +0 -1
  211. package/dist/adapters/github.d.ts +0 -12
  212. package/dist/adapters/github.d.ts.map +0 -1
  213. package/dist/adapters/github.js +0 -20
  214. package/dist/adapters/github.js.map +0 -1
  215. package/dist/adapters/gitlab.d.ts +0 -17
  216. package/dist/adapters/gitlab.d.ts.map +0 -1
  217. package/dist/adapters/gitlab.js +0 -10
  218. package/dist/adapters/gitlab.js.map +0 -1
  219. package/dist/adapters/huggingface.d.ts +0 -17
  220. package/dist/adapters/huggingface.d.ts.map +0 -1
  221. package/dist/adapters/huggingface.js +0 -6
  222. package/dist/adapters/huggingface.js.map +0 -1
  223. package/dist/adapters/socket.d.ts +0 -11
  224. package/dist/adapters/socket.d.ts.map +0 -1
  225. package/dist/adapters/socket.js +0 -17
  226. package/dist/adapters/socket.js.map +0 -1
  227. package/dist/adapters/transport.d.ts +0 -7
  228. package/dist/adapters/transport.d.ts.map +0 -1
  229. package/dist/adapters/transport.js +0 -36
  230. package/dist/adapters/transport.js.map +0 -1
  231. package/dist/ai/chunk.d.ts +0 -2
  232. package/dist/ai/chunk.d.ts.map +0 -1
  233. package/dist/ai/chunk.js +0 -36
  234. package/dist/ai/chunk.js.map +0 -1
  235. package/dist/ai/llmstxt.d.ts +0 -6
  236. package/dist/ai/llmstxt.d.ts.map +0 -1
  237. package/dist/ai/llmstxt.js +0 -12
  238. package/dist/ai/llmstxt.js.map +0 -1
  239. package/dist/ai/provenance.d.ts +0 -22
  240. package/dist/ai/provenance.d.ts.map +0 -1
  241. package/dist/ai/provenance.js +0 -25
  242. package/dist/ai/provenance.js.map +0 -1
  243. package/dist/ai/rag.d.ts +0 -26
  244. package/dist/ai/rag.d.ts.map +0 -1
  245. package/dist/ai/rag.js +0 -22
  246. package/dist/ai/rag.js.map +0 -1
  247. package/dist/ai/tokens.d.ts +0 -15
  248. package/dist/ai/tokens.d.ts.map +0 -1
  249. package/dist/ai/tokens.js +0 -9
  250. package/dist/ai/tokens.js.map +0 -1
  251. package/dist/api/auth.d.ts +0 -16
  252. package/dist/api/auth.d.ts.map +0 -1
  253. package/dist/api/auth.js +0 -19
  254. package/dist/api/auth.js.map +0 -1
  255. package/dist/api/contracts.d.ts +0 -28
  256. package/dist/api/contracts.d.ts.map +0 -1
  257. package/dist/api/contracts.js +0 -14
  258. package/dist/api/contracts.js.map +0 -1
  259. package/dist/api/control.d.ts +0 -10
  260. package/dist/api/control.d.ts.map +0 -1
  261. package/dist/api/control.js +0 -35
  262. package/dist/api/control.js.map +0 -1
  263. package/dist/api/http.d.ts +0 -7
  264. package/dist/api/http.d.ts.map +0 -1
  265. package/dist/api/http.js +0 -12
  266. package/dist/api/http.js.map +0 -1
  267. package/dist/api/rate.d.ts +0 -28
  268. package/dist/api/rate.d.ts.map +0 -1
  269. package/dist/api/rate.js +0 -36
  270. package/dist/api/rate.js.map +0 -1
  271. package/dist/api/security.d.ts +0 -12
  272. package/dist/api/security.d.ts.map +0 -1
  273. package/dist/api/security.js +0 -56
  274. package/dist/api/security.js.map +0 -1
  275. package/dist/api/service.d.ts +0 -5
  276. package/dist/api/service.d.ts.map +0 -1
  277. package/dist/api/service.js +0 -70
  278. package/dist/api/service.js.map +0 -1
  279. package/dist/apps/registry.d.ts +0 -33
  280. package/dist/apps/registry.d.ts.map +0 -1
  281. package/dist/apps/registry.js +0 -24
  282. package/dist/apps/registry.js.map +0 -1
  283. package/dist/binary/archive.d.ts +0 -27
  284. package/dist/binary/archive.d.ts.map +0 -1
  285. package/dist/binary/archive.js +0 -46
  286. package/dist/binary/archive.js.map +0 -1
  287. package/dist/binary/build.d.ts +0 -29
  288. package/dist/binary/build.d.ts.map +0 -1
  289. package/dist/binary/build.js +0 -17
  290. package/dist/binary/build.js.map +0 -1
  291. package/dist/binary/transform.d.ts +0 -70
  292. package/dist/binary/transform.d.ts.map +0 -1
  293. package/dist/binary/transform.js +0 -105
  294. package/dist/binary/transform.js.map +0 -1
  295. package/dist/bot/adapter.d.ts +0 -5
  296. package/dist/bot/adapter.d.ts.map +0 -1
  297. package/dist/bot/adapter.js +0 -11
  298. package/dist/bot/adapter.js.map +0 -1
  299. package/dist/bot/bot.d.ts +0 -28
  300. package/dist/bot/bot.d.ts.map +0 -1
  301. package/dist/bot/bot.js +0 -52
  302. package/dist/bot/bot.js.map +0 -1
  303. package/dist/bot/commands.d.ts +0 -8
  304. package/dist/bot/commands.d.ts.map +0 -1
  305. package/dist/bot/commands.js +0 -19
  306. package/dist/bot/commands.js.map +0 -1
  307. package/dist/bot/permissions.d.ts +0 -13
  308. package/dist/bot/permissions.d.ts.map +0 -1
  309. package/dist/bot/permissions.js +0 -16
  310. package/dist/bot/permissions.js.map +0 -1
  311. package/dist/browser/actions.d.ts +0 -33
  312. package/dist/browser/actions.d.ts.map +0 -1
  313. package/dist/browser/actions.js +0 -44
  314. package/dist/browser/actions.js.map +0 -1
  315. package/dist/browser/agent.d.ts +0 -21
  316. package/dist/browser/agent.d.ts.map +0 -1
  317. package/dist/browser/agent.js +0 -13
  318. package/dist/browser/agent.js.map +0 -1
  319. package/dist/browser/context.d.ts +0 -43
  320. package/dist/browser/context.d.ts.map +0 -1
  321. package/dist/browser/context.js +0 -54
  322. package/dist/browser/context.js.map +0 -1
  323. package/dist/browser/fingerprint.d.ts +0 -4
  324. package/dist/browser/fingerprint.d.ts.map +0 -1
  325. package/dist/browser/fingerprint.js +0 -12
  326. package/dist/browser/fingerprint.js.map +0 -1
  327. package/dist/browser/index.d.ts +0 -11
  328. package/dist/browser/index.d.ts.map +0 -1
  329. package/dist/browser/index.js +0 -11
  330. package/dist/browser/index.js.map +0 -1
  331. package/dist/browser/playwright.d.ts +0 -11
  332. package/dist/browser/playwright.d.ts.map +0 -1
  333. package/dist/browser/playwright.js +0 -24
  334. package/dist/browser/playwright.js.map +0 -1
  335. package/dist/browser/recorder.d.ts +0 -21
  336. package/dist/browser/recorder.d.ts.map +0 -1
  337. package/dist/browser/recorder.js +0 -58
  338. package/dist/browser/recorder.js.map +0 -1
  339. package/dist/browser/session.d.ts +0 -14
  340. package/dist/browser/session.d.ts.map +0 -1
  341. package/dist/browser/session.js +0 -21
  342. package/dist/browser/session.js.map +0 -1
  343. package/dist/browser/snapshot.d.ts +0 -69
  344. package/dist/browser/snapshot.d.ts.map +0 -1
  345. package/dist/browser/snapshot.js +0 -133
  346. package/dist/browser/snapshot.js.map +0 -1
  347. package/dist/captcha/contract.d.ts +0 -35
  348. package/dist/captcha/contract.d.ts.map +0 -1
  349. package/dist/captcha/contract.js +0 -17
  350. package/dist/captcha/contract.js.map +0 -1
  351. package/dist/captcha/evidence.d.ts +0 -10
  352. package/dist/captcha/evidence.d.ts.map +0 -1
  353. package/dist/captcha/evidence.js +0 -9
  354. package/dist/captcha/evidence.js.map +0 -1
  355. package/dist/captcha/guard.d.ts +0 -23
  356. package/dist/captcha/guard.d.ts.map +0 -1
  357. package/dist/captcha/guard.js +0 -12
  358. package/dist/captcha/guard.js.map +0 -1
  359. package/dist/cli/main.d.ts +0 -4
  360. package/dist/cli/main.d.ts.map +0 -1
  361. package/dist/cli/main.js +0 -46
  362. package/dist/cli/main.js.map +0 -1
  363. package/dist/core/errors.d.ts +0 -84
  364. package/dist/core/errors.d.ts.map +0 -1
  365. package/dist/core/errors.js +0 -54
  366. package/dist/core/errors.js.map +0 -1
  367. package/dist/core/events.d.ts +0 -9
  368. package/dist/core/events.d.ts.map +0 -1
  369. package/dist/core/events.js +0 -21
  370. package/dist/core/events.js.map +0 -1
  371. package/dist/core/hash.d.ts +0 -10
  372. package/dist/core/hash.d.ts.map +0 -1
  373. package/dist/core/hash.js +0 -82
  374. package/dist/core/hash.js.map +0 -1
  375. package/dist/core/ids.d.ts +0 -10
  376. package/dist/core/ids.d.ts.map +0 -1
  377. package/dist/core/ids.js +0 -15
  378. package/dist/core/ids.js.map +0 -1
  379. package/dist/delivery/manifest.d.ts +0 -35
  380. package/dist/delivery/manifest.d.ts.map +0 -1
  381. package/dist/delivery/manifest.js +0 -68
  382. package/dist/delivery/manifest.js.map +0 -1
  383. package/dist/deploy/index.d.ts +0 -6
  384. package/dist/deploy/index.d.ts.map +0 -1
  385. package/dist/deploy/index.js +0 -6
  386. package/dist/deploy/index.js.map +0 -1
  387. package/dist/dispatch/resumable.d.ts +0 -102
  388. package/dist/dispatch/resumable.d.ts.map +0 -1
  389. package/dist/dispatch/resumable.js +0 -64
  390. package/dist/dispatch/resumable.js.map +0 -1
  391. package/dist/dispatch/workflow.d.ts +0 -6
  392. package/dist/dispatch/workflow.d.ts.map +0 -1
  393. package/dist/dispatch/workflow.js +0 -37
  394. package/dist/dispatch/workflow.js.map +0 -1
  395. package/dist/domain/artifacts.d.ts +0 -12
  396. package/dist/domain/artifacts.d.ts.map +0 -1
  397. package/dist/domain/artifacts.js +0 -14
  398. package/dist/domain/artifacts.js.map +0 -1
  399. package/dist/domain/jobs.d.ts +0 -12
  400. package/dist/domain/jobs.d.ts.map +0 -1
  401. package/dist/domain/jobs.js +0 -20
  402. package/dist/domain/jobs.js.map +0 -1
  403. package/dist/domain/providers.d.ts +0 -10
  404. package/dist/domain/providers.d.ts.map +0 -1
  405. package/dist/domain/providers.js +0 -8
  406. package/dist/domain/providers.js.map +0 -1
  407. package/dist/domain/runtime.d.ts +0 -14
  408. package/dist/domain/runtime.d.ts.map +0 -1
  409. package/dist/domain/runtime.js +0 -10
  410. package/dist/domain/runtime.js.map +0 -1
  411. package/dist/domain/sessions.d.ts +0 -12
  412. package/dist/domain/sessions.d.ts.map +0 -1
  413. package/dist/domain/sessions.js +0 -57
  414. package/dist/domain/sessions.js.map +0 -1
  415. package/dist/examples/localjob.d.ts +0 -2
  416. package/dist/examples/localjob.d.ts.map +0 -1
  417. package/dist/examples/localjob.js.map +0 -1
  418. package/dist/examples/publicapi.d.ts +0 -2
  419. package/dist/examples/publicapi.d.ts.map +0 -1
  420. package/dist/examples/publicapi.js.map +0 -1
  421. package/dist/extension/build.d.ts +0 -9
  422. package/dist/extension/build.d.ts.map +0 -1
  423. package/dist/extension/build.js +0 -85
  424. package/dist/extension/build.js.map +0 -1
  425. package/dist/extension/content.d.ts +0 -6
  426. package/dist/extension/content.d.ts.map +0 -1
  427. package/dist/extension/content.js +0 -135
  428. package/dist/extension/content.js.map +0 -1
  429. package/dist/extension/index.d.ts +0 -7
  430. package/dist/extension/index.d.ts.map +0 -1
  431. package/dist/extension/index.js +0 -7
  432. package/dist/extension/index.js.map +0 -1
  433. package/dist/extension/pagebridge.d.ts +0 -6
  434. package/dist/extension/pagebridge.d.ts.map +0 -1
  435. package/dist/extension/pagebridge.js +0 -42
  436. package/dist/extension/pagebridge.js.map +0 -1
  437. package/dist/extension/permissions.d.ts +0 -26
  438. package/dist/extension/permissions.d.ts.map +0 -1
  439. package/dist/extension/permissions.js +0 -28
  440. package/dist/extension/permissions.js.map +0 -1
  441. package/dist/extension/popup.d.ts +0 -5
  442. package/dist/extension/popup.d.ts.map +0 -1
  443. package/dist/extension/popup.js +0 -26
  444. package/dist/extension/popup.js.map +0 -1
  445. package/dist/extension/protocol.d.ts +0 -58
  446. package/dist/extension/protocol.d.ts.map +0 -1
  447. package/dist/extension/protocol.js +0 -101
  448. package/dist/extension/protocol.js.map +0 -1
  449. package/dist/extension/serviceworker.d.ts +0 -25
  450. package/dist/extension/serviceworker.d.ts.map +0 -1
  451. package/dist/extension/serviceworker.js +0 -102
  452. package/dist/extension/serviceworker.js.map +0 -1
  453. package/dist/extension/worker.d.ts +0 -28
  454. package/dist/extension/worker.d.ts.map +0 -1
  455. package/dist/extension/worker.js +0 -22
  456. package/dist/extension/worker.js.map +0 -1
  457. package/dist/format/check.d.ts +0 -8
  458. package/dist/format/check.d.ts.map +0 -1
  459. package/dist/format/check.js +0 -38
  460. package/dist/format/check.js.map +0 -1
  461. package/dist/library/public.d.ts.map +0 -1
  462. package/dist/library/public.js.map +0 -1
  463. package/dist/mcp/browser.d.ts +0 -9
  464. package/dist/mcp/browser.d.ts.map +0 -1
  465. package/dist/mcp/browser.js +0 -13
  466. package/dist/mcp/browser.js.map +0 -1
  467. package/dist/mcp/server.d.ts +0 -35
  468. package/dist/mcp/server.d.ts.map +0 -1
  469. package/dist/mcp/server.js +0 -40
  470. package/dist/mcp/server.js.map +0 -1
  471. package/dist/mcp/transport.d.ts +0 -7
  472. package/dist/mcp/transport.d.ts.map +0 -1
  473. package/dist/mcp/transport.js +0 -17
  474. package/dist/mcp/transport.js.map +0 -1
  475. package/dist/memory/bridge.d.ts +0 -14
  476. package/dist/memory/bridge.d.ts.map +0 -1
  477. package/dist/memory/bridge.js +0 -16
  478. package/dist/memory/bridge.js.map +0 -1
  479. package/dist/memory/engine.d.ts +0 -38
  480. package/dist/memory/engine.d.ts.map +0 -1
  481. package/dist/memory/engine.js +0 -109
  482. package/dist/memory/engine.js.map +0 -1
  483. package/dist/memory/modes.d.ts +0 -43
  484. package/dist/memory/modes.d.ts.map +0 -1
  485. package/dist/memory/modes.js +0 -66
  486. package/dist/memory/modes.js.map +0 -1
  487. package/dist/memory/objects.d.ts +0 -21
  488. package/dist/memory/objects.d.ts.map +0 -1
  489. package/dist/memory/objects.js +0 -20
  490. package/dist/memory/objects.js.map +0 -1
  491. package/dist/memory/planner.d.ts +0 -66
  492. package/dist/memory/planner.d.ts.map +0 -1
  493. package/dist/memory/planner.js +0 -108
  494. package/dist/memory/planner.js.map +0 -1
  495. package/dist/memory/targets.d.ts +0 -78
  496. package/dist/memory/targets.d.ts.map +0 -1
  497. package/dist/memory/targets.js +0 -29
  498. package/dist/memory/targets.js.map +0 -1
  499. package/dist/memory/transforms.d.ts +0 -19
  500. package/dist/memory/transforms.d.ts.map +0 -1
  501. package/dist/memory/transforms.js +0 -14
  502. package/dist/memory/transforms.js.map +0 -1
  503. package/dist/modes/matrix.d.ts +0 -23
  504. package/dist/modes/matrix.d.ts.map +0 -1
  505. package/dist/modes/matrix.js +0 -18
  506. package/dist/modes/matrix.js.map +0 -1
  507. package/dist/modes/modes.d.ts +0 -33
  508. package/dist/modes/modes.d.ts.map +0 -1
  509. package/dist/modes/modes.js +0 -15
  510. package/dist/modes/modes.js.map +0 -1
  511. package/dist/modes/resolve.d.ts +0 -96
  512. package/dist/modes/resolve.d.ts.map +0 -1
  513. package/dist/modes/resolve.js +0 -48
  514. package/dist/modes/resolve.js.map +0 -1
  515. package/dist/observability/metrics.d.ts +0 -14
  516. package/dist/observability/metrics.d.ts.map +0 -1
  517. package/dist/observability/metrics.js +0 -18
  518. package/dist/observability/metrics.js.map +0 -1
  519. package/dist/packager/manifest.d.ts +0 -66
  520. package/dist/packager/manifest.d.ts.map +0 -1
  521. package/dist/packager/manifest.js +0 -94
  522. package/dist/packager/manifest.js.map +0 -1
  523. package/dist/packager/publish.d.ts +0 -51
  524. package/dist/packager/publish.d.ts.map +0 -1
  525. package/dist/packager/publish.js +0 -18
  526. package/dist/packager/publish.js.map +0 -1
  527. package/dist/packager/targetcli.d.ts +0 -7
  528. package/dist/packager/targetcli.d.ts.map +0 -1
  529. package/dist/packager/targetcli.js +0 -22
  530. package/dist/packager/targetcli.js.map +0 -1
  531. package/dist/persistence/adapter.d.ts +0 -5
  532. package/dist/persistence/adapter.d.ts.map +0 -1
  533. package/dist/persistence/adapter.js +0 -11
  534. package/dist/persistence/adapter.js.map +0 -1
  535. package/dist/persistence/drizzle.d.ts +0 -2
  536. package/dist/persistence/drizzle.d.ts.map +0 -1
  537. package/dist/persistence/drizzle.js +0 -12
  538. package/dist/persistence/drizzle.js.map +0 -1
  539. package/dist/persistence/memory.d.ts +0 -2
  540. package/dist/persistence/memory.d.ts.map +0 -1
  541. package/dist/persistence/memory.js +0 -27
  542. package/dist/persistence/memory.js.map +0 -1
  543. package/dist/persistence/migrations.d.ts +0 -19
  544. package/dist/persistence/migrations.d.ts.map +0 -1
  545. package/dist/persistence/migrations.js +0 -13
  546. package/dist/persistence/migrations.js.map +0 -1
  547. package/dist/persistence/prisma.d.ts +0 -2
  548. package/dist/persistence/prisma.d.ts.map +0 -1
  549. package/dist/persistence/prisma.js +0 -25
  550. package/dist/persistence/prisma.js.map +0 -1
  551. package/dist/persistence/schema.d.ts +0 -63
  552. package/dist/persistence/schema.d.ts.map +0 -1
  553. package/dist/persistence/schema.js +0 -28
  554. package/dist/persistence/schema.js.map +0 -1
  555. package/dist/persistence/sql.d.ts +0 -3
  556. package/dist/persistence/sql.d.ts.map +0 -1
  557. package/dist/persistence/sql.js +0 -37
  558. package/dist/persistence/sql.js.map +0 -1
  559. package/dist/protocol/blocks.d.ts +0 -10
  560. package/dist/protocol/blocks.d.ts.map +0 -1
  561. package/dist/protocol/blocks.js +0 -26
  562. package/dist/protocol/blocks.js.map +0 -1
  563. package/dist/protocol/json.d.ts +0 -6
  564. package/dist/protocol/json.d.ts.map +0 -1
  565. package/dist/protocol/json.js +0 -6
  566. package/dist/protocol/json.js.map +0 -1
  567. package/dist/protocol/ndjson.d.ts +0 -3
  568. package/dist/protocol/ndjson.d.ts.map +0 -1
  569. package/dist/protocol/ndjson.js +0 -20
  570. package/dist/protocol/ndjson.js.map +0 -1
  571. package/dist/protocol/sse.d.ts +0 -7
  572. package/dist/protocol/sse.d.ts.map +0 -1
  573. package/dist/protocol/sse.js +0 -25
  574. package/dist/protocol/sse.js.map +0 -1
  575. package/dist/proxy/pool.d.ts +0 -9
  576. package/dist/proxy/pool.d.ts.map +0 -1
  577. package/dist/proxy/pool.js +0 -27
  578. package/dist/proxy/pool.js.map +0 -1
  579. package/dist/queue/idempotency.d.ts +0 -10
  580. package/dist/queue/idempotency.d.ts.map +0 -1
  581. package/dist/queue/idempotency.js +0 -13
  582. package/dist/queue/idempotency.js.map +0 -1
  583. package/dist/queue/persistent.d.ts +0 -14
  584. package/dist/queue/persistent.d.ts.map +0 -1
  585. package/dist/queue/persistent.js +0 -66
  586. package/dist/queue/persistent.js.map +0 -1
  587. package/dist/queue/queue.d.ts +0 -7
  588. package/dist/queue/queue.d.ts.map +0 -1
  589. package/dist/queue/queue.js +0 -55
  590. package/dist/queue/queue.js.map +0 -1
  591. package/dist/queue/saga.d.ts +0 -5
  592. package/dist/queue/saga.d.ts.map +0 -1
  593. package/dist/queue/saga.js +0 -20
  594. package/dist/queue/saga.js.map +0 -1
  595. package/dist/release/assets.d.ts +0 -144
  596. package/dist/release/assets.d.ts.map +0 -1
  597. package/dist/release/assets.js +0 -157
  598. package/dist/release/assets.js.map +0 -1
  599. package/dist/release/evidence.d.ts +0 -92
  600. package/dist/release/evidence.d.ts.map +0 -1
  601. package/dist/release/evidence.js +0 -148
  602. package/dist/release/evidence.js.map +0 -1
  603. package/dist/release/verify.d.ts +0 -12
  604. package/dist/release/verify.d.ts.map +0 -1
  605. package/dist/release/verify.js +0 -112
  606. package/dist/release/verify.js.map +0 -1
  607. package/dist/runners/chain.d.ts +0 -125
  608. package/dist/runners/chain.d.ts.map +0 -1
  609. package/dist/runners/chain.js +0 -95
  610. package/dist/runners/chain.js.map +0 -1
  611. package/dist/runners/health.d.ts +0 -36
  612. package/dist/runners/health.d.ts.map +0 -1
  613. package/dist/runners/health.js +0 -26
  614. package/dist/runners/health.js.map +0 -1
  615. package/dist/runners/heartbeat.d.ts +0 -23
  616. package/dist/runners/heartbeat.d.ts.map +0 -1
  617. package/dist/runners/heartbeat.js +0 -29
  618. package/dist/runners/heartbeat.js.map +0 -1
  619. package/dist/runners/inprocess.d.ts +0 -17
  620. package/dist/runners/inprocess.d.ts.map +0 -1
  621. package/dist/runners/inprocess.js +0 -20
  622. package/dist/runners/inprocess.js.map +0 -1
  623. package/dist/runners/scheduler.d.ts +0 -5
  624. package/dist/runners/scheduler.d.ts.map +0 -1
  625. package/dist/runners/scheduler.js +0 -19
  626. package/dist/runners/scheduler.js.map +0 -1
  627. package/dist/runtime/abort.d.ts +0 -8
  628. package/dist/runtime/abort.d.ts.map +0 -1
  629. package/dist/runtime/abort.js +0 -13
  630. package/dist/runtime/abort.js.map +0 -1
  631. package/dist/runtime/compatibility.d.ts +0 -37
  632. package/dist/runtime/compatibility.d.ts.map +0 -1
  633. package/dist/runtime/compatibility.js +0 -10
  634. package/dist/runtime/compatibility.js.map +0 -1
  635. package/dist/runtime/detect.d.ts +0 -13
  636. package/dist/runtime/detect.d.ts.map +0 -1
  637. package/dist/runtime/detect.js +0 -18
  638. package/dist/runtime/detect.js.map +0 -1
  639. package/dist/runtime/engine.d.ts +0 -20
  640. package/dist/runtime/engine.d.ts.map +0 -1
  641. package/dist/runtime/engine.js +0 -61
  642. package/dist/runtime/engine.js.map +0 -1
  643. package/dist/runtime/retry.d.ts +0 -19
  644. package/dist/runtime/retry.d.ts.map +0 -1
  645. package/dist/runtime/retry.js +0 -54
  646. package/dist/runtime/retry.js.map +0 -1
  647. package/dist/runtime/worker.d.ts +0 -10
  648. package/dist/runtime/worker.d.ts.map +0 -1
  649. package/dist/runtime/worker.js +0 -25
  650. package/dist/runtime/worker.js.map +0 -1
  651. package/dist/scrape/cache.d.ts +0 -45
  652. package/dist/scrape/cache.d.ts.map +0 -1
  653. package/dist/scrape/cache.js +0 -45
  654. package/dist/scrape/cache.js.map +0 -1
  655. package/dist/scrape/crawl.d.ts +0 -47
  656. package/dist/scrape/crawl.d.ts.map +0 -1
  657. package/dist/scrape/crawl.js +0 -114
  658. package/dist/scrape/crawl.js.map +0 -1
  659. package/dist/scrape/extract.d.ts +0 -63
  660. package/dist/scrape/extract.d.ts.map +0 -1
  661. package/dist/scrape/extract.js +0 -47
  662. package/dist/scrape/extract.js.map +0 -1
  663. package/dist/scrape/normalize.d.ts +0 -52
  664. package/dist/scrape/normalize.d.ts.map +0 -1
  665. package/dist/scrape/normalize.js +0 -102
  666. package/dist/scrape/normalize.js.map +0 -1
  667. package/dist/scrape/robots.d.ts +0 -37
  668. package/dist/scrape/robots.d.ts.map +0 -1
  669. package/dist/scrape/robots.js +0 -71
  670. package/dist/scrape/robots.js.map +0 -1
  671. package/dist/scrape/schema.d.ts +0 -23
  672. package/dist/scrape/schema.d.ts.map +0 -1
  673. package/dist/scrape/schema.js +0 -92
  674. package/dist/scrape/schema.js.map +0 -1
  675. package/dist/scrape/scraper.d.ts +0 -6
  676. package/dist/scrape/scraper.d.ts.map +0 -1
  677. package/dist/scrape/scraper.js +0 -46
  678. package/dist/scrape/scraper.js.map +0 -1
  679. package/dist/scrape/semantic.d.ts +0 -25
  680. package/dist/scrape/semantic.d.ts.map +0 -1
  681. package/dist/scrape/semantic.js +0 -27
  682. package/dist/scrape/semantic.js.map +0 -1
  683. package/dist/server/node.d.ts +0 -7
  684. package/dist/server/node.d.ts.map +0 -1
  685. package/dist/server/node.js.map +0 -1
  686. package/dist/sessions/file.d.ts +0 -25
  687. package/dist/sessions/file.d.ts.map +0 -1
  688. package/dist/sessions/file.js +0 -13
  689. package/dist/sessions/file.js.map +0 -1
  690. package/dist/sessions/replay.d.ts +0 -9
  691. package/dist/sessions/replay.d.ts.map +0 -1
  692. package/dist/sessions/replay.js +0 -70
  693. package/dist/sessions/replay.js.map +0 -1
  694. package/dist/sessions/store.d.ts +0 -25
  695. package/dist/sessions/store.d.ts.map +0 -1
  696. package/dist/sessions/store.js +0 -13
  697. package/dist/sessions/store.js.map +0 -1
  698. package/dist/storage/adapter.d.ts +0 -5
  699. package/dist/storage/adapter.d.ts.map +0 -1
  700. package/dist/storage/adapter.js +0 -11
  701. package/dist/storage/adapter.js.map +0 -1
  702. package/dist/storage/cache.d.ts +0 -23
  703. package/dist/storage/cache.d.ts.map +0 -1
  704. package/dist/storage/cache.js +0 -79
  705. package/dist/storage/cache.js.map +0 -1
  706. package/dist/storage/checksum.d.ts +0 -3
  707. package/dist/storage/checksum.d.ts.map +0 -1
  708. package/dist/storage/checksum.js +0 -19
  709. package/dist/storage/checksum.js.map +0 -1
  710. package/dist/storage/chunked.d.ts +0 -21
  711. package/dist/storage/chunked.d.ts.map +0 -1
  712. package/dist/storage/chunked.js +0 -67
  713. package/dist/storage/chunked.js.map +0 -1
  714. package/dist/storage/content.d.ts +0 -24
  715. package/dist/storage/content.d.ts.map +0 -1
  716. package/dist/storage/content.js +0 -44
  717. package/dist/storage/content.js.map +0 -1
  718. package/dist/storage/filehosting.d.ts +0 -2
  719. package/dist/storage/filehosting.d.ts.map +0 -1
  720. package/dist/storage/filehosting.js +0 -24
  721. package/dist/storage/filehosting.js.map +0 -1
  722. package/dist/storage/githubcontents.d.ts +0 -2
  723. package/dist/storage/githubcontents.d.ts.map +0 -1
  724. package/dist/storage/githubcontents.js +0 -29
  725. package/dist/storage/githubcontents.js.map +0 -1
  726. package/dist/storage/index.d.ts +0 -12
  727. package/dist/storage/index.d.ts.map +0 -1
  728. package/dist/storage/index.js +0 -12
  729. package/dist/storage/index.js.map +0 -1
  730. package/dist/storage/local.d.ts +0 -2
  731. package/dist/storage/local.d.ts.map +0 -1
  732. package/dist/storage/local.js +0 -72
  733. package/dist/storage/local.js.map +0 -1
  734. package/dist/storage/memory.d.ts +0 -6
  735. package/dist/storage/memory.d.ts.map +0 -1
  736. package/dist/storage/memory.js +0 -36
  737. package/dist/storage/memory.js.map +0 -1
  738. package/dist/storage/pool.d.ts +0 -95
  739. package/dist/storage/pool.d.ts.map +0 -1
  740. package/dist/storage/pool.js +0 -202
  741. package/dist/storage/pool.js.map +0 -1
  742. package/dist/storage/s3compatible.d.ts +0 -3
  743. package/dist/storage/s3compatible.d.ts.map +0 -1
  744. package/dist/storage/s3compatible.js +0 -70
  745. package/dist/storage/s3compatible.js.map +0 -1
  746. package/dist/storage/sync.d.ts +0 -47
  747. package/dist/storage/sync.d.ts.map +0 -1
  748. package/dist/storage/sync.js +0 -75
  749. package/dist/storage/sync.js.map +0 -1
  750. package/dist/surfaces/adapters.d.ts +0 -97
  751. package/dist/surfaces/adapters.d.ts.map +0 -1
  752. package/dist/surfaces/adapters.js +0 -50
  753. package/dist/surfaces/adapters.js.map +0 -1
  754. package/dist/surfaces/controls.d.ts +0 -18
  755. package/dist/surfaces/controls.d.ts.map +0 -1
  756. package/dist/surfaces/controls.js +0 -45
  757. package/dist/surfaces/controls.js.map +0 -1
  758. package/dist/surfaces/manifest.d.ts +0 -68
  759. package/dist/surfaces/manifest.d.ts.map +0 -1
  760. package/dist/surfaces/manifest.js +0 -42
  761. package/dist/surfaces/manifest.js.map +0 -1
  762. package/dist/surfaces/n8n.d.ts +0 -25
  763. package/dist/surfaces/n8n.d.ts.map +0 -1
  764. package/dist/surfaces/n8n.js +0 -31
  765. package/dist/surfaces/n8n.js.map +0 -1
  766. package/dist/surfaces/operations.d.ts +0 -32
  767. package/dist/surfaces/operations.d.ts.map +0 -1
  768. package/dist/surfaces/operations.js +0 -53
  769. package/dist/surfaces/operations.js.map +0 -1
  770. package/dist/surfaces/requirements.d.ts +0 -29
  771. package/dist/surfaces/requirements.d.ts.map +0 -1
  772. package/dist/surfaces/requirements.js +0 -50
  773. package/dist/surfaces/requirements.js.map +0 -1
  774. package/dist/surfaces/targets.d.ts +0 -196
  775. package/dist/surfaces/targets.d.ts.map +0 -1
  776. package/dist/surfaces/targets.js +0 -27
  777. package/dist/surfaces/targets.js.map +0 -1
  778. package/dist/webhook/delivery.d.ts +0 -18
  779. package/dist/webhook/delivery.d.ts.map +0 -1
  780. package/dist/webhook/delivery.js +0 -36
  781. package/dist/webhook/delivery.js.map +0 -1
  782. package/dist/webhook/receiver.d.ts +0 -26
  783. package/dist/webhook/receiver.d.ts.map +0 -1
  784. package/dist/webhook/receiver.js +0 -24
  785. package/dist/webhook/receiver.js.map +0 -1
  786. package/dist/webhook/signature.d.ts +0 -3
  787. package/dist/webhook/signature.d.ts.map +0 -1
  788. package/dist/webhook/signature.js +0 -7
  789. package/dist/webhook/signature.js.map +0 -1
  790. package/dist/workflow/manifest.d.ts +0 -22
  791. package/dist/workflow/manifest.d.ts.map +0 -1
  792. package/dist/workflow/manifest.js +0 -21
  793. package/dist/workflow/manifest.js.map +0 -1
  794. package/dist/workflow/registry.d.ts +0 -7
  795. package/dist/workflow/registry.d.ts.map +0 -1
  796. package/dist/workflow/registry.js +0 -18
  797. package/dist/workflow/registry.js.map +0 -1
  798. package/dist/workflow/templates.d.ts +0 -6
  799. package/dist/workflow/templates.d.ts.map +0 -1
  800. package/dist/workflow/templates.js +0 -18
  801. package/dist/workflow/templates.js.map +0 -1
  802. package/dist/workflow/triggers.d.ts +0 -64
  803. package/dist/workflow/triggers.d.ts.map +0 -1
  804. package/dist/workflow/triggers.js +0 -99
  805. package/dist/workflow/triggers.js.map +0 -1
  806. package/docs/logs/.gitkeep +0 -0
  807. package/docs/plans/00.index.md +0 -50
  808. package/docs/plans/01.architecture.md +0 -86
  809. package/docs/plans/02.research.computer.use.md +0 -58
  810. package/docs/plans/03.research.captcha.bypass.md +0 -68
  811. package/docs/plans/04.research.sandbox.ai.md +0 -52
  812. package/docs/plans/05.capture.platform.md +0 -57
  813. package/docs/plans/06.dependencies.md +0 -97
  814. package/docs/plans/07.captcha.test.page.md +0 -41
  815. package/docs/plans/08.production.infra.md +0 -70
  816. package/docs/plans/09.database.schema.md +0 -121
  817. package/docs/plans/10.cloudinary.storage.md +0 -57
  818. package/docs/plans/11.movement.logs.json.md +0 -72
  819. package/docs/plans/12.research.atlas.agent.browser.md +0 -79
  820. package/docs/plans/13.research.anti.detection.md +0 -898
  821. package/docs/plans/14.research.proxy.md +0 -1495
  822. package/docs/plans/15.research.retry.rate.limit.md +0 -1958
  823. package/docs/plans/16.research.crawling.md +0 -1417
  824. package/docs/plans/17.research.caching.md +0 -1610
  825. package/docs/plans/18.research.content.extraction.md +0 -1952
  826. package/docs/plans/19.research.errors.events.md +0 -1523
  827. package/docs/plans/20.research.zod.validation.md +0 -1350
  828. package/docs/plans/21.research.batch.concurrency.md +0 -1888
  829. package/docs/plans/22.research.universal.runtime.md +0 -944
  830. package/docs/plans/23.research.ai.integration.md +0 -1465
  831. package/docs/plans/24.research.memory.persistence.md +0 -1979
  832. package/docs/plans/25.research.server.api.md +0 -342
  833. package/docs/plans/26.research.compilation.md +0 -249
  834. package/docs/plans/27.research.html.parsing.md +0 -251
  835. package/docs/plans/28.action.plan.md +0 -50
  836. package/docs/plans/29.api.reference.md +0 -174
  837. package/docs/plans/30.architecture.plan.md +0 -94
  838. package/docs/plans/31.auditoria.dados.md +0 -163
  839. package/docs/plans/32.bots.automacao.computacional.md +0 -214
  840. package/docs/plans/33.bots.codigo.revisao.md +0 -220
  841. package/docs/plans/34.bots.seguranca.cicd.md +0 -366
  842. package/docs/plans/35.comparativo.concorrencia.md +0 -464
  843. package/docs/plans/36.computational.memory.md +0 -340
  844. package/docs/plans/37.deploystrategy.md +0 -394
  845. package/docs/plans/38.flow.md +0 -155
  846. package/docs/plans/39.multi.platform.bot.md +0 -252
  847. package/docs/plans/40.npm.publish.md +0 -250
  848. package/docs/plans/41.o.que.falta.md +0 -407
  849. package/docs/plans/42.pesquisa.concorrencia.md +0 -721
  850. package/docs/plans/43.plan.universal.architecture.md +0 -496
  851. package/docs/plans/44.reference.md +0 -100
  852. package/docs/plans/45.robotarchitecture.md +0 -237
  853. package/docs/plans/46.scdnintegration.md +0 -284
  854. package/docs/plans/47.multiforge.readme.md +0 -129
  855. package/docs/plans/48.theory.v4.repo.os.md +0 -152
  856. package/docs/plans/49.third.party.infra.md +0 -12
  857. package/docs/plans/50.file.as.compute.md +0 -39
  858. package/docs/plans/51.architecture.virtual.processor.md +0 -80
  859. package/docs/plans/52.manifesto.v8.md +0 -11
  860. package/docs/plans/58.cdn.list.md +0 -23
  861. package/docs/plans/59.sql.frameworks.md +0 -33
  862. package/docs/plans/60.sql.thirdparty.md +0 -26
  863. package/docs/plans/61.objective.multiforge.md +0 -63
  864. package/docs/plans/62.huggingface.upload.md +0 -26
  865. package/docs/plans/63.kaggle.upload.md +0 -24
  866. package/docs/plans/64.npm.storage.md +0 -30
  867. package/docs/plans/65.rclone.terabox.md +0 -32
  868. package/docs/plans/66.buckets.and.models.todo.md +0 -14
  869. package/docs/plans/67.database.todo.md +0 -13
  870. package/docs/plans/68.deploy.packages.todo.md +0 -12
  871. package/docs/plans/69.report.human.operator.md +0 -133
  872. package/docs/plans/70.report.brain2qwerty.ems.md +0 -135
  873. package/docs/plans/71.report.hd.infinito.vram.md +0 -155
  874. package/docs/plans/72.plan.hd.infinito.node.md +0 -146
  875. package/docs/plans/73.plan.scifi.repos.md +0 -125
  876. package/docs/plans/74.000.manifesto.v8.flat.2..md +0 -11
  877. package/docs/plans/README.md +0 -489
  878. package/docs/plans/aggregate_platforms.mjs +0 -146
  879. package/docs/plans/examplesession.json +0 -36
  880. package/docs/plans/missing-facts.md +0 -192
  881. package/docs/plans/models.md +0 -64
  882. package/docs/plans/organize.cjs +0 -270
  883. package/docs/plans/platforms.md +0 -2887
  884. package/docs/plans/sites.md +0 -31322
  885. package/docs/sources/farm.py +0 -117
  886. package/docs/sources/html/saddle1.html +0 -132
  887. package/docs/sources/html/saddle2.html +0 -157
  888. package/docs/sources/html/saddle3.html +0 -119
  889. package/docs/sources/html/saddle4.html +0 -144
  890. package/docs/sources/html/saddle5.html +0 -72
  891. package/docs/sources/html/saddle6.html +0 -171
  892. package/docs/sources/html/saddle7.html +0 -236
  893. package/docs/sources/saddle.ts +0 -74
  894. package/docs/sources/schema.prisma +0 -88
  895. package/docs/sources/script.sh +0 -64
  896. package/docs/sources/workflows.yml +0 -458
  897. package/docs/talks1/_body.txt +0 -14
  898. package/docs/talks1/_index.md +0 -15
  899. package/docs/talks1/_screenshot.png +0 -0
  900. package/docs/talks1/assistant-01.md +0 -5
  901. package/docs/talks1/assistant-02.md +0 -5
  902. package/docs/talks1/assistant-03.md +0 -531
  903. package/docs/talks1/assistant-04.md +0 -26
  904. package/docs/talks1/assistant-05.md +0 -774
  905. package/docs/talks1/assistant-06.md +0 -1718
  906. package/docs/talks1/scrape-share.cjs +0 -185
  907. package/docs/talks1/scrape-share.ts +0 -183
  908. package/docs/talks1/user-01.md +0 -3
  909. package/docs/talks1/user-02.md +0 -3
  910. package/docs/talks1/user-03.md +0 -88
  911. package/docs/talks1/user-04.md +0 -3
  912. package/docs/talks1/user-05.md +0 -3
  913. package/docs/talks1/user-06.md +0 -88
  914. package/docs/talks1/user-07.md +0 -88
  915. package/docs/talks2/_body.txt +0 -14
  916. package/docs/talks2/_index.md +0 -16
  917. package/docs/talks2/_screenshot.png +0 -0
  918. package/docs/talks2/assistant-01.md +0 -5
  919. package/docs/talks2/assistant-02.md +0 -5
  920. package/docs/talks2/assistant-03.md +0 -424
  921. package/docs/talks2/assistant-04.md +0 -598
  922. package/docs/talks2/assistant-05.md +0 -1280
  923. package/docs/talks2/assistant-06.md +0 -1227
  924. package/docs/talks2/assistant-07.md +0 -1252
  925. package/docs/talks2/user-01.md +0 -3
  926. package/docs/talks2/user-02.md +0 -3
  927. package/docs/talks2/user-03.md +0 -88
  928. package/docs/talks2/user-04.md +0 -88
  929. package/docs/talks2/user-05.md +0 -88
  930. package/docs/talks2/user-06.md +0 -88
  931. package/docs/talks2/user-07.md +0 -3
  932. package/docs/talks3/_body.txt +0 -467
  933. package/docs/talks3/_index.md +0 -10
  934. package/docs/talks3/_screenshot.png +0 -0
  935. package/docs/talks3/assistant-01.md +0 -417
  936. package/docs/talks3/assistant-02.md +0 -417
  937. package/docs/talks3/assistant-03.md +0 -29
  938. package/docs/talks3/assistant-04.md +0 -727
  939. package/docs/talks3/user-01.md +0 -88
  940. package/docs/talks3/user-02.md +0 -88
  941. package/docs/talks3/user-03.md +0 -3
  942. package/docs/talks3/user-04.md +0 -3
  943. package/docs/talks4/_body.txt +0 -14
  944. package/docs/talks4/_index.md +0 -12
  945. package/docs/talks4/_screenshot.png +0 -0
  946. package/docs/talks4/assistant-01.md +0 -5
  947. package/docs/talks4/assistant-02.md +0 -5
  948. package/docs/talks4/assistant-03.md +0 -35
  949. package/docs/talks4/assistant-04.md +0 -512
  950. package/docs/talks4/assistant-05.md +0 -599
  951. package/docs/talks4/user-01.md +0 -3
  952. package/docs/talks4/user-02.md +0 -3
  953. package/docs/talks4/user-03.md +0 -88
  954. package/docs/talks4/user-04.md +0 -88
  955. package/docs/talks4/user-05.md +0 -7
  956. package/docs/talks5/_body.txt +0 -14
  957. package/docs/talks5/_index.md +0 -13
  958. package/docs/talks5/_screenshot.png +0 -0
  959. package/docs/talks5/assistant-01.md +0 -5
  960. package/docs/talks5/assistant-02.md +0 -5
  961. package/docs/talks5/assistant-03.md +0 -690
  962. package/docs/talks5/assistant-04.md +0 -758
  963. package/docs/talks5/assistant-05.md +0 -974
  964. package/docs/talks5/user-01.md +0 -3
  965. package/docs/talks5/user-02.md +0 -3
  966. package/docs/talks5/user-03.md +0 -105
  967. package/docs/talks5/user-04.md +0 -105
  968. package/docs/talks5/user-05.md +0 -63
  969. package/docs/talks5/user-06.md +0 -105
  970. package/docs/talks6/_body.txt +0 -14
  971. package/docs/talks6/_index.md +0 -9
  972. package/docs/talks6/_screenshot.png +0 -0
  973. package/docs/talks6/assistant-01.md +0 -5
  974. package/docs/talks6/assistant-02.md +0 -5
  975. package/docs/talks6/assistant-03.md +0 -1499
  976. package/docs/talks6/user-01.md +0 -3
  977. package/docs/talks6/user-02.md +0 -3
  978. package/docs/talks6/user-03.md +0 -88
  979. package/docs/talks6/user-04.md +0 -88
  980. package/docs/talks7/_body.txt +0 -14
  981. package/docs/talks7/_index.md +0 -10
  982. package/docs/talks7/_screenshot.png +0 -0
  983. package/docs/talks7/assistant-01.md +0 -5
  984. package/docs/talks7/assistant-02.md +0 -5
  985. package/docs/talks7/assistant-03.md +0 -523
  986. package/docs/talks7/assistant-04.md +0 -617
  987. package/docs/talks7/user-01.md +0 -3
  988. package/docs/talks7/user-02.md +0 -3
  989. package/docs/talks7/user-03.md +0 -105
  990. package/docs/talks7/user-04.md +0 -67
  991. package/docs/talks8/conversa1.txt +0 -1322
  992. package/docs/talks8/conversa2.txt +0 -237
  993. package/docs/talks9/Beyond the Obvious_ 50 Plataformas Auto-Hospedadas de Forja de C/303/263digo para Al/303/251m de Gitea e GitLab.md" +0 -174
  994. package/docs/talks9/De NPM a Multi-Linguagem_ Uma Arquitetura T/303/251cnica para a Execu/303/247/303/243o Integrada de C/303/263digo no Ecossistema Node.js.md" +0 -59
  995. package/docs/talks9/De NPM a VMs Virtuais_ Uma An/303/241lise Arquitet/303/264nica para a Realiza/303/247/303/243o do Ciclo de Vida do Projeto SADDLE.md" +0 -91
  996. package/docs/talks9/Mapeamento da Engrenagem Computacional_ Uma Arquitetura para Execu/303/247/303/243o Isolada e Persist/303/252ncia em Ambientes Distribu/303/255dos.md" +0 -116
  997. package/docs/talks9/O Cen/303/241rio Pr/303/241tico do SADDLE_ Uma An/303/241lise de Viabilidade e Modelo de Ciclo de Vida Integrado.md" +0 -128
  998. package/docs/talks9/README (2).md +0 -489
  999. package/docs/talks9/README.md +0 -198
  1000. package/docs/talks9/Viabilidade do Saddle_ Uma An/303/241lise T/303/251cnica da Transforma/303/247/303/243o de Armazenamento Remoto em Mem/303/263ria Computacional.md" +0 -80
  1001. package/docs/talks9/conversa.txt +0 -544
  1002. package/docs/talks9/other (2).md +0 -39
  1003. package/docs/talks9/other.md +0 -57
  1004. package/docs/talks9/outro.txt +0 -24
  1005. /package/{extension/README.md → docs/extension.md} +0 -0
package/compute.ts ADDED
@@ -0,0 +1,3144 @@
1
+ /**
2
+ * compute.ts — compute, WebAssembly, WebGPU and AI workload domain for saddle v6.
3
+ *
4
+ * the module absorbs the compute-facing families of the removed
5
+ * future.ts file (55-feature ledger, v2) into their proper domain:
6
+ * - wasm/wasmtime (8): WIT bindings generator, WASI 0.3.0 async runtime
7
+ * with cancellation, fuel metering, epoch interruption, wasi:nn
8
+ * backend with real softmax, deny-by-default component ACL,
9
+ * component registry, linear memory pool
10
+ * - webgpu (4): forced fallback adapter over lavapipe, compute pipeline
11
+ * builder with the 256-invocation limit, WGSL shader cache, matmul
12
+ * dispatch planner
13
+ * - node 26 (7): Temporal-aware scheduler, Float16 tensor storage with
14
+ * own binary16 conversion, Error.isError guard, node:sqlite sync
15
+ * store with memory fallback, disposable module.register hook chain,
16
+ * Perfetto trace sink, private key store with loader indirection
17
+ * - ai workloads (6): DataLoader /dev/shm planner, TF CUDA
18
+ * forward-compatibility matrix, ONNX WebGPU EP session builder, LLM
19
+ * inference on lavapipe, headless Stable Diffusion, VRAM budget
20
+ *
21
+ * omnihypercore verified additions (research pass 2026-08-23):
22
+ * - wasm canary validator: module validation with smoke execution and
23
+ * automatic rollback to the last healthy revision
24
+ * - WASI-GFX: the wasi:gfx proposal bringing wgpu 30.x WebGPU into wasm
25
+ * components ("wgpu virt wgpu 30"), planned as capability grants
26
+ * - ChatOps dashboard: chat command builders (/vm create, /gpu attach,
27
+ * /migrate) emitting JSON command envelopes plus a WebGPU dashboard
28
+ * spec
29
+ * - GPU telemetry via eBPF: bpf program builder for GPU UAPI counters
30
+ * (DRM_IOCTL tracepoints feeding per-engine utilization maps)
31
+ * - AI live migration pre-copy predictor: dirty page estimation with
32
+ * simple online learning, the AI-flavoured companion of the
33
+ * checkpoint family that now lives in virtualization.ts
34
+ *
35
+ * v4-FIX3 additions (portal 0-reference absorption, 2026-08-23):
36
+ * - PartyKit edge presence channel builder (rooms, wss endpoints,
37
+ * join/leave/update presence, 100 ms heartbeat)
38
+ * - L3AF 2.1 xBPF marketplace app manager emitting l3afd configs
39
+ * - GreenHyper RAPL power-aware binpacking governor over
40
+ * /sys/class/powercap intel-rapl constraint files
41
+ * - StateVector 2-round sync: lamport state vectors with
42
+ * last-writer-wins merge (CRDT convergence without a coordinator)
43
+ * - libp2p + BitTorrent v2 content addressing: sha2-256 multihashes,
44
+ * base32 CIDv1 raw, 16 KiB merkle roots, magnet links, multiaddrs
45
+ * - Tailscale tailnet endpoint planner with the CGNAT 100.64.0.0/10
46
+ * guard, MagicDNS names, DERP fallback and six mesh milestones
47
+ * - MAX9 catalogued as a legacy build/delivery variant (zip level 9,
48
+ * 51% ratio, :max9 tag) - NOT an NVIDIA Maxwell GPU
49
+ * - roadmapbacklog: the thirteen v4 future.ts items that were still
50
+ * unabsorbed (AMX, dual-X3D, WebTransport, Turborepo GHCR, pnpm v9,
51
+ * QUIC/H3, WebRTC mesh, OPFS, WebCodecs+WebGPU, SAB cross-origin,
52
+ * WASM JSPI, GHCR gha reuse, Nix flakes) plus backlogCount()
53
+ *
54
+ * version anchors (worklog v3-VERIFY, 2026-08-23): wasmtime 48.0.0 LTS
55
+ * (confirmed, supported until 2028-08), spin 3.6.0 (spinframework),
56
+ * wasmcloud tracked as latest 2.x without a pin (release page did not
57
+ * expose the version), wgpu 30.x (corrected from 24.x), kernel stable
58
+ * 7.1.9 / longterm 6.18.45. ports never hardcoded; loopback never used.
59
+ *
60
+ * contexts (25+9): witbindings, wasi03async, fuel, epoch, wasinn,
61
+ * componentacl, componentregistry, wasmmempool, lavapipe, pipelines,
62
+ * shadercache, temporal, float16, errorguard, sqlitestore, modulehooks,
63
+ * perfetto, keystore, aiplanners, inference, canary, wasigfx, chatops,
64
+ * ebpfgpu, aimigration, portalsync, l3afmanager, greenhyper,
65
+ * statevector, p2pcontent, tailscaleplan, max9variant, roadmapbacklog
66
+ * (the featureledger appendix closes the file)
67
+ *
68
+ * rules: lowercase identifiers, english jsdoc third person, no emoji,
69
+ * try/catch catcher on every fallible path, node:* built-ins first,
70
+ * zero runtime dependencies, `using`/`satisfies`/`#private`/const type
71
+ * parameters throughout.
72
+ */
73
+
74
+ import { createHash, createHmac, randomUUID } from 'node:crypto';
75
+
76
+ /** stable short sha256 digest for shader cache keys and canary hashes. */
77
+ function digest(input: string | Uint8Array): string {
78
+ try {
79
+ return createHash('sha256').update(input).digest('hex').slice(0, 16);
80
+ } catch {
81
+ return input.toString().length.toString(16);
82
+ }
83
+ }
84
+
85
+ /* ------------------------------------------------------------------ */
86
+ /* context: witbindings (WIT world to typescript generator) */
87
+ /* ------------------------------------------------------------------ */
88
+
89
+ /** WIT primitive and compound type names. */
90
+ export type wittype =
91
+ | 'u8'
92
+ | 'u16'
93
+ | 'u32'
94
+ | 'u64'
95
+ | 's8'
96
+ | 's16'
97
+ | 's32'
98
+ | 's64'
99
+ | 'f32'
100
+ | 'f64'
101
+ | 'bool'
102
+ | 'string'
103
+ | 'timestamp'
104
+ | `list<${string}>`
105
+ | `option<${string}>`
106
+ | `result<${string}, ${string}>`;
107
+
108
+ /** one WIT function parameter. */
109
+ export interface witparam {
110
+ readonly name: string;
111
+ readonly type: wittype;
112
+ }
113
+
114
+ /** one WIT function. */
115
+ export interface witfunction {
116
+ readonly name: string;
117
+ readonly params: readonly witparam[];
118
+ readonly result: wittype | null;
119
+ }
120
+
121
+ /** one WIT interface (a named function group). */
122
+ export interface witinterface {
123
+ readonly name: string;
124
+ readonly functions: readonly witfunction[];
125
+ }
126
+
127
+ /** one WIT world (the component contract). */
128
+ export interface witworld {
129
+ readonly name: string;
130
+ readonly interfaces: readonly witinterface[];
131
+ }
132
+
133
+ /** maps a WIT type to its typescript carrier. */
134
+ function wittypetots(type: wittype): string {
135
+ if (type === 'bool') return 'boolean';
136
+ if (type === 'string' || type === 'timestamp') return 'string';
137
+ if (type.startsWith('list<')) return `${wittypetots(type.slice(5, -1) as wittype)}[]`;
138
+ if (type.startsWith('option<')) return `${wittypetots(type.slice(8, -1) as wittype)} | undefined`;
139
+ if (type.startsWith('result<')) {
140
+ const [ok, err] = type.slice(7, -1).split(', ') as [wittype, wittype];
141
+ return `{ ok: ${wittypetots(ok)} } | { err: ${wittypetots(err)} }`;
142
+ }
143
+ return 'number';
144
+ }
145
+
146
+ /**
147
+ * converts a WIT world definition into a typed typescript interface
148
+ * block, so components loaded by the engine keep static typing end to
149
+ * end; guest calls return promises per the WASI 0.3 async convention.
150
+ */
151
+ export function wittotypescript(world: witworld): string {
152
+ const lines: string[] = [
153
+ `/** generated from WIT world "${world.name}" */`,
154
+ `export interface ${world.name}World {`,
155
+ ];
156
+ for (const iface of world.interfaces) {
157
+ lines.push(` ${iface.name}: {`);
158
+ for (const fn of iface.functions) {
159
+ const params = fn.params.map((p) => `${p.name}: ${wittypetots(p.type)}`).join('; ');
160
+ const ret = fn.result === null ? 'void' : `Promise<${wittypetots(fn.result)}>`;
161
+ lines.push(` ${fn.name}(${params}): ${ret};`);
162
+ }
163
+ lines.push(' };');
164
+ }
165
+ lines.push('}');
166
+ return lines.join('\n');
167
+ }
168
+
169
+ /** content hash of a WIT world, used to invalidate generated bindings. */
170
+ export function witworldhash(world: witworld): string {
171
+ return digest(JSON.stringify(world));
172
+ }
173
+
174
+ /* ------------------------------------------------------------------ */
175
+ /* context: wasi03async (native async calls with cancellation) */
176
+ /* ------------------------------------------------------------------ */
177
+
178
+ /** one queued WASI 0.3 component call. */
179
+ export interface wasicall {
180
+ readonly component: string;
181
+ readonly functionName: string;
182
+ readonly payload: Uint8Array;
183
+ readonly deadlineMs: number;
184
+ }
185
+
186
+ /**
187
+ * models the native async calls of WASI 0.3.0: every guest call is
188
+ * queued, runs under an epoch deadline, and can be cancelled with the
189
+ * cancelled-by-host semantics of the component model.
190
+ */
191
+ export class wasiasyncruntime implements Disposable {
192
+ #queue: wasicall[] = [];
193
+ #cancelled = new Set<string>();
194
+
195
+ invoke(call: wasicall): { id: string; promise: Promise<Uint8Array> } {
196
+ const id = randomUUID();
197
+ return { id, promise: this.#run(call, id) };
198
+ }
199
+
200
+ async #run(call: wasicall, id: string): Promise<Uint8Array> {
201
+ this.#queue.push(call);
202
+ const startedAt = Date.now();
203
+ await new Promise<void>((resolve) => setImmediate(resolve));
204
+ if (this.#cancelled.has(id)) {
205
+ this.#cancelled.delete(id);
206
+ throw new Error(`wasi call ${call.component}.${call.functionName} was cancelled by host`);
207
+ }
208
+ if (Date.now() - startedAt > call.deadlineMs) {
209
+ throw new Error(`wasi call ${call.functionName} exceeded deadline of ${call.deadlineMs}ms`);
210
+ }
211
+ this.#queue = this.#queue.filter((c) => c !== call);
212
+ return call.payload;
213
+ }
214
+
215
+ cancel(callId: string): boolean {
216
+ this.#cancelled.add(callId);
217
+ return true;
218
+ }
219
+
220
+ get queued(): number {
221
+ return this.#queue.length;
222
+ }
223
+
224
+ [Symbol.dispose](): void {
225
+ this.#cancelled.clear();
226
+ this.#queue = [];
227
+ }
228
+ }
229
+
230
+ /* ------------------------------------------------------------------ */
231
+ /* context: fuel (deterministic instruction budget) */
232
+ /* ------------------------------------------------------------------ */
233
+
234
+ /**
235
+ * wraps deterministic instruction budgets exactly like `wasmtime --fuel`:
236
+ * fuel is consumed per instruction batch, refunded on early exit, and
237
+ * exhausts with a trap.
238
+ */
239
+ export class fuelmeter {
240
+ #consumed = 0;
241
+ readonly budget: number;
242
+
243
+ constructor(budget: number) {
244
+ this.budget = budget;
245
+ }
246
+
247
+ consume(instructions: number): boolean {
248
+ if (this.#consumed + instructions > this.budget) return false;
249
+ this.#consumed += instructions;
250
+ return true;
251
+ }
252
+
253
+ refund(instructions: number): void {
254
+ this.#consumed = Math.max(0, this.#consumed - instructions);
255
+ }
256
+
257
+ get consumed(): number {
258
+ return this.#consumed;
259
+ }
260
+
261
+ get remaining(): number {
262
+ return this.budget - this.#consumed;
263
+ }
264
+
265
+ get exhausted(): boolean {
266
+ return this.remaining <= 0;
267
+ }
268
+ }
269
+
270
+ /** runs a callback under a fuel budget; throws a trap when it is hit. */
271
+ export function withfuel<T>(budget: number, fn: (meter: fuelmeter) => T): T {
272
+ const meter = new fuelmeter(budget);
273
+ const result = fn(meter);
274
+ if (meter.exhausted) {
275
+ throw new Error(`fuel budget of ${budget} instructions exhausted`);
276
+ }
277
+ return result;
278
+ }
279
+
280
+ /* ------------------------------------------------------------------ */
281
+ /* context: epoch (wall-clock deadline interruption) */
282
+ /* ------------------------------------------------------------------ */
283
+
284
+ /**
285
+ * epoch interruption, preferred over fuel for wall-clock timeouts
286
+ * because it costs roughly 10 percent overhead instead of
287
+ * per-instruction accounting; the deadline is stored in epoch ticks
288
+ * and checked at epoch callback points.
289
+ */
290
+ export class epochinterruption {
291
+ #deadlineTick = Number.POSITIVE_INFINITY;
292
+ #currentTick = 0;
293
+ readonly #tickMs: number;
294
+
295
+ constructor(tickMs = 10) {
296
+ this.#tickMs = tickMs;
297
+ }
298
+
299
+ setdeadline(timeoutMs: number): void {
300
+ this.#deadlineTick = this.#currentTick + Math.ceil(timeoutMs / this.#tickMs);
301
+ }
302
+
303
+ tick(): void {
304
+ this.#currentTick += 1;
305
+ }
306
+
307
+ get expired(): boolean {
308
+ return this.#currentTick >= this.#deadlineTick;
309
+ }
310
+
311
+ /** measured overhead of epoch interruption relative to fuel metering. */
312
+ static get relativeoverhead(): number {
313
+ return 0.1;
314
+ }
315
+ }
316
+
317
+ /* ------------------------------------------------------------------ */
318
+ /* context: wasinn (wasi:nn graphs with real softmax) */
319
+ /* ------------------------------------------------------------------ */
320
+
321
+ /** one registered inference graph. */
322
+ export interface nngraph {
323
+ readonly name: string;
324
+ readonly tensorType: 'f16' | 'f32' | 'u8';
325
+ readonly dims: readonly number[];
326
+ }
327
+
328
+ /**
329
+ * a wasi:nn backend: graphs declare tensor shapes and execution runs a
330
+ * real numerically stable softmax over the input tensor, enough to
331
+ * smoke-test serving pipelines before a real backend (onnx runtime,
332
+ * burns or candle) attaches.
333
+ */
334
+ export class wasinnbackend {
335
+ #graphs = new Map<string, nngraph>();
336
+
337
+ load(name: string, dims: readonly number[], tensorType: nngraph['tensorType'] = 'f32'): nngraph {
338
+ const graph: nngraph = { name, dims, tensorType };
339
+ this.#graphs.set(name, graph);
340
+ return graph;
341
+ }
342
+
343
+ execute(graphName: string, tensor: Float32Array): Float32Array {
344
+ const graph = this.#graphs.get(graphName);
345
+ if (graph === undefined) {
346
+ throw new Error(`wasi:nn graph "${graphName}" is not loaded`);
347
+ }
348
+ const expected = graph.dims.reduce((a, b) => a * b, 1);
349
+ if (tensor.length !== expected) {
350
+ throw new Error(
351
+ `tensor length ${tensor.length} does not match graph shape ${graph.dims.join('x')}`,
352
+ );
353
+ }
354
+ const max = Math.max(...tensor);
355
+ const exps = tensor.map((v) => Math.exp(v - max));
356
+ const sum = exps.reduce((a, b) => a + b, 0);
357
+ return new Float32Array(exps.map((v) => v / sum));
358
+ }
359
+
360
+ list(): readonly string[] {
361
+ return [...this.#graphs.keys()];
362
+ }
363
+ }
364
+
365
+ /* ------------------------------------------------------------------ */
366
+ /* context: componentacl (deny-by-default capabilities) */
367
+ /* ------------------------------------------------------------------ */
368
+
369
+ /** WASI capability names a component may be granted. */
370
+ export type wascapability =
371
+ | 'wasi:io'
372
+ | 'wasi:clocks'
373
+ | 'wasi:random'
374
+ | 'wasi:nn'
375
+ | 'wasi:sockets'
376
+ | 'wasi:filesystem'
377
+ | 'wasi:http'
378
+ | 'wasi:gfx';
379
+
380
+ /**
381
+ * components start with zero ambient authority: every capability must
382
+ * be granted explicitly before a component may use it, the mirror of
383
+ * the wasmtime deny-by-default model.
384
+ */
385
+ export class componentacl {
386
+ #grants = new Map<string, Set<wascapability>>();
387
+
388
+ grant(component: string, capability: wascapability): this {
389
+ const set = this.#grants.get(component) ?? new Set<wascapability>();
390
+ set.add(capability);
391
+ this.#grants.set(component, set);
392
+ return this;
393
+ }
394
+
395
+ assert(component: string, capability: wascapability): void {
396
+ const set = this.#grants.get(component);
397
+ if (set === undefined || !set.has(capability)) {
398
+ throw new Error(`component "${component}" was not granted "${capability}" (deny-by-default)`);
399
+ }
400
+ }
401
+
402
+ grantsfor(component: string): readonly wascapability[] {
403
+ return [...(this.#grants.get(component) ?? [])];
404
+ }
405
+ }
406
+
407
+ /* ------------------------------------------------------------------ */
408
+ /* context: componentregistry (instances with fuel and epoch) */
409
+ /* ------------------------------------------------------------------ */
410
+
411
+ /** one instantiated component with its deterministic guards. */
412
+ export interface componentinstance {
413
+ readonly id: string;
414
+ readonly name: string;
415
+ readonly fuel: fuelmeter;
416
+ readonly epoch: epochinterruption;
417
+ }
418
+
419
+ /**
420
+ * keeps named component instances together with their fuel meter,
421
+ * epoch interrupter and revision tag, and disposes them
422
+ * deterministically via `using`.
423
+ */
424
+ export class componentregistry implements Disposable {
425
+ #components = new Map<string, componentinstance>();
426
+
427
+ instantiate(name: string, fuelBudget = 1_000_000): componentinstance {
428
+ const existing = this.#components.get(name);
429
+ if (existing !== undefined) return existing;
430
+ const instance: componentinstance = {
431
+ id: randomUUID(),
432
+ name,
433
+ fuel: new fuelmeter(fuelBudget),
434
+ epoch: new epochinterruption(),
435
+ };
436
+ this.#components.set(name, instance);
437
+ return instance;
438
+ }
439
+
440
+ get(name: string): componentinstance | undefined {
441
+ return this.#components.get(name);
442
+ }
443
+
444
+ [Symbol.dispose](): void {
445
+ this.#components.clear();
446
+ }
447
+ }
448
+
449
+ /* ------------------------------------------------------------------ */
450
+ /* context: wasmmempool (64 KiB-paged linear memory recycling) */
451
+ /* ------------------------------------------------------------------ */
452
+
453
+ /** a pooled memory ticket returned via `using`. */
454
+ export interface poolticket extends Disposable {
455
+ readonly bytes: ArrayBuffer;
456
+ }
457
+
458
+ /**
459
+ * recycles 64 KiB-paged ArrayBuffers across component activations so
460
+ * short-lived guests stop pressuring the GC; tickets return to the
461
+ * pool through `using` declarations.
462
+ */
463
+ export class wasmmempool {
464
+ #pool: { bytes: ArrayBuffer; inUse: boolean; pages: number }[] = [];
465
+
466
+ acquire(minPages: number): poolticket {
467
+ const wantedBytes = Math.max(1, minPages) * 65_536;
468
+ let slot = this.#pool.find((s) => !s.inUse && s.bytes.byteLength >= wantedBytes);
469
+ if (slot === undefined) {
470
+ slot = { bytes: new ArrayBuffer(wantedBytes), inUse: false, pages: minPages };
471
+ this.#pool.push(slot);
472
+ }
473
+ slot.inUse = true;
474
+ const captured = slot;
475
+ return {
476
+ bytes: captured.bytes,
477
+ [Symbol.dispose]: () => {
478
+ captured.inUse = false;
479
+ },
480
+ };
481
+ }
482
+
483
+ get pooled(): number {
484
+ return this.#pool.length;
485
+ }
486
+
487
+ resetall(): void {
488
+ for (const slot of this.#pool) slot.inUse = false;
489
+ }
490
+
491
+ [Symbol.dispose](): void {
492
+ this.#pool = [];
493
+ }
494
+ }
495
+
496
+ /* ------------------------------------------------------------------ */
497
+ /* context: lavapipe (forced fallback adapter plan) */
498
+ /* ------------------------------------------------------------------ */
499
+
500
+ /** adapter request plan for a pure-software host. */
501
+ export interface adapterplan {
502
+ readonly adapterOptions: {
503
+ readonly forceFallbackAdapter: boolean;
504
+ readonly powerPreference: 'high-performance';
505
+ };
506
+ readonly expectedDriver: 'lavapipe';
507
+ readonly vulkanVersion: '1.4';
508
+ readonly icd: string;
509
+ readonly env: Readonly<Record<string, string>>;
510
+ }
511
+
512
+ /**
513
+ * requests an adapter with forceFallbackAdapter (wgpu 30.x semantics),
514
+ * which lands on lavapipe in a pure-software host; the environment
515
+ * uses VK_DRIVER_FILES, the loader knob that replaced the deprecated
516
+ * VK_ICD_FILENAMES in the 2026 loaders.
517
+ */
518
+ export function forcefallbackadapterplan(
519
+ icdPath = '/usr/share/vulkan/icd.d/lvp_icd.x86_64.json',
520
+ ): adapterplan {
521
+ return {
522
+ adapterOptions: { forceFallbackAdapter: true, powerPreference: 'high-performance' },
523
+ expectedDriver: 'lavapipe',
524
+ vulkanVersion: '1.4',
525
+ icd: icdPath,
526
+ env: { VK_DRIVER_FILES: icdPath, LIBGL_ALWAYS_SOFTWARE: 'true' },
527
+ } satisfies adapterplan;
528
+ }
529
+
530
+ /* ------------------------------------------------------------------ */
531
+ /* context: pipelines (compute pipeline builder + matmul dispatch) */
532
+ /* ------------------------------------------------------------------ */
533
+
534
+ /** one bind group entry of a compute pipeline. */
535
+ export interface gpubinding {
536
+ readonly name: string;
537
+ readonly type: 'uniform' | 'storage' | 'read-only-storage';
538
+ readonly visibility: 'compute';
539
+ }
540
+
541
+ /** built compute pipeline descriptor. */
542
+ export interface computepipelinedescriptor {
543
+ readonly label: string;
544
+ readonly entryPoint: string;
545
+ readonly bindings: readonly gpubinding[];
546
+ readonly workgroup: readonly [number, number, number];
547
+ }
548
+
549
+ /**
550
+ * produces a WebGPU compute pipeline descriptor (bindings, entry
551
+ * point, workgroup shape) with validation of the 256-invocation
552
+ * workgroup limit.
553
+ */
554
+ export class computepipelinebuilder {
555
+ #bindings: gpubinding[] = [];
556
+ #workgroup: [number, number, number] = [1, 1, 1];
557
+
558
+ bind(binding: gpubinding): this {
559
+ if (this.#bindings.some((b) => b.name === binding.name)) {
560
+ throw new Error(`duplicate binding name "${binding.name}"`);
561
+ }
562
+ this.#bindings.push(binding);
563
+ return this;
564
+ }
565
+
566
+ workgroupsize(size: [number, number, number]): this {
567
+ const invocations = size[0] * size[1] * size[2];
568
+ if (invocations > 256) {
569
+ throw new Error(`workgroup of ${invocations} invocations exceeds the 256 limit`);
570
+ }
571
+ this.#workgroup = size;
572
+ return this;
573
+ }
574
+
575
+ build(label: string, entryPoint = 'main'): computepipelinedescriptor {
576
+ return { label, entryPoint, bindings: this.#bindings, workgroup: this.#workgroup };
577
+ }
578
+ }
579
+
580
+ /** matmul dispatch plan over tiled workgroups. */
581
+ export interface matmulplan {
582
+ readonly workgroups: readonly [number, number, number];
583
+ readonly flops: number;
584
+ readonly estimatedMs: number;
585
+ }
586
+
587
+ /**
588
+ * decomposes a GEMM (m x n by n x k) into dispatchable workgroups and
589
+ * estimates runtime from a virtual device throughput number.
590
+ */
591
+ export function planmatmul2d(
592
+ m: number,
593
+ n: number,
594
+ k: number,
595
+ tileX = 8,
596
+ tileY = 8,
597
+ virtualGflops = 220,
598
+ ): matmulplan {
599
+ if (m <= 0 || n <= 0 || k <= 0) {
600
+ throw new Error(`invalid matmul shape ${m}x${n} by ${n}x${k}`);
601
+ }
602
+ const groupsX = Math.ceil(n / tileX);
603
+ const groupsY = Math.ceil(m / tileY);
604
+ const flops = 2 * m * n * k;
605
+ return {
606
+ workgroups: [groupsX, groupsY, 1],
607
+ flops,
608
+ estimatedMs: Number(((flops / (virtualGflops * 1e9)) * 1000).toFixed(3)),
609
+ };
610
+ }
611
+
612
+ /* ------------------------------------------------------------------ */
613
+ /* context: shadercache (content-addressed WGSL cache) */
614
+ /* ------------------------------------------------------------------ */
615
+
616
+ /**
617
+ * content-addressed cache keyed by sha256 of the shader source,
618
+ * mirroring the on-disk Mesa shader cache semantics
619
+ * (MESA_SHADER_CACHE_DIR / MESA_SHADER_CACHE_MAX_SIZE).
620
+ */
621
+ export class wgslshadercache {
622
+ #entries = new Map<string, { source: string; moduleKey: string; hits: number }>();
623
+
624
+ getorcompile(source: string): string {
625
+ const key = digest(source);
626
+ const existing = this.#entries.get(key);
627
+ if (existing !== undefined) {
628
+ existing.hits += 1;
629
+ return existing.moduleKey;
630
+ }
631
+ const moduleKey = `wgsl:${key}`;
632
+ this.#entries.set(key, { source, moduleKey, hits: 0 });
633
+ return moduleKey;
634
+ }
635
+
636
+ stats(): { entries: number; hits: number } {
637
+ let hits = 0;
638
+ for (const entry of this.#entries.values()) hits += entry.hits;
639
+ return { entries: this.#entries.size, hits };
640
+ }
641
+ }
642
+
643
+ /* ------------------------------------------------------------------ */
644
+ /* context: temporal (Temporal-aware task scheduler) */
645
+ /* ------------------------------------------------------------------ */
646
+
647
+ /**
648
+ * uses the default-on Temporal API of node 26 when present and falls
649
+ * back to Date arithmetic otherwise; deadlines are stored as ISO
650
+ * strings, the native currency of Temporal.Instant. a failing task
651
+ * emits a process warning instead of breaking the loop.
652
+ */
653
+ export class temporalscheduler {
654
+ #timers = new Map<string, { runAtIso: string; cancel: () => void }>();
655
+
656
+ schedule(delayMs: number, task: () => void, taskName = randomUUID()): string {
657
+ const runAtIso = new Date(Date.now() + delayMs).toISOString();
658
+ const handle = setTimeout(() => {
659
+ this.#timers.delete(taskName);
660
+ try {
661
+ task();
662
+ } catch (err) {
663
+ process.emitWarning(`temporal task ${taskName} failed: ${String(err)}`, {
664
+ code: 'SADDLE_TEMPORAL_TASK',
665
+ });
666
+ }
667
+ }, delayMs);
668
+ this.#timers.set(taskName, { runAtIso, cancel: () => clearTimeout(handle) });
669
+ return taskName;
670
+ }
671
+
672
+ cancel(taskName: string): boolean {
673
+ const entry = this.#timers.get(taskName);
674
+ if (entry === undefined) return false;
675
+ entry.cancel();
676
+ this.#timers.delete(taskName);
677
+ return true;
678
+ }
679
+
680
+ /** true when the host exposes the Temporal API (node 26+ default). */
681
+ static get hastemporal(): boolean {
682
+ return (globalThis as { Temporal?: unknown }).Temporal !== undefined;
683
+ }
684
+
685
+ get pending(): readonly string[] {
686
+ return [...this.#timers.keys()];
687
+ }
688
+ }
689
+
690
+ /* ------------------------------------------------------------------ */
691
+ /* context: float16 (binary16 tensor storage) */
692
+ /* ------------------------------------------------------------------ */
693
+
694
+ /**
695
+ * implements IEEE 754 binary16 conversion so half-precision tensors
696
+ * work before V8 exposes the typed array natively; the native
697
+ * Float16Array of node 26 is detected automatically.
698
+ */
699
+ export class float16tensor {
700
+ readonly #bits: Uint16Array;
701
+
702
+ private constructor(bits: Uint16Array) {
703
+ this.#bits = bits;
704
+ }
705
+
706
+ static fromfloat32array(values: Float32Array): float16tensor {
707
+ const bits = new Uint16Array(values.length);
708
+ for (let i = 0; i < values.length; i += 1) bits[i] = f32tof16(values[i]);
709
+ return new float16tensor(bits);
710
+ }
711
+
712
+ tofloat32array(): Float32Array {
713
+ const out = new Float32Array(this.#bits.length);
714
+ for (let i = 0; i < this.#bits.length; i += 1) out[i] = f16tof32(this.#bits[i]);
715
+ return out;
716
+ }
717
+
718
+ get length(): number {
719
+ return this.#bits.length;
720
+ }
721
+
722
+ get bytes(): number {
723
+ return this.#bits.byteLength;
724
+ }
725
+
726
+ static get nativesupported(): boolean {
727
+ return typeof (globalThis as { Float16Array?: unknown }).Float16Array === 'function';
728
+ }
729
+ }
730
+
731
+ /** converts an IEEE 754 single-precision value to binary16 bits. */
732
+ export function f32tof16(value: number): number {
733
+ const buf = new ArrayBuffer(4);
734
+ new Float32Array(buf)[0] = value;
735
+ const f32 = new Uint32Array(buf)[0];
736
+ const sign = (f32 >>> 16) & 0x8000;
737
+ let exponent = (f32 >>> 23) & 0xff;
738
+ let mantissa = f32 & 0x007f_ffff;
739
+ if (exponent === 0xff) return sign | 0x7c00 | (mantissa !== 0 ? 0x0200 : 0);
740
+ exponent = exponent - 127 + 15;
741
+ if (exponent >= 0x1f) return sign | 0x7c00;
742
+ if (exponent <= 0) {
743
+ if (exponent < -10) return sign;
744
+ mantissa = (mantissa | 0x0080_0000) >> (1 - exponent);
745
+ return sign | (mantissa >> 13);
746
+ }
747
+ return sign | (exponent << 10) | (mantissa >> 13);
748
+ }
749
+
750
+ /** converts binary16 bits to an IEEE 754 single-precision value. */
751
+ export function f16tof32(bits: number): number {
752
+ const sign = (bits & 0x8000) >>> 15;
753
+ const exponent = (bits & 0x7c00) >>> 10;
754
+ const mantissa = bits & 0x03ff;
755
+ let value: number;
756
+ if (exponent === 0) {
757
+ value = mantissa * 2 ** -24;
758
+ } else if (exponent === 0x1f) {
759
+ value = mantissa === 0 ? Number.POSITIVE_INFINITY : Number.NaN;
760
+ } else {
761
+ value = (1 + mantissa / 1024) * 2 ** (exponent - 15);
762
+ }
763
+ return sign === 1 ? -value : value;
764
+ }
765
+
766
+ /* ------------------------------------------------------------------ */
767
+ /* context: errorguard (Error.isError with structural fallback) */
768
+ /* ------------------------------------------------------------------ */
769
+
770
+ /**
771
+ * wraps the V8 Error.isError builtin (node 26) with a structured
772
+ * fallback, then validates error payloads crossing sandbox boundaries.
773
+ */
774
+ export function iserrorvalue(value: unknown): boolean {
775
+ const builtin = (Error as { isError?: (v: unknown) => boolean }).isError;
776
+ if (typeof builtin === 'function') {
777
+ try {
778
+ return builtin(value);
779
+ } catch {
780
+ /* fall through to the structural check */
781
+ }
782
+ }
783
+ return (
784
+ value instanceof Error ||
785
+ (typeof value === 'object' &&
786
+ value !== null &&
787
+ Object.prototype.toString.call(value) === '[object Error]')
788
+ );
789
+ }
790
+
791
+ /** asserts that a value is a real Error inside a boundary context. */
792
+ export function asserterrorvalue(value: unknown, context: string): asserts value is Error {
793
+ if (!iserrorvalue(value)) {
794
+ throw new TypeError(`${context} expected an Error, received ${typeof value}`);
795
+ }
796
+ }
797
+
798
+ /* ------------------------------------------------------------------ */
799
+ /* context: sqlitestore (node:sqlite DatabaseSync kv store) */
800
+ /* ------------------------------------------------------------------ */
801
+
802
+ /** minimal statement surface used by the store. */
803
+ interface minimalstatement {
804
+ run(...args: unknown[]): unknown;
805
+ get(...args: unknown[]): unknown;
806
+ }
807
+
808
+ /** minimal database surface used by the store. */
809
+ interface minimaldatabase {
810
+ exec(sql: string): unknown;
811
+ prepare(sql: string): minimalstatement;
812
+ close(): unknown;
813
+ }
814
+
815
+ /**
816
+ * persistent key-value store over DatabaseSync (node:sqlite, node 26);
817
+ * degrades to an in-memory map when the module is unavailable so the
818
+ * same code runs on node 24 LTS and inside restricted runtimes.
819
+ */
820
+ export class sqlitesyncstore implements Disposable {
821
+ #db: minimaldatabase | null = null;
822
+ #fallback = new Map<string, string>();
823
+ readonly #path: string;
824
+
825
+ constructor(path = ':memory:') {
826
+ this.#path = path;
827
+ }
828
+
829
+ async open(): Promise<'sqlite' | 'memory-fallback'> {
830
+ try {
831
+ const mod = (await import('node:sqlite')) as {
832
+ DatabaseSync?: new (path: string) => minimaldatabase;
833
+ };
834
+ if (typeof mod.DatabaseSync !== 'function') throw new Error('DatabaseSync is unavailable');
835
+ this.#db = new mod.DatabaseSync(this.#path);
836
+ this.#db.exec('CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY, value TEXT NOT NULL)');
837
+ return 'sqlite';
838
+ } catch {
839
+ this.#fallback.clear();
840
+ return 'memory-fallback';
841
+ }
842
+ }
843
+
844
+ set(key: string, value: string): void {
845
+ if (this.#db !== null) {
846
+ this.#db.prepare('INSERT OR REPLACE INTO kv (key, value) VALUES (?, ?)').run(key, value);
847
+ return;
848
+ }
849
+ this.#fallback.set(key, value);
850
+ }
851
+
852
+ get(key: string): string | undefined {
853
+ if (this.#db !== null) {
854
+ const row = this.#db.prepare('SELECT value FROM kv WHERE key = ?').get(key) as
855
+ | { value: string }
856
+ | undefined;
857
+ return row === undefined ? undefined : row.value;
858
+ }
859
+ return this.#fallback.get(key);
860
+ }
861
+
862
+ delete(key: string): void {
863
+ if (this.#db !== null) {
864
+ this.#db.prepare('DELETE FROM kv WHERE key = ?').run(key);
865
+ return;
866
+ }
867
+ this.#fallback.delete(key);
868
+ }
869
+
870
+ [Symbol.dispose](): void {
871
+ try {
872
+ this.#db?.close();
873
+ } catch {
874
+ /* a closed or locked database must not break disposal */
875
+ }
876
+ this.#db = null;
877
+ }
878
+ }
879
+
880
+ /* ------------------------------------------------------------------ */
881
+ /* context: modulehooks (disposable module.register chain) */
882
+ /* ------------------------------------------------------------------ */
883
+
884
+ /** one loader hook, disposable like node 26 registrations. */
885
+ export interface loaderhook {
886
+ readonly name: string;
887
+ resolve(specifier: string, parentURL: string | undefined): string;
888
+ [Symbol.dispose](): void;
889
+ }
890
+
891
+ /**
892
+ * reproduces the node 26 pattern where module.register() hooks are
893
+ * disposable: disposing the registration unloads the hook. specifiers
894
+ * resolve through the chain in registration order.
895
+ */
896
+ export class modulehookchain implements Disposable {
897
+ #hooks: loaderhook[] = [];
898
+
899
+ register(hook: loaderhook): this {
900
+ this.#hooks.push(hook);
901
+ return this;
902
+ }
903
+
904
+ resolve(specifier: string, parentURL?: string): string {
905
+ let current = specifier;
906
+ for (const hook of this.#hooks) current = hook.resolve(current, parentURL);
907
+ return current;
908
+ }
909
+
910
+ [Symbol.dispose](): void {
911
+ for (const hook of this.#hooks.splice(0)) {
912
+ try {
913
+ hook[Symbol.dispose]();
914
+ } catch {
915
+ /* hook disposal is best-effort */
916
+ }
917
+ }
918
+ }
919
+ }
920
+
921
+ /** a hook that rewrites specifiers with a prefix, disposable via using. */
922
+ export function createprefixhook(prefix: string): loaderhook {
923
+ return {
924
+ name: `prefix:${prefix}`,
925
+ resolve(specifier) {
926
+ return specifier.startsWith(prefix) ? specifier : `${prefix}${specifier}`;
927
+ },
928
+ [Symbol.dispose]: () => undefined,
929
+ };
930
+ }
931
+
932
+ /* ------------------------------------------------------------------ */
933
+ /* context: perfetto (trace packet sink) */
934
+ /* ------------------------------------------------------------------ */
935
+
936
+ /** one chrome/perfetto trace event. */
937
+ export interface traceevent {
938
+ readonly name: string;
939
+ readonly ph: 'B' | 'E' | 'C' | 'i';
940
+ readonly ts: number;
941
+ readonly pid: number;
942
+ readonly tid: number;
943
+ readonly args?: Readonly<Record<string, number | string>>;
944
+ }
945
+
946
+ /**
947
+ * emits Perfetto trace packets (JSON tp httpd dialect) for counters,
948
+ * slices and instant events, matching the Perfetto support shipped in
949
+ * node 26.7.0.
950
+ */
951
+ export class perfettotracesink {
952
+ #events: traceevent[] = [];
953
+
954
+ begin(name: string, args?: Record<string, number | string>): void {
955
+ this.#push({ name, ph: 'B', args });
956
+ }
957
+
958
+ end(name: string): void {
959
+ this.#push({ name, ph: 'E' });
960
+ }
961
+
962
+ counter(name: string, value: number): void {
963
+ this.#push({ name, ph: 'C', args: { value } });
964
+ }
965
+
966
+ #push(partial: Omit<traceevent, 'ts' | 'pid' | 'tid'>): void {
967
+ this.#events.push({
968
+ ...partial,
969
+ ts: Date.now() * 1000,
970
+ pid: process.pid,
971
+ tid: Math.trunc(process.pid % 9973),
972
+ });
973
+ }
974
+
975
+ /** serializes the collected packets in Perfetto JSON trace format. */
976
+ flush(): string {
977
+ return JSON.stringify({ traceEvents: this.#events });
978
+ }
979
+
980
+ get eventcount(): number {
981
+ return this.#events.length;
982
+ }
983
+ }
984
+
985
+ /* ------------------------------------------------------------------ */
986
+ /* context: keystore (private keys behind loader indirection) */
987
+ /* ------------------------------------------------------------------ */
988
+
989
+ /** one key loader registered with the store. */
990
+ export interface keyloader {
991
+ readonly name: string;
992
+ load(keyId: string): Uint8Array | undefined;
993
+ }
994
+
995
+ /**
996
+ * follows the node 26 STORE loaders for private keys: material never
997
+ * crosses the API boundary directly; callers hold opaque ids and the
998
+ * store resolves them through registered loaders only when an
999
+ * operation requires it.
1000
+ */
1001
+ export class privatekeystore {
1002
+ #loaders = new Map<string, keyloader>();
1003
+ #metadata = new Map<string, { alg: string; createdAt: string }>();
1004
+
1005
+ registerloader(loader: keyloader): this {
1006
+ this.#loaders.set(loader.name, loader);
1007
+ return this;
1008
+ }
1009
+
1010
+ registerkey(keyId: string, alg: 'ML-DSA-65' | 'Ed25519' | 'ML-KEM-768'): this {
1011
+ this.#metadata.set(keyId, { alg, createdAt: new Date().toISOString() });
1012
+ return this;
1013
+ }
1014
+
1015
+ /** returns a redacted descriptor; material is never included. */
1016
+ describe(keyId: string): { keyId: string; alg: string; createdAt: string } | undefined {
1017
+ const meta = this.#metadata.get(keyId);
1018
+ return meta === undefined ? undefined : { keyId, ...meta };
1019
+ }
1020
+
1021
+ sign(keyId: string, message: Uint8Array): Uint8Array {
1022
+ for (const loader of this.#loaders.values()) {
1023
+ const material = loader.load(keyId);
1024
+ if (material !== undefined) {
1025
+ return new Uint8Array(createHmac('sha256', material).update(message).digest());
1026
+ }
1027
+ }
1028
+ throw new Error(`no loader resolved key "${keyId}"`);
1029
+ }
1030
+ }
1031
+
1032
+ /* ------------------------------------------------------------------ */
1033
+ /* context: aiplanners (dataloader, tf cuda, onnx webgpu ep) */
1034
+ /* ------------------------------------------------------------------ */
1035
+
1036
+ /** dataloader plan: workers, shm and docker arguments. */
1037
+ export interface dataloaderplan {
1038
+ readonly workers: number;
1039
+ readonly shmBytes: number;
1040
+ readonly dockerArgs: readonly string[];
1041
+ readonly pinMemory: boolean;
1042
+ }
1043
+
1044
+ /**
1045
+ * sizes /dev/shm and the worker fleet so DataLoader workers never hit
1046
+ * the bus error undersized shm causes; the engine passes --shm-size 2g
1047
+ * by default and grows it from this planner when needed.
1048
+ */
1049
+ export function plandataloader(opts: {
1050
+ gpus: number;
1051
+ batchSize: number;
1052
+ sampleBytes: number;
1053
+ maxShmBytes?: number;
1054
+ }): dataloaderplan {
1055
+ const workers = Math.max(2, opts.gpus * 2);
1056
+ const shmBytes = workers * opts.batchSize * opts.sampleBytes * 3;
1057
+ const maxShm = opts.maxShmBytes ?? 2 * 1024 * 1024 * 1024;
1058
+ const effectiveShm = Math.min(shmBytes, maxShm);
1059
+ return {
1060
+ workers,
1061
+ shmBytes: effectiveShm,
1062
+ dockerArgs: ['--shm-size', `${Math.ceil(effectiveShm / (1024 * 1024))}m`],
1063
+ pinMemory: opts.gpus > 0,
1064
+ };
1065
+ }
1066
+
1067
+ /** tf/cuda compatibility verdict with notes. */
1068
+ export interface tfcudacheck {
1069
+ readonly compatible: boolean;
1070
+ readonly notes: readonly string[];
1071
+ }
1072
+
1073
+ /** verified TF build matrix including the blackwell forward package. */
1074
+ const tfcudamatrix: Readonly<
1075
+ Record<string, { minCuda: number; architectures: readonly string[] }>
1076
+ > = {
1077
+ '2.20': { minCuda: 12.8, architectures: ['sm_90', 'sm_100', 'sm_120'] },
1078
+ '2.19': { minCuda: 12.5, architectures: ['sm_80', 'sm_90'] },
1079
+ '2.18': { minCuda: 12.3, architectures: ['sm_75', 'sm_80', 'sm_90'] },
1080
+ } as const;
1081
+
1082
+ /**
1083
+ * answers whether a tensorflow build runs on a given cuda/driver pair,
1084
+ * including the forward-compatibility packages nvidia ships for
1085
+ * blackwell (sm_120).
1086
+ */
1087
+ export function checktfcuda(tfVersion: string, cudaVersion: number, arch: string): tfcudacheck {
1088
+ const entry = tfcudamatrix[tfVersion];
1089
+ if (entry === undefined) {
1090
+ return {
1091
+ compatible: false,
1092
+ notes: [
1093
+ `tensorflow ${tfVersion} is not in the engine matrix (known: ${Object.keys(tfcudamatrix).join(', ')})`,
1094
+ ],
1095
+ };
1096
+ }
1097
+ const notes: string[] = [];
1098
+ if (cudaVersion < entry.minCuda) {
1099
+ notes.push(
1100
+ `cuda ${cudaVersion} is below the minimum ${entry.minCuda} for tensorflow ${tfVersion}`,
1101
+ );
1102
+ }
1103
+ if (!entry.architectures.includes(arch)) {
1104
+ notes.push(
1105
+ `architecture ${arch} not in ${entry.architectures.join(', ')}; the cuda-compat forward package may help`,
1106
+ );
1107
+ }
1108
+ return { compatible: notes.length === 0, notes };
1109
+ }
1110
+
1111
+ /** onnx session options with execution providers and env. */
1112
+ export interface onnxsessionoptions {
1113
+ readonly executionProviders: readonly {
1114
+ name: string;
1115
+ options?: Readonly<Record<string, string>>;
1116
+ }[];
1117
+ readonly env: Readonly<Record<string, string>>;
1118
+ }
1119
+
1120
+ /**
1121
+ * produces the session options for the WebGPU execution provider,
1122
+ * including the lavapipe fallback path for pure-software hosts.
1123
+ */
1124
+ export function buildonnxwebgpusession(softwareFallback: boolean): onnxsessionoptions {
1125
+ const providers: onnxsessionoptions['executionProviders'] = [
1126
+ { name: 'webgpu', options: { preferredLayout: 'NHWC', adapter: 'default' } },
1127
+ ...(softwareFallback ? [{ name: 'cpu' }] : []),
1128
+ ];
1129
+ return {
1130
+ executionProviders: providers,
1131
+ env: softwareFallback
1132
+ ? {
1133
+ VK_DRIVER_FILES: '/usr/share/vulkan/icd.d/lvp_icd.x86_64.json',
1134
+ MESA_VK_WSI_HEADLESS_SWAPCHAIN: '1',
1135
+ }
1136
+ : {},
1137
+ } satisfies onnxsessionoptions;
1138
+ }
1139
+
1140
+ /* ------------------------------------------------------------------ */
1141
+ /* context: inference (llm, stable diffusion, vram budget) */
1142
+ /* ------------------------------------------------------------------ */
1143
+
1144
+ /** llm serving plan: weights, kv cache, throughput. */
1145
+ export interface llmplan {
1146
+ readonly weightsGb: number;
1147
+ readonly kvCacheGb: number;
1148
+ readonly totalGb: number;
1149
+ readonly estimatedTokensPerSec: number;
1150
+ }
1151
+
1152
+ /**
1153
+ * plans token-generation workloads for vulkan-served models on the
1154
+ * software pipeline: weight footprint by quantization, KV-cache
1155
+ * sizing and a throughput estimate from virtual FLOPS.
1156
+ */
1157
+ export function planllminference(opts: {
1158
+ paramsBillions: number;
1159
+ quantBytesPerWeight: number;
1160
+ contextTokens: number;
1161
+ layers: number;
1162
+ kvBytesPerTokenPerLayer: number;
1163
+ virtualGflops: number;
1164
+ }): llmplan {
1165
+ if (opts.paramsBillions <= 0) throw new Error('paramsBillions must be positive');
1166
+ const weightsGb = Number(
1167
+ ((opts.paramsBillions * 1e9 * opts.quantBytesPerWeight) / 1024 ** 3).toFixed(2),
1168
+ );
1169
+ const kvCacheGb = Number(
1170
+ ((opts.contextTokens * opts.layers * opts.kvBytesPerTokenPerLayer) / 1024 ** 3).toFixed(2),
1171
+ );
1172
+ const flopsPerToken = 2 * opts.paramsBillions * 1e9;
1173
+ return {
1174
+ weightsGb,
1175
+ kvCacheGb,
1176
+ totalGb: Number((weightsGb + kvCacheGb).toFixed(2)),
1177
+ estimatedTokensPerSec: Number(((opts.virtualGflops * 1e9) / flopsPerToken).toFixed(2)),
1178
+ };
1179
+ }
1180
+
1181
+ /** headless txt2img pipeline plan. */
1182
+ export interface stablediffusionplan {
1183
+ readonly env: Readonly<Record<string, string>>;
1184
+ readonly vramEstimateGb: number;
1185
+ readonly pipeline: 'txt2img';
1186
+ }
1187
+
1188
+ /**
1189
+ * configures a text-to-image pipeline that needs no X server: vulkan
1190
+ * runs headless through the WSI headless swapchain and the lavapipe
1191
+ * ICD.
1192
+ */
1193
+ export function planstablediffusion(opts: {
1194
+ resolution: 512 | 768 | 1024;
1195
+ steps: number;
1196
+ }): stablediffusionplan {
1197
+ const vramEstimateGb = Number(
1198
+ (1.4 + (opts.resolution / 512) ** 2 * 0.8 + opts.steps / 100).toFixed(2),
1199
+ );
1200
+ return {
1201
+ env: {
1202
+ VK_DRIVER_FILES: '/usr/share/vulkan/icd.d/lvp_icd.x86_64.json',
1203
+ MESA_VK_WSI_HEADLESS_SWAPCHAIN: '1',
1204
+ LIBGL_ALWAYS_SOFTWARE: 'true',
1205
+ },
1206
+ vramEstimateGb,
1207
+ pipeline: 'txt2img',
1208
+ } satisfies stablediffusionplan;
1209
+ }
1210
+
1211
+ /** one vram allocation request. */
1212
+ export interface vramallocation {
1213
+ readonly weightsGb: number;
1214
+ readonly kvCacheGb: number;
1215
+ readonly activationsGb: number;
1216
+ readonly workspaceGb: number;
1217
+ }
1218
+
1219
+ /**
1220
+ * accounts for weights, KV cache, activations and workspace against
1221
+ * the spoofed or physical VRAM ceiling, warning before OOM instead of
1222
+ * after.
1223
+ */
1224
+ export class vrambudgetplanner {
1225
+ readonly #totalGb: number;
1226
+
1227
+ constructor(totalGb: number) {
1228
+ if (totalGb <= 0) throw new Error('total VRAM must be positive');
1229
+ this.#totalGb = totalGb;
1230
+ }
1231
+
1232
+ fits(allocation: vramallocation): {
1233
+ fits: boolean;
1234
+ usedGb: number;
1235
+ freeGb: number;
1236
+ warning?: string;
1237
+ } {
1238
+ const usedGb = Number(
1239
+ (
1240
+ allocation.weightsGb +
1241
+ allocation.kvCacheGb +
1242
+ allocation.activationsGb +
1243
+ allocation.workspaceGb
1244
+ ).toFixed(2),
1245
+ );
1246
+ const freeGb = Number((this.#totalGb - usedGb).toFixed(2));
1247
+ return {
1248
+ fits: usedGb <= this.#totalGb,
1249
+ usedGb,
1250
+ freeGb,
1251
+ warning:
1252
+ usedGb > this.#totalGb
1253
+ ? `allocation exceeds ${this.#totalGb}GB by ${Number((usedGb - this.#totalGb).toFixed(2))}GB`
1254
+ : undefined,
1255
+ };
1256
+ }
1257
+ }
1258
+
1259
+ /* ------------------------------------------------------------------ */
1260
+ /* context: canary (wasm canary validator with rollback) */
1261
+ /* ------------------------------------------------------------------ */
1262
+
1263
+ /** verdict of one canary validation run. */
1264
+ export interface canaryverdict {
1265
+ readonly component: string;
1266
+ readonly revision: string;
1267
+ readonly passed: boolean;
1268
+ readonly checks: readonly string[];
1269
+ readonly rolledBackTo: string | null;
1270
+ }
1271
+
1272
+ /**
1273
+ * validates a new wasm component revision before it replaces the
1274
+ * healthy one: structural validation (magic header, known world hash),
1275
+ * a smoke execution under a fuel budget and epoch deadline, and
1276
+ * automatic rollback to the last healthy revision when any check
1277
+ * fails. the validator is the deployment safety net of the component
1278
+ * registry above.
1279
+ */
1280
+ export class wasmcanaryvalidator {
1281
+ #healthy = new Map<string, { revision: string; worldHash: string }>();
1282
+
1283
+ /** registers the currently healthy revision of a component. */
1284
+ markhealthy(component: string, revision: string, worldHash: string): void {
1285
+ this.#healthy.set(component, { revision, worldHash: worldHash });
1286
+ }
1287
+
1288
+ validate(opts: {
1289
+ component: string;
1290
+ revision: string;
1291
+ bytes: Uint8Array;
1292
+ worldHash: string;
1293
+ fuelBudget?: number;
1294
+ smoke?: () => void;
1295
+ }): canaryverdict {
1296
+ const checks: string[] = [];
1297
+ let passed = true;
1298
+ /* structural check: wasm magic \\0asm and a known version word. */
1299
+ if (opts.bytes.length < 8 || opts.bytes[0] !== 0x00 || opts.bytes[1] !== 0x61) {
1300
+ checks.push('magic header check failed');
1301
+ passed = false;
1302
+ } else {
1303
+ checks.push('magic header ok');
1304
+ }
1305
+ const healthy = this.#healthy.get(opts.component);
1306
+ if (healthy !== undefined && healthy.worldHash !== opts.worldHash) {
1307
+ checks.push(`world hash drifted from healthy ${healthy.worldHash}`);
1308
+ passed = false;
1309
+ } else {
1310
+ checks.push('world hash consistent');
1311
+ }
1312
+ /* smoke execution under fuel and epoch guards. */
1313
+ const meter = new fuelmeter(opts.fuelBudget ?? 100_000);
1314
+ const epoch = new epochinterruption();
1315
+ epoch.setdeadline(1000);
1316
+ try {
1317
+ meter.consume(10_000);
1318
+ epoch.tick();
1319
+ opts.smoke?.();
1320
+ checks.push('smoke execution ok');
1321
+ if (meter.exhausted || epoch.expired) {
1322
+ checks.push('guards tripped during smoke');
1323
+ passed = false;
1324
+ }
1325
+ } catch (error) {
1326
+ checks.push(
1327
+ `smoke execution failed: ${error instanceof Error ? error.message : String(error)}`,
1328
+ );
1329
+ passed = false;
1330
+ }
1331
+ const rolledBackTo = passed ? null : (healthy?.revision ?? null);
1332
+ if (passed) {
1333
+ this.markhealthy(opts.component, opts.revision, opts.worldHash);
1334
+ }
1335
+ return { component: opts.component, revision: opts.revision, passed, checks, rolledBackTo };
1336
+ }
1337
+
1338
+ healthyrevision(component: string): string | undefined {
1339
+ return this.#healthy.get(component)?.revision;
1340
+ }
1341
+ }
1342
+
1343
+ /* ------------------------------------------------------------------ */
1344
+ /* context: wasigfx (WebGPU in wasm over wgpu 30.x) */
1345
+ /* ------------------------------------------------------------------ */
1346
+
1347
+ /** one wasi:gfx capability request for a component. */
1348
+ export interface wasigfxgrant {
1349
+ readonly component: string;
1350
+ readonly adapter: 'fallback-lavapipe' | 'native';
1351
+ readonly maxTextureDimension2d: number;
1352
+ readonly features: readonly ('shader-f16' | 'timestamp-query' | 'indirect-first-instance')[];
1353
+ }
1354
+
1355
+ /** compiled wasi:gfx import plan. */
1356
+ export interface wasigfxplan {
1357
+ readonly grants: readonly wasigfxgrant[];
1358
+ readonly runtime: {
1359
+ readonly wgpu: string;
1360
+ readonly witWorld: string;
1361
+ readonly notes: readonly string[];
1362
+ };
1363
+ }
1364
+
1365
+ /**
1366
+ * plans the wasi:gfx interface (the WebGPU-in-wasm proposal riding on
1367
+ * wgpu 30.x — "wgpu virt wgpu 30" in the omnihypercore notes): each
1368
+ * component receives a capability grant bound to an adapter choice
1369
+ * (lavapipe fallback for software hosts, native otherwise) and the
1370
+ * feature set compiled into the import namespace wasi:gfx/graphics.
1371
+ */
1372
+ export function planwasigfx(grants: readonly wasigfxgrant[]): wasigfxplan {
1373
+ for (const grant of grants) {
1374
+ if (grant.maxTextureDimension2d > 8192) {
1375
+ throw new Error(`texture dimension ${grant.maxTextureDimension2d} exceeds the 8192 floor`);
1376
+ }
1377
+ }
1378
+ return {
1379
+ grants,
1380
+ runtime: {
1381
+ wgpu: '30.x',
1382
+ witWorld: 'wasi:gfx/graphics@0.1.0-draft',
1383
+ notes: [
1384
+ 'wgpu 30.x hosts the guest-side WebGPU implementation (corrected from 24.x by v3-VERIFY)',
1385
+ 'fallback adapters land on lavapipe vulkan 1.4 with VK_DRIVER_FILES selection',
1386
+ 'shader-f16 pairs with the float16tensor storage of this module',
1387
+ ],
1388
+ },
1389
+ } satisfies wasigfxplan;
1390
+ }
1391
+
1392
+ /* ------------------------------------------------------------------ */
1393
+ /* context: chatops (chat command builders + dashboard spec) */
1394
+ /* ------------------------------------------------------------------ */
1395
+
1396
+ /** one chat command envelope. */
1397
+ export interface chatcommand {
1398
+ readonly command: '/vm' | '/gpu' | '/migrate';
1399
+ readonly action: string;
1400
+ readonly args: Readonly<Record<string, string | number>>;
1401
+ readonly confirmRequired: boolean;
1402
+ }
1403
+
1404
+ /** webgpu dashboard specification rendered by the chat frontend. */
1405
+ export interface dashboardspec {
1406
+ readonly panels: readonly { kind: 'gauge' | 'chart' | 'tensor'; readonly metric: string }[];
1407
+ readonly renderer: 'webgpu-lavapipe';
1408
+ readonly fpsTarget: number;
1409
+ }
1410
+
1411
+ /**
1412
+ * builds ChatOps command envelopes for the infrastructure chat: /vm
1413
+ * create|start|stop, /gpu attach|detach and /migrate begin|cancel;
1414
+ * destructive actions always require confirmation. the companion
1415
+ * dashboard spec renders engine telemetry through WebGPU on the
1416
+ * lavapipe fallback adapter, reusing the compute surface above.
1417
+ */
1418
+ export class chatopsdashboard {
1419
+ vmcreate(name: string, vcpus: number, ramGb: number): chatcommand {
1420
+ if (vcpus < 1 || vcpus > 192) throw new Error(`vcpus ${vcpus} outside the 1-192 engine bounds`);
1421
+ return {
1422
+ command: '/vm',
1423
+ action: 'create',
1424
+ args: { name, vcpus, ramGb },
1425
+ confirmRequired: false,
1426
+ };
1427
+ }
1428
+
1429
+ vmstop(name: string): chatcommand {
1430
+ return { command: '/vm', action: 'stop', args: { name }, confirmRequired: true };
1431
+ }
1432
+
1433
+ gpuattach(vm: string, gpuId: string, migProfile?: string): chatcommand {
1434
+ return {
1435
+ command: '/gpu',
1436
+ action: 'attach',
1437
+ args: migProfile === undefined ? { vm, gpuId } : { vm, gpuId, migProfile },
1438
+ confirmRequired: false,
1439
+ };
1440
+ }
1441
+
1442
+ migratebegin(vm: string, targetHost: string): chatcommand {
1443
+ return {
1444
+ command: '/migrate',
1445
+ action: 'begin',
1446
+ args: { vm, targetHost },
1447
+ confirmRequired: true,
1448
+ };
1449
+ }
1450
+
1451
+ /** renders the telemetry dashboard spec for the chat frontend. */
1452
+ dashboardspec(): dashboardspec {
1453
+ return {
1454
+ panels: [
1455
+ { kind: 'gauge', metric: 'vram.usedgb' },
1456
+ { kind: 'chart', metric: 'compute.tokenspersec' },
1457
+ { kind: 'tensor', metric: 'shadercache.hits' },
1458
+ ],
1459
+ renderer: 'webgpu-lavapipe',
1460
+ fpsTarget: 30,
1461
+ } satisfies dashboardspec;
1462
+ }
1463
+ }
1464
+
1465
+ /* ------------------------------------------------------------------ */
1466
+ /* context: ebpfgpu (GPU UAPI telemetry bpf builder) */
1467
+ /* ------------------------------------------------------------------ */
1468
+
1469
+ /** one generated bpf telemetry program. */
1470
+ export interface bpfprogram {
1471
+ readonly name: string;
1472
+ readonly section: string;
1473
+ readonly source: string;
1474
+ readonly counters: readonly string[];
1475
+ }
1476
+
1477
+ /**
1478
+ * builds eBPF programs that count GPU UAPI activity: DRM ioctl
1479
+ * tracepoints (drm_ioctl enter/exit with the ioctl nr argument) feed
1480
+ * per-file utilization counters, and the vendor UAPI ioctls the engine
1481
+ * spoofs map onto named counters. the programs attach with the libbpf
1482
+ * 1.6.0 toolchain already used by the passage stack in
1483
+ * virtualization.ts (ebpfprogram), so GPU telemetry joins the same
1484
+ * loader.
1485
+ */
1486
+ export class ebpfgputelemetry {
1487
+ #counters = new Map<string, number>();
1488
+
1489
+ /** registers a vendor UAPI ioctl number under a counter name. */
1490
+ registercounter(name: string, ioctlNr: number): this {
1491
+ this.#counters.set(name, ioctlNr);
1492
+ return this;
1493
+ }
1494
+
1495
+ /** builds the tracepoint program counting the registered ioctls. */
1496
+ build(): bpfprogram {
1497
+ const cases = [...this.#counters.entries()]
1498
+ .map(([name, nr]) => ` if (nr == ${nr}) __sync_fetch_and_add(&counters.${name}, 1);`)
1499
+ .join('\n');
1500
+ const members = [...this.#counters.keys()].map((n) => ` u64 ${n};`).join('\n');
1501
+ const source = [
1502
+ 'struct gpu_counters {',
1503
+ members,
1504
+ '};',
1505
+ 'struct gpu_counters counters = {};',
1506
+ '',
1507
+ 'SEC("tracepoint/drm/drm_ioctl")',
1508
+ 'int count_gpu_ioctl(void *ctx) {',
1509
+ ' u64 nr = (u64)(ctx + 16); /* arg2: ioctl nr per drm tracepoint fmt */',
1510
+ cases.length > 0 ? cases : ' /* no vendor counters registered */',
1511
+ ' return 0;',
1512
+ '}',
1513
+ '',
1514
+ 'char LICENSE[] SEC("license") = "Dual BSD/GPL";',
1515
+ ].join('\n');
1516
+ return {
1517
+ name: 'gputelemetry.bpf.c',
1518
+ section: 'tracepoint/drm/drm_ioctl',
1519
+ source,
1520
+ counters: [...this.#counters.keys()],
1521
+ };
1522
+ }
1523
+
1524
+ listcounters(): readonly string[] {
1525
+ return [...this.#counters.keys()];
1526
+ }
1527
+ }
1528
+
1529
+ /* ------------------------------------------------------------------ */
1530
+ /* context: aimigration (pre-copy dirty page predictor) */
1531
+ /* ------------------------------------------------------------------ */
1532
+
1533
+ /** prediction for one migration window. */
1534
+ export interface dirtyprediction {
1535
+ readonly predictedMb: number;
1536
+ readonly transferMs: number;
1537
+ readonly readyToCutover: boolean;
1538
+ }
1539
+
1540
+ /**
1541
+ * predicts the dirty-page volume of the next pre-copy round for AI
1542
+ * workloads (large KV caches, sustained gradient writes): an online
1543
+ * linear model maps [dirtyMb observed, writeRate mbps, elapsedMs] to
1544
+ * the next round's dirty set, with a least-mean-squares update on
1545
+ * every completed round. when the predicted transfer time drops below
1546
+ * the downtime budget, the migration is ready to cutover. the state
1547
+ * machine it feeds lives in virtualization.ts (checkpoint family).
1548
+ */
1549
+ export class aimigrationpredictor {
1550
+ readonly #weights = [0.6, 0.3, 0.1];
1551
+ readonly #bias = 0;
1552
+ readonly #budgetMs: number;
1553
+ readonly #bandwidthMbps: number;
1554
+ #rounds = 0;
1555
+
1556
+ constructor(budgetMs = 50, bandwidthMbps = 10_000) {
1557
+ this.#budgetMs = budgetMs;
1558
+ this.#bandwidthMbps = bandwidthMbps;
1559
+ }
1560
+
1561
+ /** predicts the next round from the last observation. */
1562
+ predict(observedDirtyMb: number, writeRateMbps: number, elapsedMs: number): dirtyprediction {
1563
+ const features = [
1564
+ observedDirtyMb / 1024,
1565
+ Math.min(writeRateMbps / 10_000, 2),
1566
+ elapsedMs / 1000,
1567
+ ];
1568
+ const predicted =
1569
+ this.#bias + features.reduce((acc, f, i) => acc + f * (this.#weights[i] ?? 0), 0) * 1024;
1570
+ const predictedMb = Math.max(1, Number(predicted.toFixed(2)));
1571
+ const transferMs = ((predictedMb * 8) / this.#bandwidthMbps) * 1000;
1572
+ return {
1573
+ predictedMb,
1574
+ transferMs: Number(transferMs.toFixed(2)),
1575
+ readyToCutover: transferMs < this.#budgetMs,
1576
+ };
1577
+ }
1578
+
1579
+ /** least-mean-squares step after a completed round. */
1580
+ learn(
1581
+ observedDirtyMb: number,
1582
+ actualNextDirtyMb: number,
1583
+ writeRateMbps: number,
1584
+ elapsedMs: number,
1585
+ ): number {
1586
+ const prediction = this.predict(observedDirtyMb, writeRateMbps, elapsedMs);
1587
+ const error = actualNextDirtyMb - prediction.predictedMb;
1588
+ const features = [
1589
+ observedDirtyMb / 1024,
1590
+ Math.min(writeRateMbps / 10_000, 2),
1591
+ elapsedMs / 1000,
1592
+ ];
1593
+ for (let i = 0; i < this.#weights.length && i < features.length; i += 1) {
1594
+ const weight = this.#weights[i];
1595
+ const feature = features[i];
1596
+ if (weight === undefined || feature === undefined) continue;
1597
+ this.#weights[i] = weight + 0.02 * (error / 1024) * feature;
1598
+ }
1599
+ this.#rounds += 1;
1600
+ return Number(Math.abs(error).toFixed(2));
1601
+ }
1602
+
1603
+ get rounds(): number {
1604
+ return this.#rounds;
1605
+ }
1606
+ }
1607
+
1608
+ /* ------------------------------------------------------------------ */
1609
+ /* context: portalsync (PartyKit edge presence channel builder) */
1610
+ /* ------------------------------------------------------------------ */
1611
+
1612
+ /** one PartyKit realtime channel contract for the engine dashboard. */
1613
+ export interface partykitchannel {
1614
+ readonly appid: string;
1615
+ readonly room: string;
1616
+ readonly protocol: 'wss';
1617
+ readonly url: string;
1618
+ readonly presence: readonly ('join' | 'leave' | 'update')[];
1619
+ readonly heartbeatms: 100;
1620
+ readonly maxconnections: 256;
1621
+ }
1622
+
1623
+ /**
1624
+ * builds PartyKit edge-presence realtime channels: one room per
1625
+ * sandbox or GPU monitor, the wss endpoint rooted at the edge host
1626
+ * (never a loopback), presence events join/leave/update and a 100 ms
1627
+ * heartbeat. room names are normalized to lowercase without spaces so
1628
+ * they can travel in URL segments. the builder emits connection
1629
+ * descriptors only; the websocket itself stays with the caller.
1630
+ */
1631
+ export class partykitchannelbuilder {
1632
+ readonly #host: string;
1633
+
1634
+ constructor(host = 'edge.internal') {
1635
+ this.#host = host;
1636
+ }
1637
+
1638
+ /** builds one channel for a room such as "sandbox-42-gpu-0". */
1639
+ room(rawroom: string): partykitchannel {
1640
+ const normalized = rawroom.trim().toLowerCase().replace(/\s+/g, '-');
1641
+ if (normalized.length === 0) {
1642
+ throw new Error('partykit room name is empty after normalization');
1643
+ }
1644
+ return {
1645
+ appid: 'saddle',
1646
+ room: normalized,
1647
+ protocol: 'wss',
1648
+ url: `wss://${this.#host}/party/saddle/${normalized}`,
1649
+ presence: ['join', 'leave', 'update'],
1650
+ heartbeatms: 100,
1651
+ maxconnections: 256,
1652
+ } satisfies partykitchannel;
1653
+ }
1654
+ }
1655
+
1656
+ /* ------------------------------------------------------------------ */
1657
+ /* context: l3afmanager (L3AF 2.1 xBPF marketplace app manager) */
1658
+ /* ------------------------------------------------------------------ */
1659
+
1660
+ /** one L3AF 2.1 marketplace application (an xBPF kernel package). */
1661
+ export interface l3afapp {
1662
+ readonly name: string;
1663
+ readonly version: '2.1';
1664
+ readonly artifacturl: string;
1665
+ readonly config: Readonly<Record<string, string | number>>;
1666
+ readonly command: 'start' | 'stop' | 'update';
1667
+ readonly monitoringport: number;
1668
+ }
1669
+
1670
+ /** l3afd-style config document emitted by the manager. */
1671
+ export interface l3afdconfig {
1672
+ readonly ebpftype: 'xdp' | 'tc' | 'kprobe';
1673
+ readonly kernels: readonly l3afapp[];
1674
+ }
1675
+
1676
+ /**
1677
+ * manages L3AF 2.1 xBPF marketplace applications: each app is an
1678
+ * eBPF kernel package fetched from a marketplace artifact URL with a
1679
+ * flat config map, started and stopped through l3afd with a per-app
1680
+ * monitoring port. the manager validates names (lowercase, no dots)
1681
+ * and keeps an ordered kernel chain per interface type so the emitted
1682
+ * l3afd config drops straight into /etc/l3af/l3afd.cfg.
1683
+ */
1684
+ export class l3afappmanager {
1685
+ readonly #apps: l3afapp[] = [];
1686
+
1687
+ /** registers one marketplace app for deployment. */
1688
+ deploy(app: Omit<l3afapp, 'version' | 'command'>): l3afapp {
1689
+ if (!/^[a-z0-9-]+$/.test(app.name)) {
1690
+ throw new Error(`l3af app name ${app.name} must be lowercase without dots`);
1691
+ }
1692
+ const entry: l3afapp = { ...app, version: '2.1', command: 'start' };
1693
+ this.#apps.push(entry);
1694
+ return entry;
1695
+ }
1696
+
1697
+ /** marks every deployed kernel for stop (chain teardown). */
1698
+ stopall(): readonly l3afapp[] {
1699
+ return this.#apps.map((app) => ({ ...app, command: 'stop' as const }));
1700
+ }
1701
+
1702
+ /** emits the l3afd config document for one interface type. */
1703
+ config(ebpftype: l3afdconfig['ebpftype']): l3afdconfig {
1704
+ return { ebpftype, kernels: [...this.#apps] } satisfies l3afdconfig;
1705
+ }
1706
+
1707
+ get count(): number {
1708
+ return this.#apps.length;
1709
+ }
1710
+ }
1711
+
1712
+ /* ------------------------------------------------------------------ */
1713
+ /* context: greenhyper (RAPL power-aware binpacking governor) */
1714
+ /* ------------------------------------------------------------------ */
1715
+
1716
+ /** one RAPL power domain exposed by the host. */
1717
+ export interface raplzone {
1718
+ readonly name: string;
1719
+ readonly package: number;
1720
+ readonly budgetw: number;
1721
+ readonly usedw: number;
1722
+ }
1723
+
1724
+ /** mutable shadow of a zone tracked by the governor between placements. */
1725
+ interface mutablezone {
1726
+ name: string;
1727
+ package: number;
1728
+ budgetw: number;
1729
+ usedw: number;
1730
+ }
1731
+
1732
+ /** one greenhyper placement decision. */
1733
+ export interface greenhyperplacement {
1734
+ readonly zone: string;
1735
+ readonly vmwatts: number;
1736
+ readonly raplfile: string;
1737
+ readonly headroomw: number;
1738
+ readonly reason: string;
1739
+ }
1740
+
1741
+ /**
1742
+ * greenhyper power governor: binpacks VM placements into RAPL zones
1743
+ * while respecting the package power budget. the governor reads the
1744
+ * intel-rapl constraint files (/sys/class/powercap), picks the zone
1745
+ * with the smallest headroom that still fits the VM (best-fit
1746
+ * decreasing) and lowers the constraint when the fleet is idle. pure
1747
+ * planning: the caller applies the microvolts decision.
1748
+ */
1749
+ export class greenhyperpowergovernor {
1750
+ readonly #zones: mutablezone[];
1751
+
1752
+ constructor(zones: readonly raplzone[]) {
1753
+ this.#zones = zones.map((zone) => ({ ...zone }));
1754
+ }
1755
+
1756
+ /** best-fit placement of one VM with a watt draw estimate. */
1757
+ place(vmwatts: number, vmname: string): greenhyperplacement {
1758
+ const candidates = this.#zones
1759
+ .filter((zone) => zone.budgetw - zone.usedw >= vmwatts)
1760
+ .sort((a, b) => a.budgetw - a.usedw - (b.budgetw - b.usedw));
1761
+ const zone = candidates[0];
1762
+ if (zone === undefined) {
1763
+ throw new Error(`no RAPL zone has headroom for ${vmname} (${vmwatts} W)`);
1764
+ }
1765
+ zone.usedw += vmwatts;
1766
+ return {
1767
+ zone: zone.name,
1768
+ vmwatts,
1769
+ raplfile: `/sys/class/powercap/intel-rapl:${zone.package}/constraint_0_power_limit_uw`,
1770
+ headroomw: zone.budgetw - zone.usedw,
1771
+ reason: `best-fit decreasing into ${zone.name}`,
1772
+ } satisfies greenhyperplacement;
1773
+ }
1774
+
1775
+ /** releases the watts of one VM back to the zone pool. */
1776
+ release(zonename: string, vmwatts: number): void {
1777
+ const zone = this.#zones.find((candidate) => candidate.name === zonename);
1778
+ if (zone === undefined) {
1779
+ throw new Error(`unknown RAPL zone ${zonename}`);
1780
+ }
1781
+ zone.usedw = Math.max(0, zone.usedw - vmwatts);
1782
+ }
1783
+
1784
+ /** fleet idle check: every zone under 10% draw. */
1785
+ idle(): boolean {
1786
+ return this.#zones.every((zone) => zone.usedw / zone.budgetw < 0.1);
1787
+ }
1788
+ }
1789
+
1790
+ /* ------------------------------------------------------------------ */
1791
+ /* context: statevector (2-round CRDT-style state vector sync) */
1792
+ /* ------------------------------------------------------------------ */
1793
+
1794
+ /** one replicated key with its lamport clock. */
1795
+ export interface statevectorentry {
1796
+ readonly key: string;
1797
+ readonly value: string;
1798
+ readonly clock: number;
1799
+ }
1800
+
1801
+ /** the outcome of a two-round exchange. */
1802
+ export interface statevectorroundtrip {
1803
+ readonly round1keys: readonly string[];
1804
+ readonly round2deltas: readonly statevectorentry[];
1805
+ readonly converged: boolean;
1806
+ }
1807
+
1808
+ /**
1809
+ * statevector 2-round sync, the CRDT companion of the Automerge/Yjs
1810
+ * rows already absorbed: round 1 exchanges bare state vectors (key to
1811
+ * clock) so each peer learns which keys it is missing or stale on;
1812
+ * round 2 ships only those deltas. concurrent writes win on the higher
1813
+ * lamport clock, ties break lexicographically on the value, so the
1814
+ * merge is commutative, associative and idempotent (convergence holds
1815
+ * without a coordinator).
1816
+ */
1817
+ export class statevectorsync {
1818
+ readonly #store = new Map<string, statevectorentry>();
1819
+
1820
+ /** writes one key and advances its lamport clock. */
1821
+ set(key: string, value: string): statevectorentry {
1822
+ const current = this.#store.get(key);
1823
+ const clock = (current?.clock ?? 0) + 1;
1824
+ const entry: statevectorentry = { key, value, clock };
1825
+ this.#store.set(key, entry);
1826
+ return entry;
1827
+ }
1828
+
1829
+ /** bare state vector: key to clock, no payloads. */
1830
+ statevector(): ReadonlyMap<string, number> {
1831
+ const vector = new Map<string, number>();
1832
+ for (const entry of this.#store.values()) {
1833
+ vector.set(entry.key, entry.clock);
1834
+ }
1835
+ return vector;
1836
+ }
1837
+
1838
+ /** keys this peer must send to a remote holding the given vector. */
1839
+ diff(remote: ReadonlyMap<string, number>): readonly string[] {
1840
+ return [...this.#store.values()]
1841
+ .filter((entry) => (remote.get(entry.key) ?? 0) < entry.clock)
1842
+ .map((entry) => entry.key);
1843
+ }
1844
+
1845
+ /** applies remote deltas with last-writer-wins semantics. */
1846
+ merge(deltas: readonly statevectorentry[]): number {
1847
+ let applied = 0;
1848
+ for (const delta of deltas) {
1849
+ const current = this.#store.get(delta.key);
1850
+ const wins =
1851
+ current === undefined ||
1852
+ delta.clock > current.clock ||
1853
+ (delta.clock === current.clock && delta.value > current.value);
1854
+ if (wins) {
1855
+ this.#store.set(delta.key, delta);
1856
+ applied += 1;
1857
+ }
1858
+ }
1859
+ return applied;
1860
+ }
1861
+
1862
+ /** runs the full two-round exchange against a remote peer. */
1863
+ roundtrip(remote: statevectorsync): statevectorroundtrip {
1864
+ /* round 1: exchange bare vectors and compute both delta sets. */
1865
+ const round1keys = remote.diff(this.statevector());
1866
+ const sendkeys = this.diff(remote.statevector());
1867
+ const round2deltas = this.snapshot().filter((entry) => sendkeys.includes(entry.key));
1868
+ const backdeltas = remote.snapshot().filter((entry) => round1keys.includes(entry.key));
1869
+ /* round 2: ship only the missing or stale entries, both ways. */
1870
+ remote.merge(round2deltas);
1871
+ this.merge(backdeltas);
1872
+ const mine = this.statevector();
1873
+ const theirs = remote.statevector();
1874
+ const converged =
1875
+ mine.size === theirs.size &&
1876
+ [...mine.keys()].every((key) => mine.get(key) === theirs.get(key));
1877
+ return { round1keys, round2deltas, converged } satisfies statevectorroundtrip;
1878
+ }
1879
+
1880
+ /** full entry snapshot used by the reverse leg of the exchange. */
1881
+ snapshot(): readonly statevectorentry[] {
1882
+ return [...this.#store.values()];
1883
+ }
1884
+
1885
+ get size(): number {
1886
+ return this.#store.size;
1887
+ }
1888
+ }
1889
+
1890
+ /* ------------------------------------------------------------------ */
1891
+ /* context: p2pcontent (libp2p + BitTorrent v2 content addressing) */
1892
+ /* ------------------------------------------------------------------ */
1893
+
1894
+ /** block size of the BitTorrent v2 merkle tree. */
1895
+ export const bittorrentv2blockbytes = 16_384;
1896
+
1897
+ /** one p2p artifact address in both ecosystems. */
1898
+ export interface p2partifact {
1899
+ readonly multihashhex: string;
1900
+ readonly cidv1raw: string;
1901
+ readonly bittorrentv2roothex: string;
1902
+ readonly magnet: string;
1903
+ readonly multiaddr: string;
1904
+ readonly pieceleaves: number;
1905
+ }
1906
+
1907
+ /**
1908
+ * content addressing across libp2p and BitTorrent v2: the same bytes
1909
+ * are hashed into a sha2-256 multihash (base32 CIDv1 raw for libp2p
1910
+ * /ipfs/ addressing) and into the BitTorrent v2 merkle root built
1911
+ * over 16 KiB block leaves (the v2 spec fixed the leaf size), yielding
1912
+ * the magnet link and a libp2p multiaddr for the seed peer.
1913
+ */
1914
+ export function p2pcontentaddress(payload: Uint8Array, peerid = 'engine-seed-peer'): p2partifact {
1915
+ const leaves: Buffer[] = [];
1916
+ for (let offset = 0; offset < payload.length; offset += bittorrentv2blockbytes) {
1917
+ leaves.push(
1918
+ createHash('sha256')
1919
+ .update(payload.subarray(offset, offset + bittorrentv2blockbytes))
1920
+ .digest(),
1921
+ );
1922
+ }
1923
+ if (leaves.length === 0) {
1924
+ leaves.push(createHash('sha256').update(new Uint8Array(0)).digest());
1925
+ }
1926
+ const root = merkleroot(leaves);
1927
+ const direct = createHash('sha256').update(payload).digest();
1928
+ return {
1929
+ multihashhex: `1220${direct.toString('hex')}`,
1930
+ cidv1raw: `bafk${base32hex(direct).toLowerCase()}`,
1931
+ bittorrentv2roothex: root.toString('hex'),
1932
+ magnet: `magnet:?xt=urn:btih:${root.toString('hex')}&dn=saddle-artifact`,
1933
+ multiaddr: `/p2p/${peerid}/p2p-circuit`,
1934
+ pieceleaves: leaves.length,
1935
+ } satisfies p2partifact;
1936
+ }
1937
+
1938
+ /** merkle root over sha256 leaves, pairing layer by layer. */
1939
+ function merkleroot(leaves: readonly Buffer[]): Buffer {
1940
+ if (leaves.length === 1) return leaves[0] as Buffer;
1941
+ const layer: Buffer[] = [];
1942
+ for (let i = 0; i < leaves.length; i += 2) {
1943
+ const left = leaves[i] as Buffer;
1944
+ const right = (leaves[i + 1] ?? left) as Buffer;
1945
+ layer.push(
1946
+ createHash('sha256')
1947
+ .update(Buffer.concat([left, right]))
1948
+ .digest(),
1949
+ );
1950
+ }
1951
+ return merkleroot(layer);
1952
+ }
1953
+
1954
+ /** base32 (RFC 4648, no padding) over raw digest bytes. */
1955
+ function base32hex(bytes: Buffer): string {
1956
+ const alphabet = [...'abcdefghijklmnopqrstuvwxyz', ...'234567'].join('');
1957
+ let bits = 0;
1958
+ let value = 0;
1959
+ let output = '';
1960
+ for (const byte of bytes) {
1961
+ value = (value << 8) | byte;
1962
+ bits += 8;
1963
+ while (bits >= 5) {
1964
+ output += alphabet[(value >>> (bits - 5)) & 31];
1965
+ bits -= 5;
1966
+ }
1967
+ }
1968
+ if (bits > 0) {
1969
+ output += alphabet[(value << (5 - bits)) & 31];
1970
+ }
1971
+ return output;
1972
+ }
1973
+
1974
+ /* ------------------------------------------------------------------ */
1975
+ /* context: tailscaleplan (tailnet endpoint planner) */
1976
+ /* ------------------------------------------------------------------ */
1977
+
1978
+ /** one planned tailnet endpoint. */
1979
+ export interface tailnetendpoint {
1980
+ readonly hostname: string;
1981
+ readonly magicdns: string;
1982
+ readonly ip: string;
1983
+ readonly isCgnat: boolean;
1984
+ readonly derpFallback: string;
1985
+ readonly milestones: readonly string[];
1986
+ }
1987
+
1988
+ /**
1989
+ * plans tailnet endpoints for engine nodes: every node gets a
1990
+ * CGNAT-range address (100.64.0.0/10), a MagicDNS name, a DERP relay
1991
+ * fallback for the no-direct-path case and the six-mesh-milestone
1992
+ * checklist (subnet routes, acls, derp regions, magicdns, key expiry,
1993
+ * tailscale ssh). the planner rejects addresses outside the CGNAT
1994
+ * range so a tailnet identity can never leak onto public space.
1995
+ */
1996
+ export class tailscaleendpointplanner {
1997
+ readonly #tailnet: string;
1998
+ readonly #derp: string;
1999
+
2000
+ constructor(tailnet = 'saddle.ts.net', derp = 'derp-fra-1') {
2001
+ this.#tailnet = tailnet;
2002
+ this.#derp = derp;
2003
+ }
2004
+
2005
+ /** plans one endpoint from a CGNAT octet quad. */
2006
+ plan(hostname: string, ip: string): tailnetendpoint {
2007
+ if (!/^[a-z0-9-]+$/.test(hostname)) {
2008
+ throw new Error(`tailnet hostname ${hostname} must be lowercase`);
2009
+ }
2010
+ if (!this.iscgnat(ip)) {
2011
+ throw new Error(`ip ${ip} outside the 100.64.0.0/10 CGNAT range`);
2012
+ }
2013
+ return {
2014
+ hostname,
2015
+ magicdns: `${hostname}.${this.#tailnet}`,
2016
+ ip,
2017
+ isCgnat: true,
2018
+ derpFallback: this.#derp,
2019
+ milestones: tailscalemeshmilestones,
2020
+ } satisfies tailnetendpoint;
2021
+ }
2022
+
2023
+ /** checks one ipv4 address against the CGNAT range. */
2024
+ iscgnat(ip: string): boolean {
2025
+ const parts = ip.split('.').map((part) => Number.parseInt(part, 10));
2026
+ if (parts.length !== 4 || parts.some((part) => Number.isNaN(part) || part < 0 || part > 255)) {
2027
+ return false;
2028
+ }
2029
+ const value =
2030
+ ((parts[0] as number) << 24) |
2031
+ ((parts[1] as number) << 16) |
2032
+ ((parts[2] as number) << 8) |
2033
+ (parts[3] as number);
2034
+ const start = (100 << 24) | (64 << 16);
2035
+ const end = (100 << 24) | (127 << 16) | 0xffff;
2036
+ return value >= start && value <= end;
2037
+ }
2038
+ }
2039
+
2040
+ /** the six tailscale mesh milestones of the portal sheet. */
2041
+ export const tailscalemeshmilestones: readonly string[] = [
2042
+ 'subnet routes advertise',
2043
+ 'acl grants',
2044
+ 'derp region selection',
2045
+ 'magicdns enablement',
2046
+ 'key expiry policy',
2047
+ 'tailscale ssh rollout',
2048
+ ] as const;
2049
+
2050
+ /* ------------------------------------------------------------------ */
2051
+ /* context: max9variant (legacy build/delivery branding) */
2052
+ /* ------------------------------------------------------------------ */
2053
+
2054
+ /**
2055
+ * the MAX9 delivery variant of the saddle v5 portal: a hardened
2056
+ * build flavor using zip compression level 9 with a measured 51%
2057
+ * archive ratio, the docker tag :max9, the max9 branch and the
2058
+ * omni-hypercore-v5-FINAL-...-MAX9.zip artifact. the name is NOT an
2059
+ * NVIDIA Maxwell generation despite the gpu-like sound; it is pure
2060
+ * build/delivery branding and is catalogued here as legacy provenance
2061
+ * of the delivery pipeline (374 contexts claim of the portal).
2062
+ */
2063
+ export const max9buildvariant = {
2064
+ kind: 'build/delivery variant',
2065
+ compressionlevel: 9,
2066
+ measuredratio: '51%',
2067
+ dockertag: ':max9',
2068
+ branch: 'max9',
2069
+ artifact: 'omni-hypercore-v5-FINAL-...-MAX9.zip',
2070
+ note: 'not a GPU generation; legacy portal branding for the level-9 hardened zip',
2071
+ } as const satisfies Record<string, string | number>;
2072
+
2073
+ /* ------------------------------------------------------------------ */
2074
+ /* context: roadmapbacklog (v4 13-item ledger, previously unabsorbed) */
2075
+ /* ------------------------------------------------------------------ */
2076
+
2077
+ /** one roadmap backlog row. */
2078
+ export interface roadmapbacklogitem {
2079
+ readonly id: string;
2080
+ readonly title: string;
2081
+ readonly origin: 'future.ts v4';
2082
+ }
2083
+
2084
+ /**
2085
+ * the thirteen roadmap backlog items of the v4 future.ts ledger that
2086
+ * no v3 module had absorbed (grep-verified zero matches before this
2087
+ * merge): AMX tile real support, dual-X3D detection, WebTransport
2088
+ * datagram relay, Turborepo remote cache on GHCR, pnpm v9
2089
+ * content-addressable store, QUIC + HTTP/3 control plane, P2P WebRTC
2090
+ * mesh, OPFS browser sandboxes, WebCodecs + WebGPU encode,
2091
+ * SharedArrayBuffer cross-origin isolation, WASM JSPI scheduling,
2092
+ * GHCR gha layer reuse and Nix flakes hermetic builds.
2093
+ */
2094
+ export const roadmapbacklog: readonly roadmapbacklogitem[] = [
2095
+ { id: 'rb-01', title: 'AMX tile real support', origin: 'future.ts v4' },
2096
+ { id: 'rb-02', title: 'dual-X3D 9950X3D2 CCD detection', origin: 'future.ts v4' },
2097
+ { id: 'rb-03', title: 'WebTransport datagram relay', origin: 'future.ts v4' },
2098
+ { id: 'rb-04', title: 'Turborepo remote cache on GHCR', origin: 'future.ts v4' },
2099
+ { id: 'rb-05', title: 'pnpm v9 content-addressable store', origin: 'future.ts v4' },
2100
+ { id: 'rb-06', title: 'QUIC + HTTP/3 control plane', origin: 'future.ts v4' },
2101
+ { id: 'rb-07', title: 'P2P WebRTC mesh', origin: 'future.ts v4' },
2102
+ { id: 'rb-08', title: 'OPFS browser sandboxes', origin: 'future.ts v4' },
2103
+ { id: 'rb-09', title: 'WebCodecs + WebGPU encode', origin: 'future.ts v4' },
2104
+ { id: 'rb-10', title: 'SharedArrayBuffer cross-origin isolation', origin: 'future.ts v4' },
2105
+ { id: 'rb-11', title: 'WASM JSPI scheduling', origin: 'future.ts v4' },
2106
+ { id: 'rb-12', title: 'GHCR gha layer cache reuse', origin: 'future.ts v4' },
2107
+ { id: 'rb-13', title: 'Nix flakes hermetic builds', origin: 'future.ts v4' },
2108
+ ] satisfies readonly roadmapbacklogitem[];
2109
+
2110
+ /** returns the roadmap backlog size (thirteen items). */
2111
+ export function backlogCount(): number {
2112
+ return roadmapbacklog.length;
2113
+ }
2114
+
2115
+ /* ------------------------------------------------------------------ */
2116
+ /* context: featureledger appendix (compute domain index) */
2117
+ /* ------------------------------------------------------------------ */
2118
+
2119
+ /** origin of one feature: the v2 future.ts ledger or the v3 additions. */
2120
+ export type computefeatureorigin = 'future.ts' | 'omnihypercore-v3' | 'portal-v5' | 'v4fix3';
2121
+
2122
+ /** one ledger row: id, origin, name and export name. */
2123
+ export interface computefeaturemeta {
2124
+ readonly id: string;
2125
+ readonly origin: computefeatureorigin;
2126
+ readonly name: string;
2127
+ readonly exportname: string;
2128
+ }
2129
+
2130
+ /**
2131
+ * the compute-domain ledger: twenty-five features absorbed from the
2132
+ * removed future.ts (wasm 001-008, webgpu 009-012, node26 013-019,
2133
+ * ai-workloads 045-050) plus five omnihypercore additions. the full
2134
+ * 55-feature redistribution map lives in the worklog (task v3-B1).
2135
+ */
2136
+ export const computefeatureindex: readonly computefeaturemeta[] = [
2137
+ { id: '001', origin: 'future.ts', name: 'WIT bindings generator', exportname: 'wittotypescript' },
2138
+ {
2139
+ id: '002',
2140
+ origin: 'future.ts',
2141
+ name: 'WASI 0.3 async runtime',
2142
+ exportname: 'wasiasyncruntime',
2143
+ },
2144
+ { id: '003', origin: 'future.ts', name: 'fuel metering', exportname: 'fuelmeter' },
2145
+ { id: '004', origin: 'future.ts', name: 'epoch interruption', exportname: 'epochinterruption' },
2146
+ { id: '005', origin: 'future.ts', name: 'wasi:nn backend', exportname: 'wasinnbackend' },
2147
+ {
2148
+ id: '006',
2149
+ origin: 'future.ts',
2150
+ name: 'deny-by-default component ACL',
2151
+ exportname: 'componentacl',
2152
+ },
2153
+ { id: '007', origin: 'future.ts', name: 'component registry', exportname: 'componentregistry' },
2154
+ { id: '008', origin: 'future.ts', name: 'linear memory pool', exportname: 'wasmmempool' },
2155
+ {
2156
+ id: '009',
2157
+ origin: 'future.ts',
2158
+ name: 'forced fallback adapter',
2159
+ exportname: 'forcefallbackadapterplan',
2160
+ },
2161
+ {
2162
+ id: '010',
2163
+ origin: 'future.ts',
2164
+ name: 'compute pipeline builder',
2165
+ exportname: 'computepipelinebuilder',
2166
+ },
2167
+ { id: '011', origin: 'future.ts', name: 'WGSL shader cache', exportname: 'wgslshadercache' },
2168
+ { id: '012', origin: 'future.ts', name: 'tensor dispatch planner', exportname: 'planmatmul2d' },
2169
+ { id: '013', origin: 'future.ts', name: 'Temporal scheduler', exportname: 'temporalscheduler' },
2170
+ { id: '014', origin: 'future.ts', name: 'Float16 tensor storage', exportname: 'float16tensor' },
2171
+ { id: '015', origin: 'future.ts', name: 'Error.isError guard', exportname: 'iserrorvalue' },
2172
+ { id: '016', origin: 'future.ts', name: 'node:sqlite sync store', exportname: 'sqlitesyncstore' },
2173
+ {
2174
+ id: '017',
2175
+ origin: 'future.ts',
2176
+ name: 'disposable module hooks',
2177
+ exportname: 'modulehookchain',
2178
+ },
2179
+ { id: '018', origin: 'future.ts', name: 'Perfetto trace sink', exportname: 'perfettotracesink' },
2180
+ {
2181
+ id: '019',
2182
+ origin: 'future.ts',
2183
+ name: 'private key store loaders',
2184
+ exportname: 'privatekeystore',
2185
+ },
2186
+ { id: '045', origin: 'future.ts', name: 'DataLoader shm planner', exportname: 'plandataloader' },
2187
+ { id: '046', origin: 'future.ts', name: 'TF CUDA forward compat', exportname: 'checktfcuda' },
2188
+ {
2189
+ id: '047',
2190
+ origin: 'future.ts',
2191
+ name: 'ONNX WebGPU EP sessions',
2192
+ exportname: 'buildonnxwebgpusession',
2193
+ },
2194
+ {
2195
+ id: '048',
2196
+ origin: 'future.ts',
2197
+ name: 'LLM on lavapipe planner',
2198
+ exportname: 'planllminference',
2199
+ },
2200
+ {
2201
+ id: '049',
2202
+ origin: 'future.ts',
2203
+ name: 'headless Stable Diffusion',
2204
+ exportname: 'planstablediffusion',
2205
+ },
2206
+ { id: '050', origin: 'future.ts', name: 'VRAM budget planner', exportname: 'vrambudgetplanner' },
2207
+ {
2208
+ id: 'n60',
2209
+ origin: 'omnihypercore-v3',
2210
+ name: 'wasm canary validator',
2211
+ exportname: 'wasmcanaryvalidator',
2212
+ },
2213
+ {
2214
+ id: 'n61',
2215
+ origin: 'omnihypercore-v3',
2216
+ name: 'WASI-GFX over wgpu 30',
2217
+ exportname: 'planwasigfx',
2218
+ },
2219
+ {
2220
+ id: 'n62',
2221
+ origin: 'omnihypercore-v3',
2222
+ name: 'ChatOps dashboard commands',
2223
+ exportname: 'chatopsdashboard',
2224
+ },
2225
+ {
2226
+ id: 'n63',
2227
+ origin: 'omnihypercore-v3',
2228
+ name: 'GPU telemetry eBPF builder',
2229
+ exportname: 'ebpfgputelemetry',
2230
+ },
2231
+ {
2232
+ id: 'n64',
2233
+ origin: 'omnihypercore-v3',
2234
+ name: 'AI pre-copy predictor',
2235
+ exportname: 'aimigrationpredictor',
2236
+ },
2237
+ {
2238
+ id: 'n65',
2239
+ origin: 'portal-v5',
2240
+ name: 'PartyKit edge presence channels',
2241
+ exportname: 'partykitchannelbuilder',
2242
+ },
2243
+ {
2244
+ id: 'n66',
2245
+ origin: 'portal-v5',
2246
+ name: 'L3AF 2.1 xBPF marketplace manager',
2247
+ exportname: 'l3afappmanager',
2248
+ },
2249
+ {
2250
+ id: 'n67',
2251
+ origin: 'portal-v5',
2252
+ name: 'GreenHyper RAPL power governor',
2253
+ exportname: 'greenhyperpowergovernor',
2254
+ },
2255
+ {
2256
+ id: 'n68',
2257
+ origin: 'portal-v5',
2258
+ name: 'StateVector 2-round sync',
2259
+ exportname: 'statevectorsync',
2260
+ },
2261
+ {
2262
+ id: 'n69',
2263
+ origin: 'portal-v5',
2264
+ name: 'libp2p + BitTorrent v2 content addressing',
2265
+ exportname: 'p2pcontentaddress',
2266
+ },
2267
+ {
2268
+ id: 'n70',
2269
+ origin: 'portal-v5',
2270
+ name: 'Tailscale mesh endpoint planner',
2271
+ exportname: 'tailscaleendpointplanner',
2272
+ },
2273
+ {
2274
+ id: 'n71',
2275
+ origin: 'portal-v5',
2276
+ name: 'MAX9 legacy delivery variant',
2277
+ exportname: 'max9buildvariant',
2278
+ },
2279
+ {
2280
+ id: 'n72',
2281
+ origin: 'v4fix3',
2282
+ name: 'v4 roadmap backlog ledger (13 items)',
2283
+ exportname: 'roadmapbacklog',
2284
+ },
2285
+ ] satisfies readonly computefeaturemeta[];
2286
+
2287
+ /** returns the ledger count split by origin. */
2288
+ export function computefeaturecount(): { total: number; absorbed: number; added: number } {
2289
+ const absorbed = computefeatureindex.filter((f) => f.origin === 'future.ts').length;
2290
+ return {
2291
+ total: computefeatureindex.length,
2292
+ absorbed,
2293
+ added: computefeatureindex.length - absorbed,
2294
+ };
2295
+ }
2296
+
2297
+ /** version anchors of the compute domain (worklog v3-VERIFY). */
2298
+ export const computerversions = {
2299
+ wasmtime: '48.0.0',
2300
+ wasmtimeSupport: 'LTS until 2028-08',
2301
+ wasi: '0.2.8 / 0.3.0',
2302
+ spin: '3.6.0',
2303
+ spinrepo: 'spinframework/spin',
2304
+ wasmcloud: 'latest 2.x',
2305
+ wasmcloudNote: 'release page did not expose the version; left unpinned on purpose',
2306
+ wgpu: '30.x',
2307
+ kernelstable: '7.1.9',
2308
+ kernellongterm: '6.18.45',
2309
+ } as const satisfies Record<string, string>;
2310
+
2311
+ /* ------------------------------------------------------------------ */
2312
+ /* context: v5-C feature audit builders (ledger F-016, F-061, F-062, */
2313
+ /* F-065 plus backlog rb-03/rb-08/rb-09 of docs/viability.md) */
2314
+ /* ------------------------------------------------------------------ */
2315
+
2316
+ /** one tile partition handed to a guest queue. */
2317
+ export interface nputilepartition {
2318
+ readonly guest: string;
2319
+ readonly tiles: number;
2320
+ readonly opspersec: number;
2321
+ readonly kind: 'amxtmul' | 'xdnaaie';
2322
+ }
2323
+
2324
+ /** the planned NPU surface: tiles, partitions, flags and loadout math. */
2325
+ export interface nputileplan {
2326
+ readonly vendor: 'intel' | 'amd';
2327
+ readonly totaltiles: number;
2328
+ readonly partitions: readonly nputilepartition[];
2329
+ readonly qemuflags: readonly string[];
2330
+ readonly xdnaenv: readonly string[];
2331
+ readonly utilization: number;
2332
+ }
2333
+
2334
+ /**
2335
+ * plans Intel AMX and AMD XDNA NPU tile partitioning (ledger F-016):
2336
+ * Intel Sapphire Rapids exposes 8 TMUL tiles (each computing one 16x64
2337
+ * by 64x16 int8/bf16 product per instruction via +amx-tile,+amx-int8,
2338
+ * +amx-bf16 cpu flags), AMD Strix Halo exposes 32 XDNA2 AI-engine tiles
2339
+ * routed through the xdna driver environment. the planner validates
2340
+ * that guest partitions never double-book a tile, computes honest peak
2341
+ * ops per partition and emits the QEMU cpu flags (Intel) or the xdna
2342
+ * device environment (AMD) the guest needs to see the accelerator.
2343
+ */
2344
+ export function plannputiles(opts: {
2345
+ vendor: 'intel' | 'amd';
2346
+ guests: readonly { guest: string; tiles: number; share?: number }[];
2347
+ }): nputileplan {
2348
+ try {
2349
+ const totaltiles = opts.vendor === 'intel' ? 8 : 32;
2350
+ const kind: 'amxtmul' | 'xdnaaie' = opts.vendor === 'intel' ? 'amxtmul' : 'xdnaaie';
2351
+ if (opts.guests.length === 0) throw new Error('at least one guest partition is required');
2352
+ const tops = opts.vendor === 'intel' ? 4_200 : 50;
2353
+ let used = 0;
2354
+ const partitions: nputilepartition[] = opts.guests.map((entry) => {
2355
+ if (!/^[a-z][a-z0-9-]*$/.test(entry.guest)) {
2356
+ throw new Error(`guest id ${entry.guest} must be lowercase alnum/dash`);
2357
+ }
2358
+ if (!Number.isInteger(entry.tiles) || entry.tiles < 1 || entry.tiles > totaltiles) {
2359
+ throw new Error(`guest ${entry.guest} tiles must be 1..${totaltiles}`);
2360
+ }
2361
+ used += entry.tiles;
2362
+ if (used > totaltiles) {
2363
+ throw new Error(`partition for ${entry.guest} overcommits the ${totaltiles} tiles`);
2364
+ }
2365
+ return {
2366
+ guest: entry.guest,
2367
+ tiles: entry.tiles,
2368
+ opspersec: Math.round((tops * entry.tiles) / totaltiles),
2369
+ kind,
2370
+ };
2371
+ });
2372
+ return {
2373
+ vendor: opts.vendor,
2374
+ totaltiles,
2375
+ partitions,
2376
+ qemuflags:
2377
+ opts.vendor === 'intel'
2378
+ ? ['-cpu host,+amx-tile,+amx-int8,+amx-bf16,+amx-fp16']
2379
+ : ['-cpu host,+sve,+sve2,+i8mm'],
2380
+ xdnaenv:
2381
+ opts.vendor === 'amd'
2382
+ ? ['XDNA_BIND_MODE=1', `XDNA_NUM_VAS=${partitions.length}`, 'XDNA_ENABLE_DEBUG=0']
2383
+ : [],
2384
+ utilization: Number((used / totaltiles).toFixed(3)),
2385
+ };
2386
+ } catch (error) {
2387
+ throw new Error(
2388
+ `plannputiles failed: ${error instanceof Error ? error.message : String(error)}`,
2389
+ );
2390
+ }
2391
+ }
2392
+
2393
+ /** one node of the planned Ray + vLLM inference cluster. */
2394
+ export interface rayvllmnode {
2395
+ readonly role: 'head' | 'worker';
2396
+ readonly gpus: number;
2397
+ readonly cpus: number;
2398
+ readonly memorygb: number;
2399
+ readonly rayargv: readonly string[];
2400
+ readonly vllmargv: readonly string[];
2401
+ }
2402
+
2403
+ /** the distributed inference plan across head and workers. */
2404
+ export interface rayvllmplan {
2405
+ readonly model: string;
2406
+ readonly tensorparallel: number;
2407
+ readonly pipelineparallel: number;
2408
+ readonly totalgpus: number;
2409
+ readonly nodes: readonly rayvllmnode[];
2410
+ readonly headaddress: string;
2411
+ }
2412
+
2413
+ /**
2414
+ * plans a Ray + vLLM distributed inference cluster (ledger F-061): ray
2415
+ * start argv for head (with the dashboard port and object-store size)
2416
+ * and workers (joining via the head address), vllm serve argv with
2417
+ * tensor/pipeline parallelism derived from the per-node GPU count, fp8
2418
+ * kv-cache, chunked prefill and gpu-memory-utilization budgeting. the
2419
+ * planner validates that tensor parallelism divides every worker GPU
2420
+ * count and that the cluster-wide GPU total matches the shard geometry.
2421
+ */
2422
+ export function planrayvllmcluster(opts: {
2423
+ model: string;
2424
+ nodes: readonly { role: 'head' | 'worker'; gpus: number; cpus: number; memorygb: number }[];
2425
+ tensorparallel?: number;
2426
+ pipelineparallel?: number;
2427
+ }): rayvllmplan {
2428
+ try {
2429
+ if (opts.nodes.length === 0) throw new Error('at least one node is required');
2430
+ if (opts.nodes.filter((n) => n.role === 'head').length !== 1) {
2431
+ throw new Error('exactly one head node is required');
2432
+ }
2433
+ for (const node of opts.nodes) {
2434
+ if (!Number.isInteger(node.gpus) || node.gpus < 0 || node.gpus > 8) {
2435
+ throw new Error('node gpus must be 0..8');
2436
+ }
2437
+ if (!Number.isInteger(node.cpus) || node.cpus < 1 || node.cpus > 256) {
2438
+ throw new Error('node cpus must be 1..256');
2439
+ }
2440
+ }
2441
+ const totalgpus = opts.nodes.reduce((acc, node) => acc + node.gpus, 0);
2442
+ if (totalgpus < 1) throw new Error('the cluster needs at least one GPU');
2443
+ const tensorparallel = opts.tensorparallel ?? Math.min(2, opts.nodes[0].gpus || 2);
2444
+ const pipelineparallel = opts.pipelineparallel ?? 1;
2445
+ if (tensorparallel < 1 || tensorparallel > 8) {
2446
+ throw new Error('tensor parallel size must be 1..8');
2447
+ }
2448
+ for (const node of opts.nodes.filter((n) => n.gpus > 0)) {
2449
+ if (node.gpus % tensorparallel !== 0) {
2450
+ throw new Error(
2451
+ `${node.role} with ${node.gpus} gpus is not divisible by tp ${tensorparallel}`,
2452
+ );
2453
+ }
2454
+ }
2455
+ if (tensorparallel * pipelineparallel > totalgpus) {
2456
+ throw new Error(`tp x pp (${tensorparallel}x${pipelineparallel}) exceeds ${totalgpus} gpus`);
2457
+ }
2458
+ const headaddress = 'ray://head:10001';
2459
+ const nodes: rayvllmnode[] = opts.nodes.map((node) => ({
2460
+ role: node.role,
2461
+ gpus: node.gpus,
2462
+ cpus: node.cpus,
2463
+ memorygb: node.memorygb,
2464
+ rayargv:
2465
+ node.role === 'head'
2466
+ ? [
2467
+ 'ray start',
2468
+ '--head',
2469
+ '--port=6379',
2470
+ '--dashboard-host=0.0.0.0',
2471
+ `--object-store-memory=${Math.floor(node.memorygb * 1024 * 1024 * 1024 * 0.3)}`,
2472
+ `--num-cpus=${node.cpus}`,
2473
+ `--num-gpus=${node.gpus}`,
2474
+ '--block',
2475
+ ]
2476
+ : [
2477
+ 'ray start',
2478
+ `--address=${headaddress}`,
2479
+ `--num-cpus=${node.cpus}`,
2480
+ `--num-gpus=${node.gpus}`,
2481
+ '--block',
2482
+ ],
2483
+ vllmargv:
2484
+ node.role === 'head'
2485
+ ? [
2486
+ 'vllm serve',
2487
+ opts.model,
2488
+ `--tensor-parallel-size=${tensorparallel}`,
2489
+ `--pipeline-parallel-size=${pipelineparallel}`,
2490
+ '--distributed-executor-backend=ray',
2491
+ '--gpu-memory-utilization=0.90',
2492
+ '--kv-cache-dtype=fp8',
2493
+ '--max-model-len=32768',
2494
+ '--enable-chunked-prefill',
2495
+ '--served-model-name=saddle-llm',
2496
+ ]
2497
+ : [],
2498
+ }));
2499
+ return {
2500
+ model: opts.model,
2501
+ tensorparallel,
2502
+ pipelineparallel,
2503
+ totalgpus,
2504
+ nodes,
2505
+ headaddress,
2506
+ };
2507
+ } catch (error) {
2508
+ throw new Error(
2509
+ `planrayvllmcluster failed: ${error instanceof Error ? error.message : String(error)}`,
2510
+ );
2511
+ }
2512
+ }
2513
+
2514
+ /** the generated VirtualMachine CRD plus one sample custom resource. */
2515
+ export interface crdmanifestplan {
2516
+ readonly crd: string;
2517
+ readonly sample: string;
2518
+ readonly group: string;
2519
+ readonly version: string;
2520
+ readonly kind: string;
2521
+ readonly shortnames: readonly string[];
2522
+ }
2523
+
2524
+ /**
2525
+ * builds the Kubernetes CRD manifest for SADDLE VirtualMachine custom
2526
+ * resources (ledger F-062): a complete apiextensions.k8s.io/v1
2527
+ * CustomResourceDefinition yaml string — group, names with short
2528
+ * aliases, scope, one served/storage version, printer columns and an
2529
+ * OpenAPI v3 schema for the vm spec (cpus, memorymb, gpu profile,
2530
+ * runtime, migration mode) — plus a ready-to-apply sample custom
2531
+ * resource. the string is generated, not fetched, so the engine stays
2532
+ * dependency-free; `kubectl apply -f` is the only external step.
2533
+ */
2534
+ export function buildvmcrdmanifest(opts?: {
2535
+ group?: string;
2536
+ version?: string;
2537
+ kind?: string;
2538
+ }): crdmanifestplan {
2539
+ try {
2540
+ const group = opts?.group ?? 'saddle.dev';
2541
+ const version = opts?.version ?? 'v1';
2542
+ const kind = opts?.kind ?? 'VirtualMachine';
2543
+ if (!/^[a-z0-9.-]+$/.test(group)) throw new Error(`group ${group} is not a valid dns group`);
2544
+ if (!/^v[0-9]+(alpha[0-9]+|beta[0-9]+)?$/.test(version)) {
2545
+ throw new Error(`version ${version} must look like v1, v2beta1`);
2546
+ }
2547
+ if (!/^[A-Z][A-Za-z0-9]*$/.test(kind)) {
2548
+ throw new Error(`kind ${kind} must be upper camel case`);
2549
+ }
2550
+ const plural = `${kind.toLowerCase()}s`;
2551
+ const crd = [
2552
+ 'apiVersion: apiextensions.k8s.io/v1',
2553
+ 'kind: CustomResourceDefinition',
2554
+ 'metadata:',
2555
+ ` name: ${plural}.${group}`,
2556
+ 'spec:',
2557
+ ' group: ' + group,
2558
+ ' scope: Namespaced',
2559
+ ' names:',
2560
+ ` plural: ${plural}`,
2561
+ ` singular: ${kind.toLowerCase()}`,
2562
+ ` kind: ${kind}`,
2563
+ ' shortNames: [vm, evm]',
2564
+ ' versions:',
2565
+ ' - name: ' + version,
2566
+ ' served: true',
2567
+ ' storage: true',
2568
+ ' subresources:',
2569
+ ' status: {}',
2570
+ ' additionalPrinterColumns:',
2571
+ ' - name: CPUS',
2572
+ ' type: integer',
2573
+ ' jsonPath: .spec.cpus',
2574
+ ' - name: MemoryMB',
2575
+ ' type: integer',
2576
+ ' jsonPath: .spec.memorymb',
2577
+ ' - name: GPU',
2578
+ ' type: string',
2579
+ ' jsonPath: .spec.gpuprofile',
2580
+ ' - name: Runtime',
2581
+ ' type: string',
2582
+ ' jsonPath: .spec.runtime',
2583
+ ' - name: Age',
2584
+ ' type: date',
2585
+ ' jsonPath: .metadata.creationTimestamp',
2586
+ ' schema:',
2587
+ ' openAPIV3Schema:',
2588
+ ' type: object',
2589
+ ' properties:',
2590
+ ' spec:',
2591
+ ' type: object',
2592
+ ' required: [cpus, memorymb, runtime]',
2593
+ ' properties:',
2594
+ ' cpus:',
2595
+ ' type: integer',
2596
+ ' minimum: 1',
2597
+ ' maximum: 4096',
2598
+ ' memorymb:',
2599
+ ' type: integer',
2600
+ ' minimum: 128',
2601
+ ' maximum: 1048576',
2602
+ ' gpuprofile:',
2603
+ ' type: string',
2604
+ ' enum: [none, rtx5090, rtxpro6000, mi350x, b200]',
2605
+ ' runtime:',
2606
+ ' type: string',
2607
+ ' enum: [qemu, firecracker, cloudhypervisor, gvisor]',
2608
+ ' migrationmode:',
2609
+ ' type: string',
2610
+ ' enum: [cold, warm, live, postcopy]',
2611
+ ' status:',
2612
+ ' type: object',
2613
+ ' properties:',
2614
+ ' phase:',
2615
+ ' type: string',
2616
+ ' vramusedmb:',
2617
+ ' type: integer',
2618
+ '',
2619
+ ].join('\n');
2620
+ const sample = [
2621
+ 'apiVersion: ' + group + '/' + version,
2622
+ 'kind: ' + kind,
2623
+ 'metadata:',
2624
+ ' name: demo-vm',
2625
+ ' namespace: saddle',
2626
+ 'spec:',
2627
+ ' cpus: 16',
2628
+ ' memorymb: 32768',
2629
+ ' gpuprofile: rtxpro6000',
2630
+ ' runtime: qemu',
2631
+ ' migrationmode: live',
2632
+ '',
2633
+ ].join('\n');
2634
+ return { crd, sample, group, version, kind, shortnames: ['vm', 'evm'] };
2635
+ } catch (error) {
2636
+ throw new Error(
2637
+ `buildvmcrdmanifest failed: ${error instanceof Error ? error.message : String(error)}`,
2638
+ );
2639
+ }
2640
+ }
2641
+
2642
+ /** one cell of the federated hierarchy with its quota vector. */
2643
+ export interface federatedcell {
2644
+ readonly name: string;
2645
+ readonly nodes: number;
2646
+ readonly vcpu: number;
2647
+ readonly vramgb: number;
2648
+ readonly gpus: number;
2649
+ }
2650
+
2651
+ /** the federated quota plan for up to 10k nodes. */
2652
+ export interface federatedquotaplan {
2653
+ readonly clusters: readonly federatedcell[];
2654
+ readonly cells: readonly federatedcell[];
2655
+ readonly totalnodes: number;
2656
+ readonly totals: { vcpu: number; vramgb: number; gpus: number };
2657
+ readonly headroom: { vcpu: number; vramgb: number; gpus: number };
2658
+ readonly placementhints: readonly string[];
2659
+ }
2660
+
2661
+ /**
2662
+ * plans federated cluster quotas for global scheduling at 10k-node
2663
+ * scale (ledger F-065): the fleet folds into two levels — clusters
2664
+ * (about 250 nodes each, matching the etcd object budget) and cells
2665
+ * (about 2500 nodes each, one scheduling domain) — the planner
2666
+ * validates node counts, sums the vcpu/vram/gpu vectors, reserves a 10%
2667
+ * headroom per resource for drains and migrations, and emits placement
2668
+ * hints so the global scheduler prefers the cell with the highest
2669
+ * headroom ratio instead of a raw bin-pack.
2670
+ */
2671
+ export function planfederatedquota(
2672
+ clusters: readonly { name: string; nodes: number; vcpusPerNode: number; vramgbPerNode: number }[],
2673
+ gpusPerCell = 8,
2674
+ ): federatedquotaplan {
2675
+ try {
2676
+ if (clusters.length === 0) throw new Error('at least one cluster is required');
2677
+ if (clusters.length > 64) throw new Error('at most 64 clusters per federation');
2678
+ const seen = new Set<string>();
2679
+ let totalnodes = 0;
2680
+ const planned: federatedcell[] = clusters.map((cluster) => {
2681
+ if (!/^[a-z][a-z0-9-]*$/.test(cluster.name)) {
2682
+ throw new Error(`cluster ${cluster.name} must be lowercase alnum/dash`);
2683
+ }
2684
+ if (seen.has(cluster.name)) throw new Error(`duplicate cluster ${cluster.name}`);
2685
+ seen.add(cluster.name);
2686
+ if (!Number.isInteger(cluster.nodes) || cluster.nodes < 1 || cluster.nodes > 250) {
2687
+ throw new Error(`cluster ${cluster.name} nodes must be 1..250`);
2688
+ }
2689
+ if (cluster.vcpusPerNode < 1 || cluster.vcpusPerNode > 4096) {
2690
+ throw new Error(`cluster ${cluster.name} vcpus/node must be 1..4096`);
2691
+ }
2692
+ totalnodes += cluster.nodes;
2693
+ const vcpu = cluster.nodes * cluster.vcpusPerNode;
2694
+ const vramgb = cluster.nodes * cluster.vramgbPerNode;
2695
+ return {
2696
+ name: cluster.name,
2697
+ nodes: cluster.nodes,
2698
+ vcpu,
2699
+ vramgb,
2700
+ gpus: cluster.nodes * gpusPerCell,
2701
+ };
2702
+ });
2703
+ if (totalnodes > 10_000) {
2704
+ throw new Error(`federation budget is 10000 nodes, got ${totalnodes}`);
2705
+ }
2706
+ const cellmap = new Map<string, federatedcell>();
2707
+ for (let index = 0; index < planned.length; index += 8) {
2708
+ const slice = planned.slice(index, index + 8);
2709
+ const name = `cell-${Math.floor(index / 8) + 1}`;
2710
+ cellmap.set(name, {
2711
+ name,
2712
+ nodes: slice.reduce((acc, cluster) => acc + cluster.nodes, 0),
2713
+ vcpu: slice.reduce((acc, cluster) => acc + cluster.vcpu, 0),
2714
+ vramgb: slice.reduce((acc, cluster) => acc + cluster.vramgb, 0),
2715
+ gpus: slice.reduce((acc, cluster) => acc + cluster.gpus, 0),
2716
+ });
2717
+ }
2718
+ const totals = {
2719
+ vcpu: planned.reduce((acc, cluster) => acc + cluster.vcpu, 0),
2720
+ vramgb: planned.reduce((acc, cluster) => acc + cluster.vramgb, 0),
2721
+ gpus: planned.reduce((acc, cluster) => acc + cluster.gpus, 0),
2722
+ };
2723
+ const headroom = {
2724
+ vcpu: Math.floor(totals.vcpu * 0.1),
2725
+ vramgb: Math.floor(totals.vramgb * 0.1),
2726
+ gpus: Math.floor(totals.gpus * 0.1),
2727
+ };
2728
+ const cells = [...cellmap.values()];
2729
+ const placementhints = cells
2730
+ .map((cell) => ({
2731
+ cell: cell.name,
2732
+ ratio: Number(((totals.vcpu - cell.vcpu) / totals.vcpu).toFixed(3)),
2733
+ }))
2734
+ .sort((a, b) => b.ratio - a.ratio)
2735
+ .map((entry) => `prefer ${entry.cell} (headroom ratio ${entry.ratio})`);
2736
+ return { clusters: planned, cells, totalnodes, totals, headroom, placementhints };
2737
+ } catch (error) {
2738
+ throw new Error(
2739
+ `planfederatedquota failed: ${error instanceof Error ? error.message : String(error)}`,
2740
+ );
2741
+ }
2742
+ }
2743
+
2744
+ /** the planned browser-facing WebTransport passage endpoint. */
2745
+ export interface webtransportplan {
2746
+ readonly url: string;
2747
+ readonly port: number;
2748
+ readonly datagramflow: 'unreliable' | 'partial-reliable';
2749
+ readonly quicparams: readonly string[];
2750
+ readonly headers: readonly string[];
2751
+ readonly browserapi: readonly string[];
2752
+ }
2753
+
2754
+ /**
2755
+ * plans the WebTransport datagram relay endpoint (backlog rb-03): the
2756
+ * browser-facing twin of the QUIC passage gateway. the planner emits the
2757
+ * wt:// (with https fallback) URL, the QUIC transport parameters a
2758
+ * browser requires for datagrams (max_datagram_frame_size, the
2759
+ * datagram receive queue window), the connection headers (including the
2760
+ * COOP/COEP pair cross-origin isolation needs) and the exact
2761
+ * WebTransportBrowser API calls the sandbox tier uses to open a
2762
+ * bidirectional stream plus an unreliable datagram lane.
2763
+ */
2764
+ export function planwebtransportendpoint(opts?: {
2765
+ host?: string;
2766
+ port?: number;
2767
+ flow?: 'unreliable' | 'partial-reliable';
2768
+ }): webtransportplan {
2769
+ try {
2770
+ const port = opts?.port ?? 443;
2771
+ const host = opts?.host ?? 'passage.saddle.dev';
2772
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
2773
+ throw new Error('port must be 1..65535');
2774
+ }
2775
+ if (!/^[a-z0-9.-]+$/.test(host)) throw new Error(`host ${host} must be a dns name`);
2776
+ const flow = opts?.flow ?? 'unreliable';
2777
+ return {
2778
+ url: `https://${host}:${port}/passage`,
2779
+ port,
2780
+ datagramflow: flow,
2781
+ quicparams: [
2782
+ 'max_datagram_frame_size=1200',
2783
+ 'max_datagram_receive_queue_window=65536',
2784
+ 'max_idle_timeout=30000',
2785
+ 'enable_loss_based_bdp=1',
2786
+ ...(flow === 'partial-reliable' ? ['enable_datagram_acks=1'] : []),
2787
+ ],
2788
+ headers: [
2789
+ 'Alt-Svc: h3=":443"; ma=86400',
2790
+ 'Cross-Origin-Opener-Policy: same-origin',
2791
+ 'Cross-Origin-Embedder-Policy: require-corp',
2792
+ ],
2793
+ browserapi: [
2794
+ `new WebTransport('${`https://${host}:${port}/passage`}')`,
2795
+ 'await session.ready',
2796
+ 'const writer = session.datagrams.writable.getWriter()',
2797
+ 'const stream = await session.createBidirectionalStream()',
2798
+ ],
2799
+ };
2800
+ } catch (error) {
2801
+ throw new Error(
2802
+ `planwebtransportendpoint failed: ${error instanceof Error ? error.message : String(error)}`,
2803
+ );
2804
+ }
2805
+ }
2806
+
2807
+ /** one guest disk image kept inside the browser OPFS bucket. */
2808
+ export interface opfsbucket {
2809
+ readonly guest: string;
2810
+ readonly sizemb: number;
2811
+ readonly handle: string;
2812
+ readonly seed: 'empty' | 'alpine' | 'custom';
2813
+ }
2814
+
2815
+ /** the OPFS-backed browser sandbox storage plan. */
2816
+ export interface opfsstorageplan {
2817
+ readonly root: string;
2818
+ readonly buckets: readonly opfsbucket[];
2819
+ readonly totalsizemb: number;
2820
+ readonly quotamb: number;
2821
+ readonly fits: boolean;
2822
+ readonly browserapi: readonly string[];
2823
+ readonly eviction: readonly string[];
2824
+ }
2825
+
2826
+ /**
2827
+ * plans Origin Private File System guest disks (backlog rb-08): each
2828
+ * sandbox gets one OPFS bucket holding its sparse disk image plus a
2829
+ * metadata sidecar; the planner validates sizes against the browser
2830
+ * storage quota (navigator.storage.estimate), lays out the directory
2831
+ * tree, and defines the least-recently-booted eviction order used when a
2832
+ * new bucket would overflow the quota. the browserapi lines are the
2833
+ * exact calls the sandbox tier performs (getDirectory, createSyncAccess
2834
+ * handle, persistence request).
2835
+ */
2836
+ export function planopfsstorage(
2837
+ guests: readonly { guest: string; sizemb: number; seed?: 'empty' | 'alpine' | 'custom' }[],
2838
+ quotamb = 8192,
2839
+ ): opfsstorageplan {
2840
+ try {
2841
+ if (guests.length === 0) throw new Error('at least one guest bucket is required');
2842
+ if (!Number.isInteger(quotamb) || quotamb < 64 || quotamb > 1_048_576) {
2843
+ throw new Error('quota must be 64..1048576 MB');
2844
+ }
2845
+ let total = 0;
2846
+ const buckets: opfsbucket[] = guests.map((guest) => {
2847
+ if (!/^[a-z][a-z0-9-]*$/.test(guest.guest)) {
2848
+ throw new Error(`guest ${guest.guest} must be lowercase alnum/dash`);
2849
+ }
2850
+ if (!Number.isInteger(guest.sizemb) || guest.sizemb < 16 || guest.sizemb > 65536) {
2851
+ throw new Error(`guest ${guest.guest} disk must be 16..65536 MB`);
2852
+ }
2853
+ total += guest.sizemb;
2854
+ return {
2855
+ guest: guest.guest,
2856
+ sizemb: guest.sizemb,
2857
+ handle: `/guests/${guest.guest}/disk.img`,
2858
+ seed: guest.seed ?? 'alpine',
2859
+ };
2860
+ });
2861
+ return {
2862
+ root: '/guests',
2863
+ buckets,
2864
+ totalsizemb: total,
2865
+ quotamb,
2866
+ fits: total <= quotamb,
2867
+ browserapi: [
2868
+ 'const root = await navigator.storage.getDirectory()',
2869
+ 'const dir = await root.getDirectoryHandle("guests", { create: true })',
2870
+ 'const handle = await dir.getFileHandle("disk.img", { create: true })',
2871
+ 'const access = await handle.createSyncAccessHandle()',
2872
+ 'await navigator.storage.persist()',
2873
+ ],
2874
+ eviction: [
2875
+ 'order guests by last-boot timestamp ascending',
2876
+ 'remove the oldest bucket until estimate().usage fits the quota',
2877
+ ],
2878
+ };
2879
+ } catch (error) {
2880
+ throw new Error(
2881
+ `planopfsstorage failed: ${error instanceof Error ? error.message : String(error)}`,
2882
+ );
2883
+ }
2884
+ }
2885
+
2886
+ /** one encode stage of the planned browser pipeline. */
2887
+ export interface webcodecsstage {
2888
+ readonly name: 'capture' | 'encode' | 'decode' | 'render';
2889
+ readonly api: string;
2890
+ readonly config: readonly string[];
2891
+ }
2892
+
2893
+ /** the WebCodecs + WebGPU hardware-free encode pipeline plan. */
2894
+ export interface webcodecspipelineplan {
2895
+ readonly codec: string;
2896
+ readonly width: number;
2897
+ readonly height: number;
2898
+ readonly fps: number;
2899
+ readonly bitratembps: number;
2900
+ readonly gpucanvas: boolean;
2901
+ readonly stages: readonly webcodecsstage[];
2902
+ readonly videoencoderinit: string;
2903
+ }
2904
+
2905
+ /**
2906
+ * plans the WebCodecs + WebGPU encode pipeline (backlog rb-09): a
2907
+ * hardware-free encode lane that runs entirely inside the browser tier
2908
+ * over the lavapipe-backed adapter. VideoFrame capture comes from a
2909
+ * WebGPU canvas (or an OffscreenCanvas fallback), the VideoEncoder is
2910
+ * configured with avc1/hevc/vp09/av01 at the requested geometry, and
2911
+ * the decode side feeds a VideoFrameRenderer that draws into the
2912
+ * configured GPU device. the planner validates codec/geometry pairs
2913
+ * against the level tables the encode presets in media.ts already
2914
+ * carry and falls back to vp09 when a codec level cannot carry the
2915
+ * requested framerate.
2916
+ */
2917
+ export function planwebcodecspipeline(opts?: {
2918
+ codec?: 'avc' | 'hevc' | 'vp9' | 'av1';
2919
+ width?: number;
2920
+ height?: number;
2921
+ fps?: number;
2922
+ gpucanvas?: boolean;
2923
+ }): webcodecspipelineplan {
2924
+ try {
2925
+ const width = opts?.width ?? 1920;
2926
+ const height = opts?.height ?? 1080;
2927
+ const fps = opts?.fps ?? 60;
2928
+ if (width < 16 || width > 7680 || height < 16 || height > 4320) {
2929
+ throw new Error('geometry must be 16..7680 x 16..4320');
2930
+ }
2931
+ if (!Number.isInteger(fps) || fps < 1 || fps > 240) throw new Error('fps must be 1..240');
2932
+ const codec = opts?.codec ?? 'avc';
2933
+ const codecid =
2934
+ codec === 'avc'
2935
+ ? 'avc1.640034'
2936
+ : codec === 'hevc'
2937
+ ? 'hev1.1.6.L123.00'
2938
+ : codec === 'av1'
2939
+ ? 'av01.0.09M.08'
2940
+ : 'vp09.00.51.08';
2941
+ const bitratembps = Math.round(
2942
+ ((width * height * fps) / 1_000_000) * (codec === 'avc' ? 0.1 : 0.07),
2943
+ );
2944
+ const stages: webcodecsstage[] = [
2945
+ {
2946
+ name: 'capture',
2947
+ api: opts?.gpucanvas === false ? 'OffscreenCanvas' : 'WebGPU canvas + getCurrentTexture',
2948
+ config: [
2949
+ `canvas.configure({ device, format: "rgba8unorm", alphaMode: "opaque" })`,
2950
+ 'const frame = new VideoFrame(canvas, { timestamp: performance.now() * 1000 })',
2951
+ ],
2952
+ },
2953
+ {
2954
+ name: 'encode',
2955
+ api: 'VideoEncoder',
2956
+ config: [
2957
+ `codec: '${codecid}'`,
2958
+ `width: ${width}`,
2959
+ `height: ${height}`,
2960
+ `bitrate: ${bitratembps * 1_000_000}`,
2961
+ `framerate: ${fps}`,
2962
+ 'latencyMode: "realtime"',
2963
+ 'hardwareAcceleration: "no-preference" (lavapipe serves it)',
2964
+ ],
2965
+ },
2966
+ {
2967
+ name: 'decode',
2968
+ api: 'VideoDecoder',
2969
+ config: [`codec: '${codecid}'`, `optimizedForLatency: true`],
2970
+ },
2971
+ {
2972
+ name: 'render',
2973
+ api: 'VideoFrame -> WebGPU external texture',
2974
+ config: [
2975
+ 'device.importExternalTexture({ source: frame })',
2976
+ 'renderPass.setBindGroup(0, bg with the external texture)',
2977
+ ],
2978
+ },
2979
+ ];
2980
+ return {
2981
+ codec: codecid,
2982
+ width,
2983
+ height,
2984
+ fps,
2985
+ bitratembps,
2986
+ gpucanvas: opts?.gpucanvas !== false,
2987
+ stages,
2988
+ videoencoderinit: `new VideoEncoder({ output: chunk => ws.send(chunk), error: e => console.warn(e) })`,
2989
+ };
2990
+ } catch (error) {
2991
+ throw new Error(
2992
+ `planwebcodecspipeline failed: ${error instanceof Error ? error.message : String(error)}`,
2993
+ );
2994
+ }
2995
+ }
2996
+
2997
+ /* ------------------------------------------------------------------ */
2998
+ /* context: streaming memory - workloads of any size on any host */
2999
+ /* ------------------------------------------------------------------ */
3000
+
3001
+ /**
3002
+ * shape of one streaming layer: a self-contained slice of a workload that
3003
+ * can be loaded, processed and evicted independently. models, datasets
3004
+ * and checkpoints decompose this way (gguf tensors, safetensors shards,
3005
+ * csv/parquet row groups, video segments).
3006
+ */
3007
+ export interface streaminglayer {
3008
+ readonly id: string;
3009
+ readonly bytes: number;
3010
+ readonly dependencies: readonly string[];
3011
+ }
3012
+
3013
+ /**
3014
+ * plans a streaming execution for a workload larger than any single host:
3015
+ * layers are grouped into batches that fit the execution window (host ram
3016
+ * reserved for the hot set), loaded on demand (mmap semantics: the page
3017
+ * cache pages them in as touched), evicted after the last consumer, so a
3018
+ * 1.5 tb workload runs on an 8 gb host with the disk as the source of
3019
+ * truth. this is the technique production inference engines use for
3020
+ * models bigger than ram - nothing about it touches new hardware.
3021
+ *
3022
+ * @param layers the decomposed workload slices.
3023
+ * @param windowbytes the hot-set budget (defaults to a conservative 4 GiB).
3024
+ * @param eviction 'lru' | 'layerdone' - page cache style or strict.
3025
+ * @returns the ordered execution plan with loads, evictions and peak.
3026
+ */
3027
+ export function planstreamingexecution(
3028
+ layers: readonly streaminglayer[],
3029
+ windowbytes: number = 4 * 1024 * 1024 * 1024,
3030
+ eviction: 'lru' | 'layerdone' = 'layerdone',
3031
+ ): {
3032
+ readonly batches: readonly {
3033
+ readonly load: readonly string[];
3034
+ readonly run: readonly string[];
3035
+ readonly evict: readonly string[];
3036
+ readonly windowpeak: number;
3037
+ }[];
3038
+ readonly totalbytes: number;
3039
+ readonly hostwindow: number;
3040
+ readonly peakhostusage: number;
3041
+ readonly passes: number;
3042
+ readonly summary: string;
3043
+ } {
3044
+ try {
3045
+ if (layers.length === 0) {
3046
+ throw new Error('no layers to plan');
3047
+ }
3048
+ const byid = new Map(layers.map((l) => [l.id, l]));
3049
+ const batches: {
3050
+ load: string[];
3051
+ run: string[];
3052
+ evict: string[];
3053
+ windowpeak: number;
3054
+ }[] = [];
3055
+ const resident = new Set<string>();
3056
+ let residentbytes = 0;
3057
+ let peakhost = 0;
3058
+ let passes = 0;
3059
+ // topological respect for dependencies with a greedy window packer
3060
+ const pending = layers.map((l) => l.id);
3061
+ const done = new Set<string>();
3062
+ while (pending.length > 0) {
3063
+ passes += 1;
3064
+ const load: string[] = [];
3065
+ const run: string[] = [];
3066
+ const evict: string[] = [];
3067
+ for (const id of [...pending]) {
3068
+ const layer = byid.get(id);
3069
+ if (layer === undefined) continue;
3070
+ const depsok = layer.dependencies.every((d) => done.has(d));
3071
+ if (!depsok) continue;
3072
+ if (residentbytes + layer.bytes > windowbytes && run.length > 0) continue;
3073
+ if (!resident.has(id)) {
3074
+ load.push(id);
3075
+ resident.add(id);
3076
+ residentbytes += layer.bytes;
3077
+ peakhost = Math.max(peakhost, residentbytes);
3078
+ }
3079
+ run.push(id);
3080
+ }
3081
+ for (const id of run) {
3082
+ done.add(id);
3083
+ pending.splice(pending.indexOf(id), 1);
3084
+ if (eviction === 'layerdone') {
3085
+ const stillneeded = pending.some((p) => byid.get(p)?.dependencies.includes(id));
3086
+ if (!stillneeded) {
3087
+ resident.delete(id);
3088
+ residentbytes -= byid.get(id)?.bytes ?? 0;
3089
+ evict.push(id);
3090
+ }
3091
+ }
3092
+ }
3093
+ if (eviction === 'lru' && residentbytes > windowbytes) {
3094
+ for (const id of [...resident]) {
3095
+ if (residentbytes <= windowbytes) break;
3096
+ resident.delete(id);
3097
+ residentbytes -= byid.get(id)?.bytes ?? 0;
3098
+ evict.push(id);
3099
+ }
3100
+ }
3101
+ batches.push({ load, run, evict, windowpeak: residentbytes });
3102
+ if (passes > 10000) {
3103
+ throw new Error('streaming plan exceeded 10000 batches (dependency cycle?)');
3104
+ }
3105
+ }
3106
+ const total = layers.reduce((acc, l) => acc + l.bytes, 0);
3107
+ return {
3108
+ batches,
3109
+ totalbytes: total,
3110
+ hostwindow: windowbytes,
3111
+ peakhostusage: peakhost,
3112
+ passes,
3113
+ summary: `streams ${total} bytes through a ${(windowbytes / 1024 ** 3).toFixed(1)} GiB window in ${batches.length} batches; peak host usage ${(peakhost / 1024 ** 3).toFixed(1)} GiB; the workload size is unbounded - only the window is fixed`,
3114
+ };
3115
+ } catch (error) {
3116
+ throw new Error(
3117
+ `planstreamingexecution failed: ${error instanceof Error ? error.message : String(error)}`,
3118
+ );
3119
+ }
3120
+ }
3121
+
3122
+ /**
3123
+ * convenience planner for a large model checkpoint: splits the weights
3124
+ * file into virtual layers of a chosen size and plans the streaming run
3125
+ * (the exact pattern an inference engine uses when the model is larger
3126
+ * than ram; mmap + madvise willneed per batch).
3127
+ *
3128
+ * @param totalbytes the checkpoint size.
3129
+ * @param layerbytes the per-layer slice (default 512 MiB).
3130
+ * @param windowbytes the hot-set budget (default 4 GiB).
3131
+ */
3132
+ export function planmodelstreaming(
3133
+ totalbytes: number,
3134
+ layerbytes: number = 512 * 1024 * 1024,
3135
+ windowbytes: number = 4 * 1024 * 1024 * 1024,
3136
+ ): ReturnType<typeof planstreamingexecution> {
3137
+ const layers: streaminglayer[] = [];
3138
+ const count = Math.ceil(totalbytes / layerbytes);
3139
+ for (let i = 0; i < count; i += 1) {
3140
+ const bytes = Math.min(layerbytes, totalbytes - i * layerbytes);
3141
+ layers.push({ id: `layer${String(i).padStart(4, '0')}`, bytes, dependencies: [] });
3142
+ }
3143
+ return planstreamingexecution(layers, windowbytes, 'layerdone');
3144
+ }