@xingular/harness 0.1.7 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (683) hide show
  1. package/CHANGELOG.md +97 -1
  2. package/LICENSE +202 -0
  3. package/NOTICE.md +22 -0
  4. package/README.md +187 -85
  5. package/THIRD_PARTY_LICENSES.md +942 -0
  6. package/dist/cli.js +13701 -13498
  7. package/dist/sdk/accounts/accounts.d.ts +165 -0
  8. package/dist/sdk/accounts/accounts.js +730 -0
  9. package/dist/sdk/accounts/choices.d.ts +23 -0
  10. package/dist/sdk/accounts/choices.js +62 -0
  11. package/dist/sdk/accounts/env.d.ts +78 -0
  12. package/dist/sdk/accounts/env.js +294 -0
  13. package/dist/sdk/accounts/secrets.d.ts +16 -0
  14. package/dist/sdk/accounts/secrets.js +40 -0
  15. package/dist/sdk/accounts/store.d.ts +114 -0
  16. package/dist/sdk/accounts/store.js +255 -0
  17. package/dist/sdk/browser/config.d.ts +16 -0
  18. package/dist/sdk/browser/config.js +22 -0
  19. package/dist/sdk/browser/diagnostics.d.ts +4 -0
  20. package/dist/sdk/browser/diagnostics.js +38 -0
  21. package/dist/sdk/browser/index.d.ts +17 -0
  22. package/dist/sdk/browser/index.js +65 -0
  23. package/dist/sdk/browser/network.d.ts +10 -0
  24. package/dist/sdk/browser/network.js +59 -0
  25. package/dist/sdk/browser/session.d.ts +84 -0
  26. package/dist/sdk/browser/session.js +192 -0
  27. package/dist/sdk/browser/tools.d.ts +8 -0
  28. package/dist/sdk/browser/tools.js +329 -0
  29. package/dist/sdk/capabilities/api.d.ts +22 -0
  30. package/dist/sdk/capabilities/api.js +54 -0
  31. package/dist/sdk/capabilities/runSnapshot.d.ts +18 -0
  32. package/dist/sdk/capabilities/runSnapshot.js +47 -0
  33. package/dist/sdk/capabilities/types.d.ts +29 -0
  34. package/dist/sdk/capabilities/types.js +1 -0
  35. package/dist/sdk/cli.d.ts +2 -0
  36. package/dist/sdk/cli.js +142 -0
  37. package/dist/sdk/core/bus.d.ts +23 -0
  38. package/dist/sdk/core/bus.js +76 -0
  39. package/dist/sdk/core/caller.d.ts +22 -0
  40. package/dist/sdk/core/caller.js +16 -0
  41. package/dist/sdk/core/changes.d.ts +15 -0
  42. package/dist/sdk/core/changes.js +15 -0
  43. package/dist/sdk/core/effort.d.ts +2 -0
  44. package/dist/sdk/core/effort.js +1 -0
  45. package/dist/sdk/core/env.d.ts +18 -0
  46. package/dist/sdk/core/env.js +8 -0
  47. package/dist/sdk/core/errors.d.ts +15 -0
  48. package/dist/sdk/core/errors.js +24 -0
  49. package/dist/sdk/core/frontmatter.d.ts +12 -0
  50. package/dist/sdk/core/frontmatter.js +50 -0
  51. package/dist/sdk/core/fs.d.ts +18 -0
  52. package/dist/sdk/core/fs.js +106 -0
  53. package/dist/sdk/core/index.d.ts +24 -0
  54. package/dist/sdk/core/index.js +187 -0
  55. package/dist/sdk/core/path.d.ts +7 -0
  56. package/dist/sdk/core/path.js +79 -0
  57. package/dist/sdk/core/projects.d.ts +18 -0
  58. package/dist/sdk/core/projects.js +67 -0
  59. package/dist/sdk/core/secrets.d.ts +2 -0
  60. package/dist/sdk/core/secrets.js +5 -0
  61. package/dist/sdk/core/text.d.ts +7 -0
  62. package/dist/sdk/core/text.js +40 -0
  63. package/dist/sdk/doctor/checks.d.ts +5 -0
  64. package/dist/sdk/doctor/checks.js +208 -0
  65. package/dist/sdk/doctor/probe.d.ts +4 -0
  66. package/dist/sdk/doctor/probe.js +92 -0
  67. package/dist/sdk/doctor/service.d.ts +23 -0
  68. package/dist/sdk/doctor/service.js +144 -0
  69. package/dist/sdk/doctor/types.d.ts +141 -0
  70. package/dist/sdk/doctor/types.js +29 -0
  71. package/dist/sdk/harness.d.ts +133 -0
  72. package/dist/sdk/harness.js +299 -0
  73. package/dist/sdk/history/lessonPrompt.d.ts +2 -0
  74. package/dist/sdk/history/lessonPrompt.js +15 -0
  75. package/dist/sdk/history/lessons.d.ts +52 -0
  76. package/dist/sdk/history/lessons.js +173 -0
  77. package/dist/sdk/history/recall.d.ts +68 -0
  78. package/dist/sdk/history/recall.js +199 -0
  79. package/dist/sdk/history/trajectory.d.ts +7 -0
  80. package/dist/sdk/history/trajectory.js +216 -0
  81. package/dist/sdk/hub/catalog.json +123 -0
  82. package/dist/sdk/hub/cli.d.ts +7 -0
  83. package/dist/sdk/hub/cli.js +81 -0
  84. package/dist/sdk/hub/hub.d.ts +74 -0
  85. package/dist/sdk/hub/hub.js +474 -0
  86. package/dist/sdk/hub/schema.d.ts +280 -0
  87. package/dist/sdk/hub/schema.js +78 -0
  88. package/dist/sdk/index.d.ts +63 -0
  89. package/dist/sdk/index.js +48 -0
  90. package/dist/sdk/mcp/connections.d.ts +83 -0
  91. package/dist/sdk/mcp/connections.js +669 -0
  92. package/dist/sdk/mcp/manager.d.ts +26 -0
  93. package/dist/sdk/mcp/manager.js +124 -0
  94. package/dist/sdk/mcp/oauth.d.ts +35 -0
  95. package/dist/sdk/mcp/oauth.js +171 -0
  96. package/dist/sdk/mcp/scopeMigration.d.ts +19 -0
  97. package/dist/sdk/mcp/scopeMigration.js +198 -0
  98. package/dist/sdk/mcp/service.d.ts +160 -0
  99. package/dist/sdk/mcp/service.js +510 -0
  100. package/dist/sdk/mcp/types.d.ts +131 -0
  101. package/dist/sdk/mcp/types.js +49 -0
  102. package/dist/sdk/pi/runtime.d.ts +13 -0
  103. package/dist/sdk/pi/runtime.js +31 -0
  104. package/dist/sdk/plugins/api.d.ts +66 -0
  105. package/dist/sdk/plugins/api.js +155 -0
  106. package/dist/sdk/plugins/attachments.d.ts +46 -0
  107. package/dist/sdk/plugins/attachments.js +111 -0
  108. package/dist/sdk/plugins/builtin.d.ts +6 -0
  109. package/dist/sdk/plugins/builtin.js +68 -0
  110. package/dist/sdk/plugins/catalogue.d.ts +29 -0
  111. package/dist/sdk/plugins/catalogue.js +196 -0
  112. package/dist/sdk/plugins/cli.d.ts +13 -0
  113. package/dist/sdk/plugins/cli.js +187 -0
  114. package/dist/sdk/plugins/discover.d.ts +42 -0
  115. package/dist/sdk/plugins/discover.js +115 -0
  116. package/dist/sdk/plugins/firstParty.d.ts +50 -0
  117. package/dist/sdk/plugins/firstParty.js +113 -0
  118. package/dist/sdk/plugins/hooks.d.ts +94 -0
  119. package/dist/sdk/plugins/hooks.js +162 -0
  120. package/dist/sdk/plugins/identity.d.ts +19 -0
  121. package/dist/sdk/plugins/identity.js +48 -0
  122. package/dist/sdk/plugins/install.d.ts +20 -0
  123. package/dist/sdk/plugins/install.js +110 -0
  124. package/dist/sdk/plugins/inventory.d.ts +22 -0
  125. package/dist/sdk/plugins/inventory.js +110 -0
  126. package/dist/sdk/plugins/manifest.d.ts +13 -0
  127. package/dist/sdk/plugins/manifest.js +56 -0
  128. package/dist/sdk/plugins/plugins.d.ts +104 -0
  129. package/dist/sdk/plugins/plugins.js +164 -0
  130. package/dist/sdk/plugins/runtime.d.ts +9 -0
  131. package/dist/sdk/plugins/runtime.js +95 -0
  132. package/dist/sdk/plugins/trust.d.ts +44 -0
  133. package/dist/sdk/plugins/trust.js +120 -0
  134. package/dist/sdk/plugins/types.d.ts +159 -0
  135. package/dist/sdk/plugins/types.js +7 -0
  136. package/dist/sdk/prompt/compile.d.ts +174 -0
  137. package/dist/sdk/prompt/compile.js +396 -0
  138. package/dist/sdk/refine/cli.d.ts +4 -0
  139. package/dist/sdk/refine/cli.js +186 -0
  140. package/dist/sdk/refine/demo.d.ts +21 -0
  141. package/dist/sdk/refine/demo.js +114 -0
  142. package/dist/sdk/refine/evidence.d.ts +55 -0
  143. package/dist/sdk/refine/evidence.js +330 -0
  144. package/dist/sdk/refine/host.d.ts +51 -0
  145. package/dist/sdk/refine/host.js +76 -0
  146. package/dist/sdk/refine/ledger.d.ts +48 -0
  147. package/dist/sdk/refine/ledger.js +76 -0
  148. package/dist/sdk/refine/library.d.ts +55 -0
  149. package/dist/sdk/refine/library.js +177 -0
  150. package/dist/sdk/refine/pi.d.ts +72 -0
  151. package/dist/sdk/refine/pi.js +248 -0
  152. package/dist/sdk/refine/prompts.d.ts +45 -0
  153. package/dist/sdk/refine/prompts.js +390 -0
  154. package/dist/sdk/refine/recursion.d.ts +113 -0
  155. package/dist/sdk/refine/recursion.js +258 -0
  156. package/dist/sdk/refine/runner.d.ts +81 -0
  157. package/dist/sdk/refine/runner.js +150 -0
  158. package/dist/sdk/refine/selection.d.ts +57 -0
  159. package/dist/sdk/refine/selection.js +251 -0
  160. package/dist/sdk/refine/service.d.ts +84 -0
  161. package/dist/sdk/refine/service.js +165 -0
  162. package/dist/sdk/refine/suite.d.ts +67 -0
  163. package/dist/sdk/refine/suite.js +174 -0
  164. package/dist/sdk/refine/trajectory.d.ts +9 -0
  165. package/dist/sdk/refine/trajectory.js +68 -0
  166. package/dist/sdk/refine/types.d.ts +77 -0
  167. package/dist/sdk/refine/types.js +10 -0
  168. package/dist/sdk/server/methods.d.ts +3 -0
  169. package/dist/sdk/server/methods.js +370 -0
  170. package/dist/sdk/server/remote.d.ts +19 -0
  171. package/dist/sdk/server/remote.js +84 -0
  172. package/dist/sdk/server/rpc.d.ts +20 -0
  173. package/dist/sdk/server/rpc.js +102 -0
  174. package/dist/sdk/server/serve.d.ts +10 -0
  175. package/dist/sdk/server/serve.js +18 -0
  176. package/dist/sdk/sessions/approvals.d.ts +47 -0
  177. package/dist/sdk/sessions/approvals.js +119 -0
  178. package/dist/sdk/sessions/continuation.d.ts +48 -0
  179. package/dist/sdk/sessions/continuation.js +85 -0
  180. package/dist/sdk/sessions/contracts.d.ts +534 -0
  181. package/dist/sdk/sessions/contracts.js +299 -0
  182. package/dist/sdk/sessions/extensionUi.d.ts +19 -0
  183. package/dist/sdk/sessions/extensionUi.js +64 -0
  184. package/dist/sdk/sessions/history.d.ts +16 -0
  185. package/dist/sdk/sessions/history.js +68 -0
  186. package/dist/sdk/sessions/lineageJoin.d.ts +2 -0
  187. package/dist/sdk/sessions/lineageJoin.js +61 -0
  188. package/dist/sdk/sessions/planMode.d.ts +36 -0
  189. package/dist/sdk/sessions/planMode.js +84 -0
  190. package/dist/sdk/sessions/policy.d.ts +43 -0
  191. package/dist/sdk/sessions/policy.js +1 -0
  192. package/dist/sdk/sessions/pricing.d.ts +34 -0
  193. package/dist/sdk/sessions/pricing.js +40 -0
  194. package/dist/sdk/sessions/reasoningSummaries.d.ts +11 -0
  195. package/dist/sdk/sessions/reasoningSummaries.js +61 -0
  196. package/dist/sdk/sessions/registry.d.ts +102 -0
  197. package/dist/sdk/sessions/registry.js +383 -0
  198. package/dist/sdk/sessions/runIndex.d.ts +21 -0
  199. package/dist/sdk/sessions/runIndex.js +65 -0
  200. package/dist/sdk/sessions/runStore.d.ts +66 -0
  201. package/dist/sdk/sessions/runStore.js +335 -0
  202. package/dist/sdk/sessions/runner.d.ts +213 -0
  203. package/dist/sdk/sessions/runner.js +589 -0
  204. package/dist/sdk/sessions/sessions.d.ts +140 -0
  205. package/dist/sdk/sessions/sessions.js +394 -0
  206. package/dist/sdk/sessions/subagentService.d.ts +41 -0
  207. package/dist/sdk/sessions/subagentService.js +247 -0
  208. package/dist/sdk/sessions/subagents.d.ts +50 -0
  209. package/dist/sdk/sessions/subagents.js +68 -0
  210. package/dist/sdk/terminal/accountsMenu.d.ts +46 -0
  211. package/dist/sdk/terminal/accountsMenu.js +392 -0
  212. package/dist/sdk/terminal/cli.d.ts +20 -0
  213. package/dist/sdk/terminal/cli.js +121 -0
  214. package/dist/sdk/terminal/client.d.ts +15 -0
  215. package/dist/sdk/terminal/client.js +126 -0
  216. package/dist/sdk/terminal/control.d.ts +4 -0
  217. package/dist/sdk/terminal/control.js +25 -0
  218. package/dist/sdk/terminal/controller.d.ts +72 -0
  219. package/dist/sdk/terminal/controller.js +351 -0
  220. package/dist/sdk/terminal/hubMenu.d.ts +10 -0
  221. package/dist/sdk/terminal/hubMenu.js +34 -0
  222. package/dist/sdk/terminal/launch.d.ts +5 -0
  223. package/dist/sdk/terminal/launch.js +27 -0
  224. package/dist/sdk/terminal/logs.d.ts +14 -0
  225. package/dist/sdk/terminal/logs.js +59 -0
  226. package/dist/sdk/terminal/piExtension.d.ts +6 -0
  227. package/dist/sdk/terminal/piExtension.js +392 -0
  228. package/dist/sdk/terminal/pluginMenu.d.ts +34 -0
  229. package/dist/sdk/terminal/pluginMenu.js +101 -0
  230. package/dist/sdk/terminal/socket.d.ts +70 -0
  231. package/dist/sdk/terminal/socket.js +256 -0
  232. package/dist/sdk/terminal/standalone.d.ts +18 -0
  233. package/dist/sdk/terminal/standalone.js +146 -0
  234. package/dist/sdk/terminal/terminals.d.ts +176 -0
  235. package/dist/sdk/terminal/terminals.js +767 -0
  236. package/dist/sdk/terminal/trajectory.d.ts +10 -0
  237. package/dist/sdk/terminal/trajectory.js +30 -0
  238. package/dist/sdk/terminal/tui/chrome.d.ts +64 -0
  239. package/dist/sdk/terminal/tui/chrome.js +169 -0
  240. package/dist/sdk/terminal/tui/clipboardFiles.d.ts +11 -0
  241. package/dist/sdk/terminal/tui/clipboardFiles.js +74 -0
  242. package/dist/sdk/terminal/tui/commandPages.d.ts +7 -0
  243. package/dist/sdk/terminal/tui/commandPages.js +63 -0
  244. package/dist/sdk/terminal/tui/commands.d.ts +42 -0
  245. package/dist/sdk/terminal/tui/commands.js +276 -0
  246. package/dist/sdk/terminal/tui/dialogGeometry.d.ts +27 -0
  247. package/dist/sdk/terminal/tui/dialogGeometry.js +50 -0
  248. package/dist/sdk/terminal/tui/dialogs.d.ts +75 -0
  249. package/dist/sdk/terminal/tui/dialogs.js +363 -0
  250. package/dist/sdk/terminal/tui/keybindings.d.ts +11 -0
  251. package/dist/sdk/terminal/tui/keybindings.js +49 -0
  252. package/dist/sdk/terminal/tui/list.d.ts +73 -0
  253. package/dist/sdk/terminal/tui/list.js +196 -0
  254. package/dist/sdk/terminal/tui/modeChoices.d.ts +42 -0
  255. package/dist/sdk/terminal/tui/modeChoices.js +70 -0
  256. package/dist/sdk/terminal/tui/modelPicker.d.ts +55 -0
  257. package/dist/sdk/terminal/tui/modelPicker.js +155 -0
  258. package/dist/sdk/terminal/tui/mouse.d.ts +3 -0
  259. package/dist/sdk/terminal/tui/mouse.js +3 -0
  260. package/dist/sdk/terminal/tui/page.d.ts +14 -0
  261. package/dist/sdk/terminal/tui/page.js +1 -0
  262. package/dist/sdk/terminal/tui/pageOverlay.d.ts +27 -0
  263. package/dist/sdk/terminal/tui/pageOverlay.js +145 -0
  264. package/dist/sdk/terminal/tui/pages/accountsPage.d.ts +54 -0
  265. package/dist/sdk/terminal/tui/pages/accountsPage.js +181 -0
  266. package/dist/sdk/terminal/tui/pages/componentsPage.d.ts +47 -0
  267. package/dist/sdk/terminal/tui/pages/componentsPage.js +119 -0
  268. package/dist/sdk/terminal/tui/pages/doctorPage.d.ts +37 -0
  269. package/dist/sdk/terminal/tui/pages/doctorPage.js +145 -0
  270. package/dist/sdk/terminal/tui/pages/hubPage.d.ts +39 -0
  271. package/dist/sdk/terminal/tui/pages/hubPage.js +128 -0
  272. package/dist/sdk/terminal/tui/pages/logPage.d.ts +38 -0
  273. package/dist/sdk/terminal/tui/pages/logPage.js +189 -0
  274. package/dist/sdk/terminal/tui/pages/permissionsPage.d.ts +44 -0
  275. package/dist/sdk/terminal/tui/pages/permissionsPage.js +124 -0
  276. package/dist/sdk/terminal/tui/pages/pluginsPage.d.ts +57 -0
  277. package/dist/sdk/terminal/tui/pages/pluginsPage.js +243 -0
  278. package/dist/sdk/terminal/tui/pages/settingsPage.d.ts +55 -0
  279. package/dist/sdk/terminal/tui/pages/settingsPage.js +88 -0
  280. package/dist/sdk/terminal/tui/pages/trajectoryPage.d.ts +58 -0
  281. package/dist/sdk/terminal/tui/pages/trajectoryPage.js +377 -0
  282. package/dist/sdk/terminal/tui/piAdapter.d.ts +18 -0
  283. package/dist/sdk/terminal/tui/piAdapter.js +48 -0
  284. package/dist/sdk/terminal/tui/planMode.d.ts +2 -0
  285. package/dist/sdk/terminal/tui/planMode.js +2 -0
  286. package/dist/sdk/terminal/tui/questionPicker.d.ts +20 -0
  287. package/dist/sdk/terminal/tui/questionPicker.js +150 -0
  288. package/dist/sdk/terminal/tui/questions.d.ts +19 -0
  289. package/dist/sdk/terminal/tui/questions.js +42 -0
  290. package/dist/sdk/terminal/tui/runtime.d.ts +114 -0
  291. package/dist/sdk/terminal/tui/runtime.js +441 -0
  292. package/dist/sdk/terminal/tui/search.d.ts +22 -0
  293. package/dist/sdk/terminal/tui/search.js +60 -0
  294. package/dist/sdk/terminal/tui/selection.d.ts +16 -0
  295. package/dist/sdk/terminal/tui/selection.js +16 -0
  296. package/dist/sdk/terminal/tui/sessionUi.d.ts +3 -0
  297. package/dist/sdk/terminal/tui/sessionUi.js +30 -0
  298. package/dist/sdk/terminal/tui/settingsModel.d.ts +12 -0
  299. package/dist/sdk/terminal/tui/settingsModel.js +84 -0
  300. package/dist/sdk/terminal/tui/sideQuestion.d.ts +53 -0
  301. package/dist/sdk/terminal/tui/sideQuestion.js +197 -0
  302. package/dist/sdk/terminal/tui/skillInvocation.d.ts +9 -0
  303. package/dist/sdk/terminal/tui/skillInvocation.js +43 -0
  304. package/dist/sdk/terminal/tui/status.d.ts +18 -0
  305. package/dist/sdk/terminal/tui/status.js +67 -0
  306. package/dist/sdk/terminal/tui/theme.d.ts +45 -0
  307. package/dist/sdk/terminal/tui/theme.js +176 -0
  308. package/dist/sdk/terminal/tui/treeModel.d.ts +79 -0
  309. package/dist/sdk/terminal/tui/treeModel.js +483 -0
  310. package/dist/sdk/terminal/tui/treePicker.d.ts +49 -0
  311. package/dist/sdk/terminal/tui/treePicker.js +251 -0
  312. package/dist/sdk/terminal/tui/ui.d.ts +47 -0
  313. package/dist/sdk/terminal/tui/ui.js +148 -0
  314. package/dist/sdk/terminal/tui/workingStatus.d.ts +9 -0
  315. package/dist/sdk/terminal/tui/workingStatus.js +229 -0
  316. package/dist/sdk/tools/askUser.d.ts +6 -0
  317. package/dist/sdk/tools/askUser.js +74 -0
  318. package/dist/sdk/tools/checklist.d.ts +9 -0
  319. package/dist/sdk/tools/checklist.js +54 -0
  320. package/dist/sdk/tools/definitions.d.ts +30 -0
  321. package/dist/sdk/tools/definitions.js +117 -0
  322. package/dist/sdk/tools/dispatch.d.ts +16 -0
  323. package/dist/sdk/tools/dispatch.js +67 -0
  324. package/dist/sdk/tools/harnessTools.d.ts +6 -0
  325. package/dist/sdk/tools/harnessTools.js +20 -0
  326. package/dist/sdk/tools/sessionName.d.ts +7 -0
  327. package/dist/sdk/tools/sessionName.js +31 -0
  328. package/dist/sdk/tools/spawnSubagent.d.ts +4 -0
  329. package/dist/sdk/tools/spawnSubagent.js +49 -0
  330. package/dist/sdk/tools/types.d.ts +48 -0
  331. package/dist/sdk/tools/types.js +1 -0
  332. package/dist/sdk/variant/components.d.ts +34 -0
  333. package/dist/sdk/variant/components.js +230 -0
  334. package/dist/sdk/variant/materialise.d.ts +66 -0
  335. package/dist/sdk/variant/materialise.js +81 -0
  336. package/dist/sdk/variant/migrate.d.ts +4 -0
  337. package/dist/sdk/variant/migrate.js +41 -0
  338. package/dist/sdk/variant/seed.d.ts +20 -0
  339. package/dist/sdk/variant/seed.js +116 -0
  340. package/dist/sdk/variant/store.d.ts +82 -0
  341. package/dist/sdk/variant/store.js +151 -0
  342. package/dist/sdk/variant/surface.d.ts +56 -0
  343. package/dist/sdk/variant/surface.js +81 -0
  344. package/dist/sdk/variant/trust.d.ts +27 -0
  345. package/dist/sdk/variant/trust.js +64 -0
  346. package/dist/sdk/variant/variant.d.ts +71 -0
  347. package/dist/sdk/variant/variant.js +331 -0
  348. package/dist/sdk/web/markdown.d.ts +10 -0
  349. package/dist/sdk/web/markdown.js +66 -0
  350. package/dist/sdk/web/safeRequest.d.ts +34 -0
  351. package/dist/sdk/web/safeRequest.js +175 -0
  352. package/dist/sdk/web/search.d.ts +53 -0
  353. package/dist/sdk/web/search.js +168 -0
  354. package/dist/sdk/web/text.d.ts +2 -0
  355. package/dist/sdk/web/text.js +7 -0
  356. package/dist/sdk/web/tools.d.ts +22 -0
  357. package/dist/sdk/web/tools.js +195 -0
  358. package/dist/sdk/web/turndown-plugin-gfm.d.ts +4 -0
  359. package/dist/sdk/wiki/notes.d.ts +68 -0
  360. package/dist/sdk/wiki/notes.js +297 -0
  361. package/dist/sdk/world/actions.d.ts +23 -0
  362. package/dist/sdk/world/actions.js +31 -0
  363. package/dist/sdk/world/cli.d.ts +12 -0
  364. package/dist/sdk/world/cli.js +166 -0
  365. package/dist/sdk/world/images.d.ts +20 -0
  366. package/dist/sdk/world/images.js +48 -0
  367. package/dist/sdk/world/untrusted.d.ts +12 -0
  368. package/dist/sdk/world/untrusted.js +20 -0
  369. package/dist/sdk/world/world.d.ts +65 -0
  370. package/dist/sdk/world/world.js +80 -0
  371. package/dist/terminal-opentui.js +13612 -0
  372. package/dist/terminal-pi.js +808 -151
  373. package/docs/README.md +22 -0
  374. package/docs/architecture/00-overview.md +159 -0
  375. package/docs/architecture/01-extension-points.md +78 -0
  376. package/docs/architecture/02-sessions-and-runs.md +146 -0
  377. package/docs/architecture/03-variants.md +95 -0
  378. package/docs/architecture/04-prompt.md +102 -0
  379. package/docs/architecture/05-refinement.md +107 -0
  380. package/docs/architecture/06-plugins-and-capabilities.md +93 -0
  381. package/docs/architecture/07-tools-and-gate.md +88 -0
  382. package/docs/architecture/08-memory-and-history.md +94 -0
  383. package/docs/architecture/09-accounts-and-connectors.md +90 -0
  384. package/docs/architecture/10-server-protocol.md +84 -0
  385. package/docs/architecture/11-terminal.md +81 -0
  386. package/docs/architecture/12-storage.md +61 -0
  387. package/docs/architecture/13-world-tools.md +23 -0
  388. package/docs/architecture/README.md +114 -0
  389. package/docs/architecture/proposals/PROP-0001-reporting-section.md +106 -0
  390. package/docs/architecture/proposals/PROP-0002-retain-rollout-workspaces.md +104 -0
  391. package/docs/architecture/proposals/PROP-0003-executor-settings.md +81 -0
  392. package/docs/architecture/proposals/PROP-0004-flexible-reporting-guidance.md +71 -0
  393. package/docs/architecture/proposals/PROP-0005-xharness-cli-name.md +78 -0
  394. package/docs/architecture/proposals/PROP-0006-run-checklist.md +68 -0
  395. package/docs/architecture/proposals/PROP-0007-run-continuation.md +80 -0
  396. package/docs/architecture/proposals/PROP-0008-lessons-from-runs.md +80 -0
  397. package/docs/architecture/proposals/PROP-0009-foundation-and-variants.md +78 -0
  398. package/docs/architecture/proposals/PROP-0010-session-names.md +73 -0
  399. package/docs/architecture/proposals/PROP-0011-doctor.md +63 -0
  400. package/docs/architecture/proposals/PROP-0012-release-baseline.md +56 -0
  401. package/docs/architecture/proposals/README.md +76 -0
  402. package/docs/decisions/ADR-0002-native-subagent-lifecycle.md +49 -0
  403. package/docs/decisions/ADR-0003-shared-asynchronous-subagents.md +70 -0
  404. package/docs/decisions/ADR-0004-fullscreen-terminal-layout.md +50 -0
  405. package/docs/decisions/ADR-0005-app-provided-oauth-clients.md +54 -0
  406. package/docs/decisions/ADR-0006-opentui-only-terminal.md +47 -0
  407. package/docs/decisions/ADR-0007-reporting-section.md +33 -0
  408. package/docs/decisions/ADR-0008-flexible-reporting-guidance.md +38 -0
  409. package/docs/decisions/ADR-0009-xharness-command.md +38 -0
  410. package/docs/decisions/ADR-0010-lessons-in-the-wiki.md +31 -0
  411. package/docs/decisions/ADR-0011-foundation-and-variants.md +28 -0
  412. package/docs/decisions/ADR-0012-native-session-names.md +34 -0
  413. package/docs/decisions/ADR-0013-doctor.md +27 -0
  414. package/docs/decisions/ADR-0014-public-release-baseline.md +21 -0
  415. package/docs/doctor.md +74 -0
  416. package/docs/experiments/EXP-0001-terminal-input-latency/plan.md +81 -0
  417. package/docs/experiments/EXP-0001-terminal-input-latency/results.md +46 -0
  418. package/docs/experiments/EXP-0002-opentui-typing-latency/plan.md +70 -0
  419. package/docs/experiments/EXP-0002-opentui-typing-latency/results.md +92 -0
  420. package/docs/features/README.md +20 -0
  421. package/docs/features/automations/README.md +41 -0
  422. package/docs/features/cores/README.md +39 -0
  423. package/docs/features/google-harness2/README.md +52 -0
  424. package/docs/features/integrations/README.md +40 -0
  425. package/docs/features/interfaces/README.md +29 -0
  426. package/docs/features/memory/README.md +28 -0
  427. package/docs/releases/1.0.0.md +40 -0
  428. package/docs/releases/README.md +36 -0
  429. package/docs/study/00-index.md +78 -0
  430. package/docs/study/arms/00-matrix.md +20 -0
  431. package/docs/study/arms/01-system-prompt.md +211 -0
  432. package/docs/study/arms/02-tools.md +79 -0
  433. package/docs/study/arms/03-subagents.md +90 -0
  434. package/docs/study/arms/04-skills.md +75 -0
  435. package/docs/study/arms/05-context-management.md +90 -0
  436. package/docs/study/arms/06-memory.md +90 -0
  437. package/docs/study/arms/07-governance.md +71 -0
  438. package/docs/study/arms/08-execution.md +69 -0
  439. package/docs/study/arms/09-observability.md +60 -0
  440. package/docs/study/arms/10-refinement.md +110 -0
  441. package/docs/study/btw-comparison.md +37 -0
  442. package/docs/study/feature-gap-review.md +42 -0
  443. package/docs/study/insights.md +123 -0
  444. package/docs/study/roadmap.md +72 -0
  445. package/docs/study/sources/claude-code.md +44 -0
  446. package/docs/study/sources/deepseek-harness.md +102 -0
  447. package/docs/study/sources/harness2.md +50 -0
  448. package/docs/study/sources/kimi-cli.md +42 -0
  449. package/docs/study/sources/omp.md +93 -0
  450. package/docs/study/sources/opencode.md +43 -0
  451. package/docs/study/sources/openhands.md +46 -0
  452. package/docs/study/sources/smolagents.md +40 -0
  453. package/docs/study/sources/swe-agent.md +34 -0
  454. package/docs/system-prompt-design.md +196 -0
  455. package/docs/terminal-tui.md +473 -56
  456. package/package.json +43 -13
  457. package/docs/evidence/terminal-2026-10-17/120x40-completed-transcript.ansi.gz +0 -0
  458. package/docs/evidence/terminal-2026-10-17/40x20-settings-interaction.ansi.gz +0 -0
  459. package/docs/evidence/terminal-2026-10-17/40x20-settings-keyboard.ansi.gz +0 -0
  460. package/docs/evidence/terminal-2026-10-17/40x20-welcome.ansi.gz +0 -0
  461. package/docs/evidence/terminal-2026-10-17/80x24-approval-0.ansi.gz +0 -0
  462. package/docs/evidence/terminal-2026-10-17/80x24-question.ansi.gz +0 -0
  463. package/docs/evidence/terminal-2026-10-17/verification.json +0 -130
  464. package/docs/evidence/terminal-2026-10-17-followup/120x40-completed-transcript.ansi.gz +0 -0
  465. package/docs/evidence/terminal-2026-10-17-followup/40x20-settings-interaction.ansi.gz +0 -0
  466. package/docs/evidence/terminal-2026-10-17-followup/40x20-settings-keyboard.ansi.gz +0 -0
  467. package/docs/evidence/terminal-2026-10-17-followup/40x20-welcome.ansi.gz +0 -0
  468. package/docs/evidence/terminal-2026-10-17-followup/80x24-approval-0.ansi.gz +0 -0
  469. package/docs/evidence/terminal-2026-10-17-followup/80x24-plugin-light.ansi.gz +0 -0
  470. package/docs/evidence/terminal-2026-10-17-followup/80x24-question-batch.ansi.gz +0 -0
  471. package/docs/evidence/terminal-2026-10-17-followup/80x24-question.ansi.gz +0 -0
  472. package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-approval-0.ansi.gz +0 -0
  473. package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-plugin-light.ansi.gz +0 -0
  474. package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-question-batch.ansi.gz +0 -0
  475. package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-question.ansi.gz +0 -0
  476. package/docs/evidence/terminal-2026-10-17-followup/app-bundle/verification.json +0 -95
  477. package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-approval-0.ansi.gz +0 -0
  478. package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-plugin-light.ansi.gz +0 -0
  479. package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-question-batch.ansi.gz +0 -0
  480. package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-question.ansi.gz +0 -0
  481. package/docs/evidence/terminal-2026-10-17-followup/global-install/README.md +0 -27
  482. package/docs/evidence/terminal-2026-10-17-followup/global-install/shell-command-80x24.ansi.gz +0 -0
  483. package/docs/evidence/terminal-2026-10-17-followup/global-install/shell-command.json +0 -18
  484. package/docs/evidence/terminal-2026-10-17-followup/global-install/verification.json +0 -95
  485. package/docs/evidence/terminal-2026-10-17-followup/harness-reference-80x24.json.gz +0 -0
  486. package/docs/evidence/terminal-2026-10-17-followup/harness-reference-final-80x24.json.gz +0 -0
  487. package/docs/evidence/terminal-2026-10-17-followup/harness-reference-scenario.json +0 -60
  488. package/docs/evidence/terminal-2026-10-17-followup/live-codex/README.md +0 -24
  489. package/docs/evidence/terminal-2026-10-17-followup/live-codex/complete-80x24.ansi.gz +0 -0
  490. package/docs/evidence/terminal-2026-10-17-followup/live-codex/verification.json +0 -57
  491. package/docs/evidence/terminal-2026-10-17-followup/omp-reference-80x24-early.json.gz +0 -0
  492. package/docs/evidence/terminal-2026-10-17-followup/omp-reference-80x24.json.gz +0 -0
  493. package/docs/evidence/terminal-2026-10-17-followup/omp-reference-scenario.json +0 -58
  494. package/docs/evidence/terminal-2026-10-17-followup/omp-supplemental-static.json.gz +0 -0
  495. package/docs/evidence/terminal-2026-10-17-followup/rea-artifacts.json +0 -320
  496. package/docs/evidence/terminal-2026-10-17-followup/reference-comparison.json +0 -30
  497. package/docs/evidence/terminal-2026-10-17-followup/reference-comparison.md +0 -133
  498. package/docs/evidence/terminal-2026-10-17-followup/reference-source-observations.json +0 -20
  499. package/docs/evidence/terminal-2026-10-17-followup/verification.json +0 -207
  500. package/src/accounts/accounts.test.ts +0 -333
  501. package/src/accounts/accounts.ts +0 -658
  502. package/src/accounts/env.test.ts +0 -95
  503. package/src/accounts/env.ts +0 -338
  504. package/src/accounts/secrets.ts +0 -50
  505. package/src/accounts/store.ts +0 -338
  506. package/src/capabilities/api.ts +0 -75
  507. package/src/capabilities/runSnapshot.ts +0 -59
  508. package/src/capabilities/types.ts +0 -34
  509. package/src/cli.ts +0 -118
  510. package/src/core/bus.ts +0 -34
  511. package/src/core/changes.ts +0 -33
  512. package/src/core/env.ts +0 -27
  513. package/src/core/errors.ts +0 -32
  514. package/src/core/frontmatter.ts +0 -47
  515. package/src/core/fs.test.ts +0 -38
  516. package/src/core/fs.ts +0 -92
  517. package/src/core/index.ts +0 -193
  518. package/src/core/path.test.ts +0 -14
  519. package/src/core/path.ts +0 -79
  520. package/src/core/projects.ts +0 -90
  521. package/src/core/text.ts +0 -39
  522. package/src/harness.ts +0 -312
  523. package/src/hub/catalog.json +0 -123
  524. package/src/hub/cli.ts +0 -59
  525. package/src/hub/hub.test.ts +0 -205
  526. package/src/hub/hub.ts +0 -256
  527. package/src/hub/schema.ts +0 -81
  528. package/src/index.ts +0 -75
  529. package/src/mcp/connections.ts +0 -614
  530. package/src/mcp/manager.ts +0 -130
  531. package/src/mcp/oauth.ts +0 -195
  532. package/src/mcp/scopeMigration.test.ts +0 -79
  533. package/src/mcp/scopeMigration.ts +0 -204
  534. package/src/mcp/service.test.ts +0 -376
  535. package/src/mcp/service.ts +0 -549
  536. package/src/mcp/types.ts +0 -108
  537. package/src/memory/api.test.ts +0 -82
  538. package/src/memory/api.ts +0 -303
  539. package/src/memory/blocks.test.ts +0 -78
  540. package/src/memory/blocks.ts +0 -408
  541. package/src/memory/caller.ts +0 -69
  542. package/src/memory/curation.test.ts +0 -163
  543. package/src/memory/curation.ts +0 -64
  544. package/src/memory/dispatch.ts +0 -59
  545. package/src/memory/facts.test.ts +0 -41
  546. package/src/memory/facts.ts +0 -248
  547. package/src/memory/graph.ts +0 -103
  548. package/src/memory/health.test.ts +0 -130
  549. package/src/memory/health.ts +0 -74
  550. package/src/memory/maintainer.ts +0 -272
  551. package/src/memory/maintenance.test.ts +0 -176
  552. package/src/memory/maintenance.ts +0 -518
  553. package/src/memory/migrate.ts +0 -79
  554. package/src/memory/operations.ts +0 -150
  555. package/src/memory/proposals.test.ts +0 -168
  556. package/src/memory/proposals.ts +0 -584
  557. package/src/memory/recall.test.ts +0 -115
  558. package/src/memory/recall.ts +0 -278
  559. package/src/memory/references.ts +0 -77
  560. package/src/memory/runContext.ts +0 -93
  561. package/src/memory/secrets.ts +0 -6
  562. package/src/memory/store.test.ts +0 -66
  563. package/src/memory/store.ts +0 -385
  564. package/src/memory/wiki.test.ts +0 -47
  565. package/src/memory/wiki.ts +0 -422
  566. package/src/memory/wikiIndex.test.ts +0 -88
  567. package/src/memory/wikiIndex.ts +0 -116
  568. package/src/plugins/api.ts +0 -181
  569. package/src/plugins/attachments.test.ts +0 -84
  570. package/src/plugins/attachments.ts +0 -141
  571. package/src/plugins/builtin.ts +0 -82
  572. package/src/plugins/catalogue.test.ts +0 -254
  573. package/src/plugins/catalogue.ts +0 -251
  574. package/src/plugins/cli.ts +0 -184
  575. package/src/plugins/discover.ts +0 -151
  576. package/src/plugins/firstParty.test.ts +0 -143
  577. package/src/plugins/firstParty.ts +0 -129
  578. package/src/plugins/hooks.test.ts +0 -250
  579. package/src/plugins/hooks.ts +0 -239
  580. package/src/plugins/identity.ts +0 -37
  581. package/src/plugins/install.test.ts +0 -107
  582. package/src/plugins/install.ts +0 -106
  583. package/src/plugins/inventory.ts +0 -123
  584. package/src/plugins/manifest.ts +0 -76
  585. package/src/plugins/plugins.test.ts +0 -205
  586. package/src/plugins/plugins.ts +0 -189
  587. package/src/plugins/proposal.ts +0 -121
  588. package/src/plugins/runtime.test.ts +0 -40
  589. package/src/plugins/runtime.ts +0 -82
  590. package/src/plugins/trust.ts +0 -142
  591. package/src/plugins/types.ts +0 -149
  592. package/src/prompt/compile.test.ts +0 -261
  593. package/src/prompt/compile.ts +0 -732
  594. package/src/server/methods.ts +0 -349
  595. package/src/server/remote.ts +0 -77
  596. package/src/server/rpc.ts +0 -116
  597. package/src/server/serve.ts +0 -28
  598. package/src/server/server.test.ts +0 -174
  599. package/src/sessions/approvals.ts +0 -118
  600. package/src/sessions/contracts.ts +0 -336
  601. package/src/sessions/conversation.test.ts +0 -177
  602. package/src/sessions/extensionSession.test.ts +0 -74
  603. package/src/sessions/extensionUi.test.ts +0 -65
  604. package/src/sessions/extensionUi.ts +0 -102
  605. package/src/sessions/history.ts +0 -51
  606. package/src/sessions/lineageJoin.ts +0 -75
  607. package/src/sessions/policy.ts +0 -36
  608. package/src/sessions/pricing.ts +0 -60
  609. package/src/sessions/questionBatches.test.ts +0 -110
  610. package/src/sessions/reasoningSummaries.ts +0 -59
  611. package/src/sessions/registry.ts +0 -381
  612. package/src/sessions/runIndex.ts +0 -99
  613. package/src/sessions/runStore.test.ts +0 -152
  614. package/src/sessions/runStore.ts +0 -332
  615. package/src/sessions/runner.ts +0 -604
  616. package/src/sessions/runtimeResources.test.ts +0 -61
  617. package/src/sessions/sessions.test.ts +0 -549
  618. package/src/sessions/sessions.ts +0 -339
  619. package/src/skills/registry.test.ts +0 -104
  620. package/src/skills/registry.ts +0 -413
  621. package/src/terminal/accountsMenu.test.ts +0 -225
  622. package/src/terminal/accountsMenu.ts +0 -382
  623. package/src/terminal/cli.ts +0 -149
  624. package/src/terminal/client.ts +0 -74
  625. package/src/terminal/control.test.ts +0 -23
  626. package/src/terminal/control.ts +0 -23
  627. package/src/terminal/controller.ts +0 -291
  628. package/src/terminal/hubMenu.ts +0 -24
  629. package/src/terminal/launch.test.ts +0 -25
  630. package/src/terminal/launch.ts +0 -48
  631. package/src/terminal/piExtension.lifecycle.test.ts +0 -55
  632. package/src/terminal/piExtension.ts +0 -259
  633. package/src/terminal/pluginControl.test.ts +0 -146
  634. package/src/terminal/pluginMenu.ts +0 -112
  635. package/src/terminal/socket.test.ts +0 -71
  636. package/src/terminal/socket.ts +0 -232
  637. package/src/terminal/standalone.test.ts +0 -167
  638. package/src/terminal/standalone.ts +0 -153
  639. package/src/terminal/terminal.test.ts +0 -399
  640. package/src/terminal/terminals.ts +0 -553
  641. package/src/terminal/tui/application.test.ts +0 -114
  642. package/src/terminal/tui/application.ts +0 -491
  643. package/src/terminal/tui/commands.test.ts +0 -218
  644. package/src/terminal/tui/commands.ts +0 -361
  645. package/src/terminal/tui/dialogs.ts +0 -301
  646. package/src/terminal/tui/editor.test.ts +0 -84
  647. package/src/terminal/tui/editor.ts +0 -79
  648. package/src/terminal/tui/keybindings.test.ts +0 -22
  649. package/src/terminal/tui/keybindings.ts +0 -50
  650. package/src/terminal/tui/piAdapter.ts +0 -62
  651. package/src/terminal/tui/presentation.test.ts +0 -79
  652. package/src/terminal/tui/questions.test.ts +0 -41
  653. package/src/terminal/tui/questions.ts +0 -47
  654. package/src/terminal/tui/runtime.socket.test.ts +0 -104
  655. package/src/terminal/tui/runtime.test.ts +0 -215
  656. package/src/terminal/tui/runtime.ts +0 -388
  657. package/src/terminal/tui/sessionUi.ts +0 -24
  658. package/src/terminal/tui/settings.test.ts +0 -150
  659. package/src/terminal/tui/settings.ts +0 -188
  660. package/src/terminal/tui/status.test.ts +0 -70
  661. package/src/terminal/tui/status.ts +0 -109
  662. package/src/terminal/tui/theme.ts +0 -174
  663. package/src/terminal/tui/tools.test.ts +0 -145
  664. package/src/terminal/tui/tools.ts +0 -142
  665. package/src/terminal/tui/transcript.test.ts +0 -25
  666. package/src/terminal/tui/transcript.ts +0 -124
  667. package/src/terminal/tui/ui.test.ts +0 -313
  668. package/src/terminal/tui/ui.ts +0 -166
  669. package/src/terminal/tui/welcome.ts +0 -102
  670. package/src/tools/askUser.test.ts +0 -69
  671. package/src/tools/askUser.ts +0 -59
  672. package/src/tools/definitions.ts +0 -187
  673. package/src/tools/maintenance.ts +0 -24
  674. package/src/tools/memoryTools.ts +0 -22
  675. package/src/tools/pluginPropose.ts +0 -41
  676. package/src/tools/registryWrite.test.ts +0 -101
  677. package/src/tools/registryWrite.ts +0 -182
  678. package/src/tools/spawnSubagent.ts +0 -92
  679. package/src/tools/types.ts +0 -39
  680. package/src/web/safeRequest.ts +0 -171
  681. package/src/web/text.ts +0 -53
  682. package/src/web/tools.ts +0 -134
  683. package/src/web/web.test.ts +0 -141
