@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,184 +0,0 @@
1
- // `xingular-harness plugins …`: plugin control for a person using the harness without an
2
- // application. It runs outside any session, as the person who typed it, and acts directly.
3
- import { homedir } from 'node:os'
4
- import { join, resolve } from 'node:path'
5
- import { createInterface } from 'node:readline/promises'
6
-
7
- import { Harness, type HarnessOptions } from '../harness'
8
- import * as api from './api'
9
- import * as install from './install'
10
- import type { Plugin } from './types'
11
-
12
- export const PLUGINS_USAGE = `Usage: xingular-harness plugins <command> [options]
13
-
14
- list Every plugin and whether it is on
15
- show <plugin id> One plugin: what it brings, where it is on, its code
16
- install <source> A folder, an npm name, or a git address. It arrives off and untrusted
17
- remove <plugin id> Remove a plugin you installed
18
- update [plugin id] Fetch a newer version of one installed plugin, or of all
19
- attach <id> Switch a plugin, or one thing it brings, on
20
- detach <id> Switch it off
21
- trust <plugin id> [--yes] Show the files of its code, then trust them
22
- untrust <plugin id> Stop trusting its code
23
-
24
- Options:
25
- --data <dir> Data directory (default: ~/.xingular-harness/data)
26
- --state <dir> State directory (default: ~/.xingular-harness/state)
27
- --cwd <dir> Also list what this working folder brings
28
- --project <id> attach/detach for one project instead of everywhere
29
- --agent <id> attach/detach for one agent instead of everywhere
30
- `
31
-
32
- export interface PluginsCliDependencies {
33
- home?: string
34
- cwd?: string
35
- write?: (text: string) => void
36
- /** Asks the person a question and returns what they typed. Without it, a trust needs `--yes`. */
37
- ask?: (question: string) => Promise<string>
38
- createHarness?: (options: HarnessOptions) => Harness
39
- }
40
-
41
- function parse(argv: string[]): { positional: string[]; values: Record<string, string>; switches: Set<string> } {
42
- const positional: string[] = []
43
- const values: Record<string, string> = {}
44
- const switches = new Set<string>()
45
- for (let index = 0; index < argv.length; index += 1) {
46
- const arg = argv[index]
47
- if (!arg.startsWith('--')) positional.push(arg)
48
- else if (arg === '--yes') switches.add('yes')
49
- else if (index + 1 < argv.length) {
50
- values[arg.slice(2)] = argv[index + 1]
51
- index += 1
52
- } else throw new Error(`${arg} needs a value.`)
53
- }
54
- return { positional, values, switches }
55
- }
56
-
57
- const stateOf = (plugin: Plugin): string => {
58
- if (plugin.required) return 'required'
59
- if (plugin.trust === 'untrusted') return plugin.attachment && !plugin.attachment.global ? 'off, code not trusted' : 'code not trusted'
60
- if (plugin.attachment) return plugin.attachment.global ? 'on' : 'off'
61
- const on = plugin.contributions.filter((entry) => entry.state === 'on').length
62
- return `${on} of ${plugin.contributions.length} on`
63
- }
64
-
65
- function describe(plugin: Plugin): string {
66
- const lines = [`${plugin.title} (${plugin.id})`, ` ${plugin.description || 'No description.'}`, ` Source: ${plugin.source}${plugin.version ? ` ${plugin.version}` : ''}${plugin.path ? ` · ${plugin.path}` : ''}`, ` State: ${stateOf(plugin)}`]
67
- if (plugin.extends.length) lines.push(` Extends: ${plugin.extends.join(', ')}`)
68
- if (plugin.code) lines.push(` Code (${plugin.trust}), fingerprint ${plugin.code.hash.slice(0, 12)}:`, ...plugin.code.files.map((file) => ` ${file}`))
69
- for (const entry of plugin.contributions) lines.push(` ${entry.state === 'on' ? '●' : '○'} ${entry.kind} ${entry.name} (${entry.id})${entry.scope ? ` · ${entry.scope}` : ''}${entry.problem ? ` · last run failed: ${entry.problem}` : ''}`)
70
- return lines.join('\n')
71
- }
72
-
73
- export async function runPluginsCli(argv: string[], dependencies: PluginsCliDependencies = {}): Promise<number> {
74
- const write = dependencies.write ?? ((text: string) => void process.stdout.write(text))
75
- let parsed: ReturnType<typeof parse>
76
- try {
77
- parsed = parse(argv)
78
- } catch (error) {
79
- write(`${error instanceof Error ? error.message : String(error)}\n`)
80
- return 2
81
- }
82
- const [command, target] = parsed.positional
83
- if (!command || command === 'help') {
84
- write(PLUGINS_USAGE)
85
- return command ? 0 : 2
86
- }
87
- const base = dependencies.cwd ?? process.cwd()
88
- const root = join(dependencies.home ?? homedir(), '.xingular-harness')
89
- const options: HarnessOptions = { dataRoot: parsed.values.data ? resolve(base, parsed.values.data) : join(root, 'data'), stateDir: parsed.values.state ? resolve(base, parsed.values.state) : join(root, 'state'), productName: 'Xingular Harness' }
90
- // Built without opening the data root: a plugin command must not settle runs or migrate a Wiki
91
- // that another process may be using.
92
- const harness = dependencies.createHarness ? dependencies.createHarness(options) : new Harness(options)
93
- const scope = { cwd: parsed.values.cwd ? resolve(base, parsed.values.cwd) : null }
94
- const need = (what: string): string => {
95
- if (!target) throw new Error(`${command} needs ${what}.`)
96
- return target
97
- }
98
- const find = async (id: string): Promise<Plugin> => {
99
- const plugin = (await api.list(harness, scope)).find((candidate) => candidate.id === id)
100
- if (!plugin) throw new Error(`There is no plugin with the id "${id}". Run "plugins list" to see the ids.`)
101
- return plugin
102
- }
103
- try {
104
- switch (command) {
105
- case 'list': {
106
- const plugins = await api.list(harness, scope)
107
- const width = Math.max(...plugins.map((plugin) => plugin.id.length))
108
- write(`${plugins.map((plugin) => `${plugin.id.padEnd(width)} ${stateOf(plugin).padEnd(22)} ${plugin.title}`).join('\n')}\n`)
109
- return 0
110
- }
111
- case 'show':
112
- write(`${describe(await find(need('a plugin id')))}\n`)
113
- return 0
114
- case 'install': {
115
- const before = new Set((await api.list(harness, scope)).map((plugin) => plugin.id))
116
- const added = (await install.install(harness, { ...scope, source: need('a folder, an npm name, or a git address') })).filter((plugin) => !before.has(plugin.id))
117
- write(added.length ? `${added.map(describe).join('\n\n')}\n\nInstalled, switched off. Switch it on with "plugins attach <id>"${added.some((plugin) => plugin.code) ? ' and trust its code with "plugins trust <id>"' : ''}.\n` : 'Nothing new was installed.\n')
118
- return 0
119
- }
120
- case 'remove':
121
- await install.remove(harness, { ...scope, pluginId: need('a plugin id') })
122
- write(`Removed ${target}.\n`)
123
- return 0
124
- case 'update':
125
- await install.update(harness, { ...scope, pluginId: target ?? null })
126
- write(target ? `Updated ${target}. If its code changed, trust it again.\n` : 'Updated the installed plugins. Any whose code changed needs trust again.\n')
127
- return 0
128
- case 'attach':
129
- case 'detach': {
130
- const where = parsed.values.agent ? { agentId: parsed.values.agent } : parsed.values.project ? { projectId: parsed.values.project } : {}
131
- await api.attach(harness, { ...scope, id: need('a plugin or contribution id'), scope: where }, command === 'attach')
132
- write(`${target} is ${command === 'attach' ? 'on' : 'off'} ${parsed.values.agent ? `for agent ${parsed.values.agent}` : parsed.values.project ? `for project ${parsed.values.project}` : 'everywhere'}. It applies from the next session start.\n`)
133
- return 0
134
- }
135
- case 'trust': {
136
- const plugin = await find(need('a plugin id'))
137
- if (!plugin.code) throw new Error(`${plugin.title} has no code that waits for trust.`)
138
- if (plugin.trust === 'trusted') {
139
- write(`${plugin.title} is already trusted.\n`)
140
- return 0
141
- }
142
- write(`${plugin.title} brings code that runs inside the harness and its sessions. It can do anything you can, including reading your files and credentials.\n\nFiles:\n${plugin.code.files.map((file) => ` ${file}`).join('\n')}\n\nFingerprint ${plugin.code.hash.slice(0, 12)}\n`)
143
- if (!parsed.switches.has('yes')) {
144
- if (!dependencies.ask) throw new Error('Read the files, then run this again with --yes to trust them.')
145
- if ((await dependencies.ask('Trust this code? Type yes: ')).trim().toLowerCase() !== 'yes') {
146
- write('Not trusted.\n')
147
- return 1
148
- }
149
- }
150
- await api.trust(harness, { ...scope, hash: plugin.code.hash })
151
- write(`Trusted. If any of these files changes, it asks again.\n`)
152
- return 0
153
- }
154
- case 'untrust': {
155
- const plugin = await find(need('a plugin id'))
156
- if (!plugin.code) throw new Error(`${plugin.title} has no code to stop trusting.`)
157
- await api.untrust(harness, { ...scope, hash: plugin.code.hash })
158
- write(`${plugin.title} is not trusted any more.\n`)
159
- return 0
160
- }
161
- default:
162
- write(PLUGINS_USAGE)
163
- return 2
164
- }
165
- } catch (error) {
166
- write(`${error instanceof Error ? error.message : String(error)}\n`)
167
- return 1
168
- } finally {
169
- await harness.close()
170
- }
171
- }
172
-
173
- /** Asks on the terminal, when there is one to ask on. */
174
- export function terminalAsk(): ((question: string) => Promise<string>) | undefined {
175
- if (!process.stdin.isTTY || !process.stdout.isTTY) return undefined
176
- return async (question) => {
177
- const reader = createInterface({ input: process.stdin, output: process.stdout })
178
- try {
179
- return await reader.question(question)
180
- } finally {
181
- reader.close()
182
- }
183
- }
184
- }
@@ -1,151 +0,0 @@
1
- // What Pi would load for a session, found with Pi's own resolver so the list matches Pi's
2
- // rules. Resolving reads folders and manifests; it never runs an extension.
3
- import { existsSync } from 'node:fs'
4
- import { homedir } from 'node:os'
5
- import { join, sep } from 'node:path'
6
-
7
- import { log } from '../core/bus'
8
- import { packageManifest } from './manifest'
9
-
10
- /**
11
- * `context` is an instruction file Pi reads on its own: `AGENTS.md`, `SYSTEM.md` and their kin.
12
- * `hook` is a package's hooks file, which the harness runs itself; Pi never loads it.
13
- */
14
- export type FoundKind = 'extension' | 'skill' | 'prompt' | 'theme' | 'context' | 'hook'
15
-
16
- /** Where a found item comes from. `builtin` is one of the Pi command's own extensions. */
17
- export type FoundOrigin = 'shared' | 'package' | 'project' | 'home' | 'builtin'
18
-
19
- export interface Found {
20
- kind: FoundKind
21
- /** A file or folder, or `builtin:<name>`. */
22
- path: string
23
- origin: FoundOrigin
24
- /** Declared by the working folder: its `.pi` or `.agents` folder, or its settings. */
25
- project: boolean
26
- /** The folder of the package this belongs to. */
27
- packageRoot: string | null
28
- /** The package as its settings name it (`npm:…`, `git:…`, a folder). */
29
- source: string | null
30
- /** Left on in Pi's settings. An item switched off there is found but never loaded. */
31
- enabled: boolean
32
- }
33
-
34
- export interface ResolveInput {
35
- cwd: string
36
- /** The account's Pi agent folder. */
37
- agentDir: string
38
- /** Install a package the shared settings name and the account does not have yet. */
39
- install?: boolean
40
- /** Folders of packages kept outside Pi's settings: each is found as an installed package. */
41
- packages?: readonly string[]
42
- }
43
-
44
- /** Finds what Pi would load. Replaced in tests. */
45
- export type Resolver = (input: ResolveInput) => Promise<Found[]>
46
-
47
- /** The extensions built into the `pi` command. A terminal loads them; an SDK session has none. */
48
- export const PI_BUILTINS = ['llama.cpp', 'codemode', 'tool-search', 'mcp'] as const
49
- export const BUILTIN_PREFIX = 'builtin:'
50
-
51
- const inside = (path: string, parent: string): boolean => path === parent || path.startsWith(parent.endsWith(sep) ? parent : `${parent}${sep}`)
52
-
53
- interface Metadata {
54
- source: string
55
- scope: string
56
- origin: string
57
- baseDir?: string
58
- packageRoot?: string
59
- }
60
-
61
- function origin(path: string, metadata: Metadata): FoundOrigin {
62
- if (path.startsWith(BUILTIN_PREFIX)) return 'builtin'
63
- if (metadata.origin === 'package') return 'package'
64
- if (metadata.scope === 'project') return 'project'
65
- return metadata.baseDir && inside(metadata.baseDir, join(process.env.HOME || homedir(), '.agents')) ? 'home' : 'shared'
66
- }
67
-
68
- /**
69
- * Runs an install with the package manager's install scripts switched off, so fetching a
70
- * package runs none of its code. A package that builds itself on install then fails to load,
71
- * and says so; that is the price of installing being safe to do before trust.
72
- */
73
- export async function withoutInstallScripts<T>(action: () => Promise<T>): Promise<T> {
74
- const before = process.env.npm_config_ignore_scripts
75
- process.env.npm_config_ignore_scripts = 'true'
76
- try {
77
- return await action()
78
- } finally {
79
- if (before === undefined) delete process.env.npm_config_ignore_scripts
80
- else process.env.npm_config_ignore_scripts = before
81
- }
82
- }
83
-
84
- export const piResolver: Resolver = async ({ cwd, agentDir, install, packages }) => {
85
- const pi = await import('@earendil-works/pi-coding-agent')
86
- const settings = pi.SettingsManager.create(cwd, agentDir)
87
- const shared = new Set((settings.getGlobalSettings().packages ?? []).map((entry) => (typeof entry === 'string' ? entry : entry.source)))
88
- const manager = (): InstanceType<typeof pi.DefaultPackageManager> => new pi.DefaultPackageManager({ cwd, agentDir, settingsManager: settings, builtinExtensions: [...PI_BUILTINS] })
89
- const resolve = (installing: boolean) => manager().resolve(async (source) => (installing && shared.has(source) ? 'install' : 'skip'))
90
- let resolved: Awaited<ReturnType<typeof resolve>>
91
- try {
92
- resolved = install ? await withoutInstallScripts(() => resolve(true)) : await resolve(false)
93
- } catch (error) {
94
- if (!install) throw error
95
- // A package that cannot be installed must not stop the session; it is found again next time.
96
- log('a plugin package could not be installed', error instanceof Error ? error.message : String(error))
97
- resolved = await resolve(false)
98
- }
99
- const found: Found[] = []
100
- const add = (paths: typeof resolved): void => {
101
- const kinds = [['extension', paths.extensions], ['skill', paths.skills], ['prompt', paths.prompts], ['theme', paths.themes]] as const
102
- for (const [kind, resources] of kinds) {
103
- for (const resource of resources) {
104
- const metadata = resource.metadata as Metadata
105
- const packaged = metadata.origin === 'package'
106
- found.push({
107
- kind,
108
- path: resource.path,
109
- origin: origin(resource.path, metadata),
110
- project: metadata.scope === 'project',
111
- packageRoot: packaged ? (metadata.packageRoot ?? metadata.baseDir ?? null) : null,
112
- source: packaged ? metadata.source : null,
113
- enabled: resource.enabled
114
- })
115
- }
116
- }
117
- }
118
- add(resolved)
119
- // Packages a person keeps beside their data are read where they are; nothing is fetched for them.
120
- if (packages?.length) {
121
- try {
122
- add(await manager().resolveExtensionSources([...packages]))
123
- } catch (error) {
124
- log('a local plugin could not be read', error instanceof Error ? error.message : String(error))
125
- }
126
- }
127
- // A package may name a hooks file for the harness. Only a package a person installed counts:
128
- // a working folder's package cannot put code into the harness's own process.
129
- const roots = new Map<string, string>()
130
- for (const configured of manager().listConfiguredPackages()) if (configured.scope === 'user' && configured.installedPath) roots.set(configured.installedPath, configured.source)
131
- for (const folder of packages ?? []) roots.set(folder, folder)
132
- for (const [root, source] of roots) {
133
- const hooks = packageManifest(root).hooks
134
- if (!hooks || !existsSync(join(root, hooks))) continue
135
- found.push({ kind: 'hook', path: join(root, hooks), origin: 'package', project: false, packageRoot: root, source, enabled: true })
136
- // Pi takes a folder with none of its own resources to be an extension itself. A package that
137
- // only brings hooks is not one, and must not be handed to Pi as code to load.
138
- const fallback = found.findIndex((item) => item.kind === 'extension' && item.path === root)
139
- if (fallback !== -1) found.splice(fallback, 1)
140
- }
141
- // Pi reads these itself, whatever else is switched off. They are text, so they are only listed.
142
- const context = [
143
- ...pi.loadProjectContextFiles({ cwd, agentDir }).map((file) => file.path),
144
- ...[join(agentDir, 'SYSTEM.md'), join(agentDir, 'APPEND_SYSTEM.md'), join(cwd, '.pi', 'SYSTEM.md'), join(cwd, '.pi', 'APPEND_SYSTEM.md')].filter((path) => existsSync(path))
145
- ]
146
- for (const path of context) {
147
- const shared = inside(path, agentDir)
148
- found.push({ kind: 'context', path, origin: shared ? 'shared' : 'project', project: !shared, packageRoot: null, source: null, enabled: true })
149
- }
150
- return found
151
- }
@@ -1,143 +0,0 @@
1
- // The harness's own two plugins, run through a real Pi session with a scripted model: nothing
2
- // here is faked on Pi's side except what the model says.
3
- import { mkdtempSync } from 'node:fs'
4
- import { tmpdir } from 'node:os'
5
- import { join } from 'node:path'
6
- import { afterEach, describe, expect, it } from 'vitest'
7
-
8
- import { memorySecretStore } from '../accounts/secrets'
9
- import { createHarness, type Harness } from '../harness'
10
- import { methods } from '../server/methods'
11
- import type { RunEvent } from '../sessions/contracts'
12
- import { fauxModel } from '../../tests/faux'
13
- import { rootWithProject, writeFile } from '../../tests/helpers'
14
- import { settingValue, trimText } from './firstParty'
15
- import type { Inventory, Plugin } from './types'
16
-
17
- const open: Harness[] = []
18
- afterEach(async () => {
19
- for (const harness of open.splice(0)) await harness.close()
20
- })
21
-
22
- async function setup() {
23
- const root = rootWithProject()
24
- const base = mkdtempSync(join(tmpdir(), 'first-party-'))
25
- // The background upkeep would ask the scripted model too; it is not what is tested here.
26
- const harness = createHarness({ dataRoot: root, stateDir: join(base, 'state'), secrets: memorySecretStore(), maintainerFactory: async () => ({ prompt: async () => undefined, dispose: () => undefined, error: () => null }) })
27
- open.push(harness)
28
- harness.accounts.create('Main', 'apiKey')
29
- const model = await fauxModel(harness)
30
- const events: RunEvent[] = []
31
- harness.runs.listen((event) => void events.push(event))
32
- const table = methods(harness)
33
- const call = <T>(method: string, params: Record<string, unknown> = {}) => table[method](params) as Promise<T>
34
- let count = 0
35
- /** One reply: a real session, run until it settles. */
36
- const reply = async (agentId: string, extra: Record<string, unknown> = {}) => {
37
- const id = `run-${(count += 1)}`
38
- await harness.sessions.run({ run: { id, sessionId: agentId }, cwd: base, sessionDir: join(root, 'sessions', agentId), text: 'Go.', model: model.model, caller: { agentId, agentName: 'Helper', actor: 'Chat' }, tools: { allow: ['read'] }, ...extra })
39
- for (let waited = 0; waited < 200 && !events.some((event) => event.runId === id && (event.type === 'completed' || event.type === 'failed')); waited += 1) await new Promise((resolve) => setTimeout(resolve, 25))
40
- return events.filter((event) => event.runId === id)
41
- }
42
- return { harness, root, base, model, call, reply }
43
- }
44
-
45
- describe('Output trim', () => {
46
- it('keeps the beginning and the end of a long text and says what is missing', () => {
47
- const text = `${'a'.repeat(700)}${'b'.repeat(4000)}${'c'.repeat(300)}`
48
- const trimmed = trimText(text, 1000)
49
- expect(trimmed.startsWith('a'.repeat(700))).toBe(true)
50
- expect(trimmed.endsWith('c'.repeat(300))).toBe(true)
51
- expect(trimmed).toContain('4000 of 5000 characters were cut here by the Output trim plugin')
52
- expect(trimText('short', 1000)).toBe('short')
53
- })
54
-
55
- it('is off until switched on, then cuts what the model reads, for the agent it is on for', async () => {
56
- const { base, model, call, reply } = await setup()
57
- writeFile(join(base, 'big.txt'), `${'START '.repeat(10)}${'x'.repeat(30_000)}${' END'.repeat(10)}`)
58
- const read = () => model.script([model.calls(['read', { path: join(base, 'big.txt') }]), model.says('Done.')])
59
-
60
- read()
61
- await reply('a1')
62
- expect(model.lastToolResults()[0].length).toBeGreaterThan(30_000)
63
-
64
- const listed = await call<Plugin[]>('plugins.attach', { id: 'output-trim', scope: { agentId: 'a1' } })
65
- expect(listed.find((plugin) => plugin.id === 'output-trim')).toMatchObject({ source: 'builtin', runsCode: true, trust: 'none', required: null, attachment: { global: false, on: { agentIds: ['a1'], projectIds: [] } }, settings: [{ key: 'maxChars', value: 20_000, default: 20_000 }] })
66
- await call('plugins.configure', { pluginId: 'output-trim', settings: { maxChars: 3000 } })
67
-
68
- read()
69
- await reply('a1')
70
- // The conversation continues, so the model is sent the earlier result too; the newest is last.
71
- const seen = model.lastToolResults().at(-1)!
72
- expect(seen.length).toBeLessThan(3400)
73
- expect(seen.startsWith('START START')).toBe(true)
74
- expect(seen.endsWith('END END')).toBe(true)
75
- expect(seen).toContain('characters were cut here by the Output trim plugin')
76
-
77
- // Another agent still reads the whole result.
78
- read()
79
- await reply('a2')
80
- expect(model.lastToolResults().at(-1)!.length).toBeGreaterThan(30_000)
81
-
82
- // It reaches the agent it is on for, and is listed as that agent's.
83
- const inventory = await call<Inventory>('plugins.inventory', { context: { agentId: 'a1' }, tools: ['read'] })
84
- expect(inventory.items.filter((item) => item.pluginId === 'output-trim').map((item) => [item.kind, item.delivery])).toEqual([['code', 'code']])
85
- expect((await call<Inventory>('plugins.inventory', { context: { agentId: 'a2' }, tools: ['read'] })).items.some((item) => item.pluginId === 'output-trim')).toBe(false)
86
- })
87
-
88
- it('also runs in unattended work, where nothing on disk is loaded', async () => {
89
- const { base, model, call, reply } = await setup()
90
- writeFile(join(base, 'big.txt'), 'y'.repeat(30_000))
91
- await call('plugins.attach', { id: 'output-trim' })
92
- await call('plugins.configure', { pluginId: 'output-trim', settings: { maxChars: 2000 } })
93
- model.script([model.calls(['read', { path: join(base, 'big.txt') }]), model.says('Done.')])
94
- await reply('a1', { automatic: true })
95
- expect(model.lastToolResults()[0]).toContain('were cut here by the Output trim plugin')
96
- })
97
- })
98
-
99
- describe('Budget guard', () => {
100
- it('refuses tool calls past the limit, gives the agent one request to wrap up, then ends the reply', async () => {
101
- const { base, model, call, reply } = await setup()
102
- writeFile(join(base, 'a.txt'), 'A file.')
103
- await call('plugins.attach', { id: 'budget-guard' })
104
- await call('plugins.configure', { pluginId: 'budget-guard', settings: { maxRequests: 5 } })
105
- const again = model.calls(['read', { path: join(base, 'a.txt') }])
106
- // A model that never stops by itself: every reply asks for another read.
107
- model.script(Array.from({ length: 40 }, () => again))
108
- const events = await reply('a1')
109
- const tools = events.filter((event) => event.type === 'tool.completed').map((event) => (event.payload as { ok: boolean }).ok)
110
- // Four reads run. The fifth request's read is refused and the agent is told to wrap up; it asks
111
- // again in the sixth, which is refused too, and that ends the reply.
112
- expect(tools).toEqual([true, true, true, true, false, false])
113
- expect(model.requests()).toBe(6)
114
- expect(model.lastToolResults().at(-1)).toContain('reached its limit of 5 model requests (the Budget guard plugin)')
115
- expect(events.at(-1)?.type).toBe('completed')
116
- })
117
-
118
- it('lets a reply that wraps up when told finish in its own words', async () => {
119
- const { base, model, call, reply } = await setup()
120
- writeFile(join(base, 'a.txt'), 'A file.')
121
- await call('plugins.attach', { id: 'budget-guard' })
122
- await call('plugins.configure', { pluginId: 'budget-guard', settings: { maxRequests: 5 } })
123
- const again = model.calls(['read', { path: join(base, 'a.txt') }])
124
- model.script([again, again, again, again, again, model.says('I read the file four times; one more read is left.')])
125
- const events = await reply('a1')
126
- expect(model.requests()).toBe(6)
127
- expect(events.filter((event) => event.type === 'message.completed').map((event) => (event.payload as { text: string }).text).at(-1)).toBe('I read the file four times; one more read is left.')
128
- })
129
-
130
- it('does nothing while it is switched off, and takes only numbers inside its range', async () => {
131
- const { base, model, call, reply } = await setup()
132
- writeFile(join(base, 'a.txt'), 'A file.')
133
- const again = model.calls(['read', { path: join(base, 'a.txt') }])
134
- model.script([...Array.from({ length: 8 }, () => again), model.says('Done.')])
135
- const events = await reply('a1')
136
- expect(events.filter((event) => event.type === 'tool.completed').every((event) => (event.payload as { ok: boolean }).ok)).toBe(true)
137
- await expect(call('plugins.configure', { pluginId: 'budget-guard', settings: { maxRequests: 1 } })).rejects.toThrow('must be a number from 5 to 2000')
138
- await expect(call('plugins.configure', { pluginId: 'budget-guard', settings: { speed: 3 } })).rejects.toThrow('has no setting called "speed"')
139
- await expect(call('plugins.configure', { pluginId: 'memory', settings: {} })).rejects.toThrow('has no settings')
140
- expect(settingValue({ key: 'k', label: '', description: '', default: 60, min: 5, max: 100 }, { k: 1000 })).toBe(60)
141
- expect(settingValue({ key: 'k', label: '', description: '', default: 60, min: 5, max: 100 }, { k: 80.9 })).toBe(80)
142
- })
143
- })
@@ -1,129 +0,0 @@
1
- // Plugins that ship with the harness and are off until a person switches them on. Each is a Pi
2
- // extension, written against the same API anyone's plugin uses. They are loaded as inline
3
- // extensions, so they also run in unattended work, where nothing on disk is loaded, and they
4
- // need no trust: they are the harness's own code.
5
- //
6
- // Both act on one run: a session is opened for each request a person or the application makes,
7
- // so what they count starts at zero each time.
8
- import type { PluginDescriptor, PluginSetting } from './types'
9
-
10
- /** The part of Pi's extension API these two use. */
11
- interface Api {
12
- on(event: 'tool_result', handler: (event: ToolResult) => unknown): void
13
- on(event: 'tool_call', handler: (event: { toolName: string }) => unknown): void
14
- on(event: 'turn_start', handler: () => unknown): void
15
- on(event: 'message_end', handler: (event: { message: { role: string; usage?: { input?: number; output?: number; cacheRead?: number; cacheWrite?: number } } }) => unknown): void
16
- }
17
-
18
- interface ToolResult {
19
- toolName: string
20
- content: ({ type: 'text'; text: string } | { type: string; [key: string]: unknown })[]
21
- }
22
-
23
- export const OUTPUT_TRIM = 'output-trim'
24
- export const BUDGET_GUARD = 'budget-guard'
25
-
26
- const OUTPUT_TRIM_SETTINGS: PluginSetting[] = [
27
- { key: 'maxChars', label: 'Longest tool result', description: 'A tool result longer than this many characters is cut to its beginning and its end.', default: 20_000, min: 2_000, max: 400_000 }
28
- ]
29
-
30
- const BUDGET_GUARD_SETTINGS: PluginSetting[] = [
31
- { key: 'maxRequests', label: 'Model requests in one reply', description: 'After this many model requests in one reply or one run of work, tool calls are refused so the agent wraps up.', default: 60, min: 5, max: 2_000 },
32
- { key: 'maxTokens', label: 'Tokens in one reply', description: 'The same, counted in tokens of every kind (cached ones included). 0 means no token limit.', default: 0, min: 0, max: 500_000_000 }
33
- ]
34
-
35
- export const FIRST_PARTY_PLUGINS: readonly PluginDescriptor[] = [
36
- {
37
- id: OUTPUT_TRIM,
38
- title: 'Output trim',
39
- description: 'Cuts a very long tool result to its beginning and its end before the model reads it, and says how much was cut.',
40
- switchable: { on: false },
41
- settings: OUTPUT_TRIM_SETTINGS,
42
- code: [{ name: 'Trim long tool results', description: 'Runs after every tool call, in conversations, task threads and unattended work. Not in terminal sessions yet.' }]
43
- },
44
- {
45
- id: BUDGET_GUARD,
46
- title: 'Budget guard',
47
- description: 'Stops a reply that keeps going: past a limit of model requests or tokens, further tool calls are refused and the agent is told to wrap up.',
48
- switchable: { on: false },
49
- settings: BUDGET_GUARD_SETTINGS,
50
- code: [{ name: 'Refuse tool calls past the limit', description: 'Counts model requests and tokens in one reply, in conversations, task threads and unattended work. Not in terminal sessions yet.' }]
51
- }
52
- ]
53
-
54
- /** A setting's value: what a person saved when it is a number inside the setting's range, else its default. */
55
- export function settingValue(setting: PluginSetting, saved: Record<string, unknown> | undefined): number {
56
- const value = saved?.[setting.key]
57
- return typeof value === 'number' && Number.isFinite(value) && value >= setting.min && value <= setting.max ? Math.floor(value) : setting.default
58
- }
59
-
60
- const values = (settings: readonly PluginSetting[], saved: Record<string, unknown> | undefined): Record<string, number> => Object.fromEntries(settings.map((setting) => [setting.key, settingValue(setting, saved)]))
61
-
62
- /** The text of a long result, cut to its first 70% and its last 30% of `maxChars`, with a line between that says what is missing. */
63
- export function trimText(text: string, maxChars: number): string {
64
- if (text.length <= maxChars) return text
65
- const head = Math.floor(maxChars * 0.7)
66
- const tail = maxChars - head
67
- const cut = text.length - maxChars
68
- return `${text.slice(0, head)}\n\n[… ${cut} of ${text.length} characters were cut here by the Output trim plugin. Ask for a narrower result (a line range, a filter, a smaller file) to see them. …]\n\n${text.slice(text.length - tail)}`
69
- }
70
-
71
- export function outputTrim(saved: Record<string, unknown> | undefined): (pi: Api) => void {
72
- const { maxChars } = values(OUTPUT_TRIM_SETTINGS, saved)
73
- return (pi) => {
74
- pi.on('tool_result', (event) => {
75
- const texts = event.content.filter((part): part is { type: 'text'; text: string } => part.type === 'text' && typeof (part as { text?: unknown }).text === 'string')
76
- const total = texts.reduce((sum, part) => sum + part.text.length, 0)
77
- if (total <= maxChars) return undefined
78
- // Several text parts are read as one result; pictures and anything else pass through untouched.
79
- const trimmed = trimText(texts.map((part) => part.text).join('\n'), maxChars)
80
- return { content: [{ type: 'text', text: trimmed }, ...event.content.filter((part) => part.type !== 'text')] }
81
- })
82
- }
83
- }
84
-
85
- export function budgetGuard(saved: Record<string, unknown> | undefined): (pi: Api) => void {
86
- const { maxRequests, maxTokens } = values(BUDGET_GUARD_SETTINGS, saved)
87
- return (pi) => {
88
- let requests = 0
89
- let tokens = 0
90
- let refusedInRequest = 0
91
- pi.on('turn_start', () => {
92
- requests += 1
93
- })
94
- pi.on('message_end', (event) => {
95
- if (event.message.role !== 'assistant' || !event.message.usage) return
96
- const usage = event.message.usage
97
- tokens += (usage.input ?? 0) + (usage.output ?? 0) + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0)
98
- })
99
- // Pi's own event before a request can change what is sent and cannot refuse it, so the limit
100
- // is held where the loop can be stopped: at the tool calls that keep it going.
101
- pi.on('tool_call', () => {
102
- const over = requests >= maxRequests ? `${maxRequests} model requests` : maxTokens > 0 && tokens >= maxTokens ? `${maxTokens} tokens` : null
103
- if (!over) return undefined
104
- // The first refusal leaves the agent one request to say what is done. If it calls tools
105
- // again instead, the batch ends the reply.
106
- const again = refusedInRequest !== 0 && refusedInRequest !== requests
107
- refusedInRequest ||= requests
108
- return {
109
- block: true,
110
- terminate: again,
111
- reason: `This reply reached its limit of ${over} (the Budget guard plugin). Call no more tools. Tell the user what is done and what is left; they can raise the limit on the Plugins page.`
112
- }
113
- })
114
- }
115
- }
116
-
117
- export interface InlineExtension {
118
- name: string
119
- /** A Pi extension factory. Typed loosely so this module needs nothing of Pi's to be loaded. */
120
- factory: unknown
121
- }
122
-
123
- /** The first-party extensions switched on for a caller, each with the settings a person saved. */
124
- export function firstPartyExtensions(on: (pluginId: string) => boolean, settings: Record<string, Record<string, unknown>>): InlineExtension[] {
125
- const extensions: InlineExtension[] = []
126
- if (on(OUTPUT_TRIM)) extensions.push({ name: OUTPUT_TRIM, factory: outputTrim(settings[OUTPUT_TRIM]) })
127
- if (on(BUDGET_GUARD)) extensions.push({ name: BUDGET_GUARD, factory: budgetGuard(settings[BUDGET_GUARD]) })
128
- return extensions
129
- }