@theoremjs/studio 0.0.0-stage → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (473) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +294 -2
  3. package/agreement.d.ts +14 -0
  4. package/agreement.js +80 -0
  5. package/asks.d.ts +47 -0
  6. package/asks.js +54 -0
  7. package/browser-connection.d.ts +49 -0
  8. package/browser-connection.js +215 -0
  9. package/browser-engine.d.ts +11 -0
  10. package/browser-engine.js +11 -0
  11. package/browser-execution.d.ts +6 -0
  12. package/browser-execution.js +18 -0
  13. package/browser-live.d.ts +4 -0
  14. package/browser-live.js +132 -0
  15. package/browser-transport.d.ts +33 -0
  16. package/browser-transport.js +229 -0
  17. package/browser.d.ts +4 -0
  18. package/browser.js +4 -0
  19. package/canonical.d.ts +10 -0
  20. package/canonical.js +25 -0
  21. package/compile-workspace.d.ts +28 -0
  22. package/compile-workspace.js +295 -0
  23. package/compile.d.ts +65 -0
  24. package/compile.js +1605 -0
  25. package/concierge-demo.d.ts +17 -0
  26. package/concierge-demo.js +1230 -0
  27. package/conversation-key.d.ts +5 -0
  28. package/conversation-key.js +10 -0
  29. package/decision-example-images.d.ts +30 -0
  30. package/decision-example-images.js +30 -0
  31. package/deno.json +3 -0
  32. package/draft.d.ts +407 -0
  33. package/draft.js +505 -0
  34. package/editors.d.ts +29 -0
  35. package/editors.js +51 -0
  36. package/example-embedding-files.d.ts +11 -0
  37. package/example-embedding-files.js +20 -0
  38. package/example.d.ts +36 -0
  39. package/example.js +357 -0
  40. package/guardrail-probe.d.ts +100 -0
  41. package/guardrail-probe.js +722 -0
  42. package/held-credentials.d.ts +28 -0
  43. package/held-credentials.js +87 -0
  44. package/installed.mjs +40 -0
  45. package/lent-keys.d.ts +122 -0
  46. package/lent-keys.js +47 -0
  47. package/line-diff.d.ts +15 -0
  48. package/line-diff.js +41 -0
  49. package/live-connection.d.ts +22 -0
  50. package/live-connection.js +27 -0
  51. package/live-session-bridge.d.ts +15 -0
  52. package/live-session-bridge.js +202 -0
  53. package/map-line.d.ts +19 -0
  54. package/map-line.js +42 -0
  55. package/map.d.ts +154 -0
  56. package/map.js +525 -0
  57. package/media-accept.d.ts +19 -0
  58. package/media-accept.js +65 -0
  59. package/mod.d.ts +39 -0
  60. package/mod.js +33 -0
  61. package/origins.d.ts +52 -0
  62. package/origins.js +165 -0
  63. package/package.json +85 -4
  64. package/page/assets/Banner-ChigGVCM.js +2 -0
  65. package/page/assets/BottomSheet-BwfBDtje.js +1 -0
  66. package/page/assets/Card-Lp-0P5eL.js +1 -0
  67. package/page/assets/CardWall-CBRLdDZW.js +1 -0
  68. package/page/assets/Center-nHS1JSux.js +1 -0
  69. package/page/assets/CheckboxInput-Cj5pk-KI.js +2 -0
  70. package/page/assets/CodeBlock-C7XJ4ioE.js +51 -0
  71. package/page/assets/CollapsibleGroup-N7_SOjbm.js +2 -0
  72. package/page/assets/ContextMenu-BcXTDSnA.js +2 -0
  73. package/page/assets/DateInput-BGrQ5tPC.js +1 -0
  74. package/page/assets/Dialog-DELWQo-v.js +1 -0
  75. package/page/assets/EmptyState-C8W4JKBZ.js +1 -0
  76. package/page/assets/HoverCard-D1Y1ltWQ.js +1 -0
  77. package/page/assets/Icon-JrysT8aU.js +3 -0
  78. package/page/assets/IconAlertTriangle-BqAEqqdU.js +1 -0
  79. package/page/assets/IconButton-DlOGzJIX.js +1 -0
  80. package/page/assets/IconEye-BB5aY-wQ.js +1 -0
  81. package/page/assets/IconFileText-6zOFKKrF.js +1 -0
  82. package/page/assets/IconSearch-BTx66a40.js +1 -0
  83. package/page/assets/IconShield-DDOh7Gr5.js +1 -0
  84. package/page/assets/ItemSwipeLayer-Ckn4X6h9.js +1 -0
  85. package/page/assets/PaneState-BvA2tLQg.js +1 -0
  86. package/page/assets/PaneState-eUzUkNE8.css +1 -0
  87. package/page/assets/ScrollableArea-Ckugdj1N.js +1 -0
  88. package/page/assets/Section-DJ97F7QE.js +1 -0
  89. package/page/assets/SegmentedControlItem-h-aaXzyS.js +1 -0
  90. package/page/assets/SidePanel-BL32abk8.js +1 -0
  91. package/page/assets/StackItem-CQviqAfM.js +1 -0
  92. package/page/assets/Text-bum6zWWx.js +2 -0
  93. package/page/assets/TimestampHoverCard-DQIG0RZF.js +1 -0
  94. package/page/assets/ToolResultCharts-DsbzvrBK.js +1 -0
  95. package/page/assets/Tooltip-BH9xiRMl.js +1 -0
  96. package/page/assets/TraceInspector-B34qMQJH.js +1 -0
  97. package/page/assets/TraceOverview-Drp2Sl4p.js +57 -0
  98. package/page/assets/VStack-CkVMcNiK.js +1 -0
  99. package/page/assets/_virtual_studio-type-sources-B0u0_Vhq.js +12933 -0
  100. package/page/assets/arrive-DvbqpeRS.js +1 -0
  101. package/page/assets/browser-engine-rJc_aHJo.js +9 -0
  102. package/page/assets/browser-transport-Bn77Eq5d.js +47 -0
  103. package/page/assets/codicon-DF1abBS2.ttf +0 -0
  104. package/page/assets/compile-egress-CGwmX2BX.js +23 -0
  105. package/page/assets/container.stylex-BmOi-8DQ.js +1 -0
  106. package/page/assets/createReactComponent-CZwsv6DD.js +1 -0
  107. package/page/assets/decision-example-images-CZGr7YIT.js +1 -0
  108. package/page/assets/devWarning-DxuplmRl.js +1 -0
  109. package/page/assets/editor-DjpsE8to.css +1 -0
  110. package/page/assets/editor.api-Cd3ViPQa.js +908 -0
  111. package/page/assets/editor.worker-6GY8RhDc.js +1 -0
  112. package/page/assets/editor.worker-BmdWdrHZ.js +33 -0
  113. package/page/assets/editors-BJX_S59A.js +1 -0
  114. package/page/assets/egress-automata-B5ZGE1mT.js +1 -0
  115. package/page/assets/embedding-space-view-CfQBPDxD.js +140 -0
  116. package/page/assets/encode-files-Dq98bb-f.js +1 -0
  117. package/page/assets/export-agent-wlS68_dE.js +43 -0
  118. package/page/assets/figtree-latin-400-normal-BD4aNku5.woff +0 -0
  119. package/page/assets/figtree-latin-400-normal-g7Dtegnw.woff2 +0 -0
  120. package/page/assets/figtree-latin-500-normal-BWnGEVsr.woff2 +0 -0
  121. package/page/assets/figtree-latin-500-normal-B_yy1phn.woff +0 -0
  122. package/page/assets/figtree-latin-600-normal-BM_oTo4n.woff +0 -0
  123. package/page/assets/figtree-latin-600-normal-Cv_xCTDl.woff2 +0 -0
  124. package/page/assets/figtree-latin-700-normal-BCCt2T1q.woff +0 -0
  125. package/page/assets/figtree-latin-700-normal-th6qEP7c.woff2 +0 -0
  126. package/page/assets/focusOutline.stylex-BT0kfa4t.js +1 -0
  127. package/page/assets/focusableSelector-DXZeMU85.js +1 -0
  128. package/page/assets/groupStyles-C7Ter7zC.js +1 -0
  129. package/page/assets/guardrail-tester-BHcoZE5K.js +9 -0
  130. package/page/assets/host-patterns-DCz9Cmrt.js +1 -0
  131. package/page/assets/index-BmHkdZXS.js +105 -0
  132. package/page/assets/index-DOaHlqBo.css +1 -0
  133. package/page/assets/isRtlElement-BG33-k_H.js +1 -0
  134. package/page/assets/labels-provider-4TIWDvsE.js +3 -0
  135. package/page/assets/lent-keys-C32ems-I.js +1 -0
  136. package/page/assets/live-ingress-DsA4_80F.js +1 -0
  137. package/page/assets/live-messages-B19ZuTGI.js +1 -0
  138. package/page/assets/mergeProps-ptS-gIMz.js +1 -0
  139. package/page/assets/padding.stylex-B76907Zf.js +1 -0
  140. package/page/assets/preload-helper-DpV6A1kQ.js +1 -0
  141. package/page/assets/reader-CN15b9m8.js +7 -0
  142. package/page/assets/register-CDgivxvX.js +2 -0
  143. package/page/assets/rolldown-runtime-hePW80VL.js +1 -0
  144. package/page/assets/schemas-BQ9Jydzb.js +56 -0
  145. package/page/assets/shaped-data-BO_JTcQq.js +16 -0
  146. package/page/assets/sharedResizeObserver-4U9Xq5Oq.js +1 -0
  147. package/page/assets/stub-DDddt29Y.js +471 -0
  148. package/page/assets/studio-code-pane-BSXwNg0B.js +8 -0
  149. package/page/assets/studio-console-C1hyAfeb.js +3 -0
  150. package/page/assets/studio-decision-CZDF06ic.js +1 -0
  151. package/page/assets/studio-embedding-SqSYd_e1.js +2 -0
  152. package/page/assets/studio-host-console-BlbRUrR_.js +2 -0
  153. package/page/assets/studio-live-Dd0AEV_k.js +1 -0
  154. package/page/assets/studio-map-BIIZcltt.js +1 -0
  155. package/page/assets/studio-map-Bb06Tw1F.css +1 -0
  156. package/page/assets/studio-monaco-CbKf90i6.css +1 -0
  157. package/page/assets/studio-monaco-xfyfezQS.js +34 -0
  158. package/page/assets/studio-ts.worker-B-EXG4Sv.js +67741 -0
  159. package/page/assets/studio-ts.worker-BXtW3z-O.js +1 -0
  160. package/page/assets/studio-type-checker-Cp6IxLBp.js +23 -0
  161. package/page/assets/themeProps-3IqahpCr.js +1 -0
  162. package/page/assets/tokens.stylex-DkHf0P9C.js +1 -0
  163. package/page/assets/tool-schema-BF2BK9h8.js +18 -0
  164. package/page/assets/trace-patterns-DnYJa0nI.js +1 -0
  165. package/page/assets/trace-record-C-XxCjSg.js +1 -0
  166. package/page/assets/trace-schema-9CdEyG6Q.js +1 -0
  167. package/page/assets/tree-D1IYOD_9.js +3 -0
  168. package/page/assets/turn-client-BMBqw-ho.js +2 -0
  169. package/page/assets/types-CBmpjaet.js +1 -0
  170. package/page/assets/typescript-20GgZ8PJ.js +1 -0
  171. package/page/assets/use-one-call-BtrXMol7.js +1 -0
  172. package/page/assets/useAdaptivePresentation-BzmPCddb.js +1 -0
  173. package/page/assets/useClipboard-BgGRxRMJ.js +1 -0
  174. package/page/assets/useContainerReveal-BFy8N_QU.js +1 -0
  175. package/page/assets/useDevWarning-Z9SoixOP.js +1 -0
  176. package/page/assets/useLayerDismissal-B_G7IWpC.js +1 -0
  177. package/page/assets/useMediaQuery-BCT0NUeZ.js +1 -0
  178. package/page/assets/useScrollLock-Dyf6PHJt.js +1 -0
  179. package/page/assets/useScrollableArea-0NzdiBCq.js +1 -0
  180. package/page/assets/useTooltip-CJM_xhLB.js +1 -0
  181. package/page/assets/workspace-BMzjmPeQ.js +1 -0
  182. package/page/assets/zip-DTcEfps6.js +1 -0
  183. package/page/index.html +90 -0
  184. package/page.mjs +49 -0
  185. package/people.d.ts +87 -0
  186. package/people.js +110 -0
  187. package/policy.d.ts +43 -0
  188. package/policy.js +102 -0
  189. package/ports.d.mts +32 -0
  190. package/ports.mjs +85 -0
  191. package/probe-battery.d.ts +13 -0
  192. package/probe-battery.js +404 -0
  193. package/providers.d.ts +149 -0
  194. package/providers.js +306 -0
  195. package/read-source.d.ts +48 -0
  196. package/read-source.js +1301 -0
  197. package/read-steps.d.ts +23 -0
  198. package/read-steps.js +2 -0
  199. package/reader.d.ts +11 -0
  200. package/reader.js +11 -0
  201. package/registrations.d.ts +83 -0
  202. package/registrations.js +1 -0
  203. package/requirements.d.ts +22 -0
  204. package/requirements.js +38 -0
  205. package/run-payload-core.d.ts +28 -0
  206. package/run-payload-core.js +123 -0
  207. package/run-payload.d.ts +32 -0
  208. package/run-payload.js +26 -0
  209. package/runtime-scope.d.ts +84 -0
  210. package/runtime-scope.js +308 -0
  211. package/runtime.d.ts +84 -0
  212. package/runtime.js +387 -0
  213. package/section-notes.d.ts +33 -0
  214. package/section-notes.js +38 -0
  215. package/serve-page.mjs +96 -0
  216. package/server/draft-decide.d.ts +72 -0
  217. package/server/draft-decide.js +111 -0
  218. package/server/draft-embedding.d.ts +3 -0
  219. package/server/draft-embedding.js +35 -0
  220. package/server/draft-handler.d.ts +82 -0
  221. package/server/draft-handler.js +213 -0
  222. package/server/draft-probe.d.ts +14 -0
  223. package/server/draft-probe.js +61 -0
  224. package/server/draft-replies.d.ts +22 -0
  225. package/server/draft-replies.js +46 -0
  226. package/server/edited-session.d.ts +28 -0
  227. package/server/edited-session.js +83 -0
  228. package/server/edits.d.ts +38 -0
  229. package/server/edits.js +195 -0
  230. package/server/handler.d.ts +133 -0
  231. package/server/handler.js +598 -0
  232. package/server/lent-draft.d.ts +19 -0
  233. package/server/lent-draft.js +23 -0
  234. package/server/live.d.ts +15 -0
  235. package/server/live.js +96 -0
  236. package/server/open-editor.d.ts +63 -0
  237. package/server/open-editor.js +115 -0
  238. package/server/origins.d.ts +18 -0
  239. package/server/origins.js +330 -0
  240. package/server/project-source.d.ts +208 -0
  241. package/server/project-source.js +1042 -0
  242. package/server/project.d.ts +22 -0
  243. package/server/project.js +56 -0
  244. package/server/run-traces.d.ts +20 -0
  245. package/server/run-traces.js +96 -0
  246. package/server/save-new.d.ts +29 -0
  247. package/server/save-new.js +514 -0
  248. package/server/save-plan.d.ts +46 -0
  249. package/server/save-plan.js +1134 -0
  250. package/server/save-remove.d.ts +21 -0
  251. package/server/save-remove.js +528 -0
  252. package/server/save-session.d.ts +96 -0
  253. package/server/save-session.js +418 -0
  254. package/server/save-wire.d.ts +235 -0
  255. package/server/save-wire.js +8 -0
  256. package/server/save.d.ts +52 -0
  257. package/server/save.js +209 -0
  258. package/server/serve.d.ts +23 -0
  259. package/server/serve.js +413 -0
  260. package/server/test-connection.d.ts +17 -0
  261. package/server/test-connection.js +362 -0
  262. package/server/test-key.d.ts +2 -0
  263. package/server/test-key.js +83 -0
  264. package/server/watch.d.ts +28 -0
  265. package/server/watch.js +93 -0
  266. package/server/zod-edit.d.ts +74 -0
  267. package/server/zod-edit.js +558 -0
  268. package/server.d.ts +8 -0
  269. package/server.js +8 -0
  270. package/setups.mjs +170 -0
  271. package/shared-carry.d.ts +114 -0
  272. package/shared-carry.js +523 -0
  273. package/shared-settings.d.ts +34 -0
  274. package/shared-settings.js +70 -0
  275. package/source.d.ts +75 -0
  276. package/source.js +414 -0
  277. package/start.mjs +169 -0
  278. package/stream-lines.d.ts +9 -0
  279. package/stream-lines.js +27 -0
  280. package/stub.d.ts +6 -0
  281. package/stub.js +33 -0
  282. package/surface.d.ts +53 -0
  283. package/surface.js +631 -0
  284. package/system-markup.d.ts +20 -0
  285. package/system-markup.js +51 -0
  286. package/tool-schema.d.ts +23 -0
  287. package/tool-schema.js +69 -0
  288. package/tools.d.ts +16 -0
  289. package/tools.js +91 -0
  290. package/traces.d.ts +24 -0
  291. package/traces.js +29 -0
  292. package/transport.d.ts +57 -0
  293. package/transport.js +187 -0
  294. package/tree.d.ts +32 -0
  295. package/tree.js +83 -0
  296. package/types.d.ts +62 -0
  297. package/types.js +6 -0
  298. package/ui/acting-as-button.d.ts +2 -0
  299. package/ui/acting-as-button.js +16 -0
  300. package/ui/add-provider.d.ts +13 -0
  301. package/ui/add-provider.js +20 -0
  302. package/ui/boot-mark.css +53 -0
  303. package/ui/boot-mark.d.ts +19 -0
  304. package/ui/boot-mark.js +128 -0
  305. package/ui/boot-paint.d.ts +8 -0
  306. package/ui/boot-paint.js +8 -0
  307. package/ui/brand-icons.d.ts +57 -0
  308. package/ui/brand-icons.js +148 -0
  309. package/ui/built/theme.css +830 -0
  310. package/ui/built/theme.css.d.ts +9 -0
  311. package/ui/built/theorem-studio.d.ts +13 -0
  312. package/ui/built/theorem-studio.js +705 -0
  313. package/ui/built/theorem-studio.variants.d.ts +28 -0
  314. package/ui/code/monaco-contributions.d.ts +45 -0
  315. package/ui/code/studio-code.d.ts +7 -0
  316. package/ui/code/studio-code.js +253 -0
  317. package/ui/code/studio-monaco.d.ts +44 -0
  318. package/ui/code/studio-monaco.js +48 -0
  319. package/ui/code/studio-ts.worker.d.ts +1 -0
  320. package/ui/code/studio-ts.worker.js +16 -0
  321. package/ui/code/studio-type-checker.d.ts +12 -0
  322. package/ui/code/studio-type-checker.js +308 -0
  323. package/ui/code/studio-type-sources.d.ts +26 -0
  324. package/ui/confirm-write.d.ts +12 -0
  325. package/ui/confirm-write.js +45 -0
  326. package/ui/diff-block.d.ts +21 -0
  327. package/ui/diff-block.js +17 -0
  328. package/ui/figtree.css +59 -0
  329. package/ui/file-place.d.ts +4 -0
  330. package/ui/file-place.js +26 -0
  331. package/ui/guardrail-tester.d.ts +18 -0
  332. package/ui/guardrail-tester.js +248 -0
  333. package/ui/inspector-context.d.ts +119 -0
  334. package/ui/inspector-context.js +84 -0
  335. package/ui/inspector.d.ts +247 -0
  336. package/ui/inspector.js +284 -0
  337. package/ui/lib/acting-as.d.ts +25 -0
  338. package/ui/lib/acting-as.js +90 -0
  339. package/ui/lib/boot-sequence.d.ts +32 -0
  340. package/ui/lib/boot-sequence.js +57 -0
  341. package/ui/lib/export-agent.d.ts +11 -0
  342. package/ui/lib/export-agent.js +335 -0
  343. package/ui/lib/facet-icons.d.ts +20 -0
  344. package/ui/lib/facet-icons.js +21 -0
  345. package/ui/lib/panel-motion.d.ts +8 -0
  346. package/ui/lib/panel-motion.js +74 -0
  347. package/ui/lib/project-address.d.ts +14 -0
  348. package/ui/lib/project-address.js +24 -0
  349. package/ui/lib/project-starting.d.ts +13 -0
  350. package/ui/lib/project-starting.js +30 -0
  351. package/ui/lib/project-traces.d.ts +17 -0
  352. package/ui/lib/project-traces.js +54 -0
  353. package/ui/lib/read-map.d.ts +30 -0
  354. package/ui/lib/read-map.js +173 -0
  355. package/ui/lib/read-progress.d.ts +14 -0
  356. package/ui/lib/read-progress.js +12 -0
  357. package/ui/lib/refusal-lines.d.ts +3 -0
  358. package/ui/lib/refusal-lines.js +8 -0
  359. package/ui/lib/studio-activity.d.ts +5 -0
  360. package/ui/lib/studio-activity.js +17 -0
  361. package/ui/lib/studio-console.d.ts +27 -0
  362. package/ui/lib/studio-console.js +9 -0
  363. package/ui/lib/studio-conversation.d.ts +20 -0
  364. package/ui/lib/studio-conversation.js +130 -0
  365. package/ui/lib/studio-examples.d.ts +31 -0
  366. package/ui/lib/studio-examples.js +25 -0
  367. package/ui/lib/studio-labels.d.ts +6 -0
  368. package/ui/lib/studio-labels.js +13 -0
  369. package/ui/lib/studio-map-kept.d.ts +17 -0
  370. package/ui/lib/studio-map-kept.js +48 -0
  371. package/ui/lib/studio-open.d.ts +22 -0
  372. package/ui/lib/studio-open.js +54 -0
  373. package/ui/lib/studio-page.d.ts +33 -0
  374. package/ui/lib/studio-page.js +121 -0
  375. package/ui/lib/studio-prefs.d.ts +26 -0
  376. package/ui/lib/studio-prefs.js +87 -0
  377. package/ui/lib/studio-project.d.ts +106 -0
  378. package/ui/lib/studio-project.js +177 -0
  379. package/ui/lib/studio-restore.d.ts +49 -0
  380. package/ui/lib/studio-restore.js +251 -0
  381. package/ui/lib/studio-run-open.d.ts +15 -0
  382. package/ui/lib/studio-run-open.js +16 -0
  383. package/ui/lib/studio-session.d.ts +34 -0
  384. package/ui/lib/studio-session.js +17 -0
  385. package/ui/lib/studio-settings.d.ts +15 -0
  386. package/ui/lib/studio-settings.js +48 -0
  387. package/ui/lib/studio-store.d.ts +64 -0
  388. package/ui/lib/studio-store.js +391 -0
  389. package/ui/lib/studio-watch.d.ts +12 -0
  390. package/ui/lib/studio-watch.js +27 -0
  391. package/ui/lib/tab-fills.d.ts +6 -0
  392. package/ui/lib/tab-fills.js +14 -0
  393. package/ui/lib/tool-credentials.d.ts +3 -0
  394. package/ui/lib/tool-credentials.js +23 -0
  395. package/ui/lib/tool-probe.d.ts +19 -0
  396. package/ui/lib/tool-probe.js +104 -0
  397. package/ui/lib/type-icons.d.ts +35 -0
  398. package/ui/lib/type-icons.js +38 -0
  399. package/ui/lib/when-idle.d.ts +5 -0
  400. package/ui/lib/when-idle.js +14 -0
  401. package/ui/lib/zip.d.ts +5 -0
  402. package/ui/lib/zip.js +8 -0
  403. package/ui/map-token.css +11 -0
  404. package/ui/map-token.d.ts +17 -0
  405. package/ui/map-token.js +29 -0
  406. package/ui/mcp-icon.d.ts +6 -0
  407. package/ui/mcp-icon.js +8 -0
  408. package/ui/motion.css +113 -0
  409. package/ui/pan.d.ts +32 -0
  410. package/ui/pan.js +90 -0
  411. package/ui/profile-editor.d.ts +33 -0
  412. package/ui/profile-editor.js +2638 -0
  413. package/ui/provider-editor.d.ts +15 -0
  414. package/ui/provider-editor.js +92 -0
  415. package/ui/read-map.css +48 -0
  416. package/ui/read-map.d.ts +7 -0
  417. package/ui/read-map.js +163 -0
  418. package/ui/refusal-detail.d.ts +4 -0
  419. package/ui/refusal-detail.js +21 -0
  420. package/ui/shared-change.d.ts +9 -0
  421. package/ui/shared-change.js +28 -0
  422. package/ui/shell-motion.css +121 -0
  423. package/ui/shell-motion.d.ts +73 -0
  424. package/ui/shell-motion.js +194 -0
  425. package/ui/studio-code-pane.d.ts +19 -0
  426. package/ui/studio-code-pane.js +44 -0
  427. package/ui/studio-compare.d.ts +80 -0
  428. package/ui/studio-compare.js +326 -0
  429. package/ui/studio-conflicts.d.ts +17 -0
  430. package/ui/studio-conflicts.js +87 -0
  431. package/ui/studio-connection.d.ts +64 -0
  432. package/ui/studio-connection.js +286 -0
  433. package/ui/studio-decision.d.ts +6 -0
  434. package/ui/studio-decision.js +49 -0
  435. package/ui/studio-embedding.d.ts +6 -0
  436. package/ui/studio-embedding.js +30 -0
  437. package/ui/studio-host-console.d.ts +6 -0
  438. package/ui/studio-host-console.js +17 -0
  439. package/ui/studio-host.d.ts +73 -0
  440. package/ui/studio-host.js +12 -0
  441. package/ui/studio-live.d.ts +17 -0
  442. package/ui/studio-live.js +26 -0
  443. package/ui/studio-map.css +273 -0
  444. package/ui/studio-map.d.ts +17 -0
  445. package/ui/studio-map.js +397 -0
  446. package/ui/studio-nav.d.ts +24 -0
  447. package/ui/studio-nav.js +17 -0
  448. package/ui/studio-no-project.d.ts +5 -0
  449. package/ui/studio-no-project.js +9 -0
  450. package/ui/studio-run.css +76 -0
  451. package/ui/studio-run.d.ts +14 -0
  452. package/ui/studio-run.js +119 -0
  453. package/ui/studio-runner.d.ts +45 -0
  454. package/ui/studio-runner.js +187 -0
  455. package/ui/studio-save.d.ts +49 -0
  456. package/ui/studio-save.js +307 -0
  457. package/ui/studio-screen.d.ts +61 -0
  458. package/ui/studio-screen.js +2078 -0
  459. package/ui/studio-settings.d.ts +31 -0
  460. package/ui/studio-settings.js +288 -0
  461. package/ui/studio-shell.css +27 -0
  462. package/ui/studio-shell.d.ts +43 -0
  463. package/ui/studio-shell.js +87 -0
  464. package/ui/studio-theme.d.ts +7 -0
  465. package/ui/studio-theme.js +94 -0
  466. package/ui/studio.css +427 -0
  467. package/ui/theorem-mark.css +84 -0
  468. package/ui/theorem-mark.d.ts +22 -0
  469. package/ui/theorem-mark.js +21 -0
  470. package/vite.d.mts +30 -0
  471. package/vite.mjs +277 -0
  472. package/workspace.d.ts +230 -0
  473. package/workspace.js +1054 -0
