@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
@@ -0,0 +1,331 @@
1
+ // Reading, writing, validating, repairing and comparing a variant. The edit contract is
2
+ // omitted-is-inherited: an edit names only the files it changes.
3
+ //
4
+ // Ported from Harness² (Apache-2.0, Google LLC; `harness2/core/variant.py`).
5
+ import { spawnSync } from 'node:child_process';
6
+ import { mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
7
+ import { tmpdir } from 'node:os';
8
+ import { dirname, join, relative, sep } from 'node:path';
9
+ import { componentOf, patterns } from "./surface.js";
10
+ /** Scripts the harness itself ships; a reference to one needs no file in the variant. */
11
+ export const RESERVED_SCRIPTS = new Set();
12
+ /** Normalises a relative path and refuses one that escapes the variant. */
13
+ export function normalizeRel(rel) {
14
+ let r = String(rel).trim().replace(/\\/g, '/');
15
+ if (r.startsWith('/') || r.split('/').includes('..'))
16
+ throw new Error(`path escapes the harness: ${JSON.stringify(rel)}`);
17
+ while (r.startsWith('./'))
18
+ r = r.slice(2);
19
+ return r.replace(/^\/+|\/+$/g, '');
20
+ }
21
+ /** Whether a path is on the editable surface. Never throws. */
22
+ export function isAllowed(surface, rel) {
23
+ let clean;
24
+ try {
25
+ clean = normalizeRel(rel);
26
+ }
27
+ catch {
28
+ return false;
29
+ }
30
+ return surface.alwaysOn.includes(clean) || componentOf(surface, clean) !== null;
31
+ }
32
+ export function skillNames(surface, variant) {
33
+ return names(variant, patterns(surface).skill);
34
+ }
35
+ export function subagentNames(surface, variant) {
36
+ return names(variant, patterns(surface).subagent);
37
+ }
38
+ export function pluginNames(surface, variant) {
39
+ return names(variant, patterns(surface).plugin);
40
+ }
41
+ function names(variant, rx) {
42
+ const found = new Set();
43
+ for (const rel of Object.keys(variant)) {
44
+ const m = rx.exec(rel);
45
+ if (m?.[1])
46
+ found.add(m[1]);
47
+ }
48
+ return [...found].sort();
49
+ }
50
+ function stubPaths(surface) {
51
+ return new Set(surface.stubs.map(([rel]) => rel));
52
+ }
53
+ function walk(dir) {
54
+ const out = [];
55
+ let entries;
56
+ try {
57
+ entries = readdirSync(dir);
58
+ }
59
+ catch {
60
+ return out;
61
+ }
62
+ for (const name of entries.sort()) {
63
+ if (name === '__pycache__' || name === '.DS_Store' || name === 'node_modules')
64
+ continue;
65
+ const path = join(dir, name);
66
+ let isDir = false;
67
+ try {
68
+ isDir = statSync(path).isDirectory();
69
+ }
70
+ catch {
71
+ continue;
72
+ }
73
+ if (isDir)
74
+ out.push(...walk(path));
75
+ else
76
+ out.push(path);
77
+ }
78
+ return out;
79
+ }
80
+ /**
81
+ * Every file on the editable surface under `dir`, README stubs excluded. Pass `rejected` to
82
+ * collect paths that exist but are off the surface.
83
+ */
84
+ export function readVariant(surface, dir, rejected) {
85
+ const out = {};
86
+ const stubs = stubPaths(surface);
87
+ for (const path of walk(dir)) {
88
+ const rel = relative(dir, path).split(sep).join('/');
89
+ if (stubs.has(rel))
90
+ continue;
91
+ if (!isAllowed(surface, rel)) {
92
+ rejected?.push(rel);
93
+ continue;
94
+ }
95
+ out[rel] = readFileSync(path, 'utf8');
96
+ }
97
+ return out;
98
+ }
99
+ /** Whether `dir` holds a variant: at least one always-on file. */
100
+ export function isVariant(surface, dir) {
101
+ return surface.alwaysOn.some((rel) => {
102
+ try {
103
+ return statSync(join(dir, rel)).isFile();
104
+ }
105
+ catch {
106
+ return false;
107
+ }
108
+ });
109
+ }
110
+ /**
111
+ * Writes exactly `variant` to `dest`. Always-on files are present even when empty; files off the
112
+ * surface are kept, editable files already there are replaced.
113
+ */
114
+ export function writeVariant(surface, variant, dest, options = {}) {
115
+ mkdirSync(dest, { recursive: true });
116
+ const stubs = stubPaths(surface);
117
+ for (const path of walk(dest).reverse()) {
118
+ const rel = relative(dest, path).split(sep).join('/');
119
+ if (isAllowed(surface, rel) || stubs.has(rel))
120
+ unlinkSync(path);
121
+ }
122
+ for (const rel of surface.alwaysOn) {
123
+ if (!(rel in variant)) {
124
+ const path = join(dest, rel);
125
+ mkdirSync(dirname(path), { recursive: true });
126
+ writeFileSync(path, '');
127
+ }
128
+ }
129
+ for (const [rel, text] of Object.entries(variant)) {
130
+ const path = join(dest, normalizeRel(rel));
131
+ mkdirSync(dirname(path), { recursive: true });
132
+ writeFileSync(path, text);
133
+ }
134
+ if (options.stubs !== false) {
135
+ for (const [rel, body] of surface.stubs) {
136
+ const path = join(dest, rel);
137
+ try {
138
+ statSync(path);
139
+ }
140
+ catch {
141
+ mkdirSync(dirname(path), { recursive: true });
142
+ writeFileSync(path, body);
143
+ }
144
+ }
145
+ }
146
+ return dest;
147
+ }
148
+ /** The always-on text, as the model sees it, joined in surface order. */
149
+ export function alwaysOnText(surface, variant) {
150
+ return surface.alwaysOn.map((rel) => variant[rel] ?? '').join('\n\n');
151
+ }
152
+ function frontmatterField(body, field) {
153
+ const m = new RegExp(`^${field}:\\s*(.+)$`, 'm').exec(body.slice(0, 600));
154
+ return m?.[1]?.trim() ?? null;
155
+ }
156
+ /**
157
+ * Gives every skill a trigger line in the always-on text, mutating `variant`. Returns the names
158
+ * repaired. A line that would push the text over the cap is skipped.
159
+ */
160
+ export function autotrigger(surface, variant, alwaysOnMax = surface.alwaysOnMax) {
161
+ let body = alwaysOnText(surface, variant);
162
+ const fixed = [];
163
+ for (const name of skillNames(surface, variant)) {
164
+ if (body.includes(name))
165
+ continue;
166
+ const desc = frontmatterField(variant[`${surface.skillDir}/${name}/SKILL.md`] ?? '', 'description')?.replace(/\.$/, '') ?? `the ${name} workflow applies`;
167
+ const line = surface.triggerLine.replace('{name}', name).replace('{desc}', desc);
168
+ if (body.length + line.length + 2 > alwaysOnMax)
169
+ continue;
170
+ const current = variant[surface.autotriggerFile] ?? '';
171
+ variant[surface.autotriggerFile] = current.trim() ? `${current.replace(/\s+$/, '')}\n${line}\n` : `${line}\n`;
172
+ body = alwaysOnText(surface, variant);
173
+ fixed.push(name);
174
+ }
175
+ return fixed;
176
+ }
177
+ /** A variant that does not validate. `problems` is written to be fed back to an editor as-is. */
178
+ export class VariantError extends Error {
179
+ problems;
180
+ constructor(problems) {
181
+ super(problems.join(' | '));
182
+ this.problems = problems;
183
+ this.name = 'VariantError';
184
+ }
185
+ }
186
+ function checkCompiles(rel, src, errs) {
187
+ const dir = mkdtempSync(join(tmpdir(), 'harness-script-'));
188
+ try {
189
+ const file = join(dir, rel.split('/').pop() ?? 'script');
190
+ writeFileSync(file, src);
191
+ const python = rel.endsWith('.py');
192
+ const result = python
193
+ ? spawnSync('python3', ['-c', 'import ast,sys; ast.parse(open(sys.argv[1]).read(), sys.argv[1])', file], { encoding: 'utf8', timeout: 15_000 })
194
+ : spawnSync(process.execPath, ['--check', file], { encoding: 'utf8', timeout: 15_000 });
195
+ // A missing interpreter is not the variant's fault.
196
+ if (result.error)
197
+ return;
198
+ if (result.status !== 0) {
199
+ const detail = (result.stderr || result.stdout || '').trim().split('\n').filter(Boolean).slice(-2).join(' ');
200
+ errs.push(`${rel} does not compile: ${detail.slice(0, 200)}`);
201
+ }
202
+ }
203
+ finally {
204
+ rmSync(dir, { recursive: true, force: true });
205
+ }
206
+ }
207
+ /** Throws a `VariantError` listing every problem; returns a census of a valid variant. */
208
+ export function validate(surface, variant, options = {}) {
209
+ const errs = [];
210
+ const r = patterns(surface);
211
+ const cap = options.alwaysOnMax ?? surface.alwaysOnMax;
212
+ const body = alwaysOnText(surface, variant);
213
+ if (body.length > cap)
214
+ errs.push(`always-on content is ${body.length} chars, over the ${cap} cap; ${surface.alwaysOnCapReason}`);
215
+ for (const rel of Object.keys(variant)) {
216
+ if (!isAllowed(surface, rel))
217
+ errs.push(`${rel} is not on the editable surface`);
218
+ }
219
+ const skills = skillNames(surface, variant);
220
+ for (const name of skills) {
221
+ const fm = (variant[`${surface.skillDir}/${name}/SKILL.md`] ?? '').slice(0, 600);
222
+ if (!/^description:\s*\S/m.test(fm))
223
+ errs.push(`${surface.skillDir}/${name}/SKILL.md needs a \`description:\` in its frontmatter, or the skill is dropped SILENTLY at load`);
224
+ const declared = /^name:\s*(\S+)\s*$/m.exec(fm)?.[1];
225
+ if (surface.skillNameRule === 'must_match') {
226
+ if (declared !== name)
227
+ errs.push(`${surface.skillDir}/${name}/SKILL.md needs frontmatter \`name: ${name}\` matching its folder (a mismatch is dropped SILENTLY at load)`);
228
+ }
229
+ else if (declared && declared !== name) {
230
+ errs.push(`${surface.skillDir}/${name}/SKILL.md frontmatter \`name: ${declared}\` overrides the folder name at load, so trigger lines naming \`${name}\` stop matching`);
231
+ }
232
+ if (!body.includes(name))
233
+ errs.push(`skill '${name}' is never named in the always-on text, so the agent will not know to invoke it — an untriggered skill is dead weight. ${surface.triggerHint.replaceAll('{name}', name)}`);
234
+ }
235
+ const subagents = subagentNames(surface, variant);
236
+ if (subagents.length && options.allowSubagents === false)
237
+ errs.push(`subagents are not available here: ${subagents.join(', ')}`);
238
+ for (const name of subagents) {
239
+ const fm = (variant[`${surface.subagentDir}/${name}/AGENT.md`] ?? '').slice(0, 600);
240
+ if (!/^description:\s*\S/m.test(fm))
241
+ errs.push(`${surface.subagentDir}/${name}/AGENT.md needs a \`description:\``);
242
+ }
243
+ for (const [rel, src] of Object.entries(variant)) {
244
+ if (options.compileScripts !== false && (r.script.test(rel) || (r.skillScript.test(rel) && /\.(py|mjs)$/.test(rel))))
245
+ checkCompiles(rel, src, errs);
246
+ if (r.plugin.test(rel) && !/export\s+default\b/.test(src))
247
+ errs.push(`${rel} needs a default export: a Pi extension is a function \`export default function (pi) { ... }\``);
248
+ }
249
+ const reserved = options.reservedScripts ?? RESERVED_SCRIPTS;
250
+ const have = new Set(Object.keys(variant).filter((rel) => r.script.test(rel) || r.skillScript.test(rel)));
251
+ for (const [rel, src] of Object.entries(variant)) {
252
+ if (!(r.skill.test(rel) || surface.alwaysOn.includes(rel)))
253
+ continue;
254
+ for (const m of src.matchAll(r.scriptRef)) {
255
+ const ref = m[1];
256
+ if (reserved.has(ref.split('/').pop()))
257
+ continue;
258
+ if (!have.has(ref))
259
+ errs.push(`${rel} invokes \`${ref}\` but that file is not in the harness`);
260
+ }
261
+ }
262
+ if (errs.length)
263
+ throw new VariantError(errs);
264
+ return {
265
+ alwaysOnChars: body.length,
266
+ files: Object.keys(variant).length,
267
+ skills: skills.length,
268
+ subagents: subagents.length,
269
+ scripts: Object.keys(variant).filter((rel) => r.script.test(rel)).length,
270
+ plugins: pluginNames(surface, variant).length
271
+ };
272
+ }
273
+ export function diffSummary(surface, before, after) {
274
+ const a = new Set(Object.keys(before));
275
+ const b = new Set(Object.keys(after));
276
+ return {
277
+ added: [...b].filter((rel) => !a.has(rel)).sort(),
278
+ removed: [...a].filter((rel) => !b.has(rel)).sort(),
279
+ modified: [...a].filter((rel) => b.has(rel) && before[rel] !== after[rel]).sort(),
280
+ alwaysOnCharDelta: alwaysOnText(surface, after).length - alwaysOnText(surface, before).length
281
+ };
282
+ }
283
+ export function isUnchanged(surface, before, after) {
284
+ const d = diffSummary(surface, before, after);
285
+ return !d.added.length && !d.removed.length && !d.modified.length;
286
+ }
287
+ /** Which components a diff touches. */
288
+ export function touchedComponents(surface, diff) {
289
+ const out = new Set();
290
+ for (const rel of [...diff.added, ...diff.removed, ...diff.modified]) {
291
+ const id = componentOf(surface, rel);
292
+ if (id)
293
+ out.add(id);
294
+ }
295
+ return [...out];
296
+ }
297
+ /** Pairwise line-Jaccard over variants, so a collapse to one body is visible. */
298
+ export function diversity(variants) {
299
+ const labels = Object.keys(variants).sort();
300
+ if (labels.length < 2)
301
+ return { n: labels.length, distinct: labels.length, meanLineJaccard: 1 };
302
+ const lines = (variant) => new Set(Object.entries(variant).flatMap(([rel, text]) => text.split('\n').filter((line) => line.trim()).map((line) => `${rel}:${line}`)));
303
+ const sets = Object.fromEntries(labels.map((label) => [label, lines(variants[label])]));
304
+ const sims = [];
305
+ for (let i = 0; i < labels.length; i++) {
306
+ for (const b of labels.slice(i + 1)) {
307
+ const x = sets[labels[i]];
308
+ const y = sets[b];
309
+ const union = new Set([...x, ...y]);
310
+ let shared = 0;
311
+ for (const line of x)
312
+ if (y.has(line))
313
+ shared++;
314
+ sims.push(union.size ? shared / union.size : 1);
315
+ }
316
+ }
317
+ const distinct = new Set(labels.map((label) => JSON.stringify(Object.entries(variants[label]).sort()))).size;
318
+ return { n: labels.length, distinct, meanLineJaccard: Math.round((sims.reduce((sum, value) => sum + value, 0) / sims.length) * 10_000) / 10_000 };
319
+ }
320
+ /** Applies an omitted-is-inherited edit: `null` removes a file. */
321
+ export function applyEdit(base, edit) {
322
+ const next = { ...base };
323
+ for (const [rel, text] of Object.entries(edit)) {
324
+ const clean = normalizeRel(rel);
325
+ if (text === null)
326
+ delete next[clean];
327
+ else
328
+ next[clean] = text;
329
+ }
330
+ return next;
331
+ }
@@ -0,0 +1,10 @@
1
+ /** Deeper than this, a page is not converted. */
2
+ export declare const MAX_DEPTH = 512;
3
+ export interface PageText {
4
+ title: string;
5
+ text: string;
6
+ }
7
+ /** The page's title and its visible content as Markdown. Throws when the page cannot be converted safely. */
8
+ export declare function htmlToMarkdown(html: string): PageText;
9
+ /** Plain text of Markdown: emphasis and link targets dropped, for `format: "text"`. */
10
+ export declare function markdownToText(markdown: string): string;
@@ -0,0 +1,66 @@
1
+ // A fetched page as Markdown an agent can read. Content a person would not see is dropped first:
2
+ // hidden text is a known way to slip instructions to a model. A page nested deeper than any real
3
+ // page is refused rather than converted, so a hostile page cannot stall the conversion.
4
+ import { parseHTML } from 'linkedom';
5
+ import TurndownService from 'turndown';
6
+ import { gfm } from 'turndown-plugin-gfm';
7
+ /** Deeper than this, a page is not converted. */
8
+ export const MAX_DEPTH = 512;
9
+ const INVISIBLE = 'script, style, noscript, template, iframe, object, embed, svg, canvas, head, link, meta, input[type="hidden"], [hidden], [aria-hidden="true"]';
10
+ const HIDING_STYLE = /(?:^|;)\s*(?:display\s*:\s*none|visibility\s*:\s*hidden|opacity\s*:\s*0(?:\.0+)?\s*(?:;|$)|font-size\s*:\s*0(?:px)?\s*(?:;|$))/i;
11
+ function depth(node, limit) {
12
+ let deepest = 0;
13
+ const stack = [[node, 0]];
14
+ while (stack.length) {
15
+ const [current, level] = stack.pop();
16
+ if (level > deepest)
17
+ deepest = level;
18
+ if (deepest > limit)
19
+ return deepest;
20
+ for (let index = 0; index < current.childNodes.length; index += 1) {
21
+ const child = current.childNodes[index];
22
+ if (child.nodeType === 1)
23
+ stack.push([child, level + 1]);
24
+ }
25
+ }
26
+ return deepest;
27
+ }
28
+ function converter() {
29
+ const service = new TurndownService({ headingStyle: 'atx', codeBlockStyle: 'fenced', bulletListMarker: '-', emDelimiter: '*' });
30
+ service.use(gfm);
31
+ // An image is a word, not a link the model may follow without reading the page.
32
+ service.addRule('image', { filter: 'img', replacement: (_content, node) => {
33
+ const alt = node.getAttribute('alt')?.trim();
34
+ return alt ? `[image: ${alt}]` : '';
35
+ } });
36
+ return service;
37
+ }
38
+ /** The page's title and its visible content as Markdown. Throws when the page cannot be converted safely. */
39
+ export function htmlToMarkdown(html) {
40
+ const { document } = parseHTML(html.includes('<html') ? html : `<!doctype html><html><body>${html}</body></html>`);
41
+ const title = (document.querySelector('title')?.textContent ?? '').replace(/\s+/g, ' ').trim();
42
+ const body = document.body ?? document.documentElement;
43
+ if (!body)
44
+ return { title, text: '' };
45
+ if (depth(body, MAX_DEPTH) > MAX_DEPTH)
46
+ throw new Error('The page is nested too deeply to convert safely.');
47
+ for (const element of [...body.querySelectorAll(INVISIBLE)])
48
+ element.remove();
49
+ for (const element of [...body.querySelectorAll('[style]')]) {
50
+ if (HIDING_STYLE.test(element.getAttribute('style') ?? ''))
51
+ element.remove();
52
+ }
53
+ const text = converter().turndown(body.innerHTML)
54
+ .replace(/\n{3,}/g, '\n\n')
55
+ .replace(/[ \t]+\n/g, '\n')
56
+ .trim();
57
+ return { title, text };
58
+ }
59
+ /** Plain text of Markdown: emphasis and link targets dropped, for `format: "text"`. */
60
+ export function markdownToText(markdown) {
61
+ return markdown
62
+ .replace(/!?\[([^\]]*)\]\([^)]*\)/g, '$1')
63
+ .replace(/^#{1,6}\s+/gm, '')
64
+ .replace(/(\*\*|__|\*|_|`)/g, '')
65
+ .trim();
66
+ }
@@ -0,0 +1,34 @@
1
+ import { type LookupFunction } from 'node:net';
2
+ export interface WebResponse {
3
+ status: number;
4
+ contentType: string;
5
+ body: Buffer;
6
+ /** The body reached the byte limit and was cut short. */
7
+ truncated: boolean;
8
+ /** The address finally read, after any redirects. */
9
+ url: URL;
10
+ /** Set when the address redirected to another site and `sameOrigin` stopped there. */
11
+ redirectedTo?: URL;
12
+ }
13
+ export interface WebRequestOptions {
14
+ headers?: Record<string, string>;
15
+ /** Longest a single read may stay silent, and the most time the whole request may take. */
16
+ timeoutMs: number;
17
+ maxBytes: number;
18
+ maxRedirects?: number;
19
+ /** A JSON body sends a POST (search services); without one the request is a GET. */
20
+ json?: unknown;
21
+ /** Stop at a redirect to another origin and report it, instead of following it. */
22
+ sameOrigin?: boolean;
23
+ }
24
+ /** Makes one read. Tests replace it; the default is `safeRequest`. */
25
+ export type WebRequest = (url: URL, options: WebRequestOptions) => Promise<WebResponse>;
26
+ /** Only plain web addresses: no other scheme, and no username or password in the address. */
27
+ export declare function webAddress(input: string): URL;
28
+ /** Whether an address belongs to this machine, a private network, or is otherwise not public. */
29
+ export declare function isPublicAddress(address: string): boolean;
30
+ export declare const publicLookup: LookupFunction;
31
+ /** Reads a web address, following redirects one at a time, each checked the same way. */
32
+ export declare const safeRequest: WebRequest;
33
+ /** A body as text, in the character set its content type declares (UTF-8 when it declares none). */
34
+ export declare function decodeBody(body: Buffer, contentType: string): string;
@@ -0,0 +1,175 @@
1
+ // Reads a web address on behalf of an agent without reaching the machine's own network. The
2
+ // address is checked before a request and again when the connection is made, so a name that
3
+ // resolves to a private address, or changes between the check and the connection, is refused.
4
+ import { lookup as dnsLookup } from 'node:dns';
5
+ import { request as httpRequest } from 'node:http';
6
+ import { request as httpsRequest } from 'node:https';
7
+ import { isIP } from 'node:net';
8
+ const USER_AGENT = 'Mozilla/5.0 (compatible; XingularHarness/0.1; +web tools)';
9
+ const REDIRECT_CODES = new Set([301, 302, 303, 307, 308]);
10
+ /** Only plain web addresses: no other scheme, and no username or password in the address. */
11
+ export function webAddress(input) {
12
+ let url;
13
+ try {
14
+ url = new URL(input.trim());
15
+ }
16
+ catch {
17
+ throw new Error('That is not a web address.');
18
+ }
19
+ if (url.protocol !== 'http:' && url.protocol !== 'https:')
20
+ throw new Error('Only http and https addresses can be read.');
21
+ if (url.username || url.password)
22
+ throw new Error('Addresses with a username or password cannot be read.');
23
+ if (!url.hostname)
24
+ throw new Error('That address has no host name.');
25
+ return url;
26
+ }
27
+ /** Whether an address belongs to this machine, a private network, or is otherwise not public. */
28
+ export function isPublicAddress(address) {
29
+ const mapped = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i.exec(address);
30
+ if (mapped)
31
+ return isPublicIpv4(mapped[1]);
32
+ if (address.includes(':'))
33
+ return isPublicIpv6(address.toLowerCase());
34
+ return isPublicIpv4(address);
35
+ }
36
+ function isPublicIpv4(address) {
37
+ const parts = address.split('.').map(Number);
38
+ if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255))
39
+ return false;
40
+ const [a, b, c] = parts;
41
+ if (a === 0 || a === 10 || a === 127 || a >= 224)
42
+ return false;
43
+ if (a === 100 && b >= 64 && b <= 127)
44
+ return false;
45
+ if (a === 169 && b === 254)
46
+ return false;
47
+ if (a === 172 && b >= 16 && b <= 31)
48
+ return false;
49
+ if (a === 192 && b === 168)
50
+ return false;
51
+ if (a === 192 && b === 0 && c === 0)
52
+ return false;
53
+ if (a === 198 && (b === 18 || b === 19))
54
+ return false;
55
+ return true;
56
+ }
57
+ function isPublicIpv6(address) {
58
+ // Global unicast only (2000::/3), without documentation (2001:db8::/32), Teredo (2001::/32) or 6to4 (2002::/16), which carry IPv4 addresses.
59
+ const first = parseInt(address.split(':')[0] || '0', 16);
60
+ if (Number.isNaN(first) || first < 0x2000 || first > 0x3fff)
61
+ return false;
62
+ if (/^2001:db8(:|$)/.test(address) || /^2001:0{0,4}:/.test(address) || /^2002(:|$)/.test(address))
63
+ return false;
64
+ return true;
65
+ }
66
+ /** A DNS lookup that refuses any address that is not public. It runs when the connection is made. */
67
+ function publicLookupImpl(hostname, options, callback) {
68
+ dnsLookup(hostname, { all: true, verbatim: true }, (error, addresses) => {
69
+ if (error)
70
+ return callback(error, '', 4);
71
+ if (addresses.some((entry) => !isPublicAddress(entry.address))) {
72
+ return callback(Object.assign(new Error(`${hostname} is not a public address.`), { code: 'EPRIVATE' }), '', 4);
73
+ }
74
+ if (options.all)
75
+ return callback(null, addresses);
76
+ callback(null, addresses[0].address, addresses[0].family);
77
+ });
78
+ }
79
+ export const publicLookup = publicLookupImpl;
80
+ /**
81
+ * A literal address is never looked up, so it is checked here. A name is checked by the lookup
82
+ * at connection time.
83
+ */
84
+ function assertReachable(url) {
85
+ const host = url.hostname.replace(/^\[|\]$/g, '');
86
+ if (isIP(host) && !isPublicAddress(host))
87
+ throw Object.assign(new Error(`${host} is not a public address.`), { code: 'EPRIVATE' });
88
+ }
89
+ /** Reads a web address, following redirects one at a time, each checked the same way. */
90
+ export const safeRequest = async (start, options) => {
91
+ let url = start;
92
+ for (let hop = 0;; hop += 1) {
93
+ assertReachable(url);
94
+ const response = await readOnce(url, options);
95
+ if (REDIRECT_CODES.has(response.status) && response.location) {
96
+ if (hop >= (options.maxRedirects ?? 5))
97
+ throw new Error('The address redirected too many times.');
98
+ const next = webAddress(new URL(response.location, url).toString());
99
+ // Another site is a new decision: the caller reads it with its own call.
100
+ if (options.sameOrigin && next.origin !== url.origin)
101
+ return { status: response.status, contentType: '', body: Buffer.alloc(0), truncated: false, url, redirectedTo: next };
102
+ url = next;
103
+ continue;
104
+ }
105
+ return { status: response.status, contentType: response.contentType, body: response.body, truncated: response.truncated, url };
106
+ }
107
+ };
108
+ function readOnce(url, options) {
109
+ return new Promise((resolve, reject) => {
110
+ const start = (url.protocol === 'https:' ? httpsRequest : httpRequest);
111
+ let settled = false;
112
+ const finish = (value) => {
113
+ if (settled)
114
+ return;
115
+ settled = true;
116
+ clearTimeout(total);
117
+ resolve(value);
118
+ };
119
+ const fail = (error) => {
120
+ if (settled)
121
+ return;
122
+ settled = true;
123
+ clearTimeout(total);
124
+ reject(error);
125
+ };
126
+ const payload = options.json === undefined ? null : Buffer.from(JSON.stringify(options.json));
127
+ const request = start(url, {
128
+ method: payload ? 'POST' : 'GET',
129
+ headers: { 'user-agent': USER_AGENT, accept: 'text/html,text/plain,application/json;q=0.9,*/*;q=0.5', ...(payload ? { 'content-type': 'application/json', 'content-length': String(payload.length) } : {}), ...options.headers },
130
+ lookup: publicLookup,
131
+ timeout: options.timeoutMs
132
+ }, (response) => {
133
+ const status = response.statusCode ?? 0;
134
+ const location = response.headers.location;
135
+ if (REDIRECT_CODES.has(status) && location) {
136
+ response.resume();
137
+ finish({ status, contentType: '', body: Buffer.alloc(0), truncated: false, url, location });
138
+ return;
139
+ }
140
+ const chunks = [];
141
+ let size = 0;
142
+ let truncated = false;
143
+ const done = () => finish({ status, contentType: String(response.headers['content-type'] ?? ''), body: Buffer.concat(chunks), truncated, url });
144
+ response.on('data', (chunk) => {
145
+ const room = options.maxBytes - size;
146
+ const kept = chunk.length > room ? chunk.subarray(0, Math.max(room, 0)) : chunk;
147
+ chunks.push(kept);
148
+ size += kept.length;
149
+ if (kept.length < chunk.length) {
150
+ truncated = true;
151
+ response.destroy();
152
+ done();
153
+ }
154
+ });
155
+ response.on('error', (error) => (truncated ? done() : fail(error)));
156
+ response.on('end', done);
157
+ response.on('close', done);
158
+ });
159
+ // The whole request is capped as well as each silent stretch, so a slow trickle cannot hold a turn open.
160
+ const total = setTimeout(() => request.destroy(new Error('The address took too long to answer.')), options.timeoutMs * 3);
161
+ request.on('timeout', () => request.destroy(new Error('The address took too long to answer.')));
162
+ request.on('error', fail);
163
+ request.end(payload ?? undefined);
164
+ });
165
+ }
166
+ /** A body as text, in the character set its content type declares (UTF-8 when it declares none). */
167
+ export function decodeBody(body, contentType) {
168
+ const label = /charset\s*=\s*"?([^";\s]+)/i.exec(contentType)?.[1]?.toLowerCase() ?? 'utf-8';
169
+ try {
170
+ return new TextDecoder(label).decode(body);
171
+ }
172
+ catch {
173
+ throw new Error(`The page uses a character set that cannot be read: ${label}.`);
174
+ }
175
+ }
@@ -0,0 +1,53 @@
1
+ import { type SecretStore } from "../accounts/secrets.js";
2
+ import { type WebRequest } from "./safeRequest.js";
3
+ export interface SearchHit {
4
+ title: string;
5
+ url: string;
6
+ snippet: string;
7
+ }
8
+ export interface SearchProvider {
9
+ id: string;
10
+ title: string;
11
+ /** The secret holding its key, or null for one that needs none. */
12
+ key: string | null;
13
+ search(query: string, count: number, key: string | null, request: WebRequest): Promise<SearchHit[]>;
14
+ }
15
+ /** Raised when a provider asks to be left alone; `retryAfterMs` comes from its answer. */
16
+ export declare class RateLimited extends Error {
17
+ readonly retryAfterMs: number;
18
+ constructor(retryAfterMs: number);
19
+ }
20
+ export declare const BRAVE: SearchProvider;
21
+ export declare const TAVILY: SearchProvider;
22
+ export declare const EXA: SearchProvider;
23
+ /** DuckDuckGo's plain results page. No key; off unless a person switched it on. */
24
+ export declare const DUCKDUCKGO: SearchProvider;
25
+ export declare const SEARCH_PROVIDERS: readonly SearchProvider[];
26
+ export interface SearchOptions {
27
+ secrets: SecretStore;
28
+ request: WebRequest;
29
+ /** Whether the keyless provider may be used. */
30
+ keyless: () => boolean;
31
+ providers?: readonly SearchProvider[];
32
+ now?: () => number;
33
+ }
34
+ export interface SearchAnswer {
35
+ provider: string | null;
36
+ results: SearchHit[];
37
+ /** Why each provider tried did not answer, for the model and the log. */
38
+ failures: string[];
39
+ }
40
+ /** Tries each usable provider in turn. Remembers which ones are resting. */
41
+ export declare class Searcher {
42
+ private readonly options;
43
+ private readonly resting;
44
+ constructor(options: SearchOptions);
45
+ /** Providers that could answer now: a key saved (or none needed and allowed), not resting. */
46
+ usable(): Promise<{
47
+ provider: SearchProvider;
48
+ key: string | null;
49
+ }[]>;
50
+ search(query: string, count: number): Promise<SearchAnswer>;
51
+ }
52
+ /** Several queries' results interleaved, each address once, at most `limit`. */
53
+ export declare function mergeResults(lists: readonly SearchHit[][], limit: number): SearchHit[];