@xingular/harness 0.1.6 → 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 (593) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE.md +60 -0
  4. package/README.md +265 -71
  5. package/THIRD_PARTY_LICENSES.md +942 -0
  6. package/dist/cli.js +14842 -9445
  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 +1170 -62
  373. package/docs/README.md +34 -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-0001-owned-terminal.md +87 -0
  403. package/docs/decisions/ADR-0002-native-subagent-lifecycle.md +49 -0
  404. package/docs/decisions/ADR-0003-shared-asynchronous-subagents.md +70 -0
  405. package/docs/decisions/ADR-0004-fullscreen-terminal-layout.md +50 -0
  406. package/docs/decisions/ADR-0005-app-provided-oauth-clients.md +54 -0
  407. package/docs/decisions/ADR-0006-opentui-only-terminal.md +47 -0
  408. package/docs/decisions/ADR-0007-reporting-section.md +33 -0
  409. package/docs/decisions/ADR-0008-flexible-reporting-guidance.md +38 -0
  410. package/docs/decisions/ADR-0009-xharness-command.md +38 -0
  411. package/docs/decisions/ADR-0010-lessons-in-the-wiki.md +31 -0
  412. package/docs/decisions/ADR-0011-foundation-and-variants.md +28 -0
  413. package/docs/decisions/ADR-0012-native-session-names.md +34 -0
  414. package/docs/decisions/ADR-0013-doctor.md +27 -0
  415. package/docs/decisions/ADR-0014-public-release-baseline.md +21 -0
  416. package/docs/doctor.md +74 -0
  417. package/docs/experiments/EXP-0001-terminal-input-latency/plan.md +81 -0
  418. package/docs/experiments/EXP-0001-terminal-input-latency/results.md +46 -0
  419. package/docs/experiments/EXP-0002-opentui-typing-latency/plan.md +70 -0
  420. package/docs/experiments/EXP-0002-opentui-typing-latency/results.md +92 -0
  421. package/docs/features/README.md +20 -0
  422. package/docs/features/automations/README.md +41 -0
  423. package/docs/features/cores/README.md +39 -0
  424. package/docs/features/google-harness2/README.md +52 -0
  425. package/docs/features/integrations/README.md +40 -0
  426. package/docs/features/interfaces/README.md +29 -0
  427. package/docs/features/memory/README.md +28 -0
  428. package/docs/releases/1.0.0.md +40 -0
  429. package/docs/releases/README.md +36 -0
  430. package/docs/study/00-index.md +78 -0
  431. package/docs/study/arms/00-matrix.md +20 -0
  432. package/docs/study/arms/01-system-prompt.md +211 -0
  433. package/docs/study/arms/02-tools.md +79 -0
  434. package/docs/study/arms/03-subagents.md +90 -0
  435. package/docs/study/arms/04-skills.md +75 -0
  436. package/docs/study/arms/05-context-management.md +90 -0
  437. package/docs/study/arms/06-memory.md +90 -0
  438. package/docs/study/arms/07-governance.md +71 -0
  439. package/docs/study/arms/08-execution.md +69 -0
  440. package/docs/study/arms/09-observability.md +60 -0
  441. package/docs/study/arms/10-refinement.md +110 -0
  442. package/docs/study/btw-comparison.md +37 -0
  443. package/docs/study/feature-gap-review.md +42 -0
  444. package/docs/study/insights.md +123 -0
  445. package/docs/study/roadmap.md +72 -0
  446. package/docs/study/sources/claude-code.md +44 -0
  447. package/docs/study/sources/deepseek-harness.md +102 -0
  448. package/docs/study/sources/harness2.md +50 -0
  449. package/docs/study/sources/kimi-cli.md +42 -0
  450. package/docs/study/sources/omp.md +93 -0
  451. package/docs/study/sources/opencode.md +43 -0
  452. package/docs/study/sources/openhands.md +46 -0
  453. package/docs/study/sources/smolagents.md +40 -0
  454. package/docs/study/sources/swe-agent.md +34 -0
  455. package/docs/system-prompt-design.md +196 -0
  456. package/docs/terminal-tui.md +671 -0
  457. package/package.json +48 -12
  458. package/src/accounts/accounts.test.ts +0 -223
  459. package/src/accounts/accounts.ts +0 -522
  460. package/src/accounts/env.test.ts +0 -95
  461. package/src/accounts/env.ts +0 -338
  462. package/src/accounts/secrets.ts +0 -50
  463. package/src/accounts/store.ts +0 -338
  464. package/src/capabilities/api.ts +0 -75
  465. package/src/capabilities/runSnapshot.ts +0 -59
  466. package/src/capabilities/types.ts +0 -34
  467. package/src/cli.ts +0 -114
  468. package/src/core/bus.ts +0 -34
  469. package/src/core/changes.ts +0 -33
  470. package/src/core/env.ts +0 -27
  471. package/src/core/errors.ts +0 -32
  472. package/src/core/frontmatter.ts +0 -47
  473. package/src/core/fs.test.ts +0 -38
  474. package/src/core/fs.ts +0 -92
  475. package/src/core/index.ts +0 -193
  476. package/src/core/path.test.ts +0 -14
  477. package/src/core/path.ts +0 -79
  478. package/src/core/projects.ts +0 -90
  479. package/src/core/text.ts +0 -39
  480. package/src/harness.ts +0 -299
  481. package/src/index.ts +0 -70
  482. package/src/mcp/connections.ts +0 -614
  483. package/src/mcp/manager.ts +0 -130
  484. package/src/mcp/oauth.ts +0 -195
  485. package/src/mcp/scopeMigration.test.ts +0 -79
  486. package/src/mcp/scopeMigration.ts +0 -204
  487. package/src/mcp/service.test.ts +0 -376
  488. package/src/mcp/service.ts +0 -549
  489. package/src/mcp/types.ts +0 -108
  490. package/src/memory/api.test.ts +0 -82
  491. package/src/memory/api.ts +0 -289
  492. package/src/memory/blocks.test.ts +0 -78
  493. package/src/memory/blocks.ts +0 -406
  494. package/src/memory/caller.ts +0 -57
  495. package/src/memory/dispatch.ts +0 -59
  496. package/src/memory/facts.test.ts +0 -41
  497. package/src/memory/facts.ts +0 -248
  498. package/src/memory/graph.ts +0 -103
  499. package/src/memory/health.test.ts +0 -130
  500. package/src/memory/health.ts +0 -74
  501. package/src/memory/maintainer.ts +0 -272
  502. package/src/memory/maintenance.test.ts +0 -176
  503. package/src/memory/maintenance.ts +0 -330
  504. package/src/memory/migrate.ts +0 -79
  505. package/src/memory/operations.ts +0 -138
  506. package/src/memory/proposals.test.ts +0 -168
  507. package/src/memory/proposals.ts +0 -576
  508. package/src/memory/recall.test.ts +0 -104
  509. package/src/memory/recall.ts +0 -271
  510. package/src/memory/references.ts +0 -77
  511. package/src/memory/runContext.ts +0 -93
  512. package/src/memory/secrets.ts +0 -6
  513. package/src/memory/store.test.ts +0 -66
  514. package/src/memory/store.ts +0 -385
  515. package/src/memory/wiki.test.ts +0 -47
  516. package/src/memory/wiki.ts +0 -422
  517. package/src/memory/wikiIndex.test.ts +0 -88
  518. package/src/memory/wikiIndex.ts +0 -116
  519. package/src/plugins/api.ts +0 -181
  520. package/src/plugins/attachments.test.ts +0 -84
  521. package/src/plugins/attachments.ts +0 -141
  522. package/src/plugins/builtin.ts +0 -82
  523. package/src/plugins/catalogue.test.ts +0 -254
  524. package/src/plugins/catalogue.ts +0 -251
  525. package/src/plugins/cli.ts +0 -184
  526. package/src/plugins/discover.ts +0 -151
  527. package/src/plugins/firstParty.test.ts +0 -143
  528. package/src/plugins/firstParty.ts +0 -129
  529. package/src/plugins/hooks.test.ts +0 -250
  530. package/src/plugins/hooks.ts +0 -239
  531. package/src/plugins/identity.ts +0 -37
  532. package/src/plugins/install.test.ts +0 -107
  533. package/src/plugins/install.ts +0 -106
  534. package/src/plugins/inventory.ts +0 -123
  535. package/src/plugins/manifest.ts +0 -76
  536. package/src/plugins/plugins.test.ts +0 -205
  537. package/src/plugins/plugins.ts +0 -189
  538. package/src/plugins/proposal.ts +0 -121
  539. package/src/plugins/runtime.test.ts +0 -40
  540. package/src/plugins/runtime.ts +0 -82
  541. package/src/plugins/trust.ts +0 -142
  542. package/src/plugins/types.ts +0 -149
  543. package/src/prompt/compile.test.ts +0 -261
  544. package/src/prompt/compile.ts +0 -732
  545. package/src/server/methods.ts +0 -314
  546. package/src/server/remote.ts +0 -77
  547. package/src/server/rpc.ts +0 -116
  548. package/src/server/serve.ts +0 -28
  549. package/src/server/server.test.ts +0 -174
  550. package/src/sessions/approvals.ts +0 -118
  551. package/src/sessions/contracts.ts +0 -310
  552. package/src/sessions/conversation.test.ts +0 -150
  553. package/src/sessions/extensionSession.test.ts +0 -74
  554. package/src/sessions/extensionUi.test.ts +0 -65
  555. package/src/sessions/extensionUi.ts +0 -102
  556. package/src/sessions/history.ts +0 -51
  557. package/src/sessions/lineageJoin.ts +0 -75
  558. package/src/sessions/policy.ts +0 -32
  559. package/src/sessions/pricing.ts +0 -60
  560. package/src/sessions/reasoningSummaries.ts +0 -59
  561. package/src/sessions/registry.ts +0 -294
  562. package/src/sessions/runIndex.ts +0 -99
  563. package/src/sessions/runStore.test.ts +0 -152
  564. package/src/sessions/runStore.ts +0 -332
  565. package/src/sessions/runner.ts +0 -569
  566. package/src/sessions/runtimeResources.test.ts +0 -61
  567. package/src/sessions/sessions.test.ts +0 -455
  568. package/src/sessions/sessions.ts +0 -295
  569. package/src/skills/registry.test.ts +0 -104
  570. package/src/skills/registry.ts +0 -407
  571. package/src/terminal/cli.ts +0 -149
  572. package/src/terminal/client.ts +0 -74
  573. package/src/terminal/controller.ts +0 -227
  574. package/src/terminal/launch.test.ts +0 -34
  575. package/src/terminal/launch.ts +0 -88
  576. package/src/terminal/piExtension.ts +0 -210
  577. package/src/terminal/pluginControl.test.ts +0 -146
  578. package/src/terminal/pluginMenu.ts +0 -112
  579. package/src/terminal/socket.test.ts +0 -71
  580. package/src/terminal/socket.ts +0 -221
  581. package/src/terminal/standalone.test.ts +0 -130
  582. package/src/terminal/standalone.ts +0 -144
  583. package/src/terminal/terminal.test.ts +0 -330
  584. package/src/terminal/terminals.ts +0 -443
  585. package/src/tools/askUser.ts +0 -45
  586. package/src/tools/definitions.ts +0 -187
  587. package/src/tools/memoryTools.ts +0 -22
  588. package/src/tools/pluginPropose.ts +0 -41
  589. package/src/tools/types.ts +0 -39
  590. package/src/web/safeRequest.ts +0 -171
  591. package/src/web/text.ts +0 -53
  592. package/src/web/tools.ts +0 -134
  593. package/src/web/web.test.ts +0 -141