@@ -0,0 +1,1134 @@
1
+ /**
2
+ * What Save writes: each changed value of a profile or a tool, found in the
3
+ * builder's file and rewritten there. A value written in the call is changed in
4
+ * place. A value from a constant is changed where the constant is set, when
5
+ * every profile and tool that reads the constant makes the same change, and the
6
+ * plan says when other code reads the constant too. A value from a constant that
7
+ * someone leaves as it was, or from code, is not written; the plan says where it
8
+ * is set and who shares it. A tool's schema is changed in the Zod that writes it,
9
+ * one part at a time (`zod-edit.ts`).
10
+ *
11
+ * @module
12
+ */
13
+ import { MASK, unmaskUrl } from '@theoremjs/agents/surface';
14
+ import ts from 'typescript';
15
+ import { canonical } from "../canonical.js";
16
+ import { definitionFactory } from "../providers.js";
17
+ import { AGENTS_PACKAGE, valueSource } from "../source.js";
18
+ import { keySource } from "../tool-schema.js";
19
+ import { declares, definedAs, followed, holderOf, importExtension, isHeldId, isInCall, isInTarget, propertyName, providerNamed, shapeOf, sourceOf, specifierTo, unwrapped, usersOf, } from "./project-source.js";
20
+ import { isWrittenOut, methodCall, methodSpan, readZod, unread, zodName, zodObject, zodSource, } from "./zod-edit.js";
21
+ export const NOT_PLAIN = Symbol('not plain');
22
+ function isRecord(value) {
23
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
24
+ }
25
+ /** A value the page sent with each mask in it filled in from `written`, what the file holds there. */
26
+ function fileHeld(shown, written) {
27
+ if (typeof shown !== 'string' || !shown.includes(MASK))
28
+ return shown;
29
+ return shown === MASK ? written : unmaskUrl(shown, written);
30
+ }
31
+ function same(a, b) {
32
+ return canonical(a) === canonical(b);
33
+ }
34
+ /** The value an expression writes out in full, or `NOT_PLAIN` when it names or computes anything. */
35
+ export function plain(expression) {
36
+ const node = unwrapped(expression);
37
+ if (ts.isStringLiteralLike(node))
38
+ return node.text;
39
+ if (ts.isNumericLiteral(node))
40
+ return Number(node.text);
41
+ if (ts.isPrefixUnaryExpression(node) &&
42
+ node.operator === ts.SyntaxKind.MinusToken &&
43
+ ts.isNumericLiteral(node.operand))
44
+ return -Number(node.operand.text);
45
+ if (node.kind === ts.SyntaxKind.TrueKeyword)
46
+ return true;
47
+ if (node.kind === ts.SyntaxKind.FalseKeyword)
48
+ return false;
49
+ if (node.kind === ts.SyntaxKind.NullKeyword)
50
+ return null;
51
+ if (ts.isArrayLiteralExpression(node)) {
52
+ const items = node.elements.map(plain);
53
+ return items.includes(NOT_PLAIN) ? NOT_PLAIN : items;
54
+ }
55
+ if (ts.isObjectLiteralExpression(node)) {
56
+ const entries = [];
57
+ for (const property of node.properties) {
58
+ const name = propertyName(property);
59
+ if (name === undefined || !ts.isPropertyAssignment(property))
60
+ return NOT_PLAIN;
61
+ const value = plain(property.initializer);
62
+ if (value === NOT_PLAIN)
63
+ return NOT_PLAIN;
64
+ entries.push([name, value]);
65
+ }
66
+ return Object.fromEntries(entries);
67
+ }
68
+ // Lines joined: how the studio's own printer writes text of several lines.
69
+ if (ts.isCallExpression(node) &&
70
+ ts.isPropertyAccessExpression(node.expression) &&
71
+ node.expression.name.text === 'join' &&
72
+ node.arguments.length === 1) {
73
+ const lines = plain(node.expression.expression);
74
+ const between = plain(node.arguments[0]);
75
+ if (Array.isArray(lines) &&
76
+ typeof between === 'string' &&
77
+ lines.every((line) => typeof line === 'string')) {
78
+ return lines.join(between);
79
+ }
80
+ }
81
+ return NOT_PLAIN;
82
+ }
83
+ /** The whitespace a line starts with. */
84
+ export function indentAt(source, position) {
85
+ const { line } = source.getLineAndCharacterOfPosition(position);
86
+ const start = source.getPositionOfLineAndCharacter(line, 0);
87
+ return /^[ \t]*/.exec(source.text.slice(start))?.[0] ?? '';
88
+ }
89
+ /** One level of a file's indentation: a tab, or its narrowest run of spaces. */
90
+ export function indentUnit(source) {
91
+ let narrowest = 0;
92
+ for (const line of source.text.split('\n')) {
93
+ if (line.startsWith('\t'))
94
+ return '\t';
95
+ const width = /^ +(?=\S)/.exec(line)?.[0].length ?? 0;
96
+ if (width && (!narrowest || width < narrowest))
97
+ narrowest = width;
98
+ }
99
+ return ' '.repeat(narrowest || 2);
100
+ }
101
+ /** Text of one line, in the quotes the file already uses at this spot. */
102
+ /** Quotes as the text being replaced does. With no text there, as the file's first import does. */
103
+ function quoteLike(existing, source) {
104
+ const first = existing && ts.isArrayLiteralExpression(existing)
105
+ ? existing.elements.find(ts.isStringLiteral)
106
+ : existing;
107
+ const like = first && ts.isStringLiteral(first)
108
+ ? first
109
+ : source.statements.find(ts.isImportDeclaration)?.moduleSpecifier;
110
+ const double = like?.getText(source).startsWith('"');
111
+ return (text) => {
112
+ const body = JSON.stringify(text);
113
+ if (double)
114
+ return body;
115
+ return `'${body.slice(1, -1).replaceAll('\\"', '"').replaceAll("'", "\\'")}'`;
116
+ };
117
+ }
118
+ /** A tool's schemas, by the key its file writes each under. */
119
+ const TOOL_SCHEMAS = {
120
+ inputSchema: { key: 'input', side: 'input' },
121
+ outputSchema: { key: 'output', side: 'output' },
122
+ };
123
+ /** The methods that say whether an object takes fields it does not name. */
124
+ const OPEN_METHODS = new Set(['strict', 'passthrough', 'loose']);
125
+ /** A new field's schema, written as Zod where a plain value would be. */
126
+ class ZodField {
127
+ write;
128
+ constructor(write) {
129
+ this.write = write;
130
+ }
131
+ }
132
+ /** A value as source: a new field as its Zod, anything else as it is. */
133
+ function written(value, style) {
134
+ return value instanceof ZodField ? value.write(style) : valueSource(value, style);
135
+ }
136
+ /** An object's fields written out, one to a line, for an object that held none. */
137
+ function objectSource(entries, style) {
138
+ const inner = { ...style, base: style.base + style.unit };
139
+ const lines = entries.map(([key, value]) => `${inner.base}${keySource(key, style.quote)}: ${written(value, inner)},`);
140
+ return `{\n${lines.join('\n')}\n${style.base}}`;
141
+ }
142
+ const names = (value) => Array.isArray(value) ? value.filter((each) => typeof each === 'string') : [];
143
+ const subjectId = (kind, of) => `${kind}:${of}`;
144
+ const editsText = (edits) => canonical([...edits].sort((a, b) => a.start - b.start || a.end - b.end));
145
+ /**
146
+ * Decides each shared value: its changes are written, once, when everyone it reaches makes the
147
+ * same ones. Otherwise each change to it is refused, so no profile runs on a value it did not test.
148
+ */
149
+ function settle(shared, plan) {
150
+ for (const group of Map.groupBy(shared, (each) => each.holder).values()) {
151
+ const [first] = group;
152
+ if (!first)
153
+ continue;
154
+ const made = new Set(group.map((each) => each.by));
155
+ const agreed = first.everyone.every((user) => made.has(user)) &&
156
+ group.every((each) => editsText(each.edits) === editsText(first.edits));
157
+ for (const each of group)
158
+ plan.changes.push(...(agreed ? each.changes : each.refused));
159
+ if (!agreed)
160
+ continue;
161
+ for (const edit of first.edits) {
162
+ if (!plan.edits.some((other) => canonical(other) === canonical(edit)))
163
+ plan.edits.push(edit);
164
+ }
165
+ }
166
+ }
167
+ /** A node of the file a `z.` call is in. The call itself when none is named. */
168
+ function located(call, node = call.call) {
169
+ return { node, source: call.source, ...(call.env ? { env: call.env } : {}) };
170
+ }
171
+ function partsOf(read) {
172
+ const { base } = read;
173
+ const [first] = base.call.arguments;
174
+ const part = first && !ts.isSpreadElement(first) ? located(base, first) : undefined;
175
+ const list = part && base.name === 'union' ? unwrapped(part.node) : undefined;
176
+ const members = list && ts.isArrayLiteralExpression(list) && !list.elements.some(ts.isSpreadElement)
177
+ ? list.elements
178
+ : undefined;
179
+ return { part, members };
180
+ }
181
+ /** Whether an object takes fields it does not name is the `z.` call it starts from. */
182
+ function objectCall(more, side) {
183
+ if (more === true || (isRecord(more) && Object.keys(more).length === 0))
184
+ return 'looseObject';
185
+ if (more === false)
186
+ return side === 'input' ? 'strictObject' : 'object';
187
+ return more === undefined && side === 'input' ? 'object' : undefined;
188
+ }
189
+ /** The parts of a schema that are changed where they are written. A change to any other writes it again whole. */
190
+ function inPlace(z, before, after, kind) {
191
+ const { part, members, read: { base }, } = z;
192
+ const reads = new Set(['description']);
193
+ if (z.object)
194
+ reads.add('properties').add('required');
195
+ if (kind)
196
+ reads.add('additionalProperties');
197
+ if (part && base.name === 'enum' && Array.isArray(before.enum) && Array.isArray(after.enum))
198
+ reads.add('enum');
199
+ if (part && base.name === 'array' && isRecord(before.items) && isRecord(after.items))
200
+ reads.add('items');
201
+ const [olds, news] = [before.anyOf, after.anyOf];
202
+ if (members &&
203
+ Array.isArray(olds) &&
204
+ Array.isArray(news) &&
205
+ olds.length === news.length &&
206
+ members.length === news.length) {
207
+ reads.add('anyOf');
208
+ }
209
+ return reads;
210
+ }
211
+ class Planner {
212
+ project;
213
+ subject;
214
+ target;
215
+ shared = [];
216
+ /** The subject's own call. Other profiles share it when a function makes the call for each. */
217
+ root;
218
+ /** What the subject alone writes: its own call, the arguments a function is called with for it. */
219
+ own = {
220
+ everyone: [],
221
+ sharedWith: [],
222
+ edits: [],
223
+ changes: [],
224
+ refused: [],
225
+ };
226
+ /** Each shared value the plan writes in, by where it is written. */
227
+ frames = new Map();
228
+ frame;
229
+ constructor(project, subject, target, peers) {
230
+ this.project = project;
231
+ this.subject = subject;
232
+ this.target = target;
233
+ const everyone = [subject.of, ...peers].map((name) => subjectId(subject.kind, name));
234
+ this.root = peers.length
235
+ ? {
236
+ holder: target.options,
237
+ everyone,
238
+ sharedWith: [...peers],
239
+ edits: [],
240
+ changes: [],
241
+ refused: [],
242
+ }
243
+ : this.own;
244
+ this.frame = this.root;
245
+ }
246
+ /** What the subject alone writes. */
247
+ get changes() {
248
+ return this.own.changes;
249
+ }
250
+ get edits() {
251
+ return this.own.edits;
252
+ }
253
+ note(...change) {
254
+ this.frame.changes.push(this.change(...change));
255
+ }
256
+ change(...[status, path, at, name, shared = {}]) {
257
+ return {
258
+ kind: this.subject.kind,
259
+ of: this.subject.of,
260
+ setting: path.join('.'),
261
+ status,
262
+ ...(at
263
+ ? {
264
+ file: at.source.fileName,
265
+ line: at.source.getLineAndCharacterOfPosition(at.node.getStart(at.source)).line + 1,
266
+ }
267
+ : {}),
268
+ ...(name ? { name } : {}),
269
+ // What is written in a value other code reads says so.
270
+ ...(status === 'written' && this.frame.readByCode ? { readByCode: true } : {}),
271
+ ...shared,
272
+ };
273
+ }
274
+ /** What holds `at`: the subject's own call, a constant, or a function, or nothing the studio follows. */
275
+ regionOf(at) {
276
+ if (isInTarget(this.target, at))
277
+ return this.target.options;
278
+ return isInCall(this.target, at) ? this.target.call?.node : holderOf(at)?.initializer;
279
+ }
280
+ /**
281
+ * Where a change to `origin` is written. In the subject's own call, or in a constant it alone
282
+ * reads, the change is the subject's. In a value others read too it is kept apart until the plan
283
+ * knows whether each of them makes it (`settle`). A value other code reads is written as any
284
+ * other, and each change to it says so. An id other code knows something by is not written.
285
+ */
286
+ frameAt(origin, name, path) {
287
+ if (isInTarget(this.target, origin))
288
+ return this.root;
289
+ if (isInCall(this.target, origin))
290
+ return this.own;
291
+ const holder = holderOf(origin);
292
+ if (!holder) {
293
+ this.note('constant', path, origin, name);
294
+ return undefined;
295
+ }
296
+ const users = usersOf(this.project, holder);
297
+ const everyone = [...users.profiles]
298
+ .map((id) => subjectId('profile', id))
299
+ .concat([...users.tools].map((tool) => subjectId('tool', tool)))
300
+ .concat([...users.providers].map((provider) => subjectId('provider', provider)));
301
+ const { kind, of } = this.subject;
302
+ const sharedWith = [...users.profiles]
303
+ .filter((id) => kind !== 'profile' || id !== of)
304
+ .concat([...users.tools].filter((tool) => kind !== 'tool' || tool !== of))
305
+ .concat([...users.providers].filter((provider) => kind !== 'provider' || provider !== of));
306
+ const refused = this.change('constant', path, origin, holder.name, {
307
+ ...(sharedWith.length ? { sharedWith } : {}),
308
+ ...(users.code ? { readByCode: true } : {}),
309
+ });
310
+ if (users.code && isHeldId(kind, path, origin)) {
311
+ this.frame.changes.push(refused);
312
+ return undefined;
313
+ }
314
+ if (!sharedWith.length && !users.code)
315
+ return this.own;
316
+ const frame = this.frames.get(holder.initializer) ?? {
317
+ holder: holder.initializer,
318
+ // Read by this one and by code: the change is this one's to make.
319
+ everyone: sharedWith.length ? everyone : [subjectId(kind, of)],
320
+ sharedWith,
321
+ readByCode: users.code,
322
+ edits: [],
323
+ changes: [],
324
+ refused: [],
325
+ };
326
+ this.frames.set(holder.initializer, frame);
327
+ frame.refused.push(refused);
328
+ return frame;
329
+ }
330
+ /** A value written somewhere other than where the walk is: changed there. */
331
+ enter(origin, name, before, after, path) {
332
+ const frame = this.frameAt(origin, name, path);
333
+ if (!frame)
334
+ return;
335
+ const outer = this.frame;
336
+ this.frame = frame;
337
+ this.walk(origin, before, after, path);
338
+ this.frame = outer;
339
+ }
340
+ /** A value a name, a key, or a call stands for. One the studio cannot follow is not written. */
341
+ named(at, name, before, after, path) {
342
+ const origin = followed(this.project, at);
343
+ const stayed = origin.node === unwrapped(at.node) && origin.source === at.source;
344
+ const named = ts.isIdentifier(origin.node) || ts.isPropertyAccessExpression(origin.node);
345
+ if (stayed)
346
+ this.note(named ? 'constant' : 'code', path, origin, named ? name : undefined);
347
+ else
348
+ this.enter(origin, name, before, after, path);
349
+ }
350
+ edit(source, start, end, written) {
351
+ // New lines end as the file's do. A template literal reads a carriage return as nothing.
352
+ const text = source.text.includes('\r\n') ? written.replace(/\r?\n/g, '\r\n') : written;
353
+ const edit = { file: source.fileName, start, end, text };
354
+ const held = this.frame.edits.some((other) => canonical(other) === canonical(edit));
355
+ if (!held)
356
+ this.frame.edits.push(edit);
357
+ }
358
+ style(source, position, existing) {
359
+ return {
360
+ unit: indentUnit(source),
361
+ base: indentAt(source, position),
362
+ template: true,
363
+ quote: quoteLike(existing, source),
364
+ };
365
+ }
366
+ /** The whole of the subject's call, and then what each shared value it wrote in holds. */
367
+ plan() {
368
+ const { options, source, env } = this.target;
369
+ this.walk({ node: options, source, env }, this.subject.before, this.subject.after, []);
370
+ const by = subjectId(this.subject.kind, this.subject.of);
371
+ const { root } = this;
372
+ if (root.holder && (root.changes.length || root.edits.length)) {
373
+ // A function makes this call for others too: what Save would write here, it writes for each.
374
+ const name = holderOf({ node: options, source })?.name;
375
+ const refused = root.changes.map((change) => change.status === 'written'
376
+ ? {
377
+ ...change,
378
+ status: 'constant',
379
+ ...(name ? { name } : {}),
380
+ sharedWith: root.sharedWith,
381
+ }
382
+ : change);
383
+ this.shared.push({ ...root, holder: root.holder, by, refused });
384
+ }
385
+ for (const frame of this.frames.values()) {
386
+ if (frame.holder && (frame.changes.length || frame.edits.length))
387
+ this.shared.push({ ...frame, holder: frame.holder, by });
388
+ }
389
+ }
390
+ walk(at, before, after, path) {
391
+ if (same(before, after))
392
+ return;
393
+ const node = unwrapped(at.node);
394
+ const here = { ...at, node };
395
+ const { source } = at;
396
+ const shape = isRecord(before) && isRecord(after) ? shapeOf(this.project, here) : undefined;
397
+ if (shape && isRecord(before) && isRecord(after)) {
398
+ this.object(shape, here, before, after, path);
399
+ return;
400
+ }
401
+ if (ts.isArrayLiteralExpression(node) &&
402
+ Array.isArray(before) &&
403
+ Array.isArray(after) &&
404
+ before.length === after.length &&
405
+ node.elements.length === after.length &&
406
+ !node.elements.some((element) => ts.isSpreadElement(element) || ts.isOmittedExpression(element))) {
407
+ node.elements.forEach((element, index) => {
408
+ this.walk({ ...at, node: element }, before[index], after[index], [...path, String(index)]);
409
+ });
410
+ return;
411
+ }
412
+ const value = plain(node);
413
+ if (value !== NOT_PLAIN) {
414
+ // A credential the page holds masked is the file's own text: the edit beside it keeps it.
415
+ if (this.subject.kind === 'tool' && typeof value === 'string') {
416
+ const [was, now] = [fileHeld(before, value), fileHeld(after, value)];
417
+ if (was !== before || now !== after) {
418
+ this.walk(at, was, now, path);
419
+ return;
420
+ }
421
+ }
422
+ if (!same(value, before)) {
423
+ this.note('changed', path, { source, node });
424
+ return;
425
+ }
426
+ this.write(here, after, path);
427
+ return;
428
+ }
429
+ this.named(here, node.getText(source), before, after, path);
430
+ }
431
+ /** Writes `after` over the plain value at `at`, in the file's own style. */
432
+ write(at, after, path) {
433
+ const { source, node } = at;
434
+ const start = node.getStart(source);
435
+ // A list's own quotes are for the list that replaces it.
436
+ const style = this.style(source, start, Array.isArray(after) || !ts.isArrayLiteralExpression(node) ? node : undefined);
437
+ // A list of plain values on one line stays on one line.
438
+ const inline = ts.isArrayLiteralExpression(node) &&
439
+ Array.isArray(after) &&
440
+ after.length > 0 &&
441
+ after.every((item) => item === null || typeof item !== 'object') &&
442
+ !node.getText(source).includes('\n');
443
+ const text = inline
444
+ ? `[${after.map((item) => valueSource(item, style)).join(', ')}]`
445
+ : valueSource(after, style);
446
+ this.edit(source, start, node.end, text);
447
+ this.note('written', path, { source, node });
448
+ }
449
+ /** Where a key of `shape` is written, or `at` when no property writes it. */
450
+ placeIn(shape, at, key) {
451
+ const entry = shape.entries.get(key);
452
+ return entry?.property ? { source: entry.source, node: entry.property } : at;
453
+ }
454
+ object(shape, at, before, after, path) {
455
+ const inserts = [];
456
+ const removed = [];
457
+ for (const key of new Set([...Object.keys(before), ...Object.keys(after)])) {
458
+ if (same(before[key], after[key]))
459
+ continue;
460
+ const change = {
461
+ key,
462
+ before: before[key],
463
+ after: after[key],
464
+ here: [...path, key],
465
+ };
466
+ const schema = this.subject.kind === 'tool' && path.length === 0 ? TOOL_SCHEMAS[key] : undefined;
467
+ const entry = shape.entries.get(key);
468
+ if (this.subject.kind === 'provider' && path.length === 0 && key === 'definition')
469
+ this.redefine(change);
470
+ else if (schema)
471
+ this.toolSchema(shape, at, schema, change);
472
+ else if (!entry) {
473
+ if (this.isAdded(shape, at, change))
474
+ inserts.push([key, change.after]);
475
+ }
476
+ else {
477
+ const gone = this.held(shape, at, entry, change);
478
+ if (gone)
479
+ removed.push(gone);
480
+ }
481
+ }
482
+ if (shape.own)
483
+ this.refit(shape.own, inserts, removed, path);
484
+ }
485
+ /** A tool's schema is Zod in its file: each part that changed is changed there. */
486
+ toolSchema(shape, at, schema, change) {
487
+ const { before, after, here } = change;
488
+ const { open } = shape;
489
+ const held = shape.entries.get(schema.key);
490
+ if (!held)
491
+ this.note('code', here, at);
492
+ else if (!held.value || (open && !open.after.has(schema.key)))
493
+ this.note('code', here, this.placeIn(shape, at, schema.key));
494
+ else
495
+ this.zod(held.value, before, after, here, schema.side);
496
+ }
497
+ /** Whether a key the object does not hold is added to it. */
498
+ isAdded(shape, at, { after, here }) {
499
+ // Past a spread the studio could not follow, the object may hold the key already.
500
+ if (shape.open || !shape.own)
501
+ this.note('code', here, at);
502
+ else if (after === undefined)
503
+ this.note('unfound', here, at);
504
+ else
505
+ return true;
506
+ return false;
507
+ }
508
+ /** A key the object holds, changed where it is written. Gives the property when it is to be taken out. */
509
+ held(shape, at, entry, change) {
510
+ const { key, before, after, here } = change;
511
+ const { open, own } = shape;
512
+ const { property, value } = entry;
513
+ const place = this.placeIn(shape, at, key);
514
+ if (key === 'protocol' && value && ts.isIdentifier(unwrapped(value.node))) {
515
+ this.swapKernelName(value, before, after, here, place);
516
+ }
517
+ else if (entry.receiver && !(open && !open.after.has(key)))
518
+ this.reprovide(entry.receiver, value, change);
519
+ else if (!value || (open && !open.after.has(key)))
520
+ this.note('code', here, place);
521
+ else if (property && ts.isShorthandPropertyAssignment(property))
522
+ this.named(value, key, before, after, here);
523
+ else if (after === undefined) {
524
+ // A key another object spreads in is not this one's to take out.
525
+ if (!property || !own || property.parent !== own.node)
526
+ this.note('code', here, place);
527
+ else {
528
+ this.note('written', here, place);
529
+ return property;
530
+ }
531
+ }
532
+ else if (this.regionOf(value) === this.regionOf(at))
533
+ this.walk(value, before, after, here);
534
+ else
535
+ this.enter(value, key, before, after, here);
536
+ return undefined;
537
+ }
538
+ /**
539
+ * A model's provider, changed by rewriting the name its `model()` call is made on. The provider's
540
+ * own id is never written. A provider the project gives no name, or one this file cannot reach, is
541
+ * code's to change.
542
+ */
543
+ reprovide(receiver, id, { before, after, here }) {
544
+ const node = unwrapped(receiver.node);
545
+ const place = { source: receiver.source, node };
546
+ const to = typeof after === 'string' ? providerNamed(this.project, after) : undefined;
547
+ if (!id || !ts.isIdentifier(node) || !to)
548
+ return this.note('code', here, place);
549
+ if (!same(plain(unwrapped(followed(this.project, id).node)), before))
550
+ return this.note('changed', here, place);
551
+ const name = this.brought(receiver.source, to);
552
+ if (!name)
553
+ return this.note('code', here, place);
554
+ this.edit(receiver.source, node.getStart(receiver.source), node.end, name);
555
+ this.note('written', here, place);
556
+ }
557
+ /**
558
+ * A provider's definition, changed by registering the other kernel definition where its call
559
+ * names this one.
560
+ */
561
+ redefine({ before, after, here }) {
562
+ const call = this.target.options.parent;
563
+ const definition = ts.isCallExpression(call) ? call.arguments[0] : undefined;
564
+ const { source } = this.target;
565
+ if (!definition)
566
+ return this.note('code', here, { source, node: this.target.options });
567
+ const name = (value) => typeof value === 'string' ? definitionFactory(value) : undefined;
568
+ this.swapKernelName({ node: definition, source }, name(before), name(after), here, {
569
+ source,
570
+ node: definition,
571
+ });
572
+ }
573
+ /**
574
+ * A name the kernel package exports, written at `at`, changed to another of its exports: a
575
+ * provider's definition (`geminiProvider`) or a model's protocol (`geminiInteractions`). The new
576
+ * name is imported beside the old, in place of it when nothing else in the file reads the old one.
577
+ * A name the file does not import from the kernel is code's to change.
578
+ */
579
+ swapKernelName(at, before, after, here, place) {
580
+ const node = unwrapped(at.node);
581
+ const [from, to] = [before, after].map((value) => typeof value === 'string' ? value : undefined);
582
+ if (!from || !to || !ts.isIdentifier(node) || at.env?.bound.has(node.text))
583
+ return this.note('code', here, place);
584
+ const { source } = at;
585
+ const imported = namedImports(source);
586
+ const old = imported.find((element) => element.name.text === node.text);
587
+ if (!old)
588
+ return this.note('code', here, place);
589
+ if ((old.propertyName ?? old.name).text !== from)
590
+ return this.note('changed', here, place);
591
+ const held = imported.find((element) => (element.propertyName ?? element.name).text === to);
592
+ let name = held?.name.text;
593
+ if (!name) {
594
+ name = to;
595
+ if (declares(source, to))
596
+ return this.note('code', here, place);
597
+ // The import of the old name is the only other place that reads it: the new one takes its place.
598
+ if (reads(source, node.text) === 2)
599
+ this.edit(source, old.getStart(source), old.end, to);
600
+ else {
601
+ const last = old.parent.elements.at(-1) ?? old;
602
+ this.edit(source, last.end, last.end, `, ${to}`);
603
+ }
604
+ }
605
+ this.edit(source, node.getStart(source), node.end, name);
606
+ this.note('written', here, place);
607
+ }
608
+ /** The name `source` reads the provider `to` by, importing it when the file does not yet. Undefined when it cannot. */
609
+ brought(source, to) {
610
+ if (to.source === source)
611
+ return to.name;
612
+ const imports = source.statements.filter((statement) => ts.isImportDeclaration(statement) &&
613
+ ts.isStringLiteralLike(statement.moduleSpecifier) &&
614
+ !statement.importClause?.isTypeOnly &&
615
+ sourceOf(this.project, source, statement.moduleSpecifier.text) ===
616
+ to.source);
617
+ const named = imports.flatMap((statement) => {
618
+ const bindings = statement.importClause?.namedBindings;
619
+ return bindings && ts.isNamedImports(bindings) ? [bindings] : [];
620
+ });
621
+ const held = named
622
+ .flatMap((bindings) => [...bindings.elements])
623
+ .find((element) => !element.isTypeOnly && (element.propertyName ?? element.name).text === to.name);
624
+ if (held)
625
+ return held.name.text;
626
+ if (!to.exported || declares(source, to.name))
627
+ return undefined;
628
+ const last = named[0]?.elements.at(-1);
629
+ if (last)
630
+ this.edit(source, last.end, last.end, `, ${to.name}`);
631
+ else {
632
+ const specifier = specifierTo(source.fileName, to.source.fileName, importExtension(this.project, '.ts'));
633
+ const line = `import { ${to.name} } from ${quoteLike(undefined, source)(specifier)};`;
634
+ const after = source.statements.findLast((statement) => ts.isImportDeclaration(statement));
635
+ if (after)
636
+ this.edit(source, after.end, after.end, `\n${line}`);
637
+ else
638
+ this.edit(source, 0, 0, `${line}\n`);
639
+ }
640
+ return to.name;
641
+ }
642
+ /** Takes properties out of an object and adds others to it. */
643
+ refit(own, inserts, removed, path) {
644
+ if (inserts.length && removed.length === own.node.properties.length) {
645
+ // Every property goes and others come: the object is written whole, as an empty one is.
646
+ for (const [key] of inserts)
647
+ this.note('written', [...path, key], own);
648
+ const start = own.node.getStart(own.source);
649
+ this.edit(own.source, start, own.node.end, valueSource(Object.fromEntries(inserts), this.style(own.source, start)));
650
+ return;
651
+ }
652
+ if (this.emptied(own.node, own.source, removed, inserts))
653
+ return;
654
+ for (const held of removed)
655
+ this.remove(held, own.source);
656
+ if (inserts.length)
657
+ this.insert(own.node, own.source, inserts, path);
658
+ }
659
+ /** Runs `write` where `at` is written: in the subject's own call, or in the constant that holds it. */
660
+ within(at, path, write) {
661
+ const frame = this.frameAt(at, at.node.getText(at.source), path);
662
+ if (!frame)
663
+ return;
664
+ const outer = this.frame;
665
+ this.frame = frame;
666
+ write();
667
+ this.frame = outer;
668
+ }
669
+ /**
670
+ * A schema, in the Zod that writes it. A description, the values of an enum, whether a field
671
+ * can be left out, and a field added or taken away are each changed where they are written,
672
+ * through any constant that holds them. Any other change writes the schema again whole, when
673
+ * it is written out where it stands in Zod the studio writes itself. What is left is for code.
674
+ */
675
+ zod(at, before, after, path, side, left = { was: false, now: false, path }) {
676
+ if (same(before, after) && left.was === left.now)
677
+ return;
678
+ const site = { ...at, node: unwrapped(at.node) };
679
+ const where = { source: site.source, node: site.node };
680
+ const read = readZod(this.project, site);
681
+ if (!read || !isRecord(before) || !isRecord(after)) {
682
+ this.note('code', path, where);
683
+ return;
684
+ }
685
+ const stopped = unread(read);
686
+ if (stopped) {
687
+ this.note('code', path, { source: stopped.source, node: stopped.call });
688
+ return;
689
+ }
690
+ const z = {
691
+ site,
692
+ where,
693
+ read,
694
+ ...partsOf(read),
695
+ object: zodObject(this.project, read),
696
+ };
697
+ const change = { before, after, path, side, left };
698
+ const kind = z.object ? objectCall(after.additionalProperties, side) : undefined;
699
+ const reads = inPlace(z, before, after, kind);
700
+ const changed = [...new Set([...Object.keys(before), ...Object.keys(after)])].filter((key) => !same(before[key], after[key]));
701
+ if (changed.some((key) => !reads.has(key))) {
702
+ this.rewritten(site, after, left.now, path, side);
703
+ return;
704
+ }
705
+ this.zodAdded(z, change);
706
+ if (kind)
707
+ this.zodOpen(z, kind, change);
708
+ this.zodInner(z, reads, change);
709
+ if (z.object)
710
+ this.zodFields(z.object, change);
711
+ }
712
+ /** Takes a method off a schema. */
713
+ cut(call) {
714
+ const { start, end } = methodSpan(call);
715
+ this.edit(call.source, start, end, '');
716
+ }
717
+ /** What is added to the schema goes after it, where it is used. */
718
+ zodAdded(z, change) {
719
+ const { site, where } = z;
720
+ const adds = [this.described(z, change), this.leftOut(z, change.left)].filter((each) => each !== undefined);
721
+ const added = adds.map((each) => each.text).join('');
722
+ // One edit holds all that is added; each setting it adds is its own change.
723
+ adds.forEach(({ path: here }, index) => {
724
+ this.within(site, here, () => {
725
+ if (index === 0)
726
+ this.edit(site.source, site.node.end, site.node.end, added);
727
+ this.note('written', here, where);
728
+ });
729
+ });
730
+ }
731
+ /** A schema's description, changed where it is written. Gives the method that adds one. */
732
+ described(z, { before, after, path }) {
733
+ const [was, now] = [before.description, after.description];
734
+ if (same(was, now))
735
+ return undefined;
736
+ const here = [...path, 'description'];
737
+ const { site, where } = z;
738
+ if (now !== undefined && typeof now !== 'string')
739
+ this.note('code', here, where);
740
+ else if (was === undefined && now !== undefined) {
741
+ const text = `.describe(${valueSource(now, this.style(site.source, site.node.getStart(site.source)))})`;
742
+ return { text, path: here };
743
+ }
744
+ else
745
+ this.redescribed(z, was, now, here);
746
+ return undefined;
747
+ }
748
+ /** A description the schema has, written again or taken off. */
749
+ redescribed(z, was, now, here) {
750
+ const describe = methodCall(z.read, 'describe');
751
+ const [text] = describe?.call.arguments ?? [];
752
+ if (!describe || !text || ts.isSpreadElement(text)) {
753
+ this.note('code', here, z.where);
754
+ return;
755
+ }
756
+ const call = located(describe);
757
+ this.within(call, here, () => {
758
+ if (now !== undefined) {
759
+ this.walk({ ...call, node: text }, was, now, here);
760
+ return;
761
+ }
762
+ this.cut(describe);
763
+ this.note('written', here, { source: describe.source, node: describe.call });
764
+ });
765
+ }
766
+ /** Whether a field can be left out, changed where it is written. Gives the method that lets it be. */
767
+ leftOut(z, left) {
768
+ if (!left.was && left.now)
769
+ return { text: '.optional()', path: left.path };
770
+ if (!left.was || left.now)
771
+ return undefined;
772
+ const optional = methodCall(z.read, 'optional');
773
+ if (!optional)
774
+ this.note('code', left.path, z.where);
775
+ else {
776
+ this.within({ node: optional.call, source: optional.source }, left.path, () => {
777
+ this.cut(optional);
778
+ this.note('written', left.path, { source: optional.source, node: optional.call });
779
+ });
780
+ }
781
+ return undefined;
782
+ }
783
+ /** Whether an object takes fields it does not name, said by the `z.` call it starts from. */
784
+ zodOpen(z, kind, { before, after, path }) {
785
+ if (same(before.additionalProperties, after.additionalProperties))
786
+ return;
787
+ const { base, chain } = z.read;
788
+ const here = [...path, 'additionalProperties'];
789
+ // A method that says it is taken away, and the call says it instead.
790
+ for (const call of chain.filter((each) => OPEN_METHODS.has(each.name))) {
791
+ this.within({ node: call.call, source: call.source }, here, () => this.cut(call));
792
+ }
793
+ this.within(located(base), here, () => {
794
+ const name = base.call.expression.name;
795
+ if (name.text !== kind)
796
+ this.edit(base.source, name.getStart(base.source), name.end, kind);
797
+ this.note('written', here, { source: base.source, node: base.call });
798
+ });
799
+ }
800
+ /** What a schema holds inside its `z.` call: an enum's values, a list's items, a union's members. */
801
+ zodInner(z, reads, { before, after, path, side }) {
802
+ const { part, members, read } = z;
803
+ if (part && reads.has('enum') && !same(before.enum, after.enum)) {
804
+ const here = [...path, 'enum'];
805
+ this.within(located(read.base), here, () => this.walk(part, before.enum, after.enum, here));
806
+ }
807
+ if (part && reads.has('items'))
808
+ this.zod(part, before.items, after.items, [...path, 'items'], side);
809
+ const [olds, news] = [before.anyOf, after.anyOf];
810
+ if (members && reads.has('anyOf') && Array.isArray(olds) && Array.isArray(news)) {
811
+ members.forEach((member, index) => {
812
+ this.zod(located(read.base, member), olds[index], news[index], [...path, 'anyOf', String(index)], side);
813
+ });
814
+ }
815
+ }
816
+ /** An object schema's fields: each one changed, added, or taken away. */
817
+ zodFields(object, { before, after, path, side }) {
818
+ const [fields, held] = [
819
+ isRecord(before.properties) ? before.properties : {},
820
+ isRecord(after.properties) ? after.properties : {},
821
+ ];
822
+ const [needed, needs] = [names(before.required), names(after.required)];
823
+ const own = { node: object.own.node, source: object.own.source };
824
+ const inserts = [];
825
+ const removed = [];
826
+ for (const key of new Set([...Object.keys(fields), ...Object.keys(held)])) {
827
+ const here = [...path, 'properties', key];
828
+ const entry = object.entries.get(key);
829
+ const optional = {
830
+ was: !needed.includes(key),
831
+ now: !needs.includes(key),
832
+ path: [...path, 'required', key],
833
+ };
834
+ if (key in fields && key in held) {
835
+ if (entry?.value)
836
+ this.zod(entry.value, fields[key], held[key], here, side, optional);
837
+ else if (!same(fields[key], held[key]) || optional.was !== optional.now)
838
+ this.note('code', here, own);
839
+ }
840
+ else if (key in held) {
841
+ const field = this.fieldAdded(object, held[key], optional.now, side, here);
842
+ if (field)
843
+ inserts.push([key, field]);
844
+ }
845
+ else {
846
+ const gone = this.fieldTaken(object, entry, here);
847
+ if (gone)
848
+ removed.push(gone);
849
+ }
850
+ }
851
+ if (inserts.length || removed.length)
852
+ this.refield(object, inserts, removed, [...path, 'properties']);
853
+ }
854
+ /** A field to add, as the Zod that writes it. */
855
+ fieldAdded(object, schema, optional, side, here) {
856
+ const name = zodName(object.own.source);
857
+ // The field is written as Zod once here, to know that it can be.
858
+ if (!name || zodSource(schema, optional, side, name) === undefined) {
859
+ this.note('code', here, object.own);
860
+ return undefined;
861
+ }
862
+ return new ZodField((style) => zodSource(schema, optional, side, name, style) ?? '');
863
+ }
864
+ /** A field taken away. Gives its property when the object's own literal writes it. */
865
+ fieldTaken(object, entry, here) {
866
+ const property = entry?.property;
867
+ if (!entry || !property || !ts.isObjectLiteralExpression(property.parent))
868
+ this.note('code', here, object.own);
869
+ else if (property.parent === object.own.node)
870
+ return property;
871
+ else {
872
+ this.within({ node: property.parent, source: entry.source }, here, () => {
873
+ this.remove(property, entry.source);
874
+ this.note('written', here, { source: entry.source, node: property });
875
+ });
876
+ }
877
+ return undefined;
878
+ }
879
+ /** Takes fields out of an object schema's own literal and adds others to it. */
880
+ refield(object, inserts, removed, inFields) {
881
+ const { node, source } = object.own;
882
+ const own = { node, source };
883
+ this.within(own, inFields, () => {
884
+ for (const property of removed) {
885
+ this.note('written', [...inFields, propertyName(property) ?? ''], {
886
+ source,
887
+ node: property,
888
+ });
889
+ }
890
+ if (inserts.length && removed.length === node.properties.length) {
891
+ // Every field goes and others come: the object is written whole.
892
+ for (const [key] of inserts)
893
+ this.note('written', [...inFields, key], own);
894
+ const start = node.getStart(source);
895
+ this.edit(source, start, node.end, objectSource(inserts, this.style(source, start)));
896
+ return;
897
+ }
898
+ if (this.emptied(node, source, removed, inserts))
899
+ return;
900
+ for (const property of removed)
901
+ this.remove(property, source);
902
+ if (inserts.length)
903
+ this.insert(node, source, inserts, inFields);
904
+ });
905
+ }
906
+ /** A schema written again whole, from its JSON Schema, where it stands. */
907
+ rewritten(site, after, optional, path, side) {
908
+ const where = { source: site.source, node: site.node };
909
+ const name = zodName(site.source);
910
+ const start = site.node.getStart(site.source);
911
+ const text = name && isWrittenOut(this.project, site)
912
+ ? zodSource(after, optional, side, name, this.style(site.source, start))
913
+ : undefined;
914
+ if (text === undefined) {
915
+ this.note('code', path, where);
916
+ return;
917
+ }
918
+ this.within(site, path, () => {
919
+ this.edit(site.source, start, site.node.end, text);
920
+ this.note('written', path, where);
921
+ });
922
+ }
923
+ /**
924
+ * An object every property leaves and none comes to, written `{}`. Not when it holds a comment
925
+ * no property takes with it, which would go too.
926
+ */
927
+ emptied(node, source, removed, inserts) {
928
+ if (inserts.length || removed.length !== node.properties.length)
929
+ return false;
930
+ const start = node.getStart(source);
931
+ let rest = source.text.slice(start + 1, node.end - 1);
932
+ for (const property of node.properties)
933
+ rest = rest.replace(property.getFullText(source), '');
934
+ if (!/^[\s,]*$/.test(rest))
935
+ return false;
936
+ this.edit(source, start, node.end, '{}');
937
+ return true;
938
+ }
939
+ /** Takes a property out, with its comma and the comment that leads it. */
940
+ remove(property, source) {
941
+ const { text } = source;
942
+ let start = property.getFullStart();
943
+ let end = property.end;
944
+ const comma = /^[ \t]*,/.exec(text.slice(end));
945
+ if (comma)
946
+ end += comma[0].length;
947
+ else if (text[start - 1] === ',')
948
+ start -= 1;
949
+ this.edit(source, start, end, '');
950
+ }
951
+ /** Adds properties after the last one the object has. */
952
+ insert(node, source, entries, path) {
953
+ for (const [key] of entries)
954
+ this.note('written', [...path, key], { source, node });
955
+ const last = node.properties.at(-1);
956
+ const start = node.getStart(source);
957
+ if (!last) {
958
+ this.edit(source, start, node.end, objectSource(entries, this.style(source, start)));
959
+ return;
960
+ }
961
+ const trailing = node.properties.hasTrailingComma;
962
+ const brace = node.end - 1;
963
+ const braceLine = source.getLineAndCharacterOfPosition(brace).line;
964
+ const ownLine = source.getLineAndCharacterOfPosition(last.end).line < braceLine;
965
+ if (ownLine) {
966
+ // On lines of their own, before the closing brace, so a comment after the last one stays with it.
967
+ const base = indentAt(source, last.getStart(source));
968
+ const style = { ...this.style(source, start), base };
969
+ const lines = entries.map(([key, value]) => `${base}${keySource(key, style.quote)}: ${written(value, style)},\n`);
970
+ if (!trailing)
971
+ this.edit(source, last.end, last.end, ',');
972
+ const at = source.getPositionOfLineAndCharacter(braceLine, 0);
973
+ this.edit(source, at, at, lines.join(''));
974
+ return;
975
+ }
976
+ const style = this.style(source, start);
977
+ const fields = entries
978
+ .map(([key, value]) => `${keySource(key, style.quote)}: ${written(value, style)}`)
979
+ .join(', ');
980
+ if (trailing)
981
+ this.edit(source, node.properties.end, node.properties.end, ` ${fields},`);
982
+ else
983
+ this.edit(source, last.end, last.end, `, ${fields}`);
984
+ }
985
+ }
986
+ /** Finds each subject's call in the project and plans its changes. */
987
+ export function planSave(project, subjects) {
988
+ const plan = { changes: [], edits: [] };
989
+ const shared = [];
990
+ for (const subject of subjects) {
991
+ if (same(subject.before, subject.after))
992
+ continue;
993
+ const defined = definedAs(project, subject.kind);
994
+ const targets = defined.get(subject.of) ?? [];
995
+ const whole = { kind: subject.kind, of: subject.of, setting: '' };
996
+ const [target] = targets;
997
+ if (!target) {
998
+ plan.changes.push({ ...whole, status: 'unfound' });
999
+ continue;
1000
+ }
1001
+ if (targets.length > 1) {
1002
+ // Written twice: the studio cannot tell which one runs.
1003
+ const line = target.source.getLineAndCharacterOfPosition(target.options.getStart(target.source)).line +
1004
+ 1;
1005
+ plan.changes.push({ ...whole, status: 'code', file: target.file, line });
1006
+ continue;
1007
+ }
1008
+ // The others a function makes this same call for.
1009
+ const peers = [...defined]
1010
+ .filter(([name, held]) => name !== subject.of && held.some((other) => other.options === target.options))
1011
+ .map(([name]) => name);
1012
+ const planner = new Planner(project, subject, target, peers);
1013
+ planner.plan();
1014
+ plan.changes.push(...planner.changes);
1015
+ // Two subjects may bring the same import into one file: it is written once.
1016
+ for (const edit of planner.edits) {
1017
+ if (!plan.edits.some((other) => canonical(other) === canonical(edit)))
1018
+ plan.edits.push(edit);
1019
+ }
1020
+ shared.push(...planner.shared);
1021
+ }
1022
+ settle(shared, plan);
1023
+ return plan;
1024
+ }
1025
+ /** `text` with the edits made. Throws when two edits touch the same characters. */
1026
+ export function applyEdits(text, edits) {
1027
+ const ordered = edits
1028
+ .map((edit, index) => ({ edit, index }))
1029
+ .sort((a, b) => a.edit.start - b.edit.start || a.index - b.index);
1030
+ let out = '';
1031
+ let cursor = 0;
1032
+ for (const { edit } of ordered) {
1033
+ if (edit.start < cursor)
1034
+ throw new Error('Two changes touch the same text.');
1035
+ out += text.slice(cursor, edit.start) + edit.text;
1036
+ cursor = edit.end;
1037
+ }
1038
+ return out + text.slice(cursor);
1039
+ }
1040
+ const CONTEXT_LINES = 2;
1041
+ /** The changed lines of one file, from the edits made to it. */
1042
+ export function diffHunks(text, edits) {
1043
+ const lines = text.split('\n');
1044
+ const starts = [];
1045
+ let offset = 0;
1046
+ for (const line of lines) {
1047
+ starts.push(offset);
1048
+ offset += line.length + 1;
1049
+ }
1050
+ const lineOf = (position) => {
1051
+ let low = 0;
1052
+ let high = starts.length - 1;
1053
+ while (low < high) {
1054
+ const mid = Math.ceil((low + high) / 2);
1055
+ if ((starts[mid] ?? 0) <= position)
1056
+ low = mid;
1057
+ else
1058
+ high = mid - 1;
1059
+ }
1060
+ return low;
1061
+ };
1062
+ const ordered = [...edits].sort((a, b) => a.start - b.start);
1063
+ const groups = [];
1064
+ for (const edit of ordered) {
1065
+ const first = lineOf(edit.start);
1066
+ const last = lineOf(edit.end);
1067
+ const open = groups.at(-1);
1068
+ if (open && first <= open.last) {
1069
+ open.last = Math.max(open.last, last);
1070
+ open.edits.push(edit);
1071
+ }
1072
+ else
1073
+ groups.push({ first, last, edits: [edit] });
1074
+ }
1075
+ return groups.flatMap((group) => {
1076
+ const from = starts[group.first] ?? 0;
1077
+ const to = (starts[group.last] ?? 0) + (lines[group.last]?.length ?? 0);
1078
+ const local = group.edits.map((edit) => ({
1079
+ ...edit,
1080
+ start: edit.start - from,
1081
+ end: edit.end - from,
1082
+ }));
1083
+ const was = lines.slice(group.first, group.last + 1);
1084
+ const now = applyEdits(text.slice(from, to), local).split('\n');
1085
+ let head = 0;
1086
+ while (head < was.length && head < now.length && was[head] === now[head])
1087
+ head++;
1088
+ let tail = 0;
1089
+ while (tail < was.length - head &&
1090
+ tail < now.length - head &&
1091
+ was[was.length - 1 - tail] === now[now.length - 1 - tail])
1092
+ tail++;
1093
+ const removed = was.slice(head, was.length - tail);
1094
+ const added = now.slice(head, now.length - tail);
1095
+ if (!removed.length && !added.length)
1096
+ return [];
1097
+ const at = group.first + head;
1098
+ const after = at + removed.length;
1099
+ return [
1100
+ {
1101
+ line: at + 1,
1102
+ lead: lines.slice(Math.max(0, at - CONTEXT_LINES), at),
1103
+ removed,
1104
+ added,
1105
+ trail: lines.slice(after, after + CONTEXT_LINES),
1106
+ },
1107
+ ];
1108
+ });
1109
+ }
1110
+ /** Each name the file imports by name from the kernel package, as values. */
1111
+ function namedImports(source) {
1112
+ return source.statements.flatMap((statement) => {
1113
+ if (!ts.isImportDeclaration(statement) || statement.importClause?.isTypeOnly)
1114
+ return [];
1115
+ const from = statement.moduleSpecifier;
1116
+ if (!ts.isStringLiteralLike(from) || from.text !== AGENTS_PACKAGE)
1117
+ return [];
1118
+ const bindings = statement.importClause?.namedBindings;
1119
+ return bindings && ts.isNamedImports(bindings)
1120
+ ? bindings.elements.filter((element) => !element.isTypeOnly)
1121
+ : [];
1122
+ });
1123
+ }
1124
+ /** How many times the file names `name`, its import included. */
1125
+ function reads(source, name) {
1126
+ let count = 0;
1127
+ const visit = (node) => {
1128
+ if (ts.isIdentifier(node) && node.text === name)
1129
+ count += 1;
1130
+ ts.forEachChild(node, visit);
1131
+ };
1132
+ visit(source);
1133
+ return count;
1134
+ }