@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,491 +0,0 @@
1
- import { execFile, spawn } from 'node:child_process'
2
- import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
3
- import { tmpdir } from 'node:os'
4
- import { join } from 'node:path'
5
- import { randomUUID } from 'node:crypto'
6
- import manifest from '../../../package.json'
7
- import { promisify } from 'node:util'
8
- import { CustomEditor, copyToClipboard, initTheme, type AgentSessionEvent, type ExtensionUIContext, type KeybindingsManager, type ReadonlyFooterDataProvider, type Theme } from '@earendil-works/pi-coding-agent'
9
- import { CombinedAutocompleteProvider, ProcessTerminal, TuiMainScreen, Text, matchesKey, setKeybindings, truncateToWidth, visibleWidth, wrapTextWithAnsi, type AutocompleteProvider, type Component, type EditorComponent, type TuiMouseEvent } from '@earendil-works/pi-tui'
10
-
11
- import { controlCall } from '../control'
12
- import { createTerminalCommands } from './commands'
13
- import { HarnessEditor } from './editor'
14
- import { HARNESS_KEYBINDINGS, terminalKeybindings } from './keybindings'
15
- import { loadPiAdapter, setPiTheme } from './piAdapter'
16
- import { createTerminalRuntime, type TerminalDecision, type TerminalRuntime } from './runtime'
17
- import { renderStatusBand, singleLine } from './status'
18
- import { createHarnessLightTheme, createHarnessTheme } from './theme'
19
- import { TerminalTranscript } from './transcript'
20
- import { createTerminalUi, type TerminalUi } from './ui'
21
- import { HarnessWelcome } from './welcome'
22
- import { answerTerminalQuestion } from './questions'
23
- import type { QuestionAnswer } from '../../sessions/registry'
24
-
25
- const exec = promisify(execFile)
26
- type Mounted = Component & { dispose?(): void }
27
- type EditorFactory = Parameters<ExtensionUIContext['setEditorComponent']>[0]
28
- type HeaderFactory = Parameters<ExtensionUIContext['setHeader']>[0]
29
- type FooterFactory = Parameters<ExtensionUIContext['setFooter']>[0]
30
-
31
- /** Sole owner of terminal lifecycle, layout, input dispatch, focus and screen transitions. */
32
- export class TerminalApplication implements Component {
33
- readonly terminal = new ProcessTerminal()
34
- readonly tui = new TuiMainScreen(this.terminal)
35
- private runtime?: TerminalRuntime
36
- private ui!: TerminalUi
37
- private keybindings!: KeybindingsManager
38
- private editor!: EditorComponent
39
- private transcript!: TerminalTranscript
40
- private commands!: ReturnType<typeof createTerminalCommands>
41
- private header?: Mounted
42
- private footer?: Mounted
43
- private headerFactory?: HeaderFactory
44
- private footerFactory?: FooterFactory
45
- private editorFactory?: EditorFactory
46
- private widgets = new Map<string, { component: Mounted; placement: string }>()
47
- private statuses = new Map<string, string>()
48
- private branch: string | null = null
49
- private branchListeners = new Set<() => void>()
50
- private baseAutocomplete?: AutocompleteProvider
51
- private notices: { message: string; type: string }[] = []
52
- private startedAt: number | null = null
53
- private phase?: string
54
- private scrollOffset = 0
55
- private totalLines = 0
56
- private toolsExpanded = false
57
- private thinkingChanges = 0
58
- private stopped = false
59
- private initialized = false
60
- private closing?: Promise<void>
61
- private failure?: Error
62
- private finish!: () => void
63
- private done = new Promise<void>(resolve => { this.finish = resolve })
64
- private cleanup: (() => void)[] = []
65
- private finalCleanup: (() => void)[] = []
66
- private draftSync = Promise.resolve()
67
- private lastInput = ''
68
- private commandBusy = false
69
- private pendingPolling = false
70
- private pendingBusy = false
71
- private pendingAbort?: AbortController
72
- private pendingId?: string
73
- private dismissedDecisions = new Set<string>()
74
- private mouseRegions: { component: Component; start: number; height: number }[] = []
75
-
76
- constructor(private readonly args: string[]) {}
77
- private get session() { if (!this.runtime) throw new Error('The terminal runtime is not ready.'); return this.runtime.session }
78
- private get theme(): Theme { return this.ui.theme }
79
- private change = (): void => { if (!this.stopped) this.tui.requestRender() }
80
- private notify = (message: string, type = 'info'): void => {
81
- if (message.startsWith('No models available.')) message = 'No model is configured. Use /accounts or /login to sign in.'
82
- this.notices.push({ message, type })
83
- this.notices = this.notices.slice(-8)
84
- this.change()
85
- }
86
- private attempt = (action: () => Promise<unknown> | unknown): void => { void Promise.resolve().then(action).catch(error => this.notify(error instanceof Error ? error.message : String(error), 'error')).finally(this.change) }
87
-
88
- async run(): Promise<number> {
89
- if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error('The Harness frontend requires an interactive terminal.')
90
- if (!process.env.PI_CODING_AGENT_DIR || !process.env.XINGULAR_HARNESS_SOCKET) throw new Error('Prepare a Harness terminal session before starting its frontend.')
91
- const onSignal = () => { void this.shutdown() }
92
- const onFatal = (error: unknown) => {
93
- this.failure ??= error instanceof Error ? error : new Error(String(error))
94
- void this.shutdown()
95
- }
96
- const onContinue = () => { if (!this.stopped) { this.tui.start(); this.change() } }
97
- try {
98
- const adapter = await loadPiAdapter()
99
- initTheme('dark', false)
100
- const theme = createHarnessTheme({ getColorMode: () => process.env.COLORTERM === 'truecolor' || process.env.COLORTERM === '24bit' ? 'truecolor' : '256color' } as Theme)
101
- setPiTheme(theme)
102
- this.keybindings = terminalKeybindings(process.env.PI_CODING_AGENT_DIR)
103
- setKeybindings(this.keybindings)
104
- this.transcript = new TerminalTranscript(this.tui, () => this.theme, adapter.withBuiltInRenderers)
105
- this.ui = this.makeUi(theme)
106
- this.mountEditor()
107
- this.tui.addChild(this)
108
- this.tui.setFocus(this.editor)
109
- this.cleanup.push(this.tui.addInputListener(data => this.dispatchInput(data)))
110
- for (const signal of ['SIGTERM', 'SIGINT', 'SIGHUP'] as const) { process.on(signal, onSignal); this.cleanup.push(() => { process.off(signal, onSignal) }) }
111
- process.on('uncaughtException', onFatal)
112
- process.on('unhandledRejection', onFatal)
113
- this.finalCleanup.push(() => { process.off('uncaughtException', onFatal); process.off('unhandledRejection', onFatal) })
114
- process.on('SIGCONT', onContinue)
115
- this.cleanup.push(() => { process.off('SIGCONT', onContinue) })
116
- this.tui.setShowHardwareCursor(true)
117
- this.tui.setClearOnShrink(true)
118
- this.tui.start()
119
- this.phase = 'Starting session…'
120
- this.runtime = await createTerminalRuntime(this.args, { ui: this.ui, shutdown: () => { void this.shutdown() }, notify: this.notify, changed: this.change, resources: services => {
121
- this.ui.setThemes(services.resourceLoader.getThemes().themes)
122
- const name = services.settingsManager.getTheme()
123
- const selected = name ? this.ui.getTheme(name) : undefined
124
- if (selected && this.ui.theme.name !== selected.name) this.ui.setTheme(selected)
125
- } })
126
- if (this.stopped) { await this.runtime.dispose(); if (this.failure) throw this.failure; return 0 }
127
- this.commands = createTerminalCommands({
128
- session: () => this.session, runtime: () => this.runtime!, ui: this.ui,
129
- cwd: () => this.runtime!.cwd, sessionDirectory: () => this.runtime!.sessionDirectory,
130
- refresh: () => this.refresh(), shutdown: () => { void this.shutdown() }, setEditorText: text => { this.editor.setText(text); this.syncDraft(text) },
131
- copyText: copyToClipboard, openExternal: async url => {
132
- const program = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'rundll32' : 'xdg-open'
133
- await exec(program, process.platform === 'win32' ? ['url.dll,FileProtocolHandler', url] : [url])
134
- },
135
- suspend: () => this.suspend(), hotkeys: () => this.hotkeys(),
136
- changelog: async () => {
137
- for (const path of ['../CHANGELOG.md', '../../../CHANGELOG.md']) {
138
- try { return `${manifest.name} v${manifest.version}\n\n${(await readFile(new URL(path, import.meta.url), 'utf8')).split('\n').slice(0, 24).join('\n')}` } catch { /* Bundled hosts may omit package documentation. */ }
139
- }
140
- return `${manifest.name} v${manifest.version}\nRelease notes were not bundled with this installation.`
141
- },
142
- secretInput: (title, placeholder, options) => this.ui.secretInput(title, placeholder, options)
143
- })
144
- this.cleanup.push(this.runtime.subscribe(event => this.accept(event)))
145
- this.cleanup.push(this.runtime.onBeforeSessionChange(() => this.resetPresentation()))
146
- this.cleanup.push(this.runtime.onSessionChange(() => { this.keybindings.reload(); this.startedAt = null; this.phase = undefined; this.scrollOffset = 0; this.thinkingChanges = 0; this.refresh(true) }))
147
- this.initialized = true
148
- this.phase = undefined
149
- this.refresh(true)
150
- const tick = setInterval(() => { if (this.startedAt !== null) this.change(); void this.decisions() }, 100)
151
- const git = setInterval(() => { void this.refreshBranch() }, 3000)
152
- this.cleanup.push(() => { clearInterval(tick); clearInterval(git) })
153
- await this.refreshBranch()
154
- await this.done
155
- if (this.failure) throw this.failure
156
- return 0
157
- } catch (error) {
158
- if (this.ui && !this.stopped) { this.notify(error instanceof Error ? error.message : String(error), 'error'); this.tui.renderNow(true) }
159
- throw error
160
- } finally { await this.shutdown() }
161
- }
162
-
163
- private makeUi(theme: Theme): TerminalUi {
164
- return createTerminalUi({
165
- tui: this.tui, theme, themes: [createHarnessLightTheme(theme)], keybindings: this.keybindings, changed: this.change, notify: this.notify,
166
- setEditor: factory => this.mountEditor(factory), getEditor: () => this.editor,
167
- getEditorText: () => this.editor?.getText() ?? '', setEditorText: value => { this.editor.setText(value); this.syncDraft(value) },
168
- setStatus: (key, value) => { if (value === undefined) this.statuses.delete(key); else this.statuses.set(key, value) },
169
- setWidget: (key, content, placement) => {
170
- this.release(this.widgets.get(key)?.component)
171
- if (!content) this.widgets.delete(key)
172
- else this.widgets.set(key, { component: typeof content === 'function' ? content(this.tui, this.theme) : new Text(content.join('\n'), 0, 0), placement: placement ?? 'aboveEditor' })
173
- },
174
- setHeader: factory => { this.headerFactory = factory; this.release(this.header); this.header = factory?.(this.tui, this.theme) },
175
- setFooter: factory => { this.footerFactory = factory; this.release(this.footer); this.footer = factory?.(this.tui, this.theme, this.footerData) },
176
- getAutocompleteProvider: () => this.baseAutocomplete!,
177
- setAutocompleteProvider: provider => this.editor.setAutocompleteProvider?.(provider),
178
- themeChanged: next => { setPiTheme(next); this.remountPresentation() },
179
- getToolsExpanded: () => this.toolsExpanded, setToolsExpanded: value => { this.toolsExpanded = value; this.transcript.setExpanded(value) }
180
- })
181
- }
182
- private footerData: ReadonlyFooterDataProvider = {
183
- getGitBranch: () => this.branch, getExtensionStatuses: () => this.statuses,
184
- getAvailableProviderCount: () => new Set(this.runtime?.services.modelRuntime.getAvailableSnapshot().map(model => model.provider) ?? []).size,
185
- onBranchChange: callback => { this.branchListeners.add(callback); return () => { this.branchListeners.delete(callback) } }
186
- }
187
- private async refreshBranch(): Promise<void> {
188
- if (!this.runtime || this.stopped) return
189
- let branch: string | null = null
190
- try { branch = (await exec('git', ['symbolic-ref', '--quiet', '--short', 'HEAD'], { cwd: this.runtime.cwd, timeout: 2000 })).stdout.trim() || null }
191
- catch { /* Non-repositories and detached HEAD have no branch segment. */ }
192
- if (branch !== this.branch) { this.branch = branch; for (const listener of this.branchListeners) listener(); this.change() }
193
- }
194
- private release(component?: Mounted): void {
195
- try { component?.dispose?.() }
196
- catch (error) { this.notify(`Plugin cleanup failed: ${error instanceof Error ? error.message : String(error)}`, 'error') }
197
- }
198
- private mountEditor(factory?: EditorFactory): void {
199
- const draft = this.editor?.getText() ?? ''
200
- this.release(this.editor as Mounted | undefined)
201
- this.editorFactory = factory
202
- const editorTheme = { borderColor: (text: string) => this.theme.fg('border', text), selectList: {
203
- selectedPrefix: (text: string) => this.theme.fg('accent', text), selectedText: (text: string) => this.theme.fg('accent', text), description: (text: string) => this.theme.fg('muted', text), scrollInfo: (text: string) => this.theme.fg('dim', text), noMatch: (text: string) => this.theme.fg('muted', text)
204
- } }
205
- this.editor = factory?.(this.tui, editorTheme, this.keybindings) ?? new HarnessEditor(this.tui, editorTheme, this.keybindings, {
206
- band: (width, above, below) => this.band(width, above, below), theme: () => this.theme,
207
- showHint: () => this.thinkingChanges < 3 && !this.runtime?.snapshot().hasUserMessages && this.startedAt === null,
208
- thinkingKey: this.keybindings.getKeys('app.thinking.cycle').map(key => key === 'shift+tab' ? '⇧⇥' : key).join('/')
209
- })
210
- this.editor.setText(draft)
211
- this.editor.onSubmit = text => this.submit(text)
212
- const onChange = this.editor.onChange
213
- this.editor.onChange = text => { onChange?.(text); this.syncDraft(text); this.scrollOffset = 0; this.change() }
214
- if (factory && this.editor instanceof CustomEditor) {
215
- // A trusted editor may consume keys in its own mode before delegating to CustomEditor.
216
- // Wire host actions into that delegation instead of taking the keys away in a listener.
217
- const editor = this.editor
218
- for (const action of Object.keys(HARNESS_KEYBINDINGS).filter(name => name.startsWith('app.'))) {
219
- const id = action as Parameters<CustomEditor['onAction']>[0]
220
- if (!editor.actionHandlers.has(id)) editor.onAction(id, () => { this.dispatchInput(this.lastInput, true) })
221
- }
222
- editor.onEscape ??= () => { this.dispatchInput(this.lastInput, true) }
223
- editor.onCtrlD ??= () => { void this.shutdown() }
224
- editor.onPasteImage ??= () => this.attempt(() => this.pasteClipboard())
225
- editor.onExtensionShortcut ??= data => this.extensionShortcut(data)
226
- }
227
- if (this.baseAutocomplete) this.editor.setAutocompleteProvider?.(this.ui.wrapAutocomplete(this.baseAutocomplete))
228
- this.tui.setFocus(this.editor)
229
- }
230
- private syncDraft(text: string): void {
231
- this.draftSync = this.draftSync.then(() => controlCall('terminal.draft', { dirty: text.length > 0 })).then(() => {}).catch(error => this.notify(String(error), 'error'))
232
- }
233
- private band(width: number, above = 0, below = 0): string {
234
- const state = this.runtime?.snapshot()
235
- return renderStatusBand({ cwd: state?.cwd ?? process.cwd(), model: state?.model,
236
- thinkingLevel: state?.thinkingLevel ?? 'off', branch: this.branch, account: state?.account?.label ?? 'account unknown',
237
- contextPercent: state?.context.percent ?? null, contextWindow: state?.context.window ?? 0,
238
- costUsd: state?.costUsd ?? 0, subscription: state?.subscription ?? false,
239
- running: this.startedAt !== null, elapsedMs: this.startedAt === null ? 0 : Date.now() - this.startedAt, hiddenAbove: above, hiddenBelow: below
240
- }, width, this.theme)
241
- }
242
- private submit(text: string, followUp = false): void {
243
- if (!text.trim() || !this.initialized) return
244
- if (this.commandBusy) { this.notify('Finish or cancel the active command first.', 'error'); return }
245
- const command = text.trimStart().startsWith('/')
246
- if (command) this.commandBusy = true
247
- this.editor.addToHistory?.(text)
248
- this.editor.setText(''); this.syncDraft(''); this.notices = []; this.scrollOffset = 0
249
- this.attempt(async () => {
250
- try {
251
- await this.draftSync
252
- if (this.stopped) return
253
- if (command && await this.commands.execute(text)) return
254
- if (text.trimStart().startsWith('!')) throw new Error('Use the bash tool so this action can be checked.')
255
- await this.runtime!.submit(text, followUp)
256
- } finally { if (command) this.commandBusy = false }
257
- })
258
- }
259
- private dispatchInput(data: string, delegated = false) {
260
- this.lastInput = data
261
- if (this.stopped) return { consume: true }
262
- if (this.tui.hasOverlay()) return
263
- if (!this.initialized) { if (matchesKey(data, 'ctrl+c') || matchesKey(data, 'ctrl+d')) void this.shutdown(); return { consume: true } }
264
- if (this.editorFactory && !delegated) return
265
- if (this.extensionShortcut(data)) return { consume: true }
266
- const is = (action: Parameters<KeybindingsManager['matches']>[1]) => this.keybindings.matches(data, action)
267
- if (is('app.interrupt')) {
268
- if (this.editor instanceof HarnessEditor && this.editor.isShowingAutocomplete()) return
269
- this.attempt(() => this.runtime!.interrupt())
270
- } else if (is('app.clear')) {
271
- if (this.session.isStreaming) this.attempt(() => this.runtime!.interrupt())
272
- else if (this.editor.getText()) { this.editor.setText(''); this.syncDraft('') }
273
- else void this.shutdown()
274
- } else if (is('app.exit') && this.editor.getText() === '') void this.shutdown()
275
- else if (is('app.thinking.cycle')) { this.session.cycleThinkingLevel(); this.thinkingChanges += 1 }
276
- else if (is('app.thinking.save')) this.session.setThinkingLevel(this.session.thinkingLevel, { persist: true })
277
- else if (is('app.model.cycleForward') || is('app.model.cycleBackward')) this.attempt(() => this.session.cycleModel(is('app.model.cycleBackward') ? 'backward' : 'forward'))
278
- else if (is('app.model.select')) this.attempt(() => this.commands.execute('/model'))
279
- else if (is('app.tools.expand')) this.ui.setToolsExpanded(!this.toolsExpanded)
280
- else if (is('app.thinking.toggle')) { const hidden = !this.session.settingsManager.getHideThinkingBlock(); this.session.settingsManager.setHideThinkingBlock(hidden); this.transcript.setThinking(hidden) }
281
- else if (is('app.message.followUp')) this.submit(this.editor.getExpandedText?.() ?? this.editor.getText(), true)
282
- else if (is('app.message.dequeue')) { const pending = this.session.clearQueue(); this.editor.setText([...pending.steering, ...pending.followUp, this.editor.getText()].filter(Boolean).join('\n')); this.syncDraft(this.editor.getText()) }
283
- else if (is('app.editor.external')) this.attempt(() => this.externalEditor())
284
- else if (is('app.message.copy')) this.attempt(() => this.commands.execute('/copy'))
285
- else if (is('app.clipboard.pasteImage')) this.attempt(() => this.pasteClipboard())
286
- else if (is('app.suspend')) this.attempt(() => this.suspend())
287
- else if (matchesKey(data, 'pageUp') || matchesKey(data, 'shift+pageUp')) this.scrollOffset = Math.min(Math.max(0, this.totalLines - 1), this.scrollOffset + Math.max(1, this.terminal.rows - 4))
288
- else if (matchesKey(data, 'pageDown') || matchesKey(data, 'shift+pageDown')) this.scrollOffset = Math.max(0, this.scrollOffset - Math.max(1, this.terminal.rows - 4))
289
- else if (matchesKey(data, 'ctrl+home')) this.scrollOffset = Math.max(0, this.totalLines - 1)
290
- else if (matchesKey(data, 'ctrl+end')) this.scrollOffset = 0
291
- else return
292
- this.change(); return { consume: true }
293
- }
294
- private extensionShortcut(data: string): boolean {
295
- const shortcut = [...this.session.extensionRunner.getShortcuts(this.keybindings.getResolvedBindings()).entries()].find(([key]) => matchesKey(data, key))
296
- if (!shortcut) return false
297
- this.attempt(() => shortcut[1].handler(this.session.extensionRunner.createContext()))
298
- return true
299
- }
300
- private accept(event: AgentSessionEvent): void {
301
- this.transcript.accept(event)
302
- if (event.type === 'agent_start') { this.startedAt = Date.now(); this.phase = undefined; this.terminal.setProgress(this.session.settingsManager.getShowTerminalProgress()) }
303
- if (event.type === 'agent_settled') { this.startedAt = null; this.phase = undefined; this.terminal.setProgress(false) }
304
- if (event.type === 'compaction_start') this.phase = 'Compacting context…'
305
- if (event.type === 'compaction_end') { this.phase = undefined; this.transcript.bind(this.session); if (event.errorMessage) this.notify(event.errorMessage, 'error') }
306
- if (event.type === 'auto_retry_start') this.phase = `Retry ${event.attempt}/${event.maxAttempts} in ${(event.delayMs / 1000).toFixed(1)}s: ${event.errorMessage}`
307
- if (event.type === 'auto_retry_end') this.phase = event.finalError
308
- this.change()
309
- }
310
- private refresh(history = false): void {
311
- if (!this.runtime) return
312
- this.ui.setThemes(this.session.resourceLoader.getThemes().themes)
313
- this.tui.setShowHardwareCursor(this.session.settingsManager.getShowHardwareCursor())
314
- this.transcript.bind(this.session); this.transcript.setExpanded(this.toolsExpanded)
315
- if (history) for (const message of this.session.state.messages) if (message.role === 'user') {
316
- const text = typeof message.content === 'string' ? message.content : message.content.filter(part => part.type === 'text').map(part => part.text).join('\n')
317
- if (text) this.editor.addToHistory?.(text)
318
- }
319
- this.baseAutocomplete = new CombinedAutocompleteProvider(this.commands.completions(), this.runtime.cwd)
320
- this.editor.setAutocompleteProvider?.(this.ui.wrapAutocomplete(this.baseAutocomplete))
321
- this.change()
322
- }
323
- private resetPresentation(): void {
324
- this.pendingAbort?.abort(); this.dismissedDecisions.clear()
325
- this.transcript.clearSession()
326
- this.ui.reset()
327
- this.release(this.header); this.release(this.footer)
328
- this.header = this.footer = undefined; this.headerFactory = this.footerFactory = undefined
329
- for (const widget of this.widgets.values()) this.release(widget.component)
330
- this.widgets.clear(); this.statuses.clear(); this.branchListeners.clear()
331
- this.mountEditor()
332
- // The UI object bound to the runtime stays stable; reset its session-owned state separately.
333
- }
334
- private remountPresentation(): void {
335
- this.release(this.header); this.release(this.footer)
336
- this.header = this.headerFactory?.(this.tui, this.theme)
337
- this.footer = this.footerFactory?.(this.tui, this.theme, this.footerData)
338
- this.mountEditor(this.editorFactory)
339
- if (this.runtime) this.refresh()
340
- }
341
-
342
- render(width: number): string[] {
343
- if (width < 1 || !this.ui) return []
344
- this.mouseRegions = []
345
- const welcome = this.header ?? new HarnessWelcome(this.theme, process.env.XINGULAR_HARNESS_NAME)
346
- const history = ['', ...welcome.render(width), '', '']
347
- const state = this.runtime?.snapshot()
348
- this.transcript.setThinking(state?.hideThinking ?? false, this.ui.state.hiddenThinkingLabel ?? 'Thinking…')
349
- const transcriptRows = this.transcript.render(Math.max(3, width))
350
- this.mouseRegions.push({ component: this.transcript, start: history.length, height: transcriptRows.length })
351
- history.push(...transcriptRows); this.totalLines = history.length
352
- const title = state?.name
353
- const working = this.phase ?? (this.startedAt !== null && this.ui.state.workingVisible ? this.ui.state.workingMessage ?? 'Working…' : '')
354
- const titleText = title ? this.theme.italic(this.theme.fg('dim', singleLine(title))) : ''
355
- const indicator = this.ui.state.workingIndicator
356
- const frames = indicator?.frames
357
- const frame = this.startedAt !== null && frames?.length ? frames[Math.floor((Date.now() - this.startedAt) / Math.max(1, indicator?.intervalMs ?? 80)) % frames.length] : ''
358
- const workText = working ? (frame ? frame + ' ' : '') + this.theme.fg('muted', singleLine(working)) : ''
359
- const composer = titleText || workText ? [truncateToWidth(workText, width, '') + ' '.repeat(Math.max(0, width - visibleWidth(workText) - visibleWidth(titleText))) + titleText] : []
360
- const append = (component: Component) => { const rows = component.render(width); this.mouseRegions.push({ component, start: history.length + composer.length, height: rows.length }); composer.push(...rows) }
361
- for (const widget of this.widgets.values()) if (widget.placement !== 'belowEditor') append(widget.component)
362
- if (state) for (const text of [...state.queue.steering.map(text => `Steering: ${text}`), ...state.queue.followUp.map(text => `Follow-up: ${text}`)]) composer.push(...wrapTextWithAnsi(this.theme.fg('muted', text), width))
363
- for (const { message, type } of this.notices) composer.push(...wrapTextWithAnsi(this.theme.fg(type === 'error' ? 'error' : type === 'warning' ? 'warning' : 'muted', message), width))
364
- append(this.editor)
365
- for (const widget of this.widgets.values()) if (widget.placement === 'belowEditor') append(widget.component)
366
- if (this.footer) append(this.footer)
367
- else if (this.statuses.size) composer.push(...wrapTextWithAnsi([...this.statuses.values()].join(' '), width))
368
- const available = Math.max(1, this.terminal.rows - composer.length - 1)
369
- const end = Math.max(1, history.length - this.scrollOffset)
370
- const start = this.scrollOffset ? Math.max(0, end - available) : 0
371
- for (const region of this.mouseRegions) region.start -= region.component === this.transcript ? start : this.scrollOffset ? history.length - (end - start + 1) : 0
372
- const content = this.scrollOffset ? [...history.slice(start, end), this.theme.fg('dim', `↑ Scrollback · ${this.scrollOffset} rows from latest · PgDn to return`)] : history
373
- return [...content, ...composer].map(line => truncateToWidth(line, width, ''))
374
- }
375
- invalidate(): void { this.header?.invalidate(); this.footer?.invalidate(); this.transcript?.invalidate(); this.editor?.invalidate(); for (const widget of this.widgets.values()) widget.component.invalidate() }
376
- handleMouse(event: TuiMouseEvent) {
377
- if (event.type === 'wheel') { this.scrollOffset = Math.max(0, Math.min(this.totalLines - 1, this.scrollOffset - (event.wheelDelta ?? 0))); this.change(); return { handled: true } }
378
- const region = this.mouseRegions.find(region => event.y >= region.start && event.y < region.start + region.height)
379
- return region?.component.handleMouse?.({ ...event, y: event.y - region.start, height: region.height })
380
- }
381
-
382
- private async decisions(): Promise<void> {
383
- if (this.pendingPolling || !this.runtime || this.stopped) return
384
- this.pendingPolling = true
385
- let requests: TerminalDecision[]
386
- try {
387
- requests = await this.runtime.pending()
388
- } catch (error) {
389
- if (!this.stopped) this.notify(error instanceof Error ? error.message : String(error), 'error')
390
- return
391
- } finally { this.pendingPolling = false }
392
- if (this.pendingId && !requests.some(request => request.requestId === this.pendingId)) this.pendingAbort?.abort()
393
- if (this.pendingBusy) return
394
- this.pendingBusy = true
395
- try {
396
- const request = requests.find(request => !this.dismissedDecisions.has(request.requestId))
397
- if (!request || this.tui.hasOverlay()) return
398
- const abort = new AbortController(); this.pendingAbort = abort; this.pendingId = request.requestId
399
- this.dismissedDecisions.add(request.requestId)
400
- if (request.type === 'approval') {
401
- const approved = await this.ui.confirm(`Allow ${request.target ?? 'action'}?`, request.action ?? '', { signal: abort.signal })
402
- if (!abort.signal.aborted) await this.runtime.approve(request.requestId, approved ? 'approved' : 'denied')
403
- } else {
404
- const group = request.batchId
405
- ? requests.filter(item => item.type === 'question' && item.batchId === request.batchId).sort((a, b) => (a.batchIndex ?? 0) - (b.batchIndex ?? 0))
406
- : [request]
407
- const answers: { questionId: string; answer: QuestionAnswer }[] = []
408
- for (const question of group) {
409
- const answer = await answerTerminalQuestion(this.ui, question, abort.signal)
410
- if (abort.signal.aborted) return
411
- if (!answer) { await this.runtime!.interrupt(); return }
412
- answers.push({ questionId: question.requestId, answer })
413
- }
414
- if (!abort.signal.aborted) {
415
- if (request.batchId) await this.runtime.answerBatch(request.batchId, answers)
416
- else await this.runtime.answer(request.requestId, answers[0]!.answer)
417
- }
418
- }
419
- } catch (error) {
420
- if (this.pendingId) this.dismissedDecisions.delete(this.pendingId)
421
- if (!this.stopped) this.notify(error instanceof Error ? error.message : String(error), 'error')
422
- }
423
- finally { this.pendingBusy = false; this.pendingId = undefined; this.pendingAbort = undefined }
424
- }
425
- private hotkeys(): string {
426
- return Object.entries(this.keybindings.getResolvedBindings()).filter(([name, keys]) => name.startsWith('app.') && (!Array.isArray(keys) || keys.length)).map(([name, keys]) => `${Array.isArray(keys) ? keys.join(', ') : keys}: ${name.slice(4).replaceAll('.', ' ')}`).join('\n') + '\nPgUp/PgDn: scroll transcript\nCtrl+Home/End: oldest/latest transcript'
427
- }
428
- private async pasteClipboard(): Promise<void> {
429
- const adapter = await loadPiAdapter()
430
- const files = await adapter.readClipboardFilePaths()
431
- if (files?.length) {
432
- if (files.some(file => /[\x00-\x1f\x7f]/.test(file))) throw new Error('Clipboard file paths contain control characters.')
433
- this.ui.pasteToEditor(files.join('\n'))
434
- return
435
- }
436
- const image = await adapter.readClipboardImage()
437
- if (image) {
438
- const directory = await mkdtemp(join(tmpdir(), 'harness-clipboard-'))
439
- const file = join(directory, `${randomUUID()}.${adapter.extensionForImageMimeType(image.mimeType) ?? 'png'}`)
440
- await writeFile(file, image.bytes, { mode: 0o600 })
441
- this.cleanup.push(() => { void rm(directory, { recursive: true, force: true }) })
442
- this.ui.pasteToEditor(file)
443
- return
444
- }
445
- const text = await adapter.readClipboardText()
446
- if (text) this.ui.pasteToEditor(text)
447
- }
448
- private async externalEditor(): Promise<void> {
449
- const editor = process.env.VISUAL || process.env.EDITOR
450
- if (!editor) throw new Error('Set VISUAL or EDITOR to open an external editor.')
451
- const directory = await mkdtemp(join(tmpdir(), 'harness-editor-')), file = join(directory, 'prompt.md')
452
- try {
453
- await writeFile(file, this.editor.getExpandedText?.() ?? this.editor.getText(), { mode: 0o600 })
454
- this.tui.stop()
455
- await new Promise<void>((resolve, reject) => {
456
- const child = spawn(process.env.SHELL || '/bin/sh', ['-c', `${editor} "$1"`, 'harness-editor', file], { stdio: 'inherit' })
457
- child.once('error', reject); child.once('exit', code => code === 0 ? resolve() : reject(new Error(`Editor exited with ${code}.`)))
458
- })
459
- this.editor.setText(await readFile(file, 'utf8')); this.syncDraft(this.editor.getText())
460
- } finally { await rm(directory, { recursive: true, force: true }); if (!this.stopped) { this.tui.start(); this.change() } }
461
- }
462
- private async suspend(): Promise<void> {
463
- if (process.platform === 'win32') throw new Error('Suspending is unavailable on Windows.')
464
- this.tui.stop(); process.kill(process.pid, 'SIGTSTP')
465
- }
466
- shutdown(): Promise<void> {
467
- return this.closing ??= (async () => {
468
- const protect = (action: () => void) => {
469
- try { action() } catch (error) { this.failure ??= error instanceof Error ? error : new Error(String(error)) }
470
- }
471
- this.stopped = true
472
- protect(() => this.pendingAbort?.abort())
473
- protect(() => this.ui?.dispose())
474
- for (const cleanup of this.cleanup.splice(0)) protect(cleanup)
475
- try { await this.runtime?.dispose() }
476
- catch (error) { this.failure ??= error instanceof Error ? error : new Error(String(error)) }
477
- finally {
478
- protect(() => this.transcript?.clearSession())
479
- protect(() => this.header?.dispose?.())
480
- protect(() => this.footer?.dispose?.())
481
- for (const widget of this.widgets.values()) protect(() => widget.component.dispose?.())
482
- protect(() => (this.editor as Mounted | undefined)?.dispose?.())
483
- await this.terminal.drainInput(200, 30).catch(() => {})
484
- protect(() => this.tui.stop())
485
- for (const cleanup of this.finalCleanup.splice(0)) protect(cleanup)
486
- this.finish()
487
- }
488
- })()
489
- }
490
- }
491
- export function runTerminalFrontend(args: string[]): Promise<number> { return new TerminalApplication(args).run() }