@@ -0,0 +1,767 @@
1
+ // A host owns the PTY and process supervision; Harness owns the terminal application inside it.
2
+ // This service prepares the isolated account, compiled context, trusted resource selection and
3
+ // socket bridge. The default launch runs our frontend with Pi's persistent SDK runtime.
4
+ import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
5
+ import { dirname, join } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { Type } from 'typebox';
8
+ import { freezeRun } from "../capabilities/runSnapshot.js";
9
+ import { SPAWN_SUBAGENT, WAIT_SUBAGENTS } from "../sessions/subagents.js";
10
+ import { subagentTools } from "../tools/spawnSubagent.js";
11
+ import { configDir, launchEnv } from "../accounts/env.js";
12
+ import { readAudit } from "../accounts/store.js";
13
+ import { log } from "../core/bus.js";
14
+ import { HarnessError, invalid } from "../core/errors.js";
15
+ import { newId, now } from "../core/fs.js";
16
+ import { prependSearchPath, searchPath } from "../core/path.js";
17
+ import { dispatch } from "../tools/dispatch.js";
18
+ import { materialise } from "../variant/materialise.js";
19
+ import * as componentLibrary from "../variant/components.js";
20
+ import * as variantStore from "../variant/store.js";
21
+ import { compile, copyToRun, record, render, saveForSession } from "../prompt/compile.js";
22
+ import { isActiveStatus } from "../sessions/contracts.js";
23
+ import { findRun, loadRun, saveRun, updateRun } from "../sessions/runStore.js";
24
+ import { SESSION_NAME } from "../tools/sessionName.js";
25
+ import { contributionId } from "../plugins/identity.js";
26
+ import { APPS_COMMAND, WIKI_COMMAND } from "./cli.js";
27
+ import { SOCKET_ENV, TOKEN_ENV } from "./client.js";
28
+ import { TerminalController } from "./controller.js";
29
+ import { terminalCommandFiles } from "./launch.js";
30
+ import { SessionSocket, socketPath } from "./socket.js";
31
+ import { harnessLogInfo, readHarnessLogChunk } from "./logs.js";
32
+ import { listTerminalTrajectoryRuns, readTerminalTrajectory } from "./trajectory.js";
33
+ /** Identity of the exact resource decision that this terminal was prepared with. */
34
+ function resourceIdentity(loading) {
35
+ if (!loading)
36
+ return 'null';
37
+ return JSON.stringify({
38
+ extensions: loading.extensionPaths, builtins: loading.builtins, skills: loading.skillPaths,
39
+ prompts: loading.promptPaths, themes: loading.themePaths, detached: loading.detached,
40
+ project: loading.projectCode, packages: loading.packageCode
41
+ });
42
+ }
43
+ /** Where the package's own build puts them. An application that bundles the harness passes its own. */
44
+ export function defaultAssets() {
45
+ const here = dirname(fileURLToPath(import.meta.url));
46
+ const dist = [here, join(here, '..', '..'), join(here, '..', '..', 'dist'), join(here, '..', 'dist')].find((folder) => existsSync(join(folder, 'terminal-pi.js'))) ?? here;
47
+ return {
48
+ node: process.execPath,
49
+ cli: join(dist, 'cli.js'),
50
+ piExtension: join(dist, 'terminal-pi.js'),
51
+ openTui: join(dist, 'terminal-opentui.js')
52
+ };
53
+ }
54
+ const READ_ONLY_TOOLS = new Set(['read', 'grep', 'find', 'ls']);
55
+ export class Terminals {
56
+ options;
57
+ sessions = new Map();
58
+ diagnosticOwners = new Map();
59
+ /** The variant each live session runs, for loading its skills. */
60
+ variantDirs = new Map();
61
+ /** The world tools each session was given, so a call can only reach one of them. */
62
+ worldNames = new Map();
63
+ worldReadOnly = new Set();
64
+ socket;
65
+ assets;
66
+ /** A session's `/accounts` switch, taken once its Pi process has ended. */
67
+ relaunches = new Map();
68
+ constructor(options) {
69
+ this.options = options;
70
+ this.assets = options.assets ?? defaultAssets();
71
+ options.runs.listen(event => {
72
+ this.sessions.get(event.sessionId)?.controller.observe(event);
73
+ const record = findRun(options.env.root, event.runId);
74
+ if (record?.relation !== 'subagent')
75
+ return;
76
+ let parentId = record.parentRunId;
77
+ const seen = new Set();
78
+ while (parentId && !seen.has(parentId)) {
79
+ seen.add(parentId);
80
+ const parent = findRun(options.env.root, parentId);
81
+ if (!parent)
82
+ break;
83
+ const owner = this.sessions.get(parent.sessionId);
84
+ if (owner?.controller.activeRun?.runId === parent.id) {
85
+ owner.controller.observe(event, true);
86
+ break;
87
+ }
88
+ parentId = parent.parentRunId;
89
+ }
90
+ });
91
+ this.socket = new SessionSocket(socketPath(options.stateDir), {
92
+ memory: (caller, call, params) => dispatch(options.env, caller, call, params, { variantDir: (caller.sessionId && this.variantDirs.get(caller.sessionId)) || null }),
93
+ apps: (session, call, params) => this.appsCall(session, call, params),
94
+ plugins: (grant, call, params, control) => this.pluginsCall(grant.sessionId, call, params, control),
95
+ accounts: (grant, call, params, control) => this.accountsCall(grant.sessionId, call, params, control)
96
+ });
97
+ }
98
+ /** The commands a terminal session finds on PATH. Rewritten only when they change. */
99
+ ensureCommands() {
100
+ const bin = join(this.options.stateDir, 'run', 'bin');
101
+ mkdirSync(bin, { recursive: true });
102
+ for (const name of ['agentx-wiki', 'agentx-mcp', 'agentx-wiki.cmd', 'agentx-mcp.cmd']) {
103
+ const path = join(bin, name);
104
+ try {
105
+ if (lstatSync(path).isFile())
106
+ unlinkSync(path);
107
+ }
108
+ catch {
109
+ // These names belonged to the previous generated wrappers.
110
+ }
111
+ }
112
+ for (const [name, mode] of [[WIKI_COMMAND, 'wiki'], [APPS_COMMAND, 'mcp']]) {
113
+ for (const file of terminalCommandFiles(process.platform, this.assets.node, this.assets.cli, name, mode)) {
114
+ const path = join(bin, file.name);
115
+ let current = null;
116
+ try {
117
+ current = readFileSync(path, 'utf8');
118
+ }
119
+ catch {
120
+ current = null;
121
+ }
122
+ if (current !== file.content) {
123
+ writeFileSync(path, file.content);
124
+ chmodSync(path, 0o755);
125
+ }
126
+ }
127
+ }
128
+ return bin;
129
+ }
130
+ /** The broker's tools, then the world tools whose plugins are on for this session. */
131
+ async tools(session, listing) {
132
+ const brokered = this.options.apps.brokerTools(session);
133
+ const live = this.sessions.get(session.sessionId);
134
+ const { world } = this.options;
135
+ if (!world || !live)
136
+ return brokered;
137
+ const context = { run: live.controller.activeRun, caller: live.caller, attended: true, subagent: false, sessionKey: session.sessionId, cwd: live.input.cwd };
138
+ if (!listing)
139
+ return [...brokered, ...world.tools(context, (_plugin, tool) => this.worldNames.get(session.sessionId)?.has(tool) ?? false)];
140
+ const on = await this.worldOn(live);
141
+ const tools = world.tools(context, (plugin, tool) => on(plugin, tool));
142
+ this.worldNames.set(session.sessionId, new Set(tools.map((tool) => tool.name)));
143
+ for (const tool of tools)
144
+ if (tool.readOnly)
145
+ this.worldReadOnly.add(tool.name);
146
+ return [...brokered, ...tools];
147
+ }
148
+ /** The world tools a new session will have, named in its instructions. */
149
+ async worldToolNames(input, account) {
150
+ const { world } = this.options;
151
+ if (!world || input.connections === false)
152
+ return [];
153
+ const list = this.options.pluginControl?.list;
154
+ const plugins = list ? await list({ cwd: input.cwd, accountId: account.id, projectId: input.projectId ?? null }) : null;
155
+ const on = plugins ? new Set(plugins.flatMap((plugin) => plugin.contributions.filter((item) => item.state === 'on').map((item) => item.id))) : null;
156
+ return world.plugins.flatMap((plugin) => (plugin.tools ?? []).map((tool) => tool.name).filter((name) => !on || on.has(contributionId(plugin.id, 'tool', name))));
157
+ }
158
+ /** Whether a world tool's plugin is switched on for a session's folder, account and project. */
159
+ async worldOn(live) {
160
+ const list = this.options.pluginControl?.list;
161
+ if (!list)
162
+ return () => true;
163
+ const plugins = await list({ cwd: live.input.cwd, accountId: live.account.id, projectId: live.input.projectId ?? null });
164
+ const on = new Set(plugins.flatMap((plugin) => plugin.contributions.filter((item) => item.state === 'on').map((item) => item.id)));
165
+ return (plugin, tool) => on.has(contributionId(plugin, 'tool', tool));
166
+ }
167
+ async appsCall(session, call, params) {
168
+ const { apps } = this.options;
169
+ switch (call) {
170
+ case 'mcp.list':
171
+ return apps.list(session);
172
+ case 'mcp.call':
173
+ return apps.invoke(session, String(params.name ?? ''), (params.args && typeof params.args === 'object' ? params.args : {}));
174
+ case 'mcp.revision':
175
+ return apps.revision(session);
176
+ case 'tools.list': {
177
+ const { Type } = await import('typebox');
178
+ return { tools: (await this.tools(session, true)).map((tool) => ({ name: tool.name, label: tool.label, description: tool.description, parameters: tool.parameters(Type) })) };
179
+ }
180
+ case 'tools.call': {
181
+ const tool = (await this.tools(session, false)).find((candidate) => candidate.name === params.name);
182
+ if (!tool)
183
+ throw new HarnessError('unknown_method', `Unknown tool ${String(params.name)}.`);
184
+ const result = await tool.execute((params.input && typeof params.input === 'object' ? params.input : {}));
185
+ if (typeof result === 'string')
186
+ return { text: result };
187
+ const images = [...(result.image ? [result.image] : []), ...(result.images ?? [])];
188
+ return { text: result.text, ...(images.length ? { images } : {}), ...(result.details ? { details: result.details } : {}) };
189
+ }
190
+ default:
191
+ throw new HarnessError('unknown_method', `Unknown call ${call}.`);
192
+ }
193
+ }
194
+ /**
195
+ * `/plugins` in a terminal. Listing is open to the session. A change is taken only from the
196
+ * session's own extension, which asks the person in the terminal first: the model can reach
197
+ * this socket from a shell command, and must not be able to trust code or switch it on.
198
+ */
199
+ async pluginsCall(sessionId, call, params, control) {
200
+ const api = this.options.pluginControl;
201
+ const live = this.sessions.get(sessionId);
202
+ if (!api || !live)
203
+ throw new HarnessError('unknown_method', `Unknown call ${call}.`);
204
+ const projectId = live.input.projectId ?? null;
205
+ const scope = { cwd: live.input.cwd, accountId: live.account.id, projectId };
206
+ if (call === 'hub.list' && this.options.hub)
207
+ return this.options.hub.list(params.refresh === true);
208
+ if (call === 'hub.install' && this.options.hub) {
209
+ if (!control)
210
+ throw new HarnessError('unauthorized', 'Only a person at this terminal can install items. Type /hub.');
211
+ if (typeof params.id !== 'string' || typeof params.version !== 'string')
212
+ throw invalid('An item id and version are required.');
213
+ return this.options.hub.install(params.id, params.version);
214
+ }
215
+ if (call === 'plugins.list')
216
+ return api.list(scope);
217
+ if (call === 'plugins.session')
218
+ return { projectId };
219
+ if (!control)
220
+ throw new HarnessError('unauthorized', 'Only a person at this terminal can change plugins. Type /plugins.');
221
+ const text = (name) => {
222
+ if (typeof params[name] !== 'string' || !params[name])
223
+ throw invalid(`${name} is required.`);
224
+ return params[name];
225
+ };
226
+ switch (call) {
227
+ case 'plugins.attach':
228
+ case 'plugins.detach':
229
+ return api.attach({ ...scope, id: text('id'), scope: params.everywhere === true || !projectId ? {} : { projectId } }, call === 'plugins.attach');
230
+ case 'plugins.trust':
231
+ return api.trust({ ...scope, hash: text('hash') });
232
+ case 'plugins.untrust':
233
+ return api.untrust({ ...scope, hash: text('hash') });
234
+ case 'plugins.configure': {
235
+ const settings = params.settings && typeof params.settings === 'object' && !Array.isArray(params.settings) ? params.settings : {};
236
+ return api.configure({ ...scope, pluginId: text('pluginId'), settings });
237
+ }
238
+ default:
239
+ throw new HarnessError('unknown_method', `Unknown call ${call}.`);
240
+ }
241
+ }
242
+ /**
243
+ * `/accounts` in a terminal. Every call needs the session's control key: adding, signing in,
244
+ * or removing an account is a person's choice, never the model's shell command.
245
+ */
246
+ async accountsCall(sessionId, call, params, control) {
247
+ const { accounts } = this.options;
248
+ const live = this.sessions.get(sessionId);
249
+ if (!live)
250
+ throw new HarnessError('unknown_method', `Unknown call ${call}.`);
251
+ if (!control)
252
+ throw new HarnessError('unauthorized', 'Only a person at this terminal can manage accounts. Type /accounts.');
253
+ const text = (name) => {
254
+ if (typeof params[name] !== 'string' || !params[name])
255
+ throw invalid(`${name} is required.`);
256
+ return params[name];
257
+ };
258
+ const optional = (name) => (typeof params[name] === 'string' ? params[name] : null);
259
+ switch (call) {
260
+ case 'accounts.deviceId':
261
+ return accounts.installationDeviceId();
262
+ case 'accounts.list':
263
+ return accounts.overview();
264
+ case 'accounts.session':
265
+ return { accountId: live.account.id, label: live.account.label };
266
+ case 'accounts.create':
267
+ return accounts.create(text('label'), optional('authKind') ?? 'subscription');
268
+ case 'accounts.rename':
269
+ return accounts.rename(text('accountId'), text('label'));
270
+ case 'accounts.setDefault':
271
+ return accounts.setDefault(text('accountId'));
272
+ case 'accounts.remove': {
273
+ const accountId = text('accountId');
274
+ // Every terminal in this process counts, this session's own included. Another process's terminals are not visible here.
275
+ if (accounts.terminalCount(accountId) > 0)
276
+ throw new HarnessError('account_in_use', 'A running terminal is using this account, this one included.', 'Switch this session to another account first, then remove it.');
277
+ return accounts.remove(accountId);
278
+ }
279
+ case 'accounts.refresh':
280
+ return accounts.refresh(optional('accountId'));
281
+ case 'accounts.providers':
282
+ return accounts.providers();
283
+ // A terminal reads and writes only its own account's choices, never the id a caller names.
284
+ case 'accounts.choices.get':
285
+ return accounts.choices(live.account.id);
286
+ case 'accounts.choices.set':
287
+ return accounts.choose(live.account.id, text('mode'), {
288
+ ...(typeof params.model === 'string' ? { model: params.model } : {}),
289
+ ...(typeof params.effort === 'string' ? { effort: params.effort } : {})
290
+ });
291
+ case 'accounts.loginStart':
292
+ return accounts.loginStart(text('accountId'), text('provider'), optional('method'));
293
+ case 'accounts.loginStatus':
294
+ return accounts.loginStatus(text('accountId'));
295
+ case 'accounts.loginRespond':
296
+ accounts.loginRespond(text('loginId'), text('promptId'), optional('value'));
297
+ return {};
298
+ case 'accounts.loginCancel':
299
+ accounts.loginCancel(text('accountId'), optional('loginId'));
300
+ return {};
301
+ case 'accounts.setApiKey':
302
+ return accounts.setApiKey(text('accountId'), text('key'), text('provider'));
303
+ case 'accounts.removeProvider':
304
+ return accounts.removeProvider(text('accountId'), text('provider'));
305
+ case 'accounts.signOut':
306
+ return accounts.signOut(text('accountId'));
307
+ case 'accounts.savePool': {
308
+ const pool = params.pool;
309
+ if (!pool || typeof pool.label !== 'string' || !Array.isArray(pool.accountIds) || !pool.accountIds.every((id) => typeof id === 'string'))
310
+ throw invalid('A pool needs a label and its account ids.');
311
+ return accounts.savePool({ id: typeof pool.id === 'string' ? pool.id : undefined, label: pool.label, accountIds: pool.accountIds });
312
+ }
313
+ case 'accounts.removePool':
314
+ return accounts.removePool(text('poolId'));
315
+ case 'accounts.audit':
316
+ return readAudit(accounts.dir, optional('accountId'), typeof params.limit === 'number' ? params.limit : 100);
317
+ case 'accounts.relaunch': {
318
+ // Checked here so a bad choice is refused before the session is asked to end.
319
+ const target = accounts.resolve(text('accountId'));
320
+ this.relaunches.set(sessionId, { accountId: target.id, nativeSessionId: live.controller.nativeSessionId });
321
+ return { accountId: target.id, label: target.label };
322
+ }
323
+ default:
324
+ throw new HarnessError('unknown_method', `Unknown call ${call}.`);
325
+ }
326
+ }
327
+ /** The account a session asked to switch to. Taken once, after its Pi process has ended and before `dispose`. */
328
+ takeRelaunch(sessionId) {
329
+ const request = this.relaunches.get(sessionId) ?? null;
330
+ this.relaunches.delete(sessionId);
331
+ return request;
332
+ }
333
+ live(sessionId) {
334
+ const live = this.sessions.get(sessionId);
335
+ if (!live)
336
+ throw new HarnessError('terminal_not_ready', 'The terminal controller is unavailable. Reopen this session.');
337
+ return live;
338
+ }
339
+ historyDir(input) {
340
+ return join(input.sessionFolder, 'pi');
341
+ }
342
+ runRecord(live, runId, text) {
343
+ const { input } = live;
344
+ return {
345
+ resumedFromRunId: null,
346
+ parentRunId: null,
347
+ relation: null,
348
+ terminalReason: null,
349
+ agentId: null,
350
+ trigger: null,
351
+ id: runId,
352
+ projectId: input.projectId ?? null,
353
+ mode: input.run?.mode ?? 'code',
354
+ harness: 'pi',
355
+ sessionType: 'terminal',
356
+ sessionId: input.sessionId,
357
+ nativeSessionId: live.controller.nativeSessionId,
358
+ workingDirectory: input.cwd,
359
+ worktreeId: input.run?.worktreeId ?? null,
360
+ goal: text,
361
+ attachmentCount: 0,
362
+ startedAt: now(),
363
+ endedAt: null,
364
+ status: 'queued',
365
+ exitCode: null,
366
+ handoffId: null,
367
+ accountId: live.account.id
368
+ };
369
+ }
370
+ /** The run a native turn reports into: the one reserved by `send`, or a new one for what the person typed. */
371
+ begin(live, reservedRunId, text) {
372
+ const { root } = this.options.env;
373
+ const { sessionId } = live.input;
374
+ if (this.options.runs.activeForSession(sessionId))
375
+ throw new HarnessError('session_busy', 'A native turn already owns this session.');
376
+ let runId = reservedRunId;
377
+ if (runId) {
378
+ const reserved = loadRun(root, runId);
379
+ if (reserved.sessionId !== sessionId || reserved.status !== 'queued')
380
+ throw invalid('Delivery does not own this queued run.');
381
+ }
382
+ else {
383
+ runId = newId();
384
+ saveRun(root, this.runRecord(live, runId, text || null));
385
+ copyToRun(root, live.input.sessionFolder, runId);
386
+ }
387
+ return this.options.runs.start(sessionId, runId);
388
+ }
389
+ async decide(live, run, tool, args) {
390
+ const record = loadRun(this.options.env.root, run.runId);
391
+ if (!isActiveStatus(record.status))
392
+ throw invalid(`The run is no longer active (its status is ${record.status}).`);
393
+ const call = { run: record, caller: { ...live.caller, runId: run.runId }, tool, args };
394
+ const { policy } = this.options;
395
+ if (policy.scope) {
396
+ const scope = await policy.scope(call);
397
+ if (!scope.allowed && (scope.automatic || !scope.requiresApproval))
398
+ return { allowed: false, reason: scope.reason ?? 'Outside what this session may do.' };
399
+ try {
400
+ await policy.authorize?.(call);
401
+ }
402
+ catch (error) {
403
+ return { allowed: false, reason: error instanceof Error ? error.message : 'Tool unavailable in the settings.' };
404
+ }
405
+ const requiresApproval = !!scope.requiresApproval || (scope.allowed && !!scope.humanOnly);
406
+ return {
407
+ allowed: scope.allowed,
408
+ requiresApproval,
409
+ reason: scope.reason ?? null,
410
+ rememberable: scope.allowed && !scope.humanOnly,
411
+ ...(scope.humanOnly ? { standing: scope.allowGlobalApproval ? 'switch' : 'none' } : {})
412
+ };
413
+ }
414
+ // With no application policy: reads run, a read-only session refuses the rest, and anything else asks.
415
+ const name = tool.toLowerCase();
416
+ if (name === SESSION_NAME || name === SPAWN_SUBAGENT || name === WAIT_SUBAGENTS || READ_ONLY_TOOLS.has(name) || this.worldReadOnly.has(name) || this.options.apps.toolNames.includes(name))
417
+ return { allowed: true };
418
+ if (live.input.readOnly)
419
+ return { allowed: false, reason: 'This session is read-only.' };
420
+ return { allowed: false, requiresApproval: true, rememberable: true, reason: 'This native action requires approval.' };
421
+ }
422
+ async subagentsCall(live, call, params) {
423
+ const service = this.options.subagents;
424
+ if (!service || live.input.subagents === null) {
425
+ if (call === 'terminal.subagents.tools')
426
+ return { tools: [] };
427
+ throw invalid('Subagents are disabled for this terminal.');
428
+ }
429
+ if (call === 'terminal.subagents.tools')
430
+ return { tools: subagentTools(service, '').map(tool => ({ name: tool.name, label: tool.label, description: tool.description, parameters: tool.parameters(Type) })) };
431
+ const run = live.controller.activeRun;
432
+ if (!run)
433
+ throw new HarnessError('run_not_active', 'This terminal has no active parent run.');
434
+ if (call === 'terminal.subagents.register') {
435
+ if (!Array.isArray(params.tools) || params.tools.some((name) => typeof name !== 'string'))
436
+ throw invalid('Native tool names are required.');
437
+ const readOnly = live.input.readOnly === true;
438
+ const readTools = new Set([...READ_ONLY_TOOLS, SESSION_NAME, SPAWN_SUBAGENT, WAIT_SUBAGENTS, 'ask_user', 'mcp_list_tools', 'mcp_call_tool', 'history_search', 'history_read', 'wiki_search', 'wiki_read', 'wiki_list', 'memory_facts', 'skill_load']);
439
+ const tools = params.tools.filter((name) => !readOnly || readTools.has(name));
440
+ const capabilities = this.options.resolveCapabilities ? await this.options.resolveCapabilities(live.caller, tools, live.input.cwd) : { version: 1, tools, connectors: [], contributions: [] };
441
+ freezeRun(this.options.env.root, run.runId, live.caller, capabilities);
442
+ service.register({
443
+ run: { id: run.runId, sessionId: live.input.sessionId, mode: live.input.run?.mode ?? 'code', projectId: live.input.projectId, worktreeId: live.input.run?.worktreeId },
444
+ cwd: live.input.cwd, sessionDir: this.historyDir(live.input), text: '', caller: live.caller,
445
+ model: typeof params.model === 'string' ? params.model : live.input.model,
446
+ effort: typeof params.effort === 'string' ? params.effort : null,
447
+ launch: live.input.launch, tools: { allow: capabilities.tools, gated: capabilities.tools.filter(name => !readTools.has(name)) },
448
+ connections: live.input.connections === false ? false : { readOnly }, subagents: live.input.subagents,
449
+ capabilities
450
+ }, live.account.id, capabilities);
451
+ service.restore(run.runId, params.history ?? { owners: [], receipts: [] });
452
+ return { runId: run.runId };
453
+ }
454
+ if (call === 'terminal.subagents.verify') {
455
+ await service.verify(run.runId, params.history);
456
+ return {};
457
+ }
458
+ if (call === 'terminal.subagents.delivery')
459
+ return service.delivery(run.runId, params.history, params.settle === true);
460
+ if (call === 'terminal.subagents.call') {
461
+ const tool = subagentTools(service, run.runId).find(tool => tool.name === params.name);
462
+ if (!tool)
463
+ throw invalid('Unknown subagent tool.');
464
+ const reason = await this.decide(live, run, tool.name, params.input ?? {});
465
+ if (!reason.allowed)
466
+ throw new HarnessError('tool_denied', reason.reason ?? 'Subagents are unavailable.');
467
+ const result = await tool.execute(params.input ?? {});
468
+ return typeof result === 'string' ? { text: result } : result;
469
+ }
470
+ throw invalid('Unknown subagent operation.');
471
+ }
472
+ componentsCall(live, call, params) {
473
+ const root = this.options.env.root;
474
+ const choice = live.input.variant;
475
+ const scope = choice && 'scope' in choice ? choice.scope ?? null : null;
476
+ const head = scope ? variantStore.headVariant(root, scope) : null;
477
+ const items = componentLibrary.list(root);
478
+ if (call === 'terminal.components.list') {
479
+ return {
480
+ items,
481
+ addedIds: head ? items.filter(item => componentLibrary.isInVariant(root, head.variant, item.id)).map(item => item.id) : [],
482
+ writable: Boolean(scope && head),
483
+ fingerprint: head?.fingerprint ?? null
484
+ };
485
+ }
486
+ if (call === 'terminal.components.add') {
487
+ if (typeof params.id !== 'string' || !params.id)
488
+ throw invalid('A component id is required.');
489
+ if (!scope || !head)
490
+ throw invalid('This terminal has no writable Harness scope.');
491
+ const item = items.find(candidate => candidate.id === params.id);
492
+ if (!item)
493
+ throw invalid('That component is no longer in the library.');
494
+ if (item.kind === 'prompts')
495
+ throw invalid('Prompts stay in the library and cannot be added to a Variant.');
496
+ if (componentLibrary.isInVariant(root, head.variant, item.id)) {
497
+ return { fingerprint: head.fingerprint, alreadyPresent: true };
498
+ }
499
+ const variant = componentLibrary.addToVariant(root, head.variant, item.id);
500
+ const record = variantStore.commit(root, scope, variant, {
501
+ kind: 'edit', note: 'Added library component ' + item.id, components: [item.id]
502
+ });
503
+ this.options.bus.emit({ type: 'variants.changed', scope });
504
+ return { fingerprint: record.fingerprint, alreadyPresent: false };
505
+ }
506
+ throw new HarnessError('unknown_method', 'Unknown terminal component operation.');
507
+ }
508
+ trajectoryCall(live, call, params) {
509
+ if (call === 'terminal.trajectory.list') {
510
+ return { runs: listTerminalTrajectoryRuns(this.options.env.root, live.input.sessionId, live.controller.nativeSessionId) };
511
+ }
512
+ if (call === 'terminal.trajectory.events') {
513
+ if (typeof params.runId !== 'string' || !params.runId)
514
+ throw invalid('A run id is required.');
515
+ const afterSequence = params.afterSequence;
516
+ if (afterSequence !== undefined && typeof afterSequence !== 'number')
517
+ throw invalid('The event sequence must be a number.');
518
+ return readTerminalTrajectory(this.options.env.root, live.input.sessionId, live.controller.nativeSessionId, params.runId, afterSequence);
519
+ }
520
+ throw new HarnessError('unknown_method', `Unknown call ${call}.`);
521
+ }
522
+ /** The only terminal frontend: the Bun-run OpenTUI application. */
523
+ frontendCommand() {
524
+ return {
525
+ program: process.env.XINGULAR_HARNESS_BUN || 'bun',
526
+ // Solid must resolve to its client build for OpenTUI's effects, wherever it is imported.
527
+ argsPrefix: ['--conditions=browser', this.assets.openTui]
528
+ };
529
+ }
530
+ /**
531
+ * Prepares an OpenTUI terminal session and returns what to spawn. The caller must report the
532
+ * process's end with `dispose`, also when it never starts.
533
+ */
534
+ async prepare(input) {
535
+ input = structuredClone(input);
536
+ const { env, accounts, apps, bus } = this.options;
537
+ const { root } = env;
538
+ if (!input?.sessionId || !input.cwd || !input.sessionFolder)
539
+ throw invalid('A terminal session needs an id, a working folder, and its own folder.');
540
+ await this.dispose(input.sessionId);
541
+ const command = this.frontendCommand();
542
+ // A pinned account is used as it is; it is never exchanged for another one.
543
+ const account = input.accountId ? accounts.find(input.accountId) : accounts.resolve(null, null);
544
+ const launch = launchEnv(accounts.dir, account);
545
+ const loading = (await this.options.plugins?.loading({ cwd: input.cwd, agentDir: configDir(accounts.dir, account), install: true, caller: { projectId: input.projectId ?? null } })) ?? null;
546
+ await this.socket.listen();
547
+ await accounts.terminalBegin(account);
548
+ const sessionId = input.sessionId;
549
+ const caller = { sessionId, projectId: input.projectId ?? null, actor: 'Code', surface: 'cli' };
550
+ let token = null;
551
+ try {
552
+ const broker = input.connections === false ? null : apps.grant({ sessionId, runId: null, caller, readOnly: !!input.readOnly, depth: 0 });
553
+ // The session keeps these instructions; each of its runs records a copy, and a memory
554
+ // block that changes later is sent along as a note.
555
+ const variant = input.variant ? materialise(root, input.variant, { trust: this.options.trust ?? null }) : null;
556
+ if (variant)
557
+ this.variantDirs.set(sessionId, variant.dir);
558
+ const worldTools = await this.worldToolNames(input, account);
559
+ const launchTools = [...new Set([...(input.launch?.tools ?? []), SESSION_NAME, ...worldTools])];
560
+ const pack = compile(env, caller, { ...input.launch, tools: launchTools }, variant);
561
+ record(root, pack);
562
+ mkdirSync(this.historyDir(input), { recursive: true });
563
+ saveForSession(input.sessionFolder, pack);
564
+ const live = {
565
+ input,
566
+ account,
567
+ broker,
568
+ caller,
569
+ token: '',
570
+ controller: new TerminalController({
571
+ sessionId,
572
+ nativeSessionId: input.nativeSessionId ?? null,
573
+ begin: ({ reservedRunId, text }) => this.begin(live, reservedRunId, text),
574
+ policy: (run, tool, args) => this.decide(live, run, tool, args),
575
+ allowToolGlobally: target => { this.options.approvals.allowTool('pi', target); },
576
+ native: (nativeSessionId) => bus.emit({ type: 'terminal.native', sessionId, nativeSessionId }),
577
+ telemetry: (event) => bus.emit({ type: 'terminal.telemetry', sessionId, event }),
578
+ interrupt: runId => this.options.runs.interrupt(runId),
579
+ resolveApproval: (runId, id, decision) => this.options.runs.resolveApproval(runId, id, decision),
580
+ answerQuestion: (runId, id, answer) => this.options.runs.answerQuestion(runId, id, answer),
581
+ answerQuestions: (runId, batchId, answers) => this.options.runs.answerQuestions(runId, batchId, answers)
582
+ })
583
+ };
584
+ token = this.socket.issue({
585
+ sessionId,
586
+ caller: () => ({ ...caller, runId: live.controller.activeRun?.runId ?? null }),
587
+ broker,
588
+ terminal: { accept: (event, data) => live.controller.accept(event, data), disconnected: () => live.controller.disconnected(), control: async (call, params) => {
589
+ if (call.startsWith('terminal.doctor.')) {
590
+ const doctor = this.options.doctor;
591
+ if (!doctor)
592
+ throw invalid('Diagnostics are unavailable in this host.');
593
+ if (call === 'terminal.doctor.start') {
594
+ const model = typeof params.model === 'string' ? params.model : live.input.model ?? null;
595
+ const tools = [...new Set([
596
+ ...(Array.isArray(params.tools) ? params.tools.filter((name) => typeof name === 'string') : []),
597
+ ...await this.worldToolNames(live.input, live.account)
598
+ ])];
599
+ const variantDir = this.variantDirs.get(sessionId);
600
+ const report = doctor.start({
601
+ context: { caller: live.caller, cwd: live.input.cwd, accountId: live.account.id, model, tools,
602
+ variant: variantDir ? { dir: variantDir, scope: live.input.variant?.scope } : null },
603
+ mode: params.mode === 'live' ? 'live' : 'local',
604
+ checks: Array.isArray(params.checks) ? params.checks.filter((id) => typeof id === 'string') : [],
605
+ confirmed: params.confirmed === true
606
+ });
607
+ this.diagnosticOwners.set(report.id, sessionId);
608
+ return report;
609
+ }
610
+ const id = typeof params.id === 'string' ? params.id : '';
611
+ if (this.diagnosticOwners.get(id) !== sessionId)
612
+ throw invalid('This diagnostic job belongs to another terminal.');
613
+ if (call === 'terminal.doctor.read')
614
+ return doctor.read(id);
615
+ if (call === 'terminal.doctor.cancel')
616
+ return doctor.cancel(id);
617
+ throw invalid('Unknown diagnostic operation.');
618
+ }
619
+ if (call.startsWith('terminal.subagents.'))
620
+ return this.subagentsCall(live, call, params);
621
+ if (call === 'terminal.logs.info')
622
+ return harnessLogInfo(this.options.stateDir);
623
+ if (call === 'terminal.logs.read') {
624
+ const { snapshotSize, offset } = params;
625
+ if (typeof snapshotSize !== 'number' || !Number.isSafeInteger(snapshotSize) || snapshotSize < 0 || typeof offset !== 'number' || !Number.isSafeInteger(offset) || offset < 0 || offset >= snapshotSize) {
626
+ throw invalid('A valid diagnostics snapshot size and byte offset are required.');
627
+ }
628
+ const current = harnessLogInfo(this.options.stateDir).size;
629
+ if (current === null || snapshotSize > current)
630
+ throw invalid('The diagnostics snapshot is no longer available.');
631
+ return readHarnessLogChunk(this.options.stateDir, snapshotSize, offset);
632
+ }
633
+ if (call.startsWith('terminal.components.'))
634
+ return this.componentsCall(live, call, params);
635
+ if (call.startsWith('terminal.trajectory.'))
636
+ return this.trajectoryCall(live, call, params);
637
+ if (call === 'terminal.permissions.get') {
638
+ const policy = this.options.approvals.read();
639
+ return { full: this.options.approvals.isAuto('pi'), globalTools: policy.globalTools.pi ?? [] };
640
+ }
641
+ if (call === 'terminal.permissions.setFull') {
642
+ if (typeof params.enabled !== 'boolean')
643
+ throw invalid('A boolean permission state is required.');
644
+ return { full: this.options.approvals.setAuto('pi', params.enabled).auto.pi === true };
645
+ }
646
+ if (call === 'terminal.permissions.setTool') {
647
+ if (typeof params.tool !== 'string' || !params.tool.trim() || typeof params.allowed !== 'boolean')
648
+ throw invalid('A tool name and boolean permission state are required.');
649
+ const policy = params.allowed
650
+ ? this.options.approvals.allowTool('pi', params.tool)
651
+ : this.options.approvals.revokeTool('pi', params.tool);
652
+ return { globalTools: policy.globalTools.pi ?? [] };
653
+ }
654
+ if (call !== 'terminal.resources')
655
+ return live.controller.control(call, params);
656
+ const current = await this.options.plugins?.loading({ cwd: input.cwd, agentDir: configDir(accounts.dir, account), install: false, caller: { projectId: input.projectId ?? null } }) ?? null;
657
+ if (resourceIdentity(current) !== resourceIdentity(loading))
658
+ throw new HarnessError('terminal_resources_changed', 'Trusted resources changed since this terminal was prepared. Review /plugins and reopen the terminal to load the new selection.');
659
+ return {};
660
+ } }
661
+ });
662
+ live.token = token;
663
+ this.sessions.set(sessionId, live);
664
+ const args = [...command.argsPrefix, '--append-system-prompt', render(env, pack)];
665
+ if (input.model)
666
+ args.push('--model', input.model);
667
+ if (loading) {
668
+ // Pi's own discovery is off: the terminal loads what the harness found and nothing else.
669
+ args.push('--no-extensions', '--no-skills', '--no-prompt-templates', '--no-themes');
670
+ for (const path of [...loading.extensionPaths, ...loading.builtins, ...(variant?.extensionPaths ?? [])])
671
+ args.push('--extension', path);
672
+ for (const path of [...(variant?.skillPaths ?? []), ...loading.skillPaths])
673
+ args.push('--skill', path);
674
+ for (const path of loading.promptPaths)
675
+ args.push('--prompt-template', path);
676
+ for (const path of loading.themePaths)
677
+ args.push('--theme', path);
678
+ }
679
+ // Last, so its gate sees a tool call as every other extension left it.
680
+ args.push('--extension', this.assets.piExtension, '--session-dir', this.historyDir(input));
681
+ if (input.nativeSessionId)
682
+ args.push('--session', input.nativeSessionId);
683
+ return {
684
+ program: command.program,
685
+ args,
686
+ env: {
687
+ ...launch,
688
+ XINGULAR_HARNESS_NAME: env.productName,
689
+ ...(process.platform === 'win32' ? { XINGULAR_HARNESS_NODE: this.assets.node, XINGULAR_HARNESS_CLI: this.assets.cli } : {}),
690
+ ...prependSearchPath(this.ensureCommands(), searchPath()),
691
+ [SOCKET_ENV]: this.socket.path,
692
+ [TOKEN_ENV]: token
693
+ },
694
+ accountId: account.id,
695
+ nativeSessionId: input.nativeSessionId ?? null
696
+ };
697
+ }
698
+ catch (error) {
699
+ if (token)
700
+ this.socket.revoke(token);
701
+ this.sessions.delete(sessionId);
702
+ apps.revokeSession(sessionId);
703
+ await accounts.terminalEnd(account).catch(() => undefined);
704
+ throw error;
705
+ }
706
+ }
707
+ /** Hands the terminal an instruction as a new run. Resolves once the native session has taken it. */
708
+ async send(input) {
709
+ const { env } = this.options;
710
+ const { root } = env;
711
+ if (!input.text?.trim())
712
+ throw invalid('Message is empty.');
713
+ const live = this.live(input.sessionId);
714
+ if (this.options.runs.activeForSession(input.sessionId) || live.controller.state().active)
715
+ throw new HarnessError('session_busy', 'This session is still working.');
716
+ const runId = input.runId || newId();
717
+ if (findRun(root, runId))
718
+ throw invalid('This run already exists.');
719
+ saveRun(root, this.runRecord(live, runId, input.text));
720
+ copyToRun(root, live.input.sessionFolder, runId);
721
+ try {
722
+ await live.controller.send(runId, input.text);
723
+ }
724
+ catch (error) {
725
+ updateRun(root, runId, (run) => {
726
+ if (!isActiveStatus(run.status))
727
+ return;
728
+ run.status = 'failed';
729
+ run.endedAt = now();
730
+ });
731
+ throw error;
732
+ }
733
+ return { runId };
734
+ }
735
+ /** What the person typed into the terminal. */
736
+ input(sessionId, data) {
737
+ this.sessions.get(sessionId)?.controller.onInput(data);
738
+ }
739
+ state(sessionId) {
740
+ return this.live(sessionId).controller.state();
741
+ }
742
+ has(sessionId) {
743
+ return this.sessions.has(sessionId);
744
+ }
745
+ /** The terminal process ended (or never started): its turn, tokens, and written credentials go with it. */
746
+ async dispose(sessionId) {
747
+ const live = this.sessions.get(sessionId);
748
+ if (!live)
749
+ return;
750
+ for (const [id, owner] of this.diagnosticOwners)
751
+ if (owner === sessionId) {
752
+ this.options.doctor?.cancel(id);
753
+ this.diagnosticOwners.delete(id);
754
+ }
755
+ this.sessions.delete(sessionId);
756
+ this.worldNames.delete(sessionId);
757
+ void this.options.world?.release(sessionId).catch(() => undefined);
758
+ live.controller.dispose();
759
+ this.socket.revoke(live.token);
760
+ this.options.apps.revokeSession(sessionId);
761
+ await this.options.accounts.terminalEnd(live.account).catch((error) => log('terminal credentials', error instanceof Error ? error.message : String(error)));
762
+ }
763
+ async close() {
764
+ await Promise.allSettled([...this.sessions.keys()].map((sessionId) => this.dispose(sessionId)));
765
+ await this.socket.close();
766
+ }
767
+ }