@@ -1,130 +0,0 @@
1
- // Live MCP client sessions: one per (session, connection), opened on first use and closed when
2
- // the session's run ends or the connection changes.
3
- import { Client, SSEClientTransport, StreamableHTTPClientTransport, UnauthorizedError } from '@modelcontextprotocol/client'
4
- import { StdioClientTransport } from '@modelcontextprotocol/client/stdio'
5
- import { randomUUID } from 'node:crypto'
6
-
7
- import { HarnessError } from '../core/errors'
8
- import { StoredOAuthProvider, type OAuthHost } from './oauth'
9
- import type { HttpConfig, LaunchConfig, StdioConfig } from './types'
10
-
11
- interface Live {
12
- client: Client
13
- transport: { close(): Promise<void> }
14
- config: string
15
- }
16
-
17
- export const signInRequired = (config: Pick<LaunchConfig, 'name'>): HarnessError => new HarnessError('mcp_unauthorized', `${config.name} needs you to sign in again in Connections.`)
18
- export const isSignInRequired = (error: unknown): boolean => error instanceof UnauthorizedError || (error instanceof HarnessError && error.code === 'mcp_unauthorized')
19
-
20
- export class McpManager {
21
- private readonly sessions = new Map<string, Promise<Live>>()
22
-
23
- constructor(private readonly oauth: OAuthHost) {}
24
-
25
- async close(connectionId?: string, sessionId?: string): Promise<void> {
26
- const closing = [...this.sessions].filter(([key]) => (!connectionId || key.endsWith(`:${connectionId}`)) && (!sessionId || key.startsWith(`${sessionId}:`)))
27
- for (const [key] of closing) this.sessions.delete(key)
28
- await Promise.allSettled(
29
- closing.map(async ([, session]) => {
30
- const live = await session
31
- await live.client.close()
32
- await live.transport.close()
33
- })
34
- )
35
- }
36
-
37
- /** The client for a connection in one scope, reconnecting when the connection's configuration changed. */
38
- async client(scope: string, config: LaunchConfig): Promise<Client> {
39
- const key = `${scope}:${config.id}`
40
- const fingerprint = JSON.stringify(config)
41
- const existing = this.sessions.get(key)
42
- if (existing) {
43
- const live = await existing.catch(() => null)
44
- if (live?.config === fingerprint) return live.client
45
- await live?.client.close().catch(() => undefined)
46
- this.sessions.delete(key)
47
- }
48
- const pending: Promise<Live> = (async () => {
49
- const { client, transport } = config.transport === 'http' ? await this.connectHttp(config) : await this.connectStdio(config)
50
- client.onclose = () => {
51
- if (this.sessions.get(key) === pending) this.sessions.delete(key)
52
- }
53
- return { client, transport, config: fingerprint }
54
- })()
55
- this.sessions.set(key, pending)
56
- try {
57
- return (await pending).client
58
- } catch (error) {
59
- this.sessions.delete(key)
60
- throw error
61
- }
62
- }
63
-
64
- private async connectStdio(config: StdioConfig) {
65
- const client = new Client({ name: 'xagent', version: '0.1.0' })
66
- const transport = new StdioClientTransport({ command: config.command, args: config.args, cwd: config.cwd ?? undefined, env: config.env, stderr: 'pipe' })
67
- try {
68
- await client.connect(transport, { timeout: 30000 })
69
- } catch {
70
- await transport.close()
71
- throw new HarnessError('mcp_unavailable', `${config.name} could not start or initialize. Check its executable and arguments.`)
72
- }
73
- // Servers' diagnostics may contain secrets; do not persist or forward stderr.
74
- transport.stderr?.on('data', () => undefined)
75
- return { client, transport }
76
- }
77
-
78
- private async connectHttp(config: HttpConfig) {
79
- const authProvider = config.auth === 'oauth' ? new StoredOAuthProvider(this.oauth, config, false) : undefined
80
- // Without saved tokens the only way forward is the browser, which a session may not open.
81
- if (authProvider && !(await authProvider.tokens())) throw signInRequired(config)
82
- const options = { authProvider, requestInit: { headers: config.headers } }
83
- // Servers that predate Streamable HTTP only answer on SSE.
84
- for (const Transport of [StreamableHTTPClientTransport, SSEClientTransport]) {
85
- const client = new Client({ name: 'xagent', version: '0.1.0' })
86
- const transport = new Transport(new URL(config.url), options)
87
- try {
88
- await client.connect(transport, { timeout: 30000 })
89
- return { client, transport }
90
- } catch (error) {
91
- await transport.close().catch(() => undefined)
92
- if (isSignInRequired(error)) throw signInRequired(config)
93
- }
94
- }
95
- throw new HarnessError('mcp_unavailable', `${config.name} could not be reached. Check its address and credentials.`)
96
- }
97
-
98
- /** Runs `work` on a connection opened only for it. */
99
- private async once<T>(prefix: string, config: LaunchConfig, work: (client: Client) => Promise<T>): Promise<T> {
100
- const scope = `${prefix}-${randomUUID()}`
101
- try {
102
- return await work(await this.client(scope, config))
103
- } finally {
104
- const key = `${scope}:${config.id}`
105
- const session = this.sessions.get(key)
106
- this.sessions.delete(key)
107
- if (session) await (await session).client.close().catch(() => undefined)
108
- }
109
- }
110
-
111
- /** What a connection offers, read on a connection of its own. */
112
- probe(config: LaunchConfig): Promise<{ serverName: string; tools: unknown[]; vaultId: string | null }> {
113
- return this.once('probe', config, async (client) => {
114
- const tools = (await client.listTools()).tools
115
- let vaultId: string | null = null
116
- if (tools.some((tool) => tool.name === 'x_workspace_status')) {
117
- const status = await client.callTool({ name: 'x_workspace_status', arguments: {} })
118
- const envelope = status.structuredContent as { ok?: boolean; vaultId?: string; error?: { message?: string } } | undefined
119
- if (!envelope?.ok) throw new HarnessError('mcp_unavailable', envelope?.error?.message ?? 'Workspace vault is not bound.')
120
- vaultId = envelope.vaultId ?? null
121
- }
122
- return { serverName: client.getServerVersion()?.name ?? config.name, tools, vaultId }
123
- })
124
- }
125
-
126
- /** One read on a connection of its own, outside any session. */
127
- readSource(config: LaunchConfig, tool: string, args: Record<string, unknown>): Promise<unknown> {
128
- return this.once('source', config, (client) => client.callTool({ name: tool, arguments: args }, { timeout: 60000 }))
129
- }
130
- }
package/src/mcp/oauth.ts DELETED
@@ -1,195 +0,0 @@
1
- // OAuth sign-in for a remote connection. State is kept with the connection's other secrets.
2
- // Only an interactive provider (a sign-in a person started) may open a browser; a session that
3
- // needs a new sign-in fails instead.
4
- import { auth, UnauthorizedError } from '@modelcontextprotocol/client'
5
- import type { OAuthClientInformationContext, OAuthClientMetadata, OAuthClientProvider, OAuthDiscoveryState, StoredOAuthClientInformation, StoredOAuthTokens } from '@modelcontextprotocol/client'
6
- import { randomUUID } from 'node:crypto'
7
- import { createServer } from 'node:http'
8
-
9
- import { HarnessError } from '../core/errors'
10
- import { MCP_OAUTH_PORT, MCP_OAUTH_REDIRECT_URL, type HttpConfig } from './types'
11
-
12
- interface Saved {
13
- client?: StoredOAuthClientInformation
14
- tokens?: StoredOAuthTokens
15
- discovery?: OAuthDiscoveryState
16
- staticClient?: StoredOAuthClientInformation
17
- staticIssuer?: string
18
- }
19
-
20
- /** Where a connection's OAuth state is read and written, and how a sign-in page is shown. */
21
- export interface OAuthHost {
22
- load(connectionId: string): Promise<Record<string, unknown>>
23
- save(connectionId: string, state: Record<string, unknown>): Promise<void>
24
- openUrl(url: string): Promise<void> | void
25
- /** The name shown on the provider's consent page. */
26
- clientName: string
27
- }
28
-
29
- export class StoredOAuthProvider implements OAuthClientProvider {
30
- readonly expectedState = randomUUID()
31
- private verifier: string | null = null
32
-
33
- constructor(
34
- private readonly host: OAuthHost,
35
- private readonly config: Pick<HttpConfig, 'id' | 'scopes'>,
36
- private readonly interactive: boolean
37
- ) {}
38
-
39
- get redirectUrl(): string {
40
- return MCP_OAUTH_REDIRECT_URL
41
- }
42
-
43
- get clientMetadata(): OAuthClientMetadata {
44
- return {
45
- client_name: this.host.clientName,
46
- redirect_uris: [MCP_OAUTH_REDIRECT_URL],
47
- grant_types: ['authorization_code', 'refresh_token'],
48
- response_types: ['code'],
49
- token_endpoint_auth_method: 'none',
50
- ...(this.config.scopes.length ? { scope: this.config.scopes.join(' ') } : {})
51
- }
52
- }
53
-
54
- state(): string {
55
- return this.expectedState
56
- }
57
-
58
- private load(): Promise<Saved> {
59
- return this.host.load(this.config.id) as Promise<Saved>
60
- }
61
-
62
- private async change(edit: (saved: Saved) => Saved): Promise<void> {
63
- await this.host.save(this.config.id, edit(await this.load()) as Record<string, unknown>)
64
- }
65
-
66
- async clientInformation(context?: OAuthClientInformationContext): Promise<StoredOAuthClientInformation | undefined> {
67
- const saved = await this.load()
68
- if (!saved.staticClient) return saved.client
69
- // A client the user registered themselves (providers without dynamic registration) wins. It is
70
- // tied to the authorization server seen at sign-in, so a session never offers it to another one.
71
- if (this.interactive && context && saved.staticIssuer !== context.issuer) await this.change((current) => ({ ...current, staticIssuer: context.issuer }))
72
- const issuer = this.interactive ? context?.issuer : saved.staticIssuer
73
- return issuer ? { ...saved.staticClient, issuer } : saved.staticClient
74
- }
75
-
76
- async saveClientInformation(client: StoredOAuthClientInformation): Promise<void> {
77
- await this.change((saved) => ({ ...saved, client }))
78
- }
79
-
80
- async tokens(): Promise<StoredOAuthTokens | undefined> {
81
- return (await this.load()).tokens
82
- }
83
-
84
- async saveTokens(tokens: StoredOAuthTokens): Promise<void> {
85
- await this.change((saved) => ({ ...saved, tokens }))
86
- }
87
-
88
- async discoveryState(): Promise<OAuthDiscoveryState | undefined> {
89
- return (await this.load()).discovery
90
- }
91
-
92
- async saveDiscoveryState(discovery: OAuthDiscoveryState): Promise<void> {
93
- await this.change((saved) => ({ ...saved, discovery }))
94
- }
95
-
96
- saveCodeVerifier(verifier: string): void {
97
- this.verifier = verifier
98
- }
99
-
100
- codeVerifier(): string {
101
- if (!this.verifier) throw new UnauthorizedError('Sign-in was not started from this app.')
102
- return this.verifier
103
- }
104
-
105
- async invalidateCredentials(scope: 'all' | 'client' | 'tokens' | 'verifier' | 'discovery'): Promise<void> {
106
- if (scope === 'verifier' || scope === 'all') this.verifier = null
107
- if (scope === 'verifier') return
108
- await this.change((saved) => (scope === 'all' ? {} : { ...saved, [scope]: undefined }))
109
- }
110
-
111
- async redirectToAuthorization(url: URL): Promise<void> {
112
- if (!this.interactive) throw new UnauthorizedError('Sign in again in Connections.')
113
- // Google only returns a refresh token when offline access is requested with a consent prompt.
114
- if (url.hostname === 'accounts.google.com') {
115
- url.searchParams.set('access_type', 'offline')
116
- url.searchParams.set('prompt', 'consent')
117
- }
118
- await this.host.openUrl(url.toString())
119
- }
120
- }
121
-
122
- const page = (title: string, message: string): string => `<!doctype html><meta charset="utf-8"><title>${title}</title><body style="font:16px system-ui;margin:20vh auto;max-width:28rem;text-align:center"><p>${message}</p></body>`
123
-
124
- // One sign-in owns the loopback port. Starting another ends the one still waiting, so a closed
125
- // browser tab never blocks a retry.
126
- let cancelActive: (() => Promise<void>) | null = null
127
-
128
- /** Waits on the fixed loopback address for the provider to send the browser back. */
129
- function listenForCallback(expectedState: string, clientName: string): Promise<{ result: Promise<{ code: string; iss?: string }>; close: () => Promise<void> }> {
130
- return new Promise((ready, failed) => {
131
- let settle: { resolve: (value: { code: string; iss?: string }) => void; reject: (error: Error) => void }
132
- const result = new Promise<{ code: string; iss?: string }>((resolve, reject) => {
133
- settle = { resolve, reject }
134
- })
135
- result.catch(() => undefined)
136
- const server = createServer((request, response) => {
137
- const url = new URL(request.url ?? '/', MCP_OAUTH_REDIRECT_URL)
138
- const reply = (status: number, message: string): void => {
139
- response.writeHead(status, { 'content-type': 'text/html; charset=utf-8' })
140
- response.end(page(clientName, message))
141
- }
142
- if (url.pathname !== new URL(MCP_OAUTH_REDIRECT_URL).pathname) return reply(404, 'Not found.')
143
- // Anything on this machine can reach the port; only the request carrying this sign-in's state counts.
144
- if (url.searchParams.get('state') !== expectedState) return reply(400, `This sign-in link is not the one ${clientName} is waiting for.`)
145
- const code = url.searchParams.get('code')
146
- if (!code) {
147
- reply(400, 'Sign-in was not completed. You can close this window.')
148
- return settle.reject(new HarnessError('mcp_unauthorized', url.searchParams.get('error') === 'access_denied' ? 'Sign-in was cancelled.' : 'The provider did not complete the sign-in.'))
149
- }
150
- reply(200, `Signed in. You can close this window and return to ${clientName}.`)
151
- settle.resolve({ code, iss: url.searchParams.get('iss') ?? undefined })
152
- })
153
- const timer = setTimeout(() => settle.reject(new HarnessError('mcp_unauthorized', 'Sign-in timed out. Try again.')), 300000)
154
- server.once('error', (error: NodeJS.ErrnoException) => {
155
- clearTimeout(timer)
156
- failed(new HarnessError('invalid_request', error.code === 'EADDRINUSE' ? `Port ${MCP_OAUTH_PORT} is in use. Finish or cancel the other sign-in, or quit the program using that port, then try again.` : 'The sign-in listener could not start.'))
157
- })
158
- const closed = new Promise<void>((resolve) => server.once('close', resolve))
159
- const close = (): Promise<void> => {
160
- clearTimeout(timer)
161
- if (cancelActive === cancel) cancelActive = null
162
- server.close()
163
- server.closeAllConnections()
164
- return closed
165
- }
166
- const cancel = (): Promise<void> => {
167
- settle.reject(new HarnessError('mcp_unauthorized', 'Sign-in was cancelled.'))
168
- return close()
169
- }
170
- server.listen(MCP_OAUTH_PORT, '127.0.0.1', () => {
171
- cancelActive = cancel
172
- ready({ result, close })
173
- })
174
- })
175
- }
176
-
177
- /** Opens the provider's sign-in page and waits for the browser to return to the loopback address. */
178
- export async function signIn(host: OAuthHost, config: HttpConfig): Promise<void> {
179
- const provider = new StoredOAuthProvider(host, config, true)
180
- const scope = config.scopes.join(' ') || undefined
181
- await cancelActive?.()
182
- const callback = await listenForCallback(provider.expectedState, host.clientName)
183
- try {
184
- if ((await auth(provider, { serverUrl: config.url, scope, forceReauthorization: true })) === 'REDIRECT') {
185
- const { code, iss } = await callback.result
186
- if ((await auth(provider, { serverUrl: config.url, scope, authorizationCode: code, iss })) !== 'AUTHORIZED') throw new HarnessError('mcp_unauthorized', 'The provider did not complete the sign-in.')
187
- }
188
- } catch (error) {
189
- if (error instanceof HarnessError) throw error
190
- // Provider responses can quote credentials; report only that it failed.
191
- throw new HarnessError('mcp_unauthorized', `${config.name} sign-in failed. Check the connection's address and OAuth client, then try again.`)
192
- } finally {
193
- await callback.close()
194
- }
195
- }
@@ -1,79 +0,0 @@
1
- import { createHash } from 'node:crypto'
2
- import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
3
- import { tmpdir } from 'node:os'
4
- import { join } from 'node:path'
5
- import { afterEach, describe, expect, it } from 'vitest'
6
-
7
- import * as attachments from '../plugins/attachments'
8
- import { migrateConnectionScopes, rollbackConnectionScopeMigration } from './scopeMigration'
9
-
10
- const roots: string[] = []
11
- function fixture(): { root: string; stateDir: string; sessionPath: string } {
12
- const base = mkdtempSync(join(tmpdir(), 'xingular-scope-migration-'))
13
- roots.push(base)
14
- const root = join(base, 'data')
15
- const stateDir = join(base, 'state')
16
- mkdirSync(join(root, 'config'), { recursive: true })
17
- mkdirSync(join(root, 'sessions', 'agent'), { recursive: true })
18
- mkdirSync(stateDir, { recursive: true })
19
- const sessionPath = join(root, 'sessions', 'agent', 'thread.json')
20
- writeFileSync(sessionPath, Buffer.from('{ "session": "keep these bytes\\nexactly" }\n'))
21
- mkdirSync(join(root, 'agents', 'agent-a'), { recursive: true })
22
- writeFileSync(join(root, 'agents', 'agent-a', 'agent.md'), Buffer.from('tools: [read, bash, mcp_call_tool]\n'))
23
- return { root, stateDir, sessionPath }
24
- }
25
-
26
- function digest(path: string): string {
27
- return createHash('sha256').update(readFileSync(path)).digest('hex')
28
- }
29
-
30
- afterEach(() => {
31
- for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true })
32
- })
33
-
34
- describe('connector scope migration', () => {
35
- it('moves scopes, verifies assignments, keeps session bytes, and restores exact backups', () => {
36
- const { root, stateDir, sessionPath } = fixture()
37
- const sessionHash = digest(sessionPath)
38
- const agentToolsPath = join(root, 'agents', 'agent-a', 'agent.md')
39
- const agentToolsHash = digest(agentToolsPath)
40
- const connectionBytes = Buffer.from(`[{"id":"linear","name":"Linear","kind":"remote","enabled":true,"global":true,"agentIds":["agent-a"],"excludedAgentIds":["agent-b"],"projectIds":["project-a"],"excludedProjectIds":["project-b"],"url":"https://mcp.example"}]\n`)
41
- const oldAttachmentsBytes = Buffer.from('{"version":1,"global":{"on":["existing"],"off":[]},"agents":{},"projects":{},"settings":{"keep":{"x":1}}}\n')
42
- const connectionsPath = join(stateDir, 'connections.json')
43
- const attachmentsPath = join(root, 'config', 'plugin-attachments.json')
44
- writeFileSync(connectionsPath, connectionBytes)
45
- writeFileSync(attachmentsPath, oldAttachmentsBytes)
46
-
47
- const result = migrateConnectionScopes(root, stateDir)
48
- expect(result).toMatchObject({ status: 'complete', connectionCount: 1, scopeAssignmentCount: 5, hadAttachments: true })
49
- const saved = JSON.parse(readFileSync(connectionsPath, 'utf8'))[0]
50
- expect(saved).toMatchObject({ id: 'linear', url: 'https://mcp.example' })
51
- for (const key of ['global', 'agentIds', 'excludedAgentIds', 'projectIds', 'excludedProjectIds']) expect(saved).not.toHaveProperty(key)
52
-
53
- const migrated = attachments.load(root)
54
- expect(migrated.global.on).toContain('connected-apps/connection/linear')
55
- expect(migrated.agents['agent-a'].on).toContain('connected-apps/connection/linear')
56
- expect(migrated.agents['agent-b'].off).toContain('connected-apps/connection/linear')
57
- expect(migrated.projects['project-a'].on).toContain('connected-apps/connection/linear')
58
- expect(migrated.projects['project-b'].off).toContain('connected-apps/connection/linear')
59
- expect(digest(sessionPath)).toBe(sessionHash)
60
- expect(digest(agentToolsPath)).toBe(agentToolsHash)
61
- expect(readFileSync(join(stateDir, 'connections.pre-capability-scope.json'))).toEqual(connectionBytes)
62
- expect(readFileSync(join(root, 'config', 'plugin-attachments.pre-capability-scope.json'))).toEqual(oldAttachmentsBytes)
63
-
64
- rollbackConnectionScopeMigration(root, stateDir)
65
- expect(readFileSync(connectionsPath)).toEqual(connectionBytes)
66
- expect(readFileSync(attachmentsPath)).toEqual(oldAttachmentsBytes)
67
- expect(digest(sessionPath)).toBe(sessionHash)
68
- expect(digest(agentToolsPath)).toBe(agentToolsHash)
69
- })
70
-
71
- it('is idempotent after completion without rewriting session data', () => {
72
- const { root, stateDir, sessionPath } = fixture()
73
- writeFileSync(join(stateDir, 'connections.json'), '[{"id":"linear","global":false}]\n')
74
- const before = digest(sessionPath)
75
- const first = migrateConnectionScopes(root, stateDir)
76
- expect(migrateConnectionScopes(root, stateDir)).toEqual(first)
77
- expect(digest(sessionPath)).toBe(before)
78
- })
79
- })
@@ -1,204 +0,0 @@
1
- // Moves who can reach each connected app out of the device-local connection records and into
2
- // the data root's shared capability attachment store. The originals remain as owner-only backups.
3
- import { chmodSync, existsSync, readFileSync, unlinkSync } from 'node:fs'
4
- import { join } from 'node:path'
5
-
6
- import { atomicWrite, readJson, writeJson } from '../core/fs'
7
- import * as attachments from '../plugins/attachments'
8
- import { connectionCapabilityId, connectionSchema, type Connection } from './types'
9
-
10
- interface MigrationRecord {
11
- version: 1
12
- status: 'attachments-written' | 'complete'
13
- connectionCount: number
14
- scopeAssignmentCount: number
15
- connectionsBackup: string | null
16
- attachmentsBackup: string | null
17
- hadAttachments: boolean
18
- }
19
-
20
- const recordPath = (root: string): string => join(root, 'config/capability-scope-migration.json')
21
- const attachmentsPath = (root: string): string => join(root, 'config/plugin-attachments.json')
22
- const connectionPath = (stateDir: string): string => join(stateDir, 'connections.json')
23
- const connectionsBackupPath = (stateDir: string): string => join(stateDir, 'connections.pre-capability-scope.json')
24
- const attachmentsBackupPath = (root: string): string => join(root, 'config/plugin-attachments.pre-capability-scope.json')
25
-
26
- /** Scope writes are safe only after the legacy records have been copied and stripped. */
27
- export function connectionScopeMigrationComplete(root: string): boolean {
28
- try {
29
- const marker = readJson<MigrationRecord>(recordPath(root))
30
- return marker?.version === 1 && marker.status === 'complete'
31
- } catch {
32
- return false
33
- }
34
- }
35
-
36
- interface ScopeRows {
37
- global: boolean
38
- agents: Map<string, boolean>
39
- projects: Map<string, boolean>
40
- }
41
-
42
- function unique(values: readonly string[]): string[] {
43
- return [...new Set(values)]
44
- }
45
-
46
- function scopeRows(connection: Connection): ScopeRows {
47
- const agents = new Map<string, boolean>()
48
- const projects = new Map<string, boolean>()
49
- for (const id of unique([...connection.agentIds, ...connection.excludedAgentIds])) {
50
- agents.set(id, !connection.excludedAgentIds.includes(id))
51
- }
52
- for (const id of unique([...connection.projectIds, ...connection.excludedProjectIds])) {
53
- projects.set(id, !connection.excludedProjectIds.includes(id))
54
- }
55
- return { global: connection.global, agents, projects }
56
- }
57
-
58
- function assignmentCount(rows: ScopeRows): number {
59
- return 1 + rows.agents.size + rows.projects.size
60
- }
61
-
62
- function removeId(choices: attachments.Attachments, id: string): void {
63
- attachments.forgetId(choices, id)
64
- }
65
-
66
- function put(map: { on: string[]; off: string[] }, id: string, value: boolean): void {
67
- map.on = map.on.filter((item) => item !== id)
68
- map.off = map.off.filter((item) => item !== id)
69
- map[value ? 'on' : 'off'].push(id)
70
- }
71
-
72
- function writeRows(choices: attachments.Attachments, id: string, rows: ScopeRows): void {
73
- removeId(choices, id)
74
- put(choices.global, id, rows.global)
75
- for (const [scopeId, on] of rows.agents) {
76
- const target = (choices.agents[scopeId] ??= { on: [], off: [] })
77
- put(target, id, on)
78
- }
79
- for (const [scopeId, on] of rows.projects) {
80
- const target = (choices.projects[scopeId] ??= { on: [], off: [] })
81
- put(target, id, on)
82
- }
83
- }
84
-
85
- function readValue(item: { on: string[]; off: string[] } | undefined, id: string): boolean | null {
86
- if (item?.off.includes(id)) return false
87
- if (item?.on.includes(id)) return true
88
- return null
89
- }
90
-
91
- function verifyRows(choices: attachments.Attachments, id: string, expected: ScopeRows): boolean {
92
- if (readValue(choices.global, id) !== expected.global) return false
93
- for (const [scopeId, on] of expected.agents) {
94
- if (readValue(choices.agents[scopeId], id) !== on) return false
95
- }
96
- for (const [scopeId, on] of expected.projects) {
97
- if (readValue(choices.projects[scopeId], id) !== on) return false
98
- }
99
- return true
100
- }
101
-
102
- function backup(source: string, target: string): boolean {
103
- if (!existsSync(source)) return false
104
- if (!existsSync(target)) atomicWrite(target, readFileSync(source))
105
- chmodSync(target, 0o600)
106
- return true
107
- }
108
-
109
- function setPrivate(path: string): void {
110
- try { chmodSync(path, 0o600) } catch { /* The atomic write remains usable if chmod is unavailable. */ }
111
- }
112
-
113
- function stripScopes(stateDir: string): void {
114
- const path = connectionPath(stateDir)
115
- if (!existsSync(path)) return
116
- const stored = readJson<unknown>(path)
117
- if (!Array.isArray(stored)) throw new Error('Stored connector data must be a list before its scopes can be migrated.')
118
- const records = stored.map((item) => {
119
- if (!item || typeof item !== 'object' || Array.isArray(item)) throw new Error('A stored connector record is invalid; no scope data was removed.')
120
- const { global: _global, agentIds: _agentIds, projectIds: _projectIds, excludedAgentIds: _excludedAgentIds, excludedProjectIds: _excludedProjectIds, ...connection } = item as Record<string, unknown>
121
- return connection
122
- })
123
- writeJson(path, records)
124
- setPrivate(path)
125
- }
126
-
127
- /**
128
- * Runs once when a data root opens. Both source files are backed up before either is changed;
129
- * a stage marker makes an interrupted migration safe to finish on the next open.
130
- */
131
- export function migrateConnectionScopes(root: string, stateDir: string): MigrationRecord {
132
- const marker = readJson<MigrationRecord>(recordPath(root))
133
- if (marker?.version === 1 && marker.status === 'complete') return marker
134
-
135
- if (marker?.version === 1 && marker.status === 'attachments-written') {
136
- stripScopes(stateDir)
137
- const complete = { ...marker, status: 'complete' as const }
138
- writeJson(recordPath(root), complete)
139
- return complete
140
- }
141
-
142
- const connectionsFile = connectionPath(stateDir)
143
- const hadConnections = existsSync(connectionsFile)
144
- const raw = hadConnections ? readJson<unknown>(connectionsFile) : []
145
- if (!Array.isArray(raw)) throw new Error('Stored connector data must be a list before its scopes can be migrated.')
146
- const parsed = raw.map((item) => {
147
- const result = connectionSchema.safeParse(item)
148
- if (!result.success) throw new Error('A stored connector record is invalid; no scope data was removed.')
149
- return result.data
150
- })
151
- const connectionBackup = hadConnections ? connectionsBackupPath(stateDir) : null
152
- const hadAttachments = existsSync(attachmentsPath(root))
153
- const attachmentBackup = hadAttachments ? attachmentsBackupPath(root) : null
154
- const migration: MigrationRecord = {
155
- version: 1,
156
- status: 'attachments-written',
157
- connectionCount: parsed.length,
158
- scopeAssignmentCount: parsed.reduce((count, connection) => count + assignmentCount(scopeRows(connection)), 0),
159
- connectionsBackup: connectionBackup,
160
- attachmentsBackup: attachmentBackup,
161
- hadAttachments
162
- }
163
- backup(connectionsFile, connectionBackup ?? '')
164
- backup(attachmentsPath(root), attachmentBackup ?? '')
165
-
166
- try {
167
- const choices = attachments.load(root)
168
- const expected = new Map<string, ScopeRows>()
169
- for (const connection of parsed) {
170
- const id = connectionCapabilityId(connection.id)
171
- const rows = scopeRows(connection)
172
- expected.set(id, rows)
173
- writeRows(choices, id, rows)
174
- }
175
- const migratedAssignments = [...expected.values()].reduce((count, rows) => count + assignmentCount(rows), 0)
176
- if (migratedAssignments !== migration.scopeAssignmentCount || [...expected].some(([id, rows]) => !verifyRows(choices, id, rows))) {
177
- throw new Error('Connector scope count check failed; the original files were restored.')
178
- }
179
- attachments.save(root, choices)
180
- const saved = attachments.load(root)
181
- if ([...expected].some(([id, rows]) => !verifyRows(saved, id, rows))) throw new Error('Connector scope count check failed after saving; the original files were restored.')
182
- writeJson(recordPath(root), migration)
183
- stripScopes(stateDir)
184
- const complete = { ...migration, status: 'complete' as const }
185
- writeJson(recordPath(root), complete)
186
- return complete
187
- } catch (error) {
188
- if (attachmentBackup && existsSync(attachmentBackup)) atomicWrite(attachmentsPath(root), readFileSync(attachmentBackup))
189
- else if (!hadAttachments && existsSync(attachmentsPath(root))) unlinkSync(attachmentsPath(root))
190
- if (connectionBackup && existsSync(connectionBackup)) atomicWrite(connectionsFile, readFileSync(connectionBackup))
191
- try { unlinkSync(recordPath(root)) } catch { /* No marker is also a valid retry state. */ }
192
- throw error
193
- }
194
- }
195
-
196
- /** Restores the byte-identical pre-migration files from the backups if an operator rolls back. */
197
- export function rollbackConnectionScopeMigration(root: string, stateDir: string): void {
198
- const marker = readJson<MigrationRecord>(recordPath(root))
199
- if (!marker || marker.version !== 1) return
200
- if (marker.attachmentsBackup && existsSync(marker.attachmentsBackup)) atomicWrite(attachmentsPath(root), readFileSync(marker.attachmentsBackup))
201
- else if (!marker.hadAttachments && existsSync(attachmentsPath(root))) unlinkSync(attachmentsPath(root))
202
- if (marker.connectionsBackup && existsSync(marker.connectionsBackup)) atomicWrite(connectionPath(stateDir), readFileSync(marker.connectionsBackup))
203
- unlinkSync(recordPath(root))
204
- }