@xingular/harness 2.0.0 → 2.0.1-unstable

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 (1095) hide show
  1. package/CHANGELOG.md +635 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE.md +50 -0
  4. package/README.md +241 -90
  5. package/THIRD_PARTY_LICENSES.md +942 -0
  6. package/browser-extension/README.md +32 -0
  7. package/browser-extension/background.js +612 -0
  8. package/browser-extension/manifest.json +16 -0
  9. package/browser-extension/overlay.js +97 -0
  10. package/browser-extension/popup.css +19 -0
  11. package/browser-extension/popup.html +30 -0
  12. package/browser-extension/popup.js +101 -0
  13. package/browser-extension/site-access.js +63 -0
  14. package/builtin/skills/plugin-authoring/SKILL.md +53 -0
  15. package/dist/cli.js +32851 -17155
  16. package/dist/sdk/accounts/accounts.d.ts +195 -0
  17. package/dist/sdk/accounts/accounts.js +821 -0
  18. package/dist/sdk/accounts/choices.d.ts +41 -0
  19. package/dist/sdk/accounts/choices.js +89 -0
  20. package/dist/sdk/accounts/env.d.ts +78 -0
  21. package/dist/sdk/accounts/env.js +294 -0
  22. package/dist/sdk/accounts/secrets.d.ts +28 -0
  23. package/dist/sdk/accounts/secrets.js +147 -0
  24. package/dist/sdk/accounts/store.d.ts +139 -0
  25. package/dist/sdk/accounts/store.js +255 -0
  26. package/dist/sdk/agents/builtin.d.ts +2 -0
  27. package/dist/sdk/agents/builtin.js +50 -0
  28. package/dist/sdk/agents/narrow.d.ts +13 -0
  29. package/dist/sdk/agents/narrow.js +29 -0
  30. package/dist/sdk/agents/planResearch.d.ts +27 -0
  31. package/dist/sdk/agents/planResearch.js +82 -0
  32. package/dist/sdk/agents/profiles.d.ts +93 -0
  33. package/dist/sdk/agents/profiles.js +289 -0
  34. package/dist/sdk/annotations/bridge.d.ts +44 -0
  35. package/dist/sdk/annotations/bridge.js +268 -0
  36. package/dist/sdk/annotations/capture.d.ts +158 -0
  37. package/dist/sdk/annotations/capture.js +409 -0
  38. package/dist/sdk/annotations/delivery.d.ts +79 -0
  39. package/dist/sdk/annotations/delivery.js +163 -0
  40. package/dist/sdk/annotations/inspect.d.ts +10 -0
  41. package/dist/sdk/annotations/inspect.js +34 -0
  42. package/dist/sdk/annotations/isolated.d.ts +6 -0
  43. package/dist/sdk/annotations/isolated.js +51 -0
  44. package/dist/sdk/annotations/pageScript.d.ts +28 -0
  45. package/dist/sdk/annotations/pageScript.js +871 -0
  46. package/dist/sdk/annotations/part.d.ts +8 -0
  47. package/dist/sdk/annotations/part.js +126 -0
  48. package/dist/sdk/annotations/queue.d.ts +4 -0
  49. package/dist/sdk/annotations/queue.js +45 -0
  50. package/dist/sdk/annotations/serverMethods.d.ts +3 -0
  51. package/dist/sdk/annotations/serverMethods.js +66 -0
  52. package/dist/sdk/annotations/store.d.ts +159 -0
  53. package/dist/sdk/annotations/store.js +404 -0
  54. package/dist/sdk/browser/backend.d.ts +16 -0
  55. package/dist/sdk/browser/backend.js +17 -0
  56. package/dist/sdk/browser/config.d.ts +19 -0
  57. package/dist/sdk/browser/config.js +23 -0
  58. package/dist/sdk/browser/contracts.d.ts +19 -0
  59. package/dist/sdk/browser/contracts.js +116 -0
  60. package/dist/sdk/browser/diagnostics.d.ts +4 -0
  61. package/dist/sdk/browser/diagnostics.js +38 -0
  62. package/dist/sdk/browser/extension/bridge.d.ts +55 -0
  63. package/dist/sdk/browser/extension/bridge.js +336 -0
  64. package/dist/sdk/browser/extension/session.d.ts +50 -0
  65. package/dist/sdk/browser/extension/session.js +321 -0
  66. package/dist/sdk/browser/index.d.ts +33 -0
  67. package/dist/sdk/browser/index.js +105 -0
  68. package/dist/sdk/browser/network.d.ts +10 -0
  69. package/dist/sdk/browser/network.js +59 -0
  70. package/dist/sdk/browser/serverMethods.d.ts +3 -0
  71. package/dist/sdk/browser/serverMethods.js +10 -0
  72. package/dist/sdk/browser/session.d.ts +85 -0
  73. package/dist/sdk/browser/session.js +228 -0
  74. package/dist/sdk/browser/tools.d.ts +102 -0
  75. package/dist/sdk/browser/tools.js +257 -0
  76. package/dist/sdk/capabilities/api.d.ts +22 -0
  77. package/dist/sdk/capabilities/api.js +54 -0
  78. package/dist/sdk/capabilities/runSnapshot.d.ts +18 -0
  79. package/dist/sdk/capabilities/runSnapshot.js +53 -0
  80. package/dist/sdk/capabilities/types.d.ts +29 -0
  81. package/dist/sdk/capabilities/types.js +1 -0
  82. package/dist/sdk/cli.d.ts +2 -0
  83. package/dist/sdk/cli.js +172 -0
  84. package/dist/sdk/commands/extensionOwners.d.ts +4 -0
  85. package/dist/sdk/commands/extensionOwners.js +17 -0
  86. package/dist/sdk/commands/list.d.ts +3 -0
  87. package/dist/sdk/commands/list.js +90 -0
  88. package/dist/sdk/commands/promptLoader.d.ts +5 -0
  89. package/dist/sdk/commands/promptLoader.js +76 -0
  90. package/dist/sdk/commands/promptRoutes.d.ts +13 -0
  91. package/dist/sdk/commands/promptRoutes.js +46 -0
  92. package/dist/sdk/commands/registry.d.ts +299 -0
  93. package/dist/sdk/commands/registry.js +54 -0
  94. package/dist/sdk/commands/routes.d.ts +16 -0
  95. package/dist/sdk/commands/routes.js +33 -0
  96. package/dist/sdk/commands/runtimeRouter.d.ts +4 -0
  97. package/dist/sdk/commands/runtimeRouter.js +48 -0
  98. package/dist/sdk/computer/boundary.d.ts +40 -0
  99. package/dist/sdk/computer/boundary.js +79 -0
  100. package/dist/sdk/computer/contracts.d.ts +127 -0
  101. package/dist/sdk/computer/contracts.js +13 -0
  102. package/dist/sdk/computer/control.d.ts +34 -0
  103. package/dist/sdk/computer/control.js +112 -0
  104. package/dist/sdk/computer/screenshot.d.ts +32 -0
  105. package/dist/sdk/computer/screenshot.js +35 -0
  106. package/dist/sdk/computer/terminalCapture.d.ts +26 -0
  107. package/dist/sdk/computer/terminalCapture.js +64 -0
  108. package/dist/sdk/computer/volatileImages.d.ts +9 -0
  109. package/dist/sdk/computer/volatileImages.js +50 -0
  110. package/dist/sdk/context/attribution.d.ts +18 -0
  111. package/dist/sdk/context/attribution.js +295 -0
  112. package/dist/sdk/context/runDetails.d.ts +16 -0
  113. package/dist/sdk/context/runDetails.js +22 -0
  114. package/dist/sdk/context/types.d.ts +94 -0
  115. package/dist/sdk/context/types.js +10 -0
  116. package/dist/sdk/core/bus.d.ts +23 -0
  117. package/dist/sdk/core/bus.js +76 -0
  118. package/dist/sdk/core/caller.d.ts +22 -0
  119. package/dist/sdk/core/caller.js +16 -0
  120. package/dist/sdk/core/changes.d.ts +15 -0
  121. package/dist/sdk/core/changes.js +15 -0
  122. package/dist/sdk/core/commandBudget.d.ts +23 -0
  123. package/dist/sdk/core/commandBudget.js +62 -0
  124. package/dist/sdk/core/effort.d.ts +2 -0
  125. package/dist/sdk/core/effort.js +1 -0
  126. package/dist/sdk/core/env.d.ts +18 -0
  127. package/dist/sdk/core/env.js +8 -0
  128. package/dist/sdk/core/errors.d.ts +15 -0
  129. package/dist/sdk/core/errors.js +24 -0
  130. package/dist/sdk/core/frontmatter.d.ts +12 -0
  131. package/dist/sdk/core/frontmatter.js +50 -0
  132. package/dist/sdk/core/fs.d.ts +18 -0
  133. package/dist/sdk/core/fs.js +106 -0
  134. package/dist/sdk/core/index.d.ts +24 -0
  135. package/dist/sdk/core/index.js +203 -0
  136. package/dist/sdk/core/interactiveProfile.d.ts +46 -0
  137. package/dist/sdk/core/interactiveProfile.js +143 -0
  138. package/dist/sdk/core/links.d.ts +51 -0
  139. package/dist/sdk/core/links.js +155 -0
  140. package/dist/sdk/core/path.d.ts +7 -0
  141. package/dist/sdk/core/path.js +79 -0
  142. package/dist/sdk/core/projects.d.ts +18 -0
  143. package/dist/sdk/core/projects.js +67 -0
  144. package/dist/sdk/core/secrets.d.ts +2 -0
  145. package/dist/sdk/core/secrets.js +5 -0
  146. package/dist/sdk/core/shell.d.ts +20 -0
  147. package/dist/sdk/core/shell.js +73 -0
  148. package/dist/sdk/core/text.d.ts +7 -0
  149. package/dist/sdk/core/text.js +40 -0
  150. package/dist/sdk/core/workspace.d.ts +13 -0
  151. package/dist/sdk/core/workspace.js +88 -0
  152. package/dist/sdk/core/workspaceBashTool.d.ts +20 -0
  153. package/dist/sdk/core/workspaceBashTool.js +50 -0
  154. package/dist/sdk/core/workspaceSandbox.d.ts +41 -0
  155. package/dist/sdk/core/workspaceSandbox.js +360 -0
  156. package/dist/sdk/core/workspaceWriteOperations.d.ts +57 -0
  157. package/dist/sdk/core/workspaceWriteOperations.js +245 -0
  158. package/dist/sdk/doctor/checks.d.ts +5 -0
  159. package/dist/sdk/doctor/checks.js +270 -0
  160. package/dist/sdk/doctor/probe.d.ts +4 -0
  161. package/dist/sdk/doctor/probe.js +94 -0
  162. package/dist/sdk/doctor/serverMethods.d.ts +3 -0
  163. package/dist/sdk/doctor/serverMethods.js +8 -0
  164. package/dist/sdk/doctor/service.d.ts +23 -0
  165. package/dist/sdk/doctor/service.js +144 -0
  166. package/dist/sdk/doctor/types.d.ts +141 -0
  167. package/dist/sdk/doctor/types.js +29 -0
  168. package/dist/sdk/harness.d.ts +172 -0
  169. package/dist/sdk/harness.js +387 -0
  170. package/dist/sdk/history/lessonPrompt.d.ts +2 -0
  171. package/dist/sdk/history/lessonPrompt.js +15 -0
  172. package/dist/sdk/history/lessons.d.ts +52 -0
  173. package/dist/sdk/history/lessons.js +173 -0
  174. package/dist/sdk/history/recall.d.ts +68 -0
  175. package/dist/sdk/history/recall.js +199 -0
  176. package/dist/sdk/history/serverMethods.d.ts +3 -0
  177. package/dist/sdk/history/serverMethods.js +8 -0
  178. package/dist/sdk/history/trajectory.d.ts +7 -0
  179. package/dist/sdk/history/trajectory.js +218 -0
  180. package/dist/sdk/hub/catalog.json +123 -0
  181. package/dist/sdk/hub/cli.d.ts +7 -0
  182. package/dist/sdk/hub/cli.js +81 -0
  183. package/dist/sdk/hub/hub.d.ts +74 -0
  184. package/dist/sdk/hub/hub.js +476 -0
  185. package/dist/sdk/hub/schema.d.ts +280 -0
  186. package/dist/sdk/hub/schema.js +78 -0
  187. package/dist/sdk/hub/serverMethods.d.ts +3 -0
  188. package/dist/sdk/hub/serverMethods.js +11 -0
  189. package/dist/sdk/index.d.ts +80 -0
  190. package/dist/sdk/index.js +60 -0
  191. package/dist/sdk/mcp/connections.d.ts +83 -0
  192. package/dist/sdk/mcp/connections.js +676 -0
  193. package/dist/sdk/mcp/manager.d.ts +26 -0
  194. package/dist/sdk/mcp/manager.js +124 -0
  195. package/dist/sdk/mcp/oauth.d.ts +35 -0
  196. package/dist/sdk/mcp/oauth.js +171 -0
  197. package/dist/sdk/mcp/scopeMigration.d.ts +19 -0
  198. package/dist/sdk/mcp/scopeMigration.js +198 -0
  199. package/dist/sdk/mcp/serverMethods.d.ts +3 -0
  200. package/dist/sdk/mcp/serverMethods.js +22 -0
  201. package/dist/sdk/mcp/service.d.ts +160 -0
  202. package/dist/sdk/mcp/service.js +510 -0
  203. package/dist/sdk/mcp/types.d.ts +131 -0
  204. package/dist/sdk/mcp/types.js +49 -0
  205. package/dist/sdk/pi/retryPolicy.d.ts +19 -0
  206. package/dist/sdk/pi/retryPolicy.js +11 -0
  207. package/dist/sdk/pi/runtime.d.ts +14 -0
  208. package/dist/sdk/pi/runtime.js +31 -0
  209. package/dist/sdk/plugins/api.d.ts +66 -0
  210. package/dist/sdk/plugins/api.js +155 -0
  211. package/dist/sdk/plugins/attachments.d.ts +46 -0
  212. package/dist/sdk/plugins/attachments.js +111 -0
  213. package/dist/sdk/plugins/builtin.d.ts +7 -0
  214. package/dist/sdk/plugins/builtin.js +182 -0
  215. package/dist/sdk/plugins/builtinRegistry.d.ts +62 -0
  216. package/dist/sdk/plugins/builtinRegistry.js +77 -0
  217. package/dist/sdk/plugins/catalogue.d.ts +29 -0
  218. package/dist/sdk/plugins/catalogue.js +197 -0
  219. package/dist/sdk/plugins/cli.d.ts +13 -0
  220. package/dist/sdk/plugins/cli.js +188 -0
  221. package/dist/sdk/plugins/commandPrefix.d.ts +8 -0
  222. package/dist/sdk/plugins/commandPrefix.js +21 -0
  223. package/dist/sdk/plugins/discover.d.ts +48 -0
  224. package/dist/sdk/plugins/discover.js +230 -0
  225. package/dist/sdk/plugins/firstParty.d.ts +50 -0
  226. package/dist/sdk/plugins/firstParty.js +119 -0
  227. package/dist/sdk/plugins/hooks.d.ts +94 -0
  228. package/dist/sdk/plugins/hooks.js +162 -0
  229. package/dist/sdk/plugins/identity.d.ts +19 -0
  230. package/dist/sdk/plugins/identity.js +48 -0
  231. package/dist/sdk/plugins/install.d.ts +24 -0
  232. package/dist/sdk/plugins/install.js +160 -0
  233. package/dist/sdk/plugins/inventory.d.ts +22 -0
  234. package/dist/sdk/plugins/inventory.js +111 -0
  235. package/dist/sdk/plugins/manifest.d.ts +21 -0
  236. package/dist/sdk/plugins/manifest.js +94 -0
  237. package/dist/sdk/plugins/plugins.d.ts +106 -0
  238. package/dist/sdk/plugins/plugins.js +165 -0
  239. package/dist/sdk/plugins/proposals.d.ts +17 -0
  240. package/dist/sdk/plugins/proposals.js +147 -0
  241. package/dist/sdk/plugins/runtime.d.ts +9 -0
  242. package/dist/sdk/plugins/runtime.js +95 -0
  243. package/dist/sdk/plugins/surfaces.d.ts +5 -0
  244. package/dist/sdk/plugins/surfaces.js +11 -0
  245. package/dist/sdk/plugins/trust.d.ts +44 -0
  246. package/dist/sdk/plugins/trust.js +120 -0
  247. package/dist/sdk/plugins/types.d.ts +174 -0
  248. package/dist/sdk/plugins/types.js +7 -0
  249. package/dist/sdk/prompt/compile.d.ts +245 -0
  250. package/dist/sdk/prompt/compile.js +535 -0
  251. package/dist/sdk/prompt/reporting.d.ts +2 -0
  252. package/dist/sdk/prompt/reporting.js +94 -0
  253. package/dist/sdk/prompt/standard.d.ts +32 -0
  254. package/dist/sdk/prompt/standard.js +76 -0
  255. package/dist/sdk/refine/cli.d.ts +4 -0
  256. package/dist/sdk/refine/cli.js +186 -0
  257. package/dist/sdk/refine/demo.d.ts +21 -0
  258. package/dist/sdk/refine/demo.js +114 -0
  259. package/dist/sdk/refine/evidence.d.ts +55 -0
  260. package/dist/sdk/refine/evidence.js +330 -0
  261. package/dist/sdk/refine/host.d.ts +59 -0
  262. package/dist/sdk/refine/host.js +105 -0
  263. package/dist/sdk/refine/ledger.d.ts +48 -0
  264. package/dist/sdk/refine/ledger.js +76 -0
  265. package/dist/sdk/refine/library.d.ts +55 -0
  266. package/dist/sdk/refine/library.js +177 -0
  267. package/dist/sdk/refine/pi.d.ts +72 -0
  268. package/dist/sdk/refine/pi.js +248 -0
  269. package/dist/sdk/refine/prompts.d.ts +60 -0
  270. package/dist/sdk/refine/prompts.js +434 -0
  271. package/dist/sdk/refine/recursion.d.ts +113 -0
  272. package/dist/sdk/refine/recursion.js +258 -0
  273. package/dist/sdk/refine/runner.d.ts +83 -0
  274. package/dist/sdk/refine/runner.js +151 -0
  275. package/dist/sdk/refine/selection.d.ts +57 -0
  276. package/dist/sdk/refine/selection.js +251 -0
  277. package/dist/sdk/refine/serverMethods.d.ts +3 -0
  278. package/dist/sdk/refine/serverMethods.js +23 -0
  279. package/dist/sdk/refine/service.d.ts +115 -0
  280. package/dist/sdk/refine/service.js +280 -0
  281. package/dist/sdk/refine/suite.d.ts +67 -0
  282. package/dist/sdk/refine/suite.js +174 -0
  283. package/dist/sdk/refine/trajectory.d.ts +9 -0
  284. package/dist/sdk/refine/trajectory.js +73 -0
  285. package/dist/sdk/refine/types.d.ts +93 -0
  286. package/dist/sdk/refine/types.js +10 -0
  287. package/dist/sdk/remote/addresses.d.ts +19 -0
  288. package/dist/sdk/remote/addresses.js +52 -0
  289. package/dist/sdk/remote/pairing.d.ts +59 -0
  290. package/dist/sdk/remote/pairing.js +160 -0
  291. package/dist/sdk/remote/seal.d.ts +28 -0
  292. package/dist/sdk/remote/seal.js +84 -0
  293. package/dist/sdk/remote/server.d.ts +28 -0
  294. package/dist/sdk/remote/server.js +158 -0
  295. package/dist/sdk/remote/service.d.ts +125 -0
  296. package/dist/sdk/remote/service.js +330 -0
  297. package/dist/sdk/remote/webClient.d.ts +4 -0
  298. package/dist/sdk/remote/webClient.js +452 -0
  299. package/dist/sdk/server/methods.d.ts +3 -0
  300. package/dist/sdk/server/methods.js +365 -0
  301. package/dist/sdk/server/params.d.ts +4 -0
  302. package/dist/sdk/server/params.js +11 -0
  303. package/dist/sdk/server/remote.d.ts +19 -0
  304. package/dist/sdk/server/remote.js +109 -0
  305. package/dist/sdk/server/rpc.d.ts +20 -0
  306. package/dist/sdk/server/rpc.js +102 -0
  307. package/dist/sdk/server/serve.d.ts +13 -0
  308. package/dist/sdk/server/serve.js +25 -0
  309. package/dist/sdk/server/socket.d.ts +28 -0
  310. package/dist/sdk/server/socket.js +122 -0
  311. package/dist/sdk/sessions/approvals.d.ts +47 -0
  312. package/dist/sdk/sessions/approvals.js +119 -0
  313. package/dist/sdk/sessions/checkpoint.d.ts +7 -0
  314. package/dist/sdk/sessions/checkpoint.js +41 -0
  315. package/dist/sdk/sessions/compactionCheckpoint.d.ts +18 -0
  316. package/dist/sdk/sessions/compactionCheckpoint.js +79 -0
  317. package/dist/sdk/sessions/completionCheck.d.ts +51 -0
  318. package/dist/sdk/sessions/completionCheck.js +161 -0
  319. package/dist/sdk/sessions/continuation.d.ts +53 -0
  320. package/dist/sdk/sessions/continuation.js +90 -0
  321. package/dist/sdk/sessions/contracts.d.ts +991 -0
  322. package/dist/sdk/sessions/contracts.js +545 -0
  323. package/dist/sdk/sessions/control.d.ts +72 -0
  324. package/dist/sdk/sessions/control.js +158 -0
  325. package/dist/sdk/sessions/extensionUi.d.ts +19 -0
  326. package/dist/sdk/sessions/extensionUi.js +64 -0
  327. package/dist/sdk/sessions/goal.d.ts +145 -0
  328. package/dist/sdk/sessions/goal.js +405 -0
  329. package/dist/sdk/sessions/goalLoop.d.ts +37 -0
  330. package/dist/sdk/sessions/goalLoop.js +125 -0
  331. package/dist/sdk/sessions/goalPrompts.d.ts +4 -0
  332. package/dist/sdk/sessions/goalPrompts.js +93 -0
  333. package/dist/sdk/sessions/goalStop.d.ts +27 -0
  334. package/dist/sdk/sessions/goalStop.js +41 -0
  335. package/dist/sdk/sessions/history.d.ts +16 -0
  336. package/dist/sdk/sessions/history.js +68 -0
  337. package/dist/sdk/sessions/initialBash.d.ts +7 -0
  338. package/dist/sdk/sessions/initialBash.js +7 -0
  339. package/dist/sdk/sessions/lineageJoin.d.ts +2 -0
  340. package/dist/sdk/sessions/lineageJoin.js +61 -0
  341. package/dist/sdk/sessions/live.d.ts +101 -0
  342. package/dist/sdk/sessions/live.js +265 -0
  343. package/dist/sdk/sessions/planMode.d.ts +116 -0
  344. package/dist/sdk/sessions/planMode.js +281 -0
  345. package/dist/sdk/sessions/policy.d.ts +43 -0
  346. package/dist/sdk/sessions/policy.js +1 -0
  347. package/dist/sdk/sessions/pricing.d.ts +34 -0
  348. package/dist/sdk/sessions/pricing.js +40 -0
  349. package/dist/sdk/sessions/reasoningSummaries.d.ts +11 -0
  350. package/dist/sdk/sessions/reasoningSummaries.js +61 -0
  351. package/dist/sdk/sessions/registry.d.ts +124 -0
  352. package/dist/sdk/sessions/registry.js +392 -0
  353. package/dist/sdk/sessions/runIndex.d.ts +21 -0
  354. package/dist/sdk/sessions/runIndex.js +65 -0
  355. package/dist/sdk/sessions/runStore.d.ts +82 -0
  356. package/dist/sdk/sessions/runStore.js +393 -0
  357. package/dist/sdk/sessions/runner.d.ts +291 -0
  358. package/dist/sdk/sessions/runner.js +1040 -0
  359. package/dist/sdk/sessions/sessionState.d.ts +13 -0
  360. package/dist/sdk/sessions/sessionState.js +31 -0
  361. package/dist/sdk/sessions/sessions.d.ts +238 -0
  362. package/dist/sdk/sessions/sessions.js +923 -0
  363. package/dist/sdk/sessions/sideQuestionServerMethods.d.ts +3 -0
  364. package/dist/sdk/sessions/sideQuestionServerMethods.js +19 -0
  365. package/dist/sdk/sessions/sideQuestions.d.ts +66 -0
  366. package/dist/sdk/sessions/sideQuestions.js +290 -0
  367. package/dist/sdk/sessions/stallWatch.d.ts +35 -0
  368. package/dist/sdk/sessions/stallWatch.js +42 -0
  369. package/dist/sdk/sessions/subagentServerMethods.d.ts +3 -0
  370. package/dist/sdk/sessions/subagentServerMethods.js +41 -0
  371. package/dist/sdk/sessions/subagentService.d.ts +88 -0
  372. package/dist/sdk/sessions/subagentService.js +546 -0
  373. package/dist/sdk/sessions/subagents.d.ts +98 -0
  374. package/dist/sdk/sessions/subagents.js +136 -0
  375. package/dist/sdk/sessions/workSlice.d.ts +167 -0
  376. package/dist/sdk/sessions/workSlice.js +266 -0
  377. package/dist/sdk/sessions/workspaceBoundary.d.ts +15 -0
  378. package/dist/sdk/sessions/workspaceBoundary.js +46 -0
  379. package/dist/sdk/terminal/accountsMenu.d.ts +46 -0
  380. package/dist/sdk/terminal/accountsMenu.js +392 -0
  381. package/dist/sdk/terminal/browser.d.ts +62 -0
  382. package/dist/sdk/terminal/browser.js +41 -0
  383. package/dist/sdk/terminal/browserControl.d.ts +29 -0
  384. package/dist/sdk/terminal/browserControl.js +187 -0
  385. package/dist/sdk/terminal/cli.d.ts +20 -0
  386. package/dist/sdk/terminal/cli.js +121 -0
  387. package/dist/sdk/terminal/client.d.ts +20 -0
  388. package/dist/sdk/terminal/client.js +148 -0
  389. package/dist/sdk/terminal/commandHelp.d.ts +17 -0
  390. package/dist/sdk/terminal/commandHelp.js +20 -0
  391. package/dist/sdk/terminal/control.d.ts +4 -0
  392. package/dist/sdk/terminal/control.js +25 -0
  393. package/dist/sdk/terminal/controller.d.ts +136 -0
  394. package/dist/sdk/terminal/controller.js +579 -0
  395. package/dist/sdk/terminal/credentialStore.d.ts +4 -0
  396. package/dist/sdk/terminal/credentialStore.js +33 -0
  397. package/dist/sdk/terminal/daemon.d.ts +44 -0
  398. package/dist/sdk/terminal/daemon.js +264 -0
  399. package/dist/sdk/terminal/hubMenu.d.ts +10 -0
  400. package/dist/sdk/terminal/hubMenu.js +34 -0
  401. package/dist/sdk/terminal/launch.d.ts +5 -0
  402. package/dist/sdk/terminal/launch.js +27 -0
  403. package/dist/sdk/terminal/liveTerminal.d.ts +96 -0
  404. package/dist/sdk/terminal/liveTerminal.js +436 -0
  405. package/dist/sdk/terminal/logs.d.ts +14 -0
  406. package/dist/sdk/terminal/logs.js +59 -0
  407. package/dist/sdk/terminal/opentui/ansi.d.ts +25 -0
  408. package/dist/sdk/terminal/opentui/ansi.js +192 -0
  409. package/dist/sdk/terminal/opentui/model.d.ts +163 -0
  410. package/dist/sdk/terminal/opentui/model.js +484 -0
  411. package/dist/sdk/terminal/opentui/subagentsWidget.d.ts +28 -0
  412. package/dist/sdk/terminal/opentui/subagentsWidget.js +54 -0
  413. package/dist/sdk/terminal/opentui/theme.d.ts +47 -0
  414. package/dist/sdk/terminal/opentui/theme.js +93 -0
  415. package/dist/sdk/terminal/piExtension.d.ts +12 -0
  416. package/dist/sdk/terminal/piExtension.js +847 -0
  417. package/dist/sdk/terminal/pluginMenu.d.ts +34 -0
  418. package/dist/sdk/terminal/pluginMenu.js +101 -0
  419. package/dist/sdk/terminal/socket.d.ts +73 -0
  420. package/dist/sdk/terminal/socket.js +287 -0
  421. package/dist/sdk/terminal/standalone.d.ts +52 -0
  422. package/dist/sdk/terminal/standalone.js +329 -0
  423. package/dist/sdk/terminal/subagentMonitor.d.ts +62 -0
  424. package/dist/sdk/terminal/subagentMonitor.js +176 -0
  425. package/dist/sdk/terminal/terminals.d.ts +312 -0
  426. package/dist/sdk/terminal/terminals.js +1782 -0
  427. package/dist/sdk/terminal/toolName.d.ts +6 -0
  428. package/dist/sdk/terminal/toolName.js +12 -0
  429. package/dist/sdk/terminal/trajectory.d.ts +15 -0
  430. package/dist/sdk/terminal/trajectory.js +38 -0
  431. package/dist/sdk/terminal/tui/annotateCommand.d.ts +7 -0
  432. package/dist/sdk/terminal/tui/annotateCommand.js +36 -0
  433. package/dist/sdk/terminal/tui/annotateSend.d.ts +30 -0
  434. package/dist/sdk/terminal/tui/annotateSend.js +40 -0
  435. package/dist/sdk/terminal/tui/chrome.d.ts +68 -0
  436. package/dist/sdk/terminal/tui/chrome.js +182 -0
  437. package/dist/sdk/terminal/tui/clipboardFiles.d.ts +11 -0
  438. package/dist/sdk/terminal/tui/clipboardFiles.js +74 -0
  439. package/dist/sdk/terminal/tui/commandPages.d.ts +16 -0
  440. package/dist/sdk/terminal/tui/commandPages.js +136 -0
  441. package/dist/sdk/terminal/tui/commands.d.ts +71 -0
  442. package/dist/sdk/terminal/tui/commands.js +496 -0
  443. package/dist/sdk/terminal/tui/dialogGeometry.d.ts +27 -0
  444. package/dist/sdk/terminal/tui/dialogGeometry.js +52 -0
  445. package/dist/sdk/terminal/tui/dialogs.d.ts +81 -0
  446. package/dist/sdk/terminal/tui/dialogs.js +372 -0
  447. package/dist/sdk/terminal/tui/goalCommand.d.ts +19 -0
  448. package/dist/sdk/terminal/tui/goalCommand.js +83 -0
  449. package/dist/sdk/terminal/tui/harnessRuntime.d.ts +8 -0
  450. package/dist/sdk/terminal/tui/harnessRuntime.js +771 -0
  451. package/dist/sdk/terminal/tui/keybindings.d.ts +11 -0
  452. package/dist/sdk/terminal/tui/keybindings.js +49 -0
  453. package/dist/sdk/terminal/tui/list.d.ts +86 -0
  454. package/dist/sdk/terminal/tui/list.js +278 -0
  455. package/dist/sdk/terminal/tui/markdown.d.ts +16 -0
  456. package/dist/sdk/terminal/tui/markdown.js +55 -0
  457. package/dist/sdk/terminal/tui/modeChoices.d.ts +61 -0
  458. package/dist/sdk/terminal/tui/modeChoices.js +95 -0
  459. package/dist/sdk/terminal/tui/modelPicker.d.ts +55 -0
  460. package/dist/sdk/terminal/tui/modelPicker.js +150 -0
  461. package/dist/sdk/terminal/tui/mouse.d.ts +3 -0
  462. package/dist/sdk/terminal/tui/mouse.js +3 -0
  463. package/dist/sdk/terminal/tui/mouseReport.d.ts +30 -0
  464. package/dist/sdk/terminal/tui/mouseReport.js +80 -0
  465. package/dist/sdk/terminal/tui/page.d.ts +17 -0
  466. package/dist/sdk/terminal/tui/page.js +1 -0
  467. package/dist/sdk/terminal/tui/pageOverlay.d.ts +30 -0
  468. package/dist/sdk/terminal/tui/pageOverlay.js +183 -0
  469. package/dist/sdk/terminal/tui/pages/accountsPage.d.ts +76 -0
  470. package/dist/sdk/terminal/tui/pages/accountsPage.js +409 -0
  471. package/dist/sdk/terminal/tui/pages/agentsPage.d.ts +74 -0
  472. package/dist/sdk/terminal/tui/pages/agentsPage.js +193 -0
  473. package/dist/sdk/terminal/tui/pages/annotatePage.d.ts +148 -0
  474. package/dist/sdk/terminal/tui/pages/annotatePage.js +576 -0
  475. package/dist/sdk/terminal/tui/pages/browserPage.d.ts +87 -0
  476. package/dist/sdk/terminal/tui/pages/browserPage.js +739 -0
  477. package/dist/sdk/terminal/tui/pages/componentsPage.d.ts +47 -0
  478. package/dist/sdk/terminal/tui/pages/componentsPage.js +119 -0
  479. package/dist/sdk/terminal/tui/pages/contextPage.d.ts +91 -0
  480. package/dist/sdk/terminal/tui/pages/contextPage.js +585 -0
  481. package/dist/sdk/terminal/tui/pages/doctorPage.d.ts +39 -0
  482. package/dist/sdk/terminal/tui/pages/doctorPage.js +233 -0
  483. package/dist/sdk/terminal/tui/pages/followUpQueuePage.d.ts +34 -0
  484. package/dist/sdk/terminal/tui/pages/followUpQueuePage.js +130 -0
  485. package/dist/sdk/terminal/tui/pages/hubPage.d.ts +52 -0
  486. package/dist/sdk/terminal/tui/pages/hubPage.js +292 -0
  487. package/dist/sdk/terminal/tui/pages/logPage.d.ts +38 -0
  488. package/dist/sdk/terminal/tui/pages/logPage.js +189 -0
  489. package/dist/sdk/terminal/tui/pages/permissionsPage.d.ts +44 -0
  490. package/dist/sdk/terminal/tui/pages/permissionsPage.js +124 -0
  491. package/dist/sdk/terminal/tui/pages/pluginsPage.d.ts +65 -0
  492. package/dist/sdk/terminal/tui/pages/pluginsPage.js +519 -0
  493. package/dist/sdk/terminal/tui/pages/remoteControlPage.d.ts +32 -0
  494. package/dist/sdk/terminal/tui/pages/remoteControlPage.js +170 -0
  495. package/dist/sdk/terminal/tui/pages/settingsPage.d.ts +63 -0
  496. package/dist/sdk/terminal/tui/pages/settingsPage.js +103 -0
  497. package/dist/sdk/terminal/tui/pages/subagentsPage.d.ts +24 -0
  498. package/dist/sdk/terminal/tui/pages/subagentsPage.js +155 -0
  499. package/dist/sdk/terminal/tui/pages/textPage.d.ts +17 -0
  500. package/dist/sdk/terminal/tui/pages/textPage.js +52 -0
  501. package/dist/sdk/terminal/tui/pages/trajectoryPage.d.ts +66 -0
  502. package/dist/sdk/terminal/tui/pages/trajectoryPage.js +490 -0
  503. package/dist/sdk/terminal/tui/pages/usagePage.d.ts +45 -0
  504. package/dist/sdk/terminal/tui/pages/usagePage.js +397 -0
  505. package/dist/sdk/terminal/tui/pages/worktreesPage.d.ts +72 -0
  506. package/dist/sdk/terminal/tui/pages/worktreesPage.js +203 -0
  507. package/dist/sdk/terminal/tui/piAdapter.d.ts +17 -0
  508. package/dist/sdk/terminal/tui/piAdapter.js +46 -0
  509. package/dist/sdk/terminal/tui/planMode.d.ts +2 -0
  510. package/dist/sdk/terminal/tui/planMode.js +2 -0
  511. package/dist/sdk/terminal/tui/pluginCommands.d.ts +16 -0
  512. package/dist/sdk/terminal/tui/pluginCommands.js +41 -0
  513. package/dist/sdk/terminal/tui/qr.d.ts +4 -0
  514. package/dist/sdk/terminal/tui/qr.js +35 -0
  515. package/dist/sdk/terminal/tui/questionPicker.d.ts +20 -0
  516. package/dist/sdk/terminal/tui/questionPicker.js +150 -0
  517. package/dist/sdk/terminal/tui/questions.d.ts +21 -0
  518. package/dist/sdk/terminal/tui/questions.js +42 -0
  519. package/dist/sdk/terminal/tui/runtime.d.ts +117 -0
  520. package/dist/sdk/terminal/tui/runtime.js +544 -0
  521. package/dist/sdk/terminal/tui/search.d.ts +22 -0
  522. package/dist/sdk/terminal/tui/search.js +61 -0
  523. package/dist/sdk/terminal/tui/selection.d.ts +16 -0
  524. package/dist/sdk/terminal/tui/selection.js +16 -0
  525. package/dist/sdk/terminal/tui/sessionPicker.d.ts +40 -0
  526. package/dist/sdk/terminal/tui/sessionPicker.js +141 -0
  527. package/dist/sdk/terminal/tui/sessionUi.d.ts +3 -0
  528. package/dist/sdk/terminal/tui/sessionUi.js +30 -0
  529. package/dist/sdk/terminal/tui/settingsModel.d.ts +14 -0
  530. package/dist/sdk/terminal/tui/settingsModel.js +195 -0
  531. package/dist/sdk/terminal/tui/sideQuestion.d.ts +57 -0
  532. package/dist/sdk/terminal/tui/sideQuestion.js +241 -0
  533. package/dist/sdk/terminal/tui/skillInvocation.d.ts +28 -0
  534. package/dist/sdk/terminal/tui/skillInvocation.js +63 -0
  535. package/dist/sdk/terminal/tui/status.d.ts +18 -0
  536. package/dist/sdk/terminal/tui/status.js +67 -0
  537. package/dist/sdk/terminal/tui/theme.d.ts +54 -0
  538. package/dist/sdk/terminal/tui/theme.js +185 -0
  539. package/dist/sdk/terminal/tui/thinkingLevels.d.ts +11 -0
  540. package/dist/sdk/terminal/tui/thinkingLevels.js +31 -0
  541. package/dist/sdk/terminal/tui/treeModel.d.ts +79 -0
  542. package/dist/sdk/terminal/tui/treeModel.js +483 -0
  543. package/dist/sdk/terminal/tui/treePicker.d.ts +49 -0
  544. package/dist/sdk/terminal/tui/treePicker.js +251 -0
  545. package/dist/sdk/terminal/tui/ui.d.ts +49 -0
  546. package/dist/sdk/terminal/tui/ui.js +149 -0
  547. package/dist/sdk/terminal/tui/variantCommands.d.ts +32 -0
  548. package/dist/sdk/terminal/tui/variantCommands.js +359 -0
  549. package/dist/sdk/terminal/tui/workingStatus.d.ts +9 -0
  550. package/dist/sdk/terminal/tui/workingStatus.js +229 -0
  551. package/dist/sdk/terminal/variantControl.d.ts +24 -0
  552. package/dist/sdk/terminal/variantControl.js +145 -0
  553. package/dist/sdk/tools/askUser.d.ts +6 -0
  554. package/dist/sdk/tools/askUser.js +92 -0
  555. package/dist/sdk/tools/defaultSkills.d.ts +23 -0
  556. package/dist/sdk/tools/defaultSkills.js +46 -0
  557. package/dist/sdk/tools/definitions.d.ts +32 -0
  558. package/dist/sdk/tools/definitions.js +133 -0
  559. package/dist/sdk/tools/dispatch.d.ts +16 -0
  560. package/dist/sdk/tools/dispatch.js +92 -0
  561. package/dist/sdk/tools/getCapabilities.d.ts +31 -0
  562. package/dist/sdk/tools/getCapabilities.js +34 -0
  563. package/dist/sdk/tools/harnessTools.d.ts +8 -0
  564. package/dist/sdk/tools/harnessTools.js +20 -0
  565. package/dist/sdk/tools/sessionName.d.ts +16 -0
  566. package/dist/sdk/tools/sessionName.js +78 -0
  567. package/dist/sdk/tools/spawnSubagent.d.ts +4 -0
  568. package/dist/sdk/tools/spawnSubagent.js +63 -0
  569. package/dist/sdk/tools/todo.d.ts +15 -0
  570. package/dist/sdk/tools/todo.js +66 -0
  571. package/dist/sdk/tools/types.d.ts +48 -0
  572. package/dist/sdk/tools/types.js +1 -0
  573. package/dist/sdk/usage/analytics.d.ts +20 -0
  574. package/dist/sdk/usage/analytics.js +151 -0
  575. package/dist/sdk/usage/ledger.d.ts +38 -0
  576. package/dist/sdk/usage/ledger.js +142 -0
  577. package/dist/sdk/usage/limits/adapters.d.ts +11 -0
  578. package/dist/sdk/usage/limits/adapters.js +349 -0
  579. package/dist/sdk/usage/limits/capture.d.ts +8 -0
  580. package/dist/sdk/usage/limits/capture.js +47 -0
  581. package/dist/sdk/usage/limits/http.d.ts +12 -0
  582. package/dist/sdk/usage/limits/http.js +51 -0
  583. package/dist/sdk/usage/limits/normalize.d.ts +15 -0
  584. package/dist/sdk/usage/limits/normalize.js +211 -0
  585. package/dist/sdk/usage/limits/service.d.ts +30 -0
  586. package/dist/sdk/usage/limits/service.js +176 -0
  587. package/dist/sdk/usage/limits/types.d.ts +65 -0
  588. package/dist/sdk/usage/limits/types.js +1 -0
  589. package/dist/sdk/usage/serverMethods.d.ts +3 -0
  590. package/dist/sdk/usage/serverMethods.js +22 -0
  591. package/dist/sdk/usage/service.d.ts +32 -0
  592. package/dist/sdk/usage/service.js +66 -0
  593. package/dist/sdk/usage/settings.d.ts +7 -0
  594. package/dist/sdk/usage/settings.js +28 -0
  595. package/dist/sdk/usage/types.d.ts +60 -0
  596. package/dist/sdk/usage/types.js +4 -0
  597. package/dist/sdk/variant/components.d.ts +34 -0
  598. package/dist/sdk/variant/components.js +230 -0
  599. package/dist/sdk/variant/materialise.d.ts +66 -0
  600. package/dist/sdk/variant/materialise.js +81 -0
  601. package/dist/sdk/variant/migrate.d.ts +4 -0
  602. package/dist/sdk/variant/migrate.js +41 -0
  603. package/dist/sdk/variant/seed.d.ts +20 -0
  604. package/dist/sdk/variant/seed.js +116 -0
  605. package/dist/sdk/variant/store.d.ts +114 -0
  606. package/dist/sdk/variant/store.js +256 -0
  607. package/dist/sdk/variant/surface.d.ts +56 -0
  608. package/dist/sdk/variant/surface.js +81 -0
  609. package/dist/sdk/variant/trust.d.ts +27 -0
  610. package/dist/sdk/variant/trust.js +64 -0
  611. package/dist/sdk/variant/variant.d.ts +71 -0
  612. package/dist/sdk/variant/variant.js +331 -0
  613. package/dist/sdk/web/markdown.d.ts +10 -0
  614. package/dist/sdk/web/markdown.js +66 -0
  615. package/dist/sdk/web/part.d.ts +10 -0
  616. package/dist/sdk/web/part.js +6 -0
  617. package/dist/sdk/web/safeRequest.d.ts +34 -0
  618. package/dist/sdk/web/safeRequest.js +175 -0
  619. package/dist/sdk/web/search.d.ts +53 -0
  620. package/dist/sdk/web/search.js +168 -0
  621. package/dist/sdk/web/serverMethods.d.ts +3 -0
  622. package/dist/sdk/web/serverMethods.js +7 -0
  623. package/dist/sdk/web/text.d.ts +2 -0
  624. package/dist/sdk/web/text.js +7 -0
  625. package/dist/sdk/web/tools.d.ts +22 -0
  626. package/dist/sdk/web/tools.js +195 -0
  627. package/dist/sdk/web/turndown-plugin-gfm.d.ts +4 -0
  628. package/dist/sdk/wiki/notes.d.ts +68 -0
  629. package/dist/sdk/wiki/notes.js +297 -0
  630. package/dist/sdk/wiki/serverMethods.d.ts +3 -0
  631. package/dist/sdk/wiki/serverMethods.js +26 -0
  632. package/dist/sdk/worktrees/cli.d.ts +11 -0
  633. package/dist/sdk/worktrees/cli.js +146 -0
  634. package/dist/sdk/worktrees/copy.d.ts +9 -0
  635. package/dist/sdk/worktrees/copy.js +111 -0
  636. package/dist/sdk/worktrees/git.d.ts +18 -0
  637. package/dist/sdk/worktrees/git.js +89 -0
  638. package/dist/sdk/worktrees/serverMethods.d.ts +3 -0
  639. package/dist/sdk/worktrees/serverMethods.js +30 -0
  640. package/dist/sdk/worktrees/service.d.ts +73 -0
  641. package/dist/sdk/worktrees/service.js +483 -0
  642. package/dist/sdk/worktrees/setup.d.ts +23 -0
  643. package/dist/sdk/worktrees/setup.js +65 -0
  644. package/dist/sdk/worktrees/snapshot.d.ts +16 -0
  645. package/dist/sdk/worktrees/snapshot.js +83 -0
  646. package/dist/sdk/worktrees/types.d.ts +91 -0
  647. package/dist/sdk/worktrees/types.js +1 -0
  648. package/dist/sdk/world/actions.d.ts +23 -0
  649. package/dist/sdk/world/actions.js +31 -0
  650. package/dist/sdk/world/cli.d.ts +12 -0
  651. package/dist/sdk/world/cli.js +166 -0
  652. package/dist/sdk/world/images.d.ts +20 -0
  653. package/dist/sdk/world/images.js +48 -0
  654. package/dist/sdk/world/untrusted.d.ts +12 -0
  655. package/dist/sdk/world/untrusted.js +20 -0
  656. package/dist/sdk/world/world.d.ts +75 -0
  657. package/dist/sdk/world/world.js +117 -0
  658. package/dist/terminal-opentui.js +30986 -0
  659. package/dist/terminal-pi.js +3008 -610
  660. package/docs/README.md +45 -1
  661. package/docs/architecture/00-overview.md +171 -0
  662. package/docs/architecture/01-extension-points.md +78 -0
  663. package/docs/architecture/02-sessions-and-runs.md +505 -0
  664. package/docs/architecture/03-variants.md +122 -0
  665. package/docs/architecture/04-prompt.md +277 -0
  666. package/docs/architecture/05-refinement.md +134 -0
  667. package/docs/architecture/06-plugins-and-capabilities.md +208 -0
  668. package/docs/architecture/07-tools-and-gate.md +245 -0
  669. package/docs/architecture/08-memory-and-history.md +100 -0
  670. package/docs/architecture/09-accounts-and-connectors.md +153 -0
  671. package/docs/architecture/10-server-protocol.md +403 -0
  672. package/docs/architecture/11-terminal.md +555 -0
  673. package/docs/architecture/12-storage.md +118 -0
  674. package/docs/architecture/13-world-tools.md +114 -0
  675. package/docs/architecture/14-surfaces-and-parity.md +151 -0
  676. package/docs/architecture/15-remote-control.md +120 -0
  677. package/docs/architecture/README.md +143 -0
  678. package/docs/architecture/proposals/PROP-0001-reporting-section.md +106 -0
  679. package/docs/architecture/proposals/PROP-0002-retain-rollout-workspaces.md +104 -0
  680. package/docs/architecture/proposals/PROP-0003-executor-settings.md +81 -0
  681. package/docs/architecture/proposals/PROP-0004-flexible-reporting-guidance.md +71 -0
  682. package/docs/architecture/proposals/PROP-0005-xharness-cli-name.md +78 -0
  683. package/docs/architecture/proposals/PROP-0006-run-checklist.md +72 -0
  684. package/docs/architecture/proposals/PROP-0007-run-continuation.md +80 -0
  685. package/docs/architecture/proposals/PROP-0008-lessons-from-runs.md +80 -0
  686. package/docs/architecture/proposals/PROP-0009-foundation-and-variants.md +78 -0
  687. package/docs/architecture/proposals/PROP-0010-session-names.md +73 -0
  688. package/docs/architecture/proposals/PROP-0011-doctor.md +63 -0
  689. package/docs/architecture/proposals/PROP-0012-release-baseline.md +56 -0
  690. package/docs/architecture/proposals/PROP-0013-resource-browser.md +58 -0
  691. package/docs/architecture/proposals/PROP-0014-fullscreen-browser-tabs.md +58 -0
  692. package/docs/architecture/proposals/PROP-0015-one-capability-surface.md +88 -0
  693. package/docs/architecture/proposals/PROP-0016-managed-worktrees.md +156 -0
  694. package/docs/architecture/proposals/PROP-0017-page-annotations.md +131 -0
  695. package/docs/architecture/proposals/PROP-0018-annotation-queue-and-app-targets.md +183 -0
  696. package/docs/architecture/proposals/PROP-0019-contextual-side-questions.md +122 -0
  697. package/docs/architecture/proposals/PROP-0020-builtin-feature-plugins.md +105 -0
  698. package/docs/architecture/proposals/PROP-0021-command-namespaces-and-duplicates.md +99 -0
  699. package/docs/architecture/proposals/PROP-0022-plan-only-questions.md +102 -0
  700. package/docs/architecture/proposals/PROP-0023-xagent-cli-name.md +61 -0
  701. package/docs/architecture/proposals/PROP-0024-question-sketch-previews.md +130 -0
  702. package/docs/architecture/proposals/PROP-0025-contextual-question-wireframes.md +49 -0
  703. package/docs/architecture/proposals/PROP-0026-connected-sketch-display.md +96 -0
  704. package/docs/architecture/proposals/PROP-0027-xingular-agent-product-identity.md +60 -0
  705. package/docs/architecture/proposals/PROP-0028-normalize-question-wireframes.md +55 -0
  706. package/docs/architecture/proposals/PROP-0029-agent-profiles.md +124 -0
  707. package/docs/architecture/proposals/PROP-0030-subagent-title.md +86 -0
  708. package/docs/architecture/proposals/PROP-0031-plugin-command-owner-namespaces.md +54 -0
  709. package/docs/architecture/proposals/PROP-0032-planning-quality.md +196 -0
  710. package/docs/architecture/proposals/PROP-0033-compaction-plan-checkpoints.md +75 -0
  711. package/docs/architecture/proposals/PROP-0034-conversation-fast-mode.md +120 -0
  712. package/docs/architecture/proposals/PROP-0035-retain-subagent-status.md +47 -0
  713. package/docs/architecture/proposals/PROP-0036-first-class-harness-commands.md +59 -0
  714. package/docs/architecture/proposals/PROP-0037-workspace-write-boundary.md +63 -0
  715. package/docs/architecture/proposals/PROP-0038-annotate-open-shadow-roots.md +73 -0
  716. package/docs/architecture/proposals/PROP-0039-workspace-boundary-hardening.md +139 -0
  717. package/docs/architecture/proposals/PROP-0040-one-launch-path.md +87 -0
  718. package/docs/architecture/proposals/PROP-0041-bounded-work-slices.md +260 -0
  719. package/docs/architecture/proposals/PROP-0042-usage-and-provider-limits.md +127 -0
  720. package/docs/architecture/proposals/PROP-0043-read-only-plan-bash.md +74 -0
  721. package/docs/architecture/proposals/PROP-0044-work-slice-defaults.md +93 -0
  722. package/docs/architecture/proposals/PROP-0045-rename-checklist-to-todo.md +132 -0
  723. package/docs/architecture/proposals/PROP-0046-progress-aware-stall-guards.md +82 -0
  724. package/docs/architecture/proposals/PROP-0047-plugins-hub-separation.md +122 -0
  725. package/docs/architecture/proposals/PROP-0048-simple-annotate.md +85 -0
  726. package/docs/architecture/proposals/PROP-0049-context-attribution.md +415 -0
  727. package/docs/architecture/proposals/PROP-0050-run-stop-codes-and-refusals.md +30 -0
  728. package/docs/architecture/proposals/PROP-0051-unfinished-run-handoff.md +110 -0
  729. package/docs/architecture/proposals/PROP-0052-terminal-refine-and-variant-switch.md +111 -0
  730. package/docs/architecture/proposals/README.md +123 -0
  731. package/docs/decisions/ADR-0001-owned-terminal.md +10 -3
  732. package/docs/decisions/ADR-0003-shared-asynchronous-subagents.md +3 -1
  733. package/docs/decisions/ADR-0004-fullscreen-terminal-layout.md +3 -2
  734. package/docs/decisions/ADR-0006-opentui-only-terminal.md +47 -0
  735. package/docs/decisions/ADR-0007-reporting-section.md +33 -0
  736. package/docs/decisions/ADR-0008-flexible-reporting-guidance.md +41 -0
  737. package/docs/decisions/ADR-0009-xharness-command.md +39 -0
  738. package/docs/decisions/ADR-0010-lessons-in-the-wiki.md +31 -0
  739. package/docs/decisions/ADR-0011-foundation-and-variants.md +28 -0
  740. package/docs/decisions/ADR-0012-native-session-names.md +34 -0
  741. package/docs/decisions/ADR-0013-doctor.md +27 -0
  742. package/docs/decisions/ADR-0014-public-release-baseline.md +22 -0
  743. package/docs/decisions/ADR-0015-resource-browser.md +28 -0
  744. package/docs/decisions/ADR-0016-fullscreen-browser-tabs.md +31 -0
  745. package/docs/decisions/ADR-0017-one-capability-surface.md +34 -0
  746. package/docs/decisions/ADR-0018-builtin-feature-plugins.md +35 -0
  747. package/docs/decisions/ADR-0019-managed-git-worktrees.md +31 -0
  748. package/docs/decisions/ADR-0020-page-annotations.md +37 -0
  749. package/docs/decisions/ADR-0021-required-command-namespaces.md +40 -0
  750. package/docs/decisions/ADR-0022-plan-only-question-tools.md +34 -0
  751. package/docs/decisions/ADR-0023-contextual-side-question-runs.md +35 -0
  752. package/docs/decisions/ADR-0024-xagent-command.md +40 -0
  753. package/docs/decisions/ADR-0025-question-choice-sketches.md +49 -0
  754. package/docs/decisions/ADR-0026-contextual-question-wireframes.md +58 -0
  755. package/docs/decisions/ADR-0027-connected-sketch-display.md +62 -0
  756. package/docs/decisions/ADR-0028-xingular-agent-product-identity.md +56 -0
  757. package/docs/decisions/ADR-0029-normalized-question-wireframe-borders.md +33 -0
  758. package/docs/decisions/ADR-0030-agent-profiles.md +43 -0
  759. package/docs/decisions/ADR-0031-plugin-command-owner-namespaces.md +37 -0
  760. package/docs/decisions/ADR-0032-compaction-run-checkpoints.md +35 -0
  761. package/docs/decisions/ADR-0033-plan-mode-research-delegation.md +47 -0
  762. package/docs/decisions/ADR-0034-first-class-harness-terminal-commands.md +25 -0
  763. package/docs/decisions/ADR-0035-one-launch-path.md +40 -0
  764. package/docs/decisions/ADR-0036-workspace-write-operation-boundary.md +54 -0
  765. package/docs/decisions/ADR-0037-usage-ledger-and-provider-limits.md +44 -0
  766. package/docs/decisions/ADR-0038-plan-bash-os-read-only.md +29 -0
  767. package/docs/decisions/ADR-0039-progress-aware-stall-guards.md +43 -0
  768. package/docs/decisions/ADR-0040-architecture-change-workflow.md +31 -0
  769. package/docs/decisions/ADR-0041-plugins-hub-separation.md +41 -0
  770. package/docs/decisions/ADR-0042-windows-shell-without-sandbox.md +40 -0
  771. package/docs/decisions/ADR-0043-parents-continue-stopped-subagents.md +49 -0
  772. package/docs/decisions/ADR-0044-annotations-pending-or-sent.md +24 -0
  773. package/docs/decisions/ADR-0045-default-plugin-authoring-skill.md +9 -0
  774. package/docs/decisions/ADR-0046-hash-bound-plugin-proposals.md +20 -0
  775. package/docs/decisions/ADR-0047-links-open-with-the-os-handler.md +47 -0
  776. package/docs/decisions/ADR-0048-required-skills-from-a-message.md +42 -0
  777. package/docs/decisions/ADR-0050-approved-window-screenshots.md +19 -0
  778. package/docs/decisions/ADR-0051-terminal-decision-wait.md +10 -0
  779. package/docs/decisions/ADR-0052-per-profile-subagent-budgets.md +25 -0
  780. package/docs/decisions/ADR-0053-no-repeated-work-guard.md +45 -0
  781. package/docs/decisions/ADR-0054-terminal-reply-heartbeat-and-stall.md +52 -0
  782. package/docs/decisions/ADR-0055-plan-output-repair.md +51 -0
  783. package/docs/decisions/ADR-0056-remote-control.md +53 -0
  784. package/docs/decisions/ADR-0057-structured-plan-outcomes.md +39 -0
  785. package/docs/decisions/ADR-0058-ask-user-only-plan-clarification.md +36 -0
  786. package/docs/decisions/ADR-0059-run-diagnostic-evidence.md +35 -0
  787. package/docs/decisions/ADR-0060-approvals-are-controls.md +40 -0
  788. package/docs/decisions/ADR-0061-strict-plugin-command-namespaces.md +41 -0
  789. package/docs/decisions/ADR-0062-shared-account-state.md +67 -0
  790. package/docs/decisions/ADR-0063-unfinished-work-handoffs.md +34 -0
  791. package/docs/decisions/ADR-0064-account-model-fallback-on-resume.md +79 -0
  792. package/docs/decisions/ADR-0065-variant-switch-at-turn-boundaries.md +31 -0
  793. package/docs/decisions/ADR-0066-guided-refinement-from-turn-snapshots.md +37 -0
  794. package/docs/decisions/ADR-0067-cache-stable-compaction-checkpoint.md +54 -0
  795. package/docs/decisions/ADR-0068-scope-and-completion-report.md +60 -0
  796. package/docs/decisions/ADR-0069-stop-gate-and-finish.md +136 -0
  797. package/docs/decisions/ADR-0070-terminal-slice-continuation.md +49 -0
  798. package/docs/decisions/ADR-0071-completion-check.md +104 -0
  799. package/docs/decisions/ADR-0072-codex-prompts-and-goal.md +108 -0
  800. package/docs/decisions/ADR-0073-large-reporting-prompt.md +89 -0
  801. package/docs/decisions/ADR-0074-report-content-not-report-blocks.md +90 -0
  802. package/docs/decisions/ADR-0075-no-todo-gate.md +84 -0
  803. package/docs/decisions/ADR-0076-codex-final-answer-verbatim.md +98 -0
  804. package/docs/decisions/ADR-0077-plan-runs-may-conclude-no-plan-needed.md +120 -0
  805. package/docs/decisions/ADR-0078-child-aware-stall-watch.md +46 -0
  806. package/docs/decisions/ADR-0079-phase-led-reporting-guidance.md +72 -0
  807. package/docs/decisions/ADR-0080-fixed-width-question-sketch-canvas.md +50 -0
  808. package/docs/decisions/ADR-0081-no-work-slice-budget-by-default.md +36 -0
  809. package/docs/decisions/ADR-0082-connected-brave-browser.md +72 -0
  810. package/docs/decisions/ADR-0083-one-live-session-runtime.md +256 -0
  811. package/docs/doctor.md +95 -0
  812. package/docs/experiments/EXP-0001-terminal-input-latency/plan.md +81 -0
  813. package/docs/experiments/EXP-0001-terminal-input-latency/results.md +46 -0
  814. package/docs/experiments/EXP-0002-opentui-typing-latency/plan.md +70 -0
  815. package/docs/experiments/EXP-0002-opentui-typing-latency/results.md +92 -0
  816. package/docs/experiments/EXP-0003-performance-audit/data/session-runtime-replication.json +420 -0
  817. package/docs/experiments/EXP-0003-performance-audit/data/session-runtime.json +420 -0
  818. package/docs/experiments/EXP-0003-performance-audit/data/terminal-compact.json +376 -0
  819. package/docs/experiments/EXP-0003-performance-audit/data/terminal-history.json +422 -0
  820. package/docs/experiments/EXP-0003-performance-audit/data/terminal-question.json +95 -0
  821. package/docs/experiments/EXP-0003-performance-audit/data/terminal-wide.json +1036 -0
  822. package/docs/experiments/EXP-0003-performance-audit/plan.md +109 -0
  823. package/docs/experiments/EXP-0003-performance-audit/results.md +151 -0
  824. package/docs/features/README.md +46 -0
  825. package/docs/features/automations/README.md +41 -0
  826. package/docs/features/cores/README.md +39 -0
  827. package/docs/features/google-harness2/README.md +52 -0
  828. package/docs/features/integrations/README.md +40 -0
  829. package/docs/features/interfaces/README.md +29 -0
  830. package/docs/features/memory/README.md +28 -0
  831. package/docs/future-work.md +48 -0
  832. package/docs/handoff-2026-10-25-app-parity-and-solo-tabs.md +202 -0
  833. package/docs/parity.md +158 -0
  834. package/docs/releases/1.0.0.md +40 -0
  835. package/docs/releases/1.1.1.md +28 -0
  836. package/docs/releases/1.2.0.md +49 -0
  837. package/docs/releases/1.3.0.md +21 -0
  838. package/docs/releases/1.3.1-unstable.1.md +8 -0
  839. package/docs/releases/1.3.1-unstable.2.md +11 -0
  840. package/docs/releases/1.3.1-unstable.3.md +54 -0
  841. package/docs/releases/1.3.1-unstable.4.md +56 -0
  842. package/docs/releases/1.3.1-unstable.5.md +53 -0
  843. package/docs/releases/1.3.1-unstable.6.md +31 -0
  844. package/docs/releases/1.3.1-unstable.7.md +17 -0
  845. package/docs/releases/1.3.1-unstable.8.md +19 -0
  846. package/docs/releases/1.3.1-unstable.9.md +54 -0
  847. package/docs/releases/1.4.0-unstable.md +25 -0
  848. package/docs/releases/1.5.0-unstable.md +32 -0
  849. package/docs/releases/1.5.1-unstable.md +24 -0
  850. package/docs/releases/1.6.0-unstable.md +59 -0
  851. package/docs/releases/2.0.1-unstable.md +63 -0
  852. package/docs/releases/4.0.0.md +47 -0
  853. package/docs/releases/README.md +49 -0
  854. package/docs/study/00-index.md +112 -0
  855. package/docs/study/STUDY-0003-long-horizon-runs.md +210 -0
  856. package/docs/study/STUDY-0004-turn-end-controls.md +130 -0
  857. package/docs/study/STUDY-0005-skill-installation-guidance.md +146 -0
  858. package/docs/study/STUDY-0006-reporting-prompts.md +119 -0
  859. package/docs/study/STUDY-0007-claude-code-prompt.md +162 -0
  860. package/docs/study/STUDY-0008-browser-control.md +281 -0
  861. package/docs/study/STUDY-0009-claude-code-reporting.md +205 -0
  862. package/docs/study/arms/00-matrix.md +20 -0
  863. package/docs/study/arms/01-system-prompt.md +211 -0
  864. package/docs/study/arms/02-tools.md +79 -0
  865. package/docs/study/arms/03-subagents.md +90 -0
  866. package/docs/study/arms/04-skills.md +81 -0
  867. package/docs/study/arms/05-context-management.md +90 -0
  868. package/docs/study/arms/06-memory.md +90 -0
  869. package/docs/study/arms/07-governance.md +71 -0
  870. package/docs/study/arms/08-execution.md +69 -0
  871. package/docs/study/arms/09-observability.md +60 -0
  872. package/docs/study/arms/10-refinement.md +110 -0
  873. package/docs/study/autonomous-engineering-sources.md +277 -0
  874. package/docs/study/autonomy-sources.md +147 -0
  875. package/docs/study/btw-comparison.md +37 -0
  876. package/docs/study/feature-gap-review.md +42 -0
  877. package/docs/study/insights.md +123 -0
  878. package/docs/study/proactive-autonomy.md +408 -0
  879. package/docs/study/roadmap.md +72 -0
  880. package/docs/study/sources/claude-code.md +56 -0
  881. package/docs/study/sources/deepseek-harness.md +102 -0
  882. package/docs/study/sources/harness2.md +50 -0
  883. package/docs/study/sources/kimi-cli.md +42 -0
  884. package/docs/study/sources/omp.md +93 -0
  885. package/docs/study/sources/opencode.md +43 -0
  886. package/docs/study/sources/openhands.md +46 -0
  887. package/docs/study/sources/smolagents.md +40 -0
  888. package/docs/study/sources/swe-agent.md +34 -0
  889. package/docs/study/tui-resource-browser.md +78 -0
  890. package/docs/system-prompt-design.md +196 -0
  891. package/docs/terminal-tui.md +887 -109
  892. package/package.json +46 -13
  893. package/src/accounts/accounts.test.ts +0 -419
  894. package/src/accounts/accounts.ts +0 -760
  895. package/src/accounts/env.test.ts +0 -95
  896. package/src/accounts/env.ts +0 -338
  897. package/src/accounts/secrets.ts +0 -50
  898. package/src/accounts/store.ts +0 -338
  899. package/src/capabilities/api.ts +0 -75
  900. package/src/capabilities/runSnapshot.ts +0 -59
  901. package/src/capabilities/types.ts +0 -34
  902. package/src/cli.ts +0 -118
  903. package/src/core/bus.test.ts +0 -22
  904. package/src/core/bus.ts +0 -49
  905. package/src/core/changes.ts +0 -33
  906. package/src/core/env.ts +0 -27
  907. package/src/core/errors.ts +0 -32
  908. package/src/core/frontmatter.ts +0 -47
  909. package/src/core/fs.test.ts +0 -38
  910. package/src/core/fs.ts +0 -92
  911. package/src/core/index.ts +0 -193
  912. package/src/core/path.test.ts +0 -14
  913. package/src/core/path.ts +0 -79
  914. package/src/core/projects.ts +0 -90
  915. package/src/core/text.ts +0 -39
  916. package/src/harness.ts +0 -322
  917. package/src/hub/catalog.json +0 -123
  918. package/src/hub/cli.ts +0 -59
  919. package/src/hub/hub.test.ts +0 -205
  920. package/src/hub/hub.ts +0 -256
  921. package/src/hub/schema.ts +0 -81
  922. package/src/index.ts +0 -80
  923. package/src/mcp/connections.ts +0 -641
  924. package/src/mcp/manager.ts +0 -130
  925. package/src/mcp/oauth.ts +0 -195
  926. package/src/mcp/scopeMigration.test.ts +0 -79
  927. package/src/mcp/scopeMigration.ts +0 -204
  928. package/src/mcp/service.test.ts +0 -422
  929. package/src/mcp/service.ts +0 -560
  930. package/src/mcp/types.ts +0 -118
  931. package/src/memory/api.test.ts +0 -82
  932. package/src/memory/api.ts +0 -303
  933. package/src/memory/blocks.test.ts +0 -78
  934. package/src/memory/blocks.ts +0 -408
  935. package/src/memory/caller.ts +0 -69
  936. package/src/memory/curation.test.ts +0 -206
  937. package/src/memory/curation.ts +0 -64
  938. package/src/memory/dispatch.ts +0 -64
  939. package/src/memory/facts.test.ts +0 -41
  940. package/src/memory/facts.ts +0 -248
  941. package/src/memory/graph.ts +0 -103
  942. package/src/memory/health.test.ts +0 -130
  943. package/src/memory/health.ts +0 -74
  944. package/src/memory/maintainer.ts +0 -272
  945. package/src/memory/maintenance.test.ts +0 -192
  946. package/src/memory/maintenance.ts +0 -679
  947. package/src/memory/migrate.ts +0 -79
  948. package/src/memory/operations.ts +0 -150
  949. package/src/memory/proposals.test.ts +0 -168
  950. package/src/memory/proposals.ts +0 -584
  951. package/src/memory/recall.test.ts +0 -115
  952. package/src/memory/recall.ts +0 -279
  953. package/src/memory/references.ts +0 -77
  954. package/src/memory/runContext.ts +0 -93
  955. package/src/memory/secrets.ts +0 -6
  956. package/src/memory/store.test.ts +0 -66
  957. package/src/memory/store.ts +0 -385
  958. package/src/memory/trajectory.test.ts +0 -178
  959. package/src/memory/trajectory.ts +0 -220
  960. package/src/memory/wiki.test.ts +0 -47
  961. package/src/memory/wiki.ts +0 -423
  962. package/src/memory/wikiIndex.test.ts +0 -88
  963. package/src/memory/wikiIndex.ts +0 -117
  964. package/src/plugins/api.ts +0 -181
  965. package/src/plugins/attachments.test.ts +0 -84
  966. package/src/plugins/attachments.ts +0 -141
  967. package/src/plugins/builtin.ts +0 -84
  968. package/src/plugins/catalogue.test.ts +0 -254
  969. package/src/plugins/catalogue.ts +0 -251
  970. package/src/plugins/cli.ts +0 -184
  971. package/src/plugins/discover.ts +0 -151
  972. package/src/plugins/firstParty.test.ts +0 -143
  973. package/src/plugins/firstParty.ts +0 -129
  974. package/src/plugins/hooks.test.ts +0 -250
  975. package/src/plugins/hooks.ts +0 -239
  976. package/src/plugins/identity.ts +0 -37
  977. package/src/plugins/install.test.ts +0 -107
  978. package/src/plugins/install.ts +0 -106
  979. package/src/plugins/inventory.ts +0 -123
  980. package/src/plugins/manifest.ts +0 -76
  981. package/src/plugins/plugins.test.ts +0 -205
  982. package/src/plugins/plugins.ts +0 -189
  983. package/src/plugins/proposal.ts +0 -121
  984. package/src/plugins/runtime.test.ts +0 -40
  985. package/src/plugins/runtime.ts +0 -82
  986. package/src/plugins/trust.ts +0 -142
  987. package/src/plugins/types.ts +0 -149
  988. package/src/prompt/compile.test.ts +0 -261
  989. package/src/prompt/compile.ts +0 -732
  990. package/src/server/methods.ts +0 -355
  991. package/src/server/remote.ts +0 -77
  992. package/src/server/rpc.ts +0 -116
  993. package/src/server/serve.ts +0 -28
  994. package/src/server/server.test.ts +0 -193
  995. package/src/sessions/approvals.test.ts +0 -49
  996. package/src/sessions/approvals.ts +0 -140
  997. package/src/sessions/contracts.ts +0 -336
  998. package/src/sessions/conversation.test.ts +0 -233
  999. package/src/sessions/extensionSession.test.ts +0 -74
  1000. package/src/sessions/extensionUi.test.ts +0 -65
  1001. package/src/sessions/extensionUi.ts +0 -102
  1002. package/src/sessions/history.ts +0 -51
  1003. package/src/sessions/lineageJoin.ts +0 -75
  1004. package/src/sessions/policy.ts +0 -36
  1005. package/src/sessions/pricing.ts +0 -60
  1006. package/src/sessions/questionBatches.test.ts +0 -110
  1007. package/src/sessions/reasoningSummaries.ts +0 -59
  1008. package/src/sessions/registry.ts +0 -412
  1009. package/src/sessions/runIndex.ts +0 -99
  1010. package/src/sessions/runStore.test.ts +0 -152
  1011. package/src/sessions/runStore.ts +0 -346
  1012. package/src/sessions/runner.ts +0 -610
  1013. package/src/sessions/runtimeResources.test.ts +0 -61
  1014. package/src/sessions/sessions.test.ts +0 -588
  1015. package/src/sessions/sessions.ts +0 -331
  1016. package/src/sessions/subagentService.test.ts +0 -159
  1017. package/src/sessions/subagentService.ts +0 -236
  1018. package/src/sessions/subagents.ts +0 -97
  1019. package/src/skills/registry.test.ts +0 -104
  1020. package/src/skills/registry.ts +0 -413
  1021. package/src/terminal/accountsMenu.test.ts +0 -225
  1022. package/src/terminal/accountsMenu.ts +0 -382
  1023. package/src/terminal/cli.ts +0 -149
  1024. package/src/terminal/client.ts +0 -74
  1025. package/src/terminal/control.test.ts +0 -23
  1026. package/src/terminal/control.ts +0 -23
  1027. package/src/terminal/controller.ts +0 -354
  1028. package/src/terminal/hubMenu.ts +0 -24
  1029. package/src/terminal/launch.test.ts +0 -25
  1030. package/src/terminal/launch.ts +0 -48
  1031. package/src/terminal/piExtension.lifecycle.test.ts +0 -55
  1032. package/src/terminal/piExtension.ts +0 -295
  1033. package/src/terminal/pluginControl.test.ts +0 -146
  1034. package/src/terminal/pluginMenu.ts +0 -112
  1035. package/src/terminal/socket.test.ts +0 -94
  1036. package/src/terminal/socket.ts +0 -256
  1037. package/src/terminal/standalone.test.ts +0 -167
  1038. package/src/terminal/standalone.ts +0 -165
  1039. package/src/terminal/terminal.test.ts +0 -534
  1040. package/src/terminal/terminals.ts +0 -661
  1041. package/src/terminal/trajectory.test.ts +0 -66
  1042. package/src/terminal/trajectory.ts +0 -44
  1043. package/src/terminal/tui/application.test.ts +0 -290
  1044. package/src/terminal/tui/application.ts +0 -653
  1045. package/src/terminal/tui/commands.test.ts +0 -278
  1046. package/src/terminal/tui/commands.ts +0 -443
  1047. package/src/terminal/tui/dialogs.ts +0 -301
  1048. package/src/terminal/tui/editor.test.ts +0 -113
  1049. package/src/terminal/tui/editor.ts +0 -109
  1050. package/src/terminal/tui/keybindings.test.ts +0 -30
  1051. package/src/terminal/tui/keybindings.ts +0 -59
  1052. package/src/terminal/tui/piAdapter.ts +0 -62
  1053. package/src/terminal/tui/planMode.test.ts +0 -10
  1054. package/src/terminal/tui/planMode.ts +0 -12
  1055. package/src/terminal/tui/presentation.test.ts +0 -79
  1056. package/src/terminal/tui/questions.test.ts +0 -41
  1057. package/src/terminal/tui/questions.ts +0 -47
  1058. package/src/terminal/tui/runtime.socket.test.ts +0 -178
  1059. package/src/terminal/tui/runtime.test.ts +0 -225
  1060. package/src/terminal/tui/runtime.ts +0 -403
  1061. package/src/terminal/tui/screen.test.ts +0 -143
  1062. package/src/terminal/tui/screen.ts +0 -121
  1063. package/src/terminal/tui/sessionUi.ts +0 -24
  1064. package/src/terminal/tui/settings.test.ts +0 -150
  1065. package/src/terminal/tui/settings.ts +0 -188
  1066. package/src/terminal/tui/skillInvocation.test.ts +0 -71
  1067. package/src/terminal/tui/skillInvocation.ts +0 -51
  1068. package/src/terminal/tui/status.test.ts +0 -48
  1069. package/src/terminal/tui/status.ts +0 -82
  1070. package/src/terminal/tui/theme.ts +0 -185
  1071. package/src/terminal/tui/tools.test.ts +0 -145
  1072. package/src/terminal/tui/tools.ts +0 -142
  1073. package/src/terminal/tui/trajectoryPanel.test.ts +0 -99
  1074. package/src/terminal/tui/trajectoryPanel.ts +0 -226
  1075. package/src/terminal/tui/transcript.test.ts +0 -39
  1076. package/src/terminal/tui/transcript.ts +0 -135
  1077. package/src/terminal/tui/ui.test.ts +0 -313
  1078. package/src/terminal/tui/ui.ts +0 -166
  1079. package/src/terminal/tui/welcome.ts +0 -102
  1080. package/src/terminal/tui/workingStatus.test.ts +0 -44
  1081. package/src/terminal/tui/workingStatus.ts +0 -238
  1082. package/src/tools/askUser.test.ts +0 -69
  1083. package/src/tools/askUser.ts +0 -59
  1084. package/src/tools/definitions.ts +0 -228
  1085. package/src/tools/maintenance.ts +0 -29
  1086. package/src/tools/memoryTools.ts +0 -22
  1087. package/src/tools/pluginPropose.ts +0 -41
  1088. package/src/tools/registryWrite.test.ts +0 -101
  1089. package/src/tools/registryWrite.ts +0 -182
  1090. package/src/tools/spawnSubagent.ts +0 -47
  1091. package/src/tools/types.ts +0 -41
  1092. package/src/web/safeRequest.ts +0 -171
  1093. package/src/web/text.ts +0 -53
  1094. package/src/web/tools.ts +0 -134
  1095. package/src/web/web.test.ts +0 -141
@@ -0,0 +1,1782 @@
1
+ import { runDetails } from "../context/runDetails.js";
2
+ // A host owns the PTY and process supervision; Harness owns the terminal application inside it.
3
+ // This service prepares the isolated account, compiled context, trusted resource selection and
4
+ // socket bridge. The default launch runs our frontend with Pi's persistent SDK runtime.
5
+ import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
6
+ import { basename, dirname, join } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+ import { Type } from 'typebox';
9
+ import { freezeRun } from "../capabilities/runSnapshot.js";
10
+ import { SPAWN_SUBAGENT, WAIT_SUBAGENTS } from "../sessions/subagents.js";
11
+ import { subagentTools } from "../tools/spawnSubagent.js";
12
+ import { configDir, launchEnv } from "../accounts/env.js";
13
+ import { readAudit } from "../accounts/store.js";
14
+ import { log } from "../core/bus.js";
15
+ import { HarnessError, invalid } from "../core/errors.js";
16
+ import { newId, now } from "../core/fs.js";
17
+ import { formatBashCommandForApproval } from "../core/workspaceBashTool.js";
18
+ import { resolveWorkspaceRoot } from "../core/workspace.js";
19
+ import { prependSearchPath, searchPath } from "../core/path.js";
20
+ import { LiveTerminal, registeredCommands } from "./liveTerminal.js";
21
+ import { loadResources } from "../sessions/runner.js";
22
+ import { loadPi } from "../pi/runtime.js";
23
+ /** A client terminal's decision and stop calls, and the live-session routes that answer them. */
24
+ const LIVE_DECISION_CALLS = {
25
+ 'terminal.pending': 'terminal.live.pending',
26
+ 'terminal.pending.wait': 'terminal.live.pending.wait',
27
+ 'terminal.approve': 'terminal.live.approve',
28
+ 'terminal.answer': 'terminal.live.answer',
29
+ 'terminal.answers': 'terminal.live.answers',
30
+ 'terminal.interrupt': 'terminal.live.interrupt'
31
+ };
32
+ import { dispatch } from "../tools/dispatch.js";
33
+ import { defaultSkillPaths } from "../tools/defaultSkills.js";
34
+ import { materialise } from "../variant/materialise.js";
35
+ import * as componentLibrary from "../variant/components.js";
36
+ import * as variantStore from "../variant/store.js";
37
+ import { attribution, rowText } from "../context/attribution.js";
38
+ import { compile, copyToRun, record, render, saveForSession } from "../prompt/compile.js";
39
+ import { isActiveStatus } from "../sessions/contracts.js";
40
+ import { findRun, listRuns, loadRun, readEvents, recordedSubagents, saveRun, updateRun } from "../sessions/runStore.js";
41
+ import { combineWorkspaceScope, inspectRunWorkspaceWrite } from "../sessions/workspaceBoundary.js";
42
+ import { TODO_WRITE } from "../tools/todo.js";
43
+ import { GOAL_TOOLS } from "../sessions/goal.js";
44
+ import { DEFAULT_TOOL_NAMES } from "../tools/definitions.js";
45
+ import { harnessTools } from "../tools/harnessTools.js";
46
+ import { HARNESS_PLUGINS } from "../plugins/builtin.js";
47
+ import * as attachments from "../plugins/attachments.js";
48
+ import { FIRST_PARTY_PLUGINS } from "../plugins/firstParty.js";
49
+ import { createProfile, listProfiles, profileDirs, profileSection, removeProfile, resolveProfile } from "../agents/profiles.js";
50
+ import { SESSION_NAME } from "../tools/sessionName.js";
51
+ import { contributionId } from "../plugins/identity.js";
52
+ import { setupScript } from "../worktrees/setup.js";
53
+ import { inspectStep } from "../annotations/inspect.js";
54
+ import * as pageNotes from "../annotations/store.js";
55
+ import { APPS_COMMAND, WIKI_COMMAND } from "./cli.js";
56
+ import { SOCKET_ENV, TOKEN_ENV } from "./client.js";
57
+ import { TerminalController } from "./controller.js";
58
+ import { terminalCommandFiles } from "./launch.js";
59
+ import { SessionSocket, socketPath } from "./socket.js";
60
+ import { harnessLogInfo, readHarnessLogChunk } from "./logs.js";
61
+ import { listTerminalTrajectoryRuns, readTerminalTrajectory, latestTerminalTodo } from "./trajectory.js";
62
+ import { BrowserControl } from "./browserControl.js";
63
+ import { RemoteControl } from "../remote/service.js";
64
+ import { snapshotWorkingTree } from "../worktrees/snapshot.js";
65
+ import { refineCall, variantCall } from "./variantControl.js";
66
+ /** Identity of the exact resource decision that this terminal was prepared with. */
67
+ function resourceIdentity(loading) {
68
+ if (!loading)
69
+ return 'null';
70
+ return JSON.stringify({
71
+ extensions: loading.extensionPaths, builtins: loading.builtins, skills: loading.skillPaths,
72
+ prompts: loading.promptPaths, resourceOwners: loading.found.filter(item => (item.kind === 'prompt' || item.kind === 'extension') && item.packageRoot), themes: loading.themePaths, profiles: loading.profilePaths, detached: loading.detached,
73
+ project: loading.projectCode, packages: loading.packageCode
74
+ });
75
+ }
76
+ /** Where the package's own build puts them. An application that bundles the harness passes its own. */
77
+ export function defaultAssets() {
78
+ const here = dirname(fileURLToPath(import.meta.url));
79
+ const dist = [here, join(here, '..', '..'), join(here, '..', '..', 'dist'), join(here, '..', 'dist')].find((folder) => existsSync(join(folder, 'terminal-pi.js'))) ?? here;
80
+ return {
81
+ node: process.execPath,
82
+ cli: join(dist, 'cli.js'),
83
+ piExtension: join(dist, 'terminal-pi.js'),
84
+ openTui: join(dist, 'terminal-opentui.js')
85
+ };
86
+ }
87
+ const READ_ONLY_TOOLS = new Set(['read', 'grep', 'find', 'ls']);
88
+ export class Terminals {
89
+ options;
90
+ sessions = new Map();
91
+ diagnosticOwners = new Map();
92
+ /** The variant each live session runs, for loading its skills. */
93
+ variantDirs = new Map();
94
+ /** The subagent profile files each live session's packages bring, chosen when it started. */
95
+ packageProfiles = new Map();
96
+ /** The world tools each session was given, so a call can only reach one of them. */
97
+ worldNames = new Map();
98
+ worldReadOnly = new Set();
99
+ socket;
100
+ assets;
101
+ /** A session's `/accounts` or `/worktree` switch, taken once its Pi process has ended. */
102
+ relaunches = new Map();
103
+ /** Refinements each session started with `/refine`. */
104
+ refinements = new Map();
105
+ /** The managed worktree each live session holds. */
106
+ worktreeLeases = new Map();
107
+ /** `/remote-control` for each live session that opened it. */
108
+ remotes = new Map();
109
+ /** Terminals whose frontend is a client of the harness (ADR-0083). */
110
+ clients = new Map();
111
+ /** Client terminals whose window closed while a reply ran; they end when it settles. */
112
+ detachedClients = new Map();
113
+ /** Each child's profile name and colour, looked up once per child run. */
114
+ childProfiles = new Map();
115
+ subagentProfile(record) {
116
+ if (!record.profile)
117
+ return null;
118
+ if (!this.childProfiles.has(record.id)) {
119
+ if (this.childProfiles.size > 500)
120
+ this.childProfiles.clear();
121
+ let color = 'blue';
122
+ try {
123
+ color = resolveProfile(this.options.env.root, record.workingDirectory, record.profile)?.color ?? color;
124
+ }
125
+ catch { }
126
+ this.childProfiles.set(record.id, { name: record.profile, color });
127
+ }
128
+ return this.childProfiles.get(record.id) ?? null;
129
+ }
130
+ /** Account, model and effort for a monitor row, from the run record. */
131
+ monitorRuntime(live, record) {
132
+ let accountLabel = record.accountId === live.account.id ? live.account.label : null;
133
+ if (accountLabel === null && record.accountId) {
134
+ try {
135
+ accountLabel = this.options.accounts.find(record.accountId).label;
136
+ }
137
+ catch { }
138
+ }
139
+ return { accountLabel, model: record.model ?? null, effort: record.effort ?? null };
140
+ }
141
+ /**
142
+ * Shows the reopened conversation's latest parent run again: the last owner on its native branch
143
+ * that ran in this folder, with every descendant flat, as each ended.
144
+ */
145
+ hydrateMonitor(live, owners) {
146
+ const { root } = this.options.env;
147
+ const native = this.nativeOf(live);
148
+ if (!native)
149
+ return;
150
+ for (const id of [...owners].reverse()) {
151
+ const record = findRun(root, id);
152
+ if (!record || record.relation || record.nativeSessionId !== native || record.workingDirectory !== live.input.cwd)
153
+ continue;
154
+ const children = [];
155
+ const seen = new Set([record.id]);
156
+ const visit = (parentId) => {
157
+ for (const childId of recordedSubagents(root, parentId)) {
158
+ if (seen.has(childId))
159
+ continue;
160
+ seen.add(childId);
161
+ const child = findRun(root, childId);
162
+ if (child?.relation !== 'subagent')
163
+ continue;
164
+ children.push(child);
165
+ visit(child.id);
166
+ }
167
+ };
168
+ visit(record.id);
169
+ const entry = (run, profile = null) => ({ record: run, events: readEvents(root, run.id), profile, runtime: this.monitorRuntime(live, run) });
170
+ live.controller.restoreMonitor(entry(record), children.map(child => entry(child, this.subagentProfile(child))));
171
+ return;
172
+ }
173
+ }
174
+ constructor(options) {
175
+ this.options = options;
176
+ this.assets = options.assets ?? defaultAssets();
177
+ options.runs.listen(event => {
178
+ const session = this.sessions.get(event.sessionId);
179
+ session?.controller.observe(event);
180
+ const record = findRun(options.env.root, event.runId);
181
+ if (record && !record.relation && session) {
182
+ session.controller.observeMain(record, event, this.monitorRuntime(session, record));
183
+ return;
184
+ }
185
+ if (record?.relation !== 'subagent')
186
+ return;
187
+ let parentId = record.parentRunId;
188
+ const seen = new Set();
189
+ while (parentId && !seen.has(parentId)) {
190
+ seen.add(parentId);
191
+ const parent = findRun(options.env.root, parentId);
192
+ if (!parent)
193
+ break;
194
+ const owner = this.sessions.get(parent.sessionId);
195
+ if (owner?.controller.activeRun?.runId === parent.id) {
196
+ owner.controller.observe(event, true);
197
+ const settings = options.subagents?.runtimeSettings(record.id);
198
+ owner.controller.observeSubagent(parent.id, record, event, this.subagentProfile(record), {
199
+ accountLabel: owner.account.label,
200
+ model: settings?.model ?? record.model ?? null,
201
+ effort: settings?.effort ?? record.effort ?? null
202
+ });
203
+ break;
204
+ }
205
+ parentId = parent.parentRunId;
206
+ }
207
+ });
208
+ this.socket = new SessionSocket(socketPath(options.stateDir), {
209
+ memory: (caller, call, params) => dispatch(options.env, caller, call, params, { variantDir: (caller.sessionId && this.variantDirs.get(caller.sessionId)) || null }),
210
+ apps: (session, call, params) => this.appsCall(session, call, params),
211
+ plugins: (grant, call, params, control) => this.pluginsCall(grant.sessionId, call, params, control),
212
+ accounts: (grant, call, params, control) => this.accountsCall(grant.sessionId, call, params, control)
213
+ });
214
+ }
215
+ /** The commands a terminal session finds on PATH. Rewritten only when they change. */
216
+ ensureCommands() {
217
+ const bin = join(this.options.stateDir, 'run', 'bin');
218
+ mkdirSync(bin, { recursive: true });
219
+ for (const name of ['agentx-wiki', 'agentx-mcp', 'agentx-wiki.cmd', 'agentx-mcp.cmd']) {
220
+ const path = join(bin, name);
221
+ try {
222
+ if (lstatSync(path).isFile())
223
+ unlinkSync(path);
224
+ }
225
+ catch {
226
+ // These names belonged to the previous generated wrappers.
227
+ }
228
+ }
229
+ for (const [name, mode] of [[WIKI_COMMAND, 'wiki'], [APPS_COMMAND, 'mcp']]) {
230
+ for (const file of terminalCommandFiles(process.platform, this.assets.node, this.assets.cli, name, mode)) {
231
+ const path = join(bin, file.name);
232
+ let current = null;
233
+ try {
234
+ current = readFileSync(path, 'utf8');
235
+ }
236
+ catch {
237
+ current = null;
238
+ }
239
+ if (current !== file.content) {
240
+ writeFileSync(path, file.content);
241
+ chmodSync(path, 0o755);
242
+ }
243
+ }
244
+ }
245
+ return bin;
246
+ }
247
+ /**
248
+ * The harness's own History, Wiki and skill tools, then the broker's, then the world tools whose
249
+ * plugins are on for this session: the same tools a structured session has (INV-PAR-1).
250
+ */
251
+ async tools(session, listing) {
252
+ const brokered = this.options.apps.brokerTools(session);
253
+ const live = this.sessions.get(session.sessionId);
254
+ if (!live)
255
+ return brokered;
256
+ const own = await this.ownTools(live, listing);
257
+ const { world } = this.options;
258
+ if (!world)
259
+ return [...own, ...brokered];
260
+ const context = { run: live.controller.activeRun, caller: live.caller, attended: true, subagent: false, sessionKey: session.sessionId, cwd: live.input.cwd };
261
+ if (!listing)
262
+ return [...own, ...brokered, ...world.tools(context, (_plugin, tool) => this.worldNames.get(session.sessionId)?.has(tool) ?? false)];
263
+ const on = await this.worldOn(live);
264
+ const tools = world.tools(context, (plugin, tool) => on(plugin, tool));
265
+ this.worldNames.set(session.sessionId, new Set(tools.map((tool) => tool.name)));
266
+ for (const tool of tools)
267
+ if (tool.readOnly)
268
+ this.worldReadOnly.add(tool.name);
269
+ return [...own, ...brokered, ...tools];
270
+ }
271
+ ownNames = new Map();
272
+ async ownTools(live, listing) {
273
+ const sessionId = live.caller.sessionId;
274
+ const caller = { ...live.caller, runId: live.controller.activeRun?.runId ?? null };
275
+ const all = harnessTools(this.options.env, caller, { variantDir: this.variantDirs.get(sessionId) ?? null })
276
+ .filter((tool) => DEFAULT_TOOL_NAMES.includes(tool.name));
277
+ if (!listing)
278
+ return all.filter((tool) => this.ownNames.get(sessionId)?.has(tool.name) ?? false);
279
+ const on = await this.worldOn(live);
280
+ const owner = (tool) => HARNESS_PLUGINS.find((plugin) => plugin.tools?.some((entry) => entry.name === tool))?.id ?? null;
281
+ const tools = all.filter((tool) => {
282
+ const plugin = owner(tool.name);
283
+ return !plugin || on(plugin, tool.name);
284
+ });
285
+ this.ownNames.set(sessionId, new Set(tools.map((tool) => tool.name)));
286
+ for (const tool of tools)
287
+ if (tool.readOnly)
288
+ this.worldReadOnly.add(tool.name);
289
+ return tools;
290
+ }
291
+ /** The world tools a new session will have, named in its instructions. */
292
+ async worldToolNames(input, account) {
293
+ const { world } = this.options;
294
+ if (!world || input.connections === false)
295
+ return [];
296
+ const list = this.options.pluginControl?.list;
297
+ const plugins = list ? await list({ cwd: input.cwd, accountId: account.id, projectId: input.projectId ?? null }) : null;
298
+ const on = plugins ? new Set(plugins.flatMap((plugin) => plugin.contributions.filter((item) => item.state === 'on').map((item) => item.id))) : null;
299
+ return world.plugins.flatMap((plugin) => (plugin.tools ?? []).map((tool) => tool.name).filter((name) => !on || on.has(contributionId(plugin.id, 'tool', name))));
300
+ }
301
+ /** The harness's own History, Wiki and skill tools a new session will have. */
302
+ async ownToolNames(input, account) {
303
+ const list = this.options.pluginControl?.list;
304
+ const plugins = list ? await list({ cwd: input.cwd, accountId: account.id, projectId: input.projectId ?? null }) : null;
305
+ const on = plugins ? new Set(plugins.flatMap((plugin) => plugin.contributions.filter((item) => item.state === 'on').map((item) => item.id))) : null;
306
+ return DEFAULT_TOOL_NAMES.filter((name) => {
307
+ const plugin = HARNESS_PLUGINS.find((entry) => entry.tools?.some((tool) => tool.name === name));
308
+ return !on || !plugin || on.has(contributionId(plugin.id, 'tool', name));
309
+ });
310
+ }
311
+ /** Whether a world tool's plugin is switched on for a session's folder, account and project. */
312
+ async worldOn(live) {
313
+ const list = this.options.pluginControl?.list;
314
+ if (!list)
315
+ return () => true;
316
+ const plugins = await list({ cwd: live.input.cwd, accountId: live.account.id, projectId: live.input.projectId ?? null });
317
+ const on = new Set(plugins.flatMap((plugin) => plugin.contributions.filter((item) => item.state === 'on').map((item) => item.id)));
318
+ return (plugin, tool) => on.has(contributionId(plugin, 'tool', tool));
319
+ }
320
+ async appsCall(session, call, params) {
321
+ const { apps } = this.options;
322
+ switch (call) {
323
+ case 'mcp.list':
324
+ return apps.list(session);
325
+ case 'mcp.call':
326
+ return apps.invoke(session, String(params.name ?? ''), (params.args && typeof params.args === 'object' ? params.args : {}));
327
+ case 'mcp.revision':
328
+ return apps.revision(session);
329
+ case 'tools.list': {
330
+ const { Type } = await import('typebox');
331
+ return { tools: (await this.tools(session, true)).map((tool) => ({ name: tool.name, label: tool.label, description: tool.description, parameters: tool.parameters(Type) })) };
332
+ }
333
+ case 'firstParty.list': {
334
+ // The harness's own extensions switched on for this session, with their settings.
335
+ const live = this.sessions.get(session.sessionId);
336
+ const list = this.options.pluginControl?.list;
337
+ if (!live || !list)
338
+ return { plugins: [] };
339
+ const plugins = await list({ cwd: live.input.cwd, accountId: live.account.id, projectId: live.input.projectId ?? null });
340
+ const on = new Set(plugins.flatMap((plugin) => plugin.contributions.filter((item) => item.state === 'on').map((item) => item.id)));
341
+ const settings = attachments.load(this.options.env.root).settings;
342
+ return { plugins: FIRST_PARTY_PLUGINS.filter((plugin) => plugin.code?.some((code) => on.has(`${plugin.id}/code/${code.name}`))).map((plugin) => ({ id: plugin.id, settings: settings[plugin.id] ?? {} })) };
343
+ }
344
+ case 'tools.call': {
345
+ const tool = (await this.tools(session, false)).find((candidate) => candidate.name === params.name);
346
+ if (!tool)
347
+ throw new HarnessError('unknown_method', `Unknown tool ${String(params.name)}.`);
348
+ const result = await tool.execute((params.input && typeof params.input === 'object' ? params.input : {}));
349
+ if (typeof result === 'string')
350
+ return { text: result };
351
+ const images = [...(result.image ? [result.image] : []), ...(result.images ?? [])];
352
+ return { text: result.text, ...(images.length ? { images } : {}), ...(result.details ? { details: result.details } : {}) };
353
+ }
354
+ default:
355
+ throw new HarnessError('unknown_method', `Unknown call ${call}.`);
356
+ }
357
+ }
358
+ /**
359
+ * `/plugins` in a terminal. Listing is open to the session. A change is taken only from the
360
+ * session's own extension, which asks the person in the terminal first: the model can reach
361
+ * this socket from a shell command, and must not be able to trust code or switch it on.
362
+ */
363
+ async pluginsCall(sessionId, call, params, control) {
364
+ const api = this.options.pluginControl;
365
+ const live = this.sessions.get(sessionId);
366
+ if (!api || !live)
367
+ throw new HarnessError('unknown_method', `Unknown call ${call}.`);
368
+ const projectId = live.input.projectId ?? null;
369
+ const scope = { cwd: live.input.cwd, accountId: live.account.id, projectId };
370
+ if (call === 'hub.list' && this.options.hub)
371
+ return this.options.hub.list(params.refresh === true);
372
+ if (call === 'hub.install' && this.options.hub) {
373
+ if (!control)
374
+ throw new HarnessError('unauthorized', 'Only a person at this terminal can install items. Type /hub.');
375
+ if (typeof params.id !== 'string' || typeof params.version !== 'string')
376
+ throw invalid('An item id and version are required.');
377
+ return this.options.hub.install(params.id, params.version);
378
+ }
379
+ if (call === 'plugins.list')
380
+ return api.list(scope);
381
+ if (call === 'plugins.session')
382
+ return { projectId };
383
+ if (!control)
384
+ throw new HarnessError('unauthorized', 'Only a person at this terminal can change plugins. Type /plugins.');
385
+ const text = (name) => {
386
+ if (typeof params[name] !== 'string' || !params[name])
387
+ throw invalid(`${name} is required.`);
388
+ return params[name];
389
+ };
390
+ switch (call) {
391
+ case 'plugins.proposals.list':
392
+ if (!api.proposals)
393
+ throw invalid('Proposal review is unavailable in this host.');
394
+ return api.proposals.list();
395
+ case 'plugins.proposals.preview':
396
+ if (!api.proposals)
397
+ throw invalid('Proposal review is unavailable in this host.');
398
+ return api.proposals.preview(text('id'));
399
+ case 'plugins.proposals.accept':
400
+ case 'plugins.proposals.reject':
401
+ if (!api.proposals)
402
+ throw invalid('Proposal review is unavailable in this host.');
403
+ return api.proposals[call === 'plugins.proposals.accept' ? 'accept' : 'reject'](text('id'), text('hash'));
404
+ case 'plugins.attach':
405
+ case 'plugins.detach':
406
+ return api.attach({ ...scope, id: text('id'), scope: params.everywhere === true || !projectId ? {} : { projectId } }, call === 'plugins.attach');
407
+ case 'plugins.trust':
408
+ return api.trust({ ...scope, hash: text('hash') });
409
+ case 'plugins.untrust':
410
+ return api.untrust({ ...scope, hash: text('hash') });
411
+ case 'plugins.configure': {
412
+ const settings = params.settings && typeof params.settings === 'object' && !Array.isArray(params.settings) ? params.settings : {};
413
+ return api.configure({ ...scope, pluginId: text('pluginId'), settings });
414
+ }
415
+ case 'plugins.install':
416
+ if (!api.install)
417
+ throw invalid('Package installation is unavailable in this host.');
418
+ return api.install({ ...scope, source: text('source') });
419
+ case 'plugins.remove':
420
+ if (!api.remove)
421
+ throw invalid('Package removal is unavailable in this host.');
422
+ return api.remove({ ...scope, pluginId: text('pluginId') });
423
+ default:
424
+ throw new HarnessError('unknown_method', `Unknown call ${call}.`);
425
+ }
426
+ }
427
+ /**
428
+ * `/accounts` in a terminal. Every call needs the session's control key: adding, signing in,
429
+ * or removing an account is a person's choice, never the model's shell command.
430
+ */
431
+ async accountsCall(sessionId, call, params, control) {
432
+ const { accounts } = this.options;
433
+ const live = this.sessions.get(sessionId);
434
+ if (!live)
435
+ throw new HarnessError('unknown_method', `Unknown call ${call}.`);
436
+ if (!control)
437
+ throw new HarnessError('unauthorized', 'Only a person at this terminal can manage accounts. Type /accounts.');
438
+ const text = (name) => {
439
+ if (typeof params[name] !== 'string' || !params[name])
440
+ throw invalid(`${name} is required.`);
441
+ return params[name];
442
+ };
443
+ const optional = (name) => (typeof params[name] === 'string' ? params[name] : null);
444
+ switch (call) {
445
+ case 'accounts.deviceId':
446
+ return accounts.installationDeviceId();
447
+ case 'accounts.list':
448
+ return accounts.overview();
449
+ case 'accounts.session':
450
+ return { accountId: live.account.id, label: live.account.label };
451
+ // The terminal's Pi keeps no credential file: it reads and writes its own account's credentials
452
+ // here, under the store's cross-process lock, never an account the caller names.
453
+ case 'accounts.credentials.read':
454
+ return { credential: (await accounts.readCredential(live.account.id, text('provider'))) ?? null };
455
+ case 'accounts.credentials.list':
456
+ return { credentials: await accounts.listCredentials(live.account.id) };
457
+ case 'accounts.credentials.lease': {
458
+ const lease = await accounts.leaseCredential(live.account.id, text('provider'));
459
+ return { leaseId: lease.leaseId, current: lease.current ?? null };
460
+ }
461
+ case 'accounts.credentials.commit': {
462
+ const credential = params.credential && typeof params.credential === 'object' ? params.credential : undefined;
463
+ return { credential: (await accounts.commitCredentialLease(live.account.id, text('leaseId'), credential)) ?? null };
464
+ }
465
+ case 'accounts.credentials.delete':
466
+ await accounts.writeCredential(live.account.id, text('provider'), null);
467
+ accounts.invalidate(live.account.id);
468
+ return {};
469
+ case 'accounts.create':
470
+ return accounts.create(text('label'), optional('authKind') ?? 'subscription');
471
+ case 'accounts.rename':
472
+ return accounts.rename(text('accountId'), text('label'));
473
+ case 'accounts.setDefault':
474
+ return accounts.setDefault(text('accountId'));
475
+ case 'accounts.remove': {
476
+ const accountId = text('accountId');
477
+ // Every terminal in this process counts, this session's own included. Another process's terminals are not visible here.
478
+ if (accounts.terminalCount(accountId) > 0)
479
+ 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.');
480
+ return accounts.remove(accountId);
481
+ }
482
+ case 'accounts.refresh':
483
+ return accounts.refresh(optional('accountId'));
484
+ case 'accounts.providers':
485
+ return accounts.providers();
486
+ // A terminal reads and writes only its own account's choices, never the id a caller names.
487
+ case 'accounts.choices.get':
488
+ return accounts.choices(live.account.id);
489
+ case 'accounts.choices.set':
490
+ return accounts.choose(live.account.id, text('mode'), {
491
+ ...(typeof params.model === 'string' ? { model: params.model } : {}),
492
+ ...(typeof params.effort === 'string' ? { effort: params.effort } : {})
493
+ });
494
+ case 'accounts.loginStart':
495
+ return accounts.loginStart(text('accountId'), text('provider'), optional('method'));
496
+ case 'accounts.loginStatus':
497
+ return accounts.loginStatus(text('accountId'));
498
+ case 'accounts.loginRespond':
499
+ accounts.loginRespond(text('loginId'), text('promptId'), optional('value'));
500
+ return {};
501
+ case 'accounts.loginCancel':
502
+ accounts.loginCancel(text('accountId'), optional('loginId'));
503
+ return {};
504
+ case 'accounts.setApiKey':
505
+ return accounts.setApiKey(text('accountId'), text('key'), text('provider'));
506
+ case 'accounts.removeProvider':
507
+ return accounts.removeProvider(text('accountId'), text('provider'));
508
+ case 'accounts.signOut':
509
+ return accounts.signOut(text('accountId'));
510
+ case 'accounts.savePool': {
511
+ const pool = params.pool;
512
+ if (!pool || typeof pool.label !== 'string' || !Array.isArray(pool.accountIds) || !pool.accountIds.every((id) => typeof id === 'string'))
513
+ throw invalid('A pool needs a label and its account ids.');
514
+ return accounts.savePool({ id: typeof pool.id === 'string' ? pool.id : undefined, label: pool.label, accountIds: pool.accountIds });
515
+ }
516
+ case 'accounts.removePool':
517
+ return accounts.removePool(text('poolId'));
518
+ case 'accounts.audit':
519
+ return readAudit(accounts.dir, optional('accountId'), typeof params.limit === 'number' ? params.limit : 100);
520
+ case 'accounts.relaunch': {
521
+ // Checked here so a bad choice is refused before the session is asked to end.
522
+ const target = accounts.resolve(text('accountId'));
523
+ this.relaunches.set(sessionId, { accountId: target.id, nativeSessionId: this.historyFileOf(live) });
524
+ return { accountId: target.id, label: target.label };
525
+ }
526
+ default:
527
+ throw new HarnessError('unknown_method', `Unknown call ${call}.`);
528
+ }
529
+ }
530
+ /** `/agents`: the agent profiles this session's folder can use (PROP-0029). */
531
+ agentsCall(live, call, params) {
532
+ const root = this.options.env.root;
533
+ const cwd = live.input.cwd;
534
+ const name = () => {
535
+ if (typeof params.name !== 'string' || !params.name.trim())
536
+ throw invalid('A profile name is required.');
537
+ return params.name.trim();
538
+ };
539
+ const scope = () => {
540
+ if (params.scope !== 'global' && params.scope !== 'project')
541
+ throw invalid('scope must be global or project.');
542
+ return params.scope;
543
+ };
544
+ switch (call.slice('terminal.agents.'.length)) {
545
+ case 'list': return { ...listProfiles(root, cwd, this.variantDirs.get(live.input.sessionId) ?? null, this.packageProfiles.get(live.input.sessionId)), dirs: profileDirs(root, cwd) };
546
+ case 'create': return createProfile(root, cwd, { name: name(), scope: scope(), from: typeof params.from === 'string' && params.from ? params.from : null });
547
+ case 'remove':
548
+ removeProfile(root, cwd, { name: name(), scope: scope() });
549
+ return {};
550
+ default: throw invalid('Unknown profile operation.');
551
+ }
552
+ }
553
+ /** `/worktree`: the managed worktrees of the repository this session works in. */
554
+ async worktreesCall(live, call, params) {
555
+ const worktrees = this.options.worktrees;
556
+ if (!worktrees)
557
+ throw invalid('Worktrees are unavailable in this host.');
558
+ const sessionId = live.input.sessionId;
559
+ const cwd = live.input.cwd;
560
+ const current = this.worktreeLeases.get(sessionId) ?? null;
561
+ const id = () => {
562
+ if (typeof params.id !== 'string' || !params.id)
563
+ throw invalid('A worktree id is required.');
564
+ return params.id;
565
+ };
566
+ const action = call.slice('terminal.worktrees.'.length);
567
+ switch (action) {
568
+ case 'list': {
569
+ let repo;
570
+ try {
571
+ repo = await worktrees.repositoryOf(cwd);
572
+ }
573
+ catch (error) {
574
+ return { repo: null, current, worktrees: [], removed: [], reason: error instanceof Error ? error.message : String(error) };
575
+ }
576
+ const statuses = await Promise.all((await worktrees.list(repo)).map((record) => worktrees.status(record.id)));
577
+ return { repo, current, worktrees: statuses, removed: worktrees.removed(repo), reason: null };
578
+ }
579
+ case 'status': return worktrees.status(id());
580
+ case 'create': return worktrees.create({
581
+ repo: cwd,
582
+ ...(typeof params.name === 'string' && params.name ? { name: params.name } : {}),
583
+ ...(typeof params.branch === 'string' && params.branch ? { branch: params.branch } : {}),
584
+ kind: params.kind === 'permanent' ? 'permanent' : 'managed'
585
+ });
586
+ case 'branch':
587
+ if (typeof params.name !== 'string' || !params.name)
588
+ throw invalid('A branch name is required.');
589
+ return worktrees.branch(id(), params.name);
590
+ case 'handoff': {
591
+ const direction = params.direction === 'toSource' || params.direction === 'toWorktree' ? params.direction : null;
592
+ if (!direction)
593
+ throw invalid('direction is toSource or toWorktree.');
594
+ return worktrees.handoff(id(), direction, { move: params.move !== false });
595
+ }
596
+ case 'remove':
597
+ if (id() === current)
598
+ throw new HarnessError('worktree_in_use', 'This session works in that worktree. Switch to another folder first.');
599
+ return worktrees.remove(id(), { force: params.force === true });
600
+ case 'restore': return worktrees.restore(id());
601
+ case 'prune': return worktrees.prune({ repo: cwd, ...(typeof params.keep === 'number' ? { keep: params.keep } : {}) });
602
+ case 'trustSetup': {
603
+ const repo = await worktrees.repositoryOf(cwd);
604
+ const script = setupScript(repo);
605
+ if (!script)
606
+ throw invalid('This repository has no .xharness/worktree-setup.');
607
+ await worktrees.trustSetup(repo, script.hash);
608
+ return { hash: script.hash };
609
+ }
610
+ case 'switch': {
611
+ // A new conversation in the chosen folder: Pi keeps one history per working folder.
612
+ const target = params.main === true ? null : id();
613
+ const folder = target ? worktrees.pathOf(target) : await worktrees.repositoryOf(cwd);
614
+ if (!folder)
615
+ throw new HarnessError('worktree_missing', 'That worktree has no folder.');
616
+ this.relaunches.set(sessionId, { accountId: live.account.id, nativeSessionId: null, cwd: folder, worktreeId: target });
617
+ return { cwd: folder, worktreeId: target };
618
+ }
619
+ default:
620
+ throw new HarnessError('unknown_method', `Unknown call ${call}.`);
621
+ }
622
+ }
623
+ /** `/context`: the selected run's request, and one row's text on request. */
624
+ contextCall(live, params) {
625
+ if (params.runId !== undefined && (typeof params.runId !== 'string' || !params.runId))
626
+ throw invalid('A valid run id is required.');
627
+ const runId = typeof params.runId === 'string' ? params.runId : live.controller.lastRun;
628
+ if (!runId)
629
+ return { attribution: null, text: null };
630
+ if (params.runId !== undefined)
631
+ readTerminalTrajectory(this.options.env.root, live.input.sessionId, this.nativeOf(live), runId);
632
+ const text = typeof params.section === 'string' && params.section ? rowText(this.options.env, runId, params.section) : null;
633
+ return { attribution: attribution(this.options.env, runId), text };
634
+ }
635
+ /** `/usage:analysis` and `/usage:limit`: usage for this session's own account, project or session. */
636
+ async usageCall(live, call, params) {
637
+ const usage = this.options.usage;
638
+ const limits = this.options.usageLimits;
639
+ if (!usage || !limits)
640
+ throw invalid('Usage is unavailable in this host.');
641
+ if (call === 'terminal.usage.summary') {
642
+ const scope = params.scope === 'session' || params.scope === 'project' || params.scope === 'all' ? params.scope : 'account';
643
+ if (scope === 'project' && !live.input.projectId)
644
+ throw invalid('This session has no project to report usage for.');
645
+ const period = params.period === 'today' || params.period === '30d' || params.period === 'all' ? params.period : '7d';
646
+ return usage.summary({
647
+ scope,
648
+ sessionId: live.input.sessionId,
649
+ accountId: live.account.id,
650
+ projectId: live.input.projectId ?? null,
651
+ period,
652
+ timezoneOffsetMinutes: typeof params.timezoneOffsetMinutes === 'number' ? params.timezoneOffsetMinutes : 0
653
+ });
654
+ }
655
+ if (call === 'terminal.usage.limits') {
656
+ return params.refresh === true ? limits.refresh(live.account.id, { force: true }) : limits.read(live.account.id);
657
+ }
658
+ if (call === 'terminal.usage.allLimits') {
659
+ return params.refresh === true ? limits.refresh() : limits.read();
660
+ }
661
+ if (call === 'terminal.usage.headers') {
662
+ if (typeof params.provider !== 'string' || !params.provider)
663
+ throw invalid('A provider is required.');
664
+ if (typeof params.status !== 'number' || !Number.isFinite(params.status))
665
+ throw invalid('A response status is required.');
666
+ const headers = params.headers && typeof params.headers === 'object' && !Array.isArray(params.headers)
667
+ ? Object.fromEntries(Object.entries(params.headers).filter((entry) => typeof entry[1] === 'string'))
668
+ : {};
669
+ if (!Object.keys(headers).length)
670
+ return {};
671
+ limits.recordHeaders(live.account.id, params.provider, { status: params.status, headers });
672
+ return {};
673
+ }
674
+ throw invalid('Unknown usage operation.');
675
+ }
676
+ /** `/annotate:notes` and `/annotate:`: Web annotations and their visible capture browser. */
677
+ async annotationsCall(live, call, params) {
678
+ const root = this.options.env.root;
679
+ const sessionId = live.input.sessionId;
680
+ const capture = this.options.noteCapture;
681
+ const id = () => {
682
+ if (typeof params.id !== 'string' || !params.id)
683
+ throw invalid('A note id is required.');
684
+ return params.id;
685
+ };
686
+ const version = () => {
687
+ if (typeof params.expectedVersion !== 'number')
688
+ throw invalid('The note version you last read is required.');
689
+ return params.expectedVersion;
690
+ };
691
+ const needCapture = () => {
692
+ if (!capture)
693
+ throw invalid('This host has no capture browser.');
694
+ return capture;
695
+ };
696
+ const needNotes = () => {
697
+ if (!this.options.pageNotes)
698
+ throw invalid('This host cannot send page notes.');
699
+ return this.options.pageNotes;
700
+ };
701
+ const action = call.slice('terminal.annotations.'.length);
702
+ switch (action) {
703
+ case 'list': {
704
+ const result = pageNotes.list(root, {
705
+ query: typeof params.query === 'string' ? params.query : null,
706
+ url: typeof params.url === 'string' ? params.url : null,
707
+ status: params.status === 'open' || params.status === 'resolved' ? params.status : null,
708
+ limit: 200
709
+ });
710
+ return { ...result, sessionId };
711
+ }
712
+ case 'read': return pageNotes.read(root, id());
713
+ case 'create': {
714
+ const note = pageNotes.create(root, { source: params.source, target: params.target, body: params.body, tags: params.tags });
715
+ return params.share === true ? needNotes().share(note.id, undefined, sessionId) : note;
716
+ }
717
+ case 'update': return pageNotes.update(root, id(), version(), (params.patch ?? {}));
718
+ case 'delete':
719
+ pageNotes.remove(root, id(), version());
720
+ return {};
721
+ // A terminal shares with, and withdraws from, its own session only.
722
+ case 'share': return needNotes().share(id(), undefined, sessionId);
723
+ case 'unshare': return pageNotes.unshare(root, id(), { sessionId });
724
+ case 'open': return needCapture().open(sessionId, typeof params.url === 'string' && params.url ? params.url : null);
725
+ case 'identity': return needCapture().identity(sessionId);
726
+ case 'begin':
727
+ if (params.kind !== 'text' && params.kind !== 'element')
728
+ throw invalid('kind is text or element.');
729
+ return needCapture().begin(sessionId, params.kind);
730
+ case 'capture':
731
+ if (typeof params.captureId !== 'string')
732
+ throw invalid('A capture id is required.');
733
+ return needCapture().capture(sessionId, params.captureId);
734
+ case 'cancel':
735
+ await needCapture().cancel(sessionId, typeof params.captureId === 'string' ? params.captureId : undefined);
736
+ return {};
737
+ case 'reveal': {
738
+ const note = pageNotes.read(root, id());
739
+ return needCapture().reveal(sessionId, note.source.url, note.target);
740
+ }
741
+ // ---- The inspector and sending (PROP-0018, PROP-0042); a terminal shares with and sends to its own session.
742
+ case 'target': return { ...needCapture().describe(sessionId), windows: await needCapture().windows(sessionId).catch(() => []) };
743
+ case 'attach': {
744
+ const kind = params.kind;
745
+ if (kind !== 'web' && kind !== 'cdp' && kind !== 'bridge')
746
+ throw invalid('kind is web, cdp or bridge.');
747
+ const spec = kind === 'cdp'
748
+ ? { kind, endpoint: typeof params.endpoint === 'string' ? params.endpoint : '', name: typeof params.name === 'string' ? params.name : null }
749
+ : kind === 'bridge' ? { kind, name: typeof params.name === 'string' ? params.name : null } : { kind };
750
+ return needCapture().attach(sessionId, spec);
751
+ }
752
+ case 'windows': return needCapture().windows(sessionId);
753
+ case 'selectWindow':
754
+ if (typeof params.window !== 'string')
755
+ throw invalid('A window id is required.');
756
+ await needCapture().selectWindow(sessionId, params.window);
757
+ return {};
758
+ case 'inspect': return needCapture().inspect(sessionId);
759
+ case 'stopInspect':
760
+ await needCapture().stopInspect(sessionId);
761
+ return {};
762
+ case 'next': return inspectStep(needCapture(), needNotes(), sessionId, sessionId, 'terminal');
763
+ case 'confirm': return needNotes().confirm(id(), version());
764
+ case 'confirmAll': return { notes: needNotes().confirmAll(sessionId) };
765
+ case 'clear': return { notes: needNotes().clear(sessionId) };
766
+ case 'send': return needNotes().send(sessionId, {
767
+ ids: Array.isArray(params.ids) ? params.ids.filter((entry) => typeof entry === 'string') : null,
768
+ prompt: typeof params.prompt === 'string' ? params.prompt : null
769
+ });
770
+ case 'discover': return { apps: await needCapture().discover() };
771
+ case 'launch':
772
+ if (typeof params.path !== 'string' || !params.path.trim())
773
+ throw invalid('Give the path of the app to start.');
774
+ return needCapture().launchApp(sessionId, params.path, Array.isArray(params.args) ? params.args.filter((entry) => typeof entry === 'string') : []);
775
+ default:
776
+ throw new HarnessError('unknown_method', `Unknown call ${call}.`);
777
+ }
778
+ }
779
+ /** The account a session asked to switch to. Taken once, after its Pi process has ended and before `dispose`. */
780
+ takeRelaunch(sessionId) {
781
+ const request = this.relaunches.get(sessionId) ?? null;
782
+ this.relaunches.delete(sessionId);
783
+ return request;
784
+ }
785
+ live(sessionId) {
786
+ const live = this.sessions.get(sessionId);
787
+ if (!live)
788
+ throw new HarnessError('terminal_not_ready', 'The terminal controller is unavailable. Reopen this session.');
789
+ return live;
790
+ }
791
+ historyDir(input) {
792
+ return join(input.sessionFolder, 'pi');
793
+ }
794
+ runRecord(live, runId, text) {
795
+ const { input } = live;
796
+ return {
797
+ resumedFromRunId: null,
798
+ parentRunId: null,
799
+ relation: null,
800
+ terminalReason: null,
801
+ agentId: null,
802
+ trigger: null,
803
+ id: runId,
804
+ projectId: input.projectId ?? null,
805
+ mode: input.run?.mode ?? 'code',
806
+ harness: 'pi',
807
+ sessionType: 'terminal',
808
+ sessionId: input.sessionId,
809
+ nativeSessionId: live.controller.nativeSessionId,
810
+ workingDirectory: input.cwd,
811
+ workspaceRoot: resolveWorkspaceRoot(input.cwd),
812
+ worktreeId: input.run?.worktreeId ?? null,
813
+ goal: text,
814
+ attachmentCount: 0,
815
+ startedAt: now(),
816
+ endedAt: null,
817
+ status: 'queued',
818
+ loadedSkills: [],
819
+ exitCode: null,
820
+ handoffId: null,
821
+ accountId: live.account.id,
822
+ variantScope: live.variant?.scope ?? null,
823
+ variantVersion: live.variant?.fingerprint ?? null
824
+ };
825
+ }
826
+ /** The run a native turn reports into: the one reserved by `send`, or a new one for what the person typed. */
827
+ begin(live, reservedRunId, text, launch = {}) {
828
+ const { root } = this.options.env;
829
+ const { sessionId } = live.input;
830
+ if (this.options.runs.activeForSession(sessionId))
831
+ throw new HarnessError('session_busy', 'A native turn already owns this session.');
832
+ let runId = reservedRunId;
833
+ if (runId) {
834
+ const reserved = loadRun(root, runId);
835
+ if (reserved.sessionId !== sessionId || reserved.status !== 'queued')
836
+ throw invalid('Delivery does not own this queued run.');
837
+ }
838
+ else {
839
+ runId = newId();
840
+ saveRun(root, this.runRecord(live, runId, text || null));
841
+ copyToRun(root, live.input.sessionFolder, runId);
842
+ }
843
+ if (launch.model || launch.effort) {
844
+ updateRun(root, runId, (record) => {
845
+ if (launch.model)
846
+ record.model = launch.model;
847
+ if (launch.effort)
848
+ record.effort = launch.effort;
849
+ });
850
+ }
851
+ return this.options.runs.start(sessionId, runId);
852
+ }
853
+ /** Records the working tree as a typed turn found it, before the agent acts (refinement replays from it). */
854
+ async snapshotTurn(live, run, text) {
855
+ if (!text.trim() || live.input.readOnly)
856
+ return;
857
+ const snapshot = await snapshotWorkingTree(live.input.cwd);
858
+ if (snapshot)
859
+ updateRun(this.options.env.root, run.runId, (record) => { record.workspaceSnapshot = snapshot; });
860
+ }
861
+ async decide(live, run, tool, args) {
862
+ const record = loadRun(this.options.env.root, run.runId);
863
+ if (!isActiveStatus(record.status))
864
+ throw invalid(`The run is no longer active (its status is ${record.status}).`);
865
+ const name = tool.toLowerCase();
866
+ const workspaceRoot = record.workspaceRoot ?? resolveWorkspaceRoot(record.workingDirectory ?? live.input.cwd);
867
+ if (live.input.readOnly && ['bash', 'edit', 'write'].includes(name))
868
+ return { allowed: false, reason: 'This session is read-only.', workspaceRoot };
869
+ let inspection;
870
+ try {
871
+ inspection = inspectRunWorkspaceWrite(record, tool, args);
872
+ }
873
+ catch (error) {
874
+ return { allowed: false, reason: error instanceof Error ? error.message : 'The active project workspace could not be verified.', workspaceRoot };
875
+ }
876
+ const call = { run: record, caller: { ...live.caller, runId: run.runId }, tool, args: inspection.input };
877
+ const { policy } = this.options;
878
+ if (policy.scope) {
879
+ const scope = combineWorkspaceScope(await policy.scope(call), inspection);
880
+ if (!scope.allowed && (scope.automatic || !scope.requiresApproval))
881
+ return { allowed: false, reason: scope.reason ?? 'Outside what this session may do.', workspaceRoot };
882
+ try {
883
+ await policy.authorize?.(call);
884
+ }
885
+ catch (error) {
886
+ return { allowed: false, reason: error instanceof Error ? error.message : 'Tool unavailable in the settings.', workspaceRoot };
887
+ }
888
+ const requiresApproval = !!scope.requiresApproval || (scope.allowed && !!scope.humanOnly);
889
+ return {
890
+ allowed: scope.allowed,
891
+ requiresApproval,
892
+ reason: scope.reason ?? null,
893
+ workspaceRoot,
894
+ rememberable: scope.allowed && !scope.humanOnly,
895
+ ...(scope.humanOnly ? { standing: scope.allowGlobalApproval ? 'switch' : 'none' } : {})
896
+ };
897
+ }
898
+ if (inspection.outside)
899
+ return { allowed: false, requiresApproval: true, rememberable: false, standing: 'switch', reason: inspection.reason, workspaceRoot };
900
+ if (name === SESSION_NAME || name === TODO_WRITE || GOAL_TOOLS.includes(name) || name === SPAWN_SUBAGENT || name === WAIT_SUBAGENTS || READ_ONLY_TOOLS.has(name) || this.worldReadOnly.has(name) || this.options.apps.toolNames.includes(name) || ['bash', 'edit', 'write'].includes(name)) {
901
+ return { allowed: true, workspaceRoot };
902
+ }
903
+ return { allowed: false, requiresApproval: true, rememberable: true, reason: 'This native action requires approval.', workspaceRoot };
904
+ }
905
+ async workspaceEscape(live, run, tool, args, reason) {
906
+ if (tool !== 'bash' || live.input.readOnly)
907
+ return false;
908
+ const record = loadRun(this.options.env.root, run.runId);
909
+ if (!isActiveStatus(record.status) || (record.trigger != null && record.trigger !== 'user'))
910
+ return false;
911
+ const call = { run: record, caller: { ...live.caller, runId: run.runId }, tool, args };
912
+ const scope = (await this.options.policy.scope?.(call)) ?? { allowed: true };
913
+ if (scope.automatic || (!scope.allowed && !scope.requiresApproval))
914
+ return false;
915
+ try {
916
+ await this.options.policy.authorize?.(call);
917
+ }
918
+ catch {
919
+ return false;
920
+ }
921
+ const operationArgs = (args && typeof args === 'object' && !Array.isArray(args) ? args : {});
922
+ const command = typeof operationArgs.command === 'string' ? operationArgs.command : '';
923
+ if (operationArgs.workspaceWriteScope !== 'outside' || !command)
924
+ return false;
925
+ const workspaceRoot = record.workspaceRoot ?? resolveWorkspaceRoot(record.workingDirectory ?? live.input.cwd);
926
+ const decision = await run.requestApproval({
927
+ target: 'workspace-write',
928
+ action: `${reason} Workspace: ${workspaceRoot}. This exact command will run once with broader filesystem access.\n\nCommand:\n${formatBashCommandForApproval(command)}`,
929
+ operation: { tool, args: operationArgs },
930
+ rememberable: false,
931
+ standing: 'none'
932
+ });
933
+ if (decision !== 'approved')
934
+ return false;
935
+ const currentRecord = loadRun(this.options.env.root, run.runId);
936
+ if (!isActiveStatus(currentRecord.status) || (currentRecord.trigger != null && currentRecord.trigger !== 'user'))
937
+ return false;
938
+ const currentCall = { run: currentRecord, caller: { ...live.caller, runId: run.runId }, tool, args: operationArgs };
939
+ const currentScope = (await this.options.policy.scope?.(currentCall)) ?? { allowed: true };
940
+ if (currentScope.automatic || (!currentScope.allowed && !currentScope.requiresApproval))
941
+ return false;
942
+ try {
943
+ await this.options.policy.authorize?.(currentCall);
944
+ return true;
945
+ }
946
+ catch {
947
+ return false;
948
+ }
949
+ }
950
+ async workspaceBoundaryBypass(live, run, tool, args) {
951
+ if (tool !== 'bash' || live.input.readOnly || !this.options.approvals.isAuto('pi'))
952
+ return false;
953
+ const record = loadRun(this.options.env.root, run.runId);
954
+ if (!isActiveStatus(record.status) || (record.trigger != null && record.trigger !== 'user'))
955
+ return false;
956
+ const operationArgs = (args && typeof args === 'object' && !Array.isArray(args) ? args : {});
957
+ if (typeof operationArgs.command !== 'string' || !operationArgs.command)
958
+ return false;
959
+ const call = { run: record, caller: { ...live.caller, runId: run.runId }, tool, args: operationArgs };
960
+ try {
961
+ const scope = (await this.options.policy.scope?.(call)) ?? { allowed: true };
962
+ if (scope.automatic || (!scope.allowed && !scope.requiresApproval) || (scope.humanOnly && !scope.allowGlobalApproval))
963
+ return false;
964
+ await this.options.policy.authorize?.(call);
965
+ return true;
966
+ }
967
+ catch {
968
+ return false;
969
+ }
970
+ }
971
+ async subagentsCall(live, call, params) {
972
+ if (call === 'terminal.subagents.monitor')
973
+ return { children: live.controller.subagentSnapshot() };
974
+ const service = this.options.subagents;
975
+ if ((!service || live.input.subagents === null) && call !== 'terminal.subagents.register') {
976
+ if (call === 'terminal.subagents.tools')
977
+ return { tools: [] };
978
+ throw invalid('Subagents are disabled for this terminal.');
979
+ }
980
+ if (call === 'terminal.subagents.tools') {
981
+ const variantDir = this.variantDirs.get(live.input.sessionId) ?? null;
982
+ const packaged = this.packageProfiles.get(live.input.sessionId) ?? [];
983
+ return {
984
+ tools: subagentTools(service, '').map(tool => ({ name: tool.name, label: tool.label, description: tool.description, parameters: tool.parameters(Type) })),
985
+ profiles: {
986
+ normal: profileSection(this.options.env.root, live.input.cwd, variantDir, false, undefined, packaged).body,
987
+ plan: profileSection(this.options.env.root, live.input.cwd, variantDir, true, undefined, packaged).body
988
+ }
989
+ };
990
+ }
991
+ const run = live.controller.activeRun;
992
+ if (!run)
993
+ throw new HarnessError('run_not_active', 'This terminal has no active parent run.');
994
+ if (call === 'terminal.subagents.planSafe') {
995
+ const reason = service.planSpawnRefusal(run.runId, params.input);
996
+ return { allowed: reason === null, ...(reason ? { reason } : {}) };
997
+ }
998
+ if (call === 'terminal.subagents.register') {
999
+ if (!Array.isArray(params.tools) || params.tools.some((name) => typeof name !== 'string'))
1000
+ throw invalid('Native tool names are required.');
1001
+ if (params.plan !== undefined && typeof params.plan !== 'boolean')
1002
+ throw invalid('plan must be a boolean.');
1003
+ const readOnly = live.input.readOnly === true;
1004
+ const readTools = new Set([...READ_ONLY_TOOLS, SESSION_NAME, TODO_WRITE, 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']);
1005
+ const tools = [...new Set(params.tools)].filter(name => (!readOnly || readTools.has(name)) && (params.plan === true || (name !== 'ask_user' && name !== 'ask_user_question')));
1006
+ const caller = { ...live.caller, runId: run.runId };
1007
+ const capabilities = this.options.resolveCapabilities ? await this.options.resolveCapabilities(caller, tools, live.input.cwd) : { version: 1, tools, connectors: [], contributions: [] };
1008
+ const variantDir = this.variantDirs.get(live.input.sessionId) ?? null;
1009
+ const packaged = this.packageProfiles.get(live.input.sessionId) ?? [];
1010
+ const subagentProfiles = {
1011
+ normal: profileSection(this.options.env.root, live.input.cwd, variantDir, false, undefined, packaged).body,
1012
+ plan: profileSection(this.options.env.root, live.input.cwd, variantDir, true, {
1013
+ frozenTools: capabilities.tools,
1014
+ contributions: capabilities.contributions,
1015
+ descriptors: HARNESS_PLUGINS
1016
+ }, packaged).body
1017
+ };
1018
+ freezeRun(this.options.env.root, run.runId, caller, capabilities);
1019
+ updateRun(this.options.env.root, run.runId, (record) => { record.effectiveMode = params.plan === true ? 'plan' : 'normal'; });
1020
+ if (!service || live.input.subagents === null)
1021
+ return { runId: run.runId, connectors: capabilities.connectors, subagentProfiles };
1022
+ service.register({
1023
+ run: { id: run.runId, sessionId: live.input.sessionId, mode: live.input.run?.mode ?? 'code', projectId: live.input.projectId, worktreeId: live.input.run?.worktreeId },
1024
+ cwd: live.input.cwd, sessionDir: this.historyDir(live.input), text: '', caller: live.caller,
1025
+ model: typeof params.model === 'string' ? params.model : live.input.model,
1026
+ effort: typeof params.effort === 'string' ? params.effort : null,
1027
+ launch: live.input.launch, tools: { allow: capabilities.tools, gated: capabilities.tools.filter(name => !readTools.has(name)) },
1028
+ connections: live.input.connections === false ? false : { readOnly }, subagents: live.input.subagents,
1029
+ plan: params.plan === true,
1030
+ variant: live.variant ? { dir: live.variant.dir, scope: live.variant.scope } : null,
1031
+ packageProfiles: packaged,
1032
+ capabilities
1033
+ }, live.account.id, capabilities);
1034
+ const history = params.history ?? { owners: [], receipts: [] };
1035
+ service.restore(run.runId, history);
1036
+ // Children an earlier reply left stopped, reported before the model answers.
1037
+ const notice = await service.delivery(run.runId, history, false);
1038
+ return { runId: run.runId, connectors: capabilities.connectors, subagentProfiles, notice };
1039
+ }
1040
+ if (call === 'terminal.subagents.verify') {
1041
+ await service.verify(run.runId, params.history);
1042
+ return {};
1043
+ }
1044
+ if (call === 'terminal.subagents.delivery') {
1045
+ // Waiting for children to settle ends when the parent is interrupted, a stall included.
1046
+ const stop = new AbortController();
1047
+ run.onInterrupt(() => stop.abort());
1048
+ return service.delivery(run.runId, params.history, params.settle === true, stop.signal);
1049
+ }
1050
+ if (call === 'terminal.subagents.call') {
1051
+ const tool = subagentTools(service, run.runId).find(tool => tool.name === params.name);
1052
+ if (!tool)
1053
+ throw invalid('Unknown subagent tool.');
1054
+ const reason = await this.decide(live, run, tool.name, params.input ?? {});
1055
+ if (!reason.allowed)
1056
+ throw new HarnessError('tool_denied', reason.reason ?? 'Subagents are unavailable.');
1057
+ const result = await tool.execute(params.input ?? {});
1058
+ return typeof result === 'string' ? { text: result } : result;
1059
+ }
1060
+ throw invalid('Unknown subagent operation.');
1061
+ }
1062
+ componentsCall(live, call, params) {
1063
+ const root = this.options.env.root;
1064
+ const choice = live.input.variant;
1065
+ const scope = choice && 'scope' in choice ? choice.scope ?? null : null;
1066
+ const head = scope ? variantStore.headVariant(root, scope) : null;
1067
+ const items = componentLibrary.list(root);
1068
+ if (call === 'terminal.components.list') {
1069
+ return {
1070
+ items,
1071
+ addedIds: head ? items.filter(item => componentLibrary.isInVariant(root, head.variant, item.id)).map(item => item.id) : [],
1072
+ writable: Boolean(scope && head),
1073
+ fingerprint: head?.fingerprint ?? null
1074
+ };
1075
+ }
1076
+ if (call === 'terminal.components.add') {
1077
+ if (typeof params.id !== 'string' || !params.id)
1078
+ throw invalid('A component id is required.');
1079
+ if (!scope || !head)
1080
+ throw invalid('This terminal has no writable Harness scope.');
1081
+ const item = items.find(candidate => candidate.id === params.id);
1082
+ if (!item)
1083
+ throw invalid('That component is no longer in the library.');
1084
+ if (item.kind === 'prompts')
1085
+ throw invalid('Prompts stay in the library and cannot be added to a Variant.');
1086
+ if (componentLibrary.isInVariant(root, head.variant, item.id)) {
1087
+ return { fingerprint: head.fingerprint, alreadyPresent: true };
1088
+ }
1089
+ const variant = componentLibrary.addToVariant(root, head.variant, item.id);
1090
+ const record = variantStore.commit(root, scope, variant, {
1091
+ kind: 'edit', note: 'Added library component ' + item.id, components: [item.id]
1092
+ });
1093
+ this.options.bus.emit({ type: 'variants.changed', scope });
1094
+ return { fingerprint: record.fingerprint, alreadyPresent: false };
1095
+ }
1096
+ throw new HarnessError('unknown_method', 'Unknown terminal component operation.');
1097
+ }
1098
+ variantContext(live) {
1099
+ const sessionId = live.input.sessionId;
1100
+ const choice = live.input.variant;
1101
+ const scope = choice && !('dir' in choice) ? choice.scope : null;
1102
+ let started = this.refinements.get(sessionId);
1103
+ if (!started)
1104
+ this.refinements.set(sessionId, started = []);
1105
+ return {
1106
+ root: this.options.env.root,
1107
+ bus: this.options.bus,
1108
+ sessionId,
1109
+ nativeSessionId: this.nativeOf(live),
1110
+ scope,
1111
+ pinned: live.variant?.fingerprint ?? null,
1112
+ active: () => Boolean(this.options.runs.activeForSession(sessionId)) || live.controller.state().active,
1113
+ relaunchOn: (version) => this.relaunches.set(sessionId, { accountId: live.account.id, nativeSessionId: this.historyFileOf(live), variantVersion: version }),
1114
+ refine: this.options.refine ?? null,
1115
+ started
1116
+ };
1117
+ }
1118
+ trajectoryCall(live, call, params) {
1119
+ if (call === 'terminal.trajectory.list') {
1120
+ return { runs: listTerminalTrajectoryRuns(this.options.env.root, live.input.sessionId, this.nativeOf(live)) };
1121
+ }
1122
+ if (call === 'terminal.trajectory.details') {
1123
+ if (typeof params.runId !== 'string' || !params.runId)
1124
+ throw invalid('A run id is required.');
1125
+ readTerminalTrajectory(this.options.env.root, live.input.sessionId, this.nativeOf(live), params.runId);
1126
+ return runDetails(this.options.env.root, params.runId);
1127
+ }
1128
+ if (call === 'terminal.trajectory.events') {
1129
+ if (typeof params.runId !== 'string' || !params.runId)
1130
+ throw invalid('A run id is required.');
1131
+ const afterSequence = params.afterSequence;
1132
+ if (afterSequence !== undefined && typeof afterSequence !== 'number')
1133
+ throw invalid('The event sequence must be a number.');
1134
+ return readTerminalTrajectory(this.options.env.root, live.input.sessionId, this.nativeOf(live), params.runId, afterSequence);
1135
+ }
1136
+ throw new HarnessError('unknown_method', `Unknown call ${call}.`);
1137
+ }
1138
+ async sideQuestionCall(live, call, params) {
1139
+ const service = this.options.sideQuestions;
1140
+ if (!service)
1141
+ throw new HarnessError('unknown_method', `Unknown call ${call}.`);
1142
+ if (call === 'terminal.sideQuestions.start') {
1143
+ if (typeof params.question !== 'string')
1144
+ throw invalid('A side question is required.');
1145
+ const context = params.context && typeof params.context === 'object' && !Array.isArray(params.context)
1146
+ ? params.context
1147
+ : undefined;
1148
+ const latest = listTerminalTrajectoryRuns(this.options.env.root, live.input.sessionId, this.nativeOf(live))
1149
+ .find(run => findRun(this.options.env.root, run.id)?.trigger !== 'side-question');
1150
+ return service.start({
1151
+ sessionId: live.input.sessionId,
1152
+ parentRunId: live.controller.activeRun?.runId ?? latest?.id ?? null,
1153
+ parentSessionId: live.input.sessionId,
1154
+ accountId: live.account.id,
1155
+ model: typeof params.model === 'string' ? params.model : live.input.model ?? null,
1156
+ effort: typeof params.effort === 'string' ? params.effort : undefined,
1157
+ cwd: live.input.cwd,
1158
+ caller: live.caller,
1159
+ question: params.question,
1160
+ context
1161
+ });
1162
+ }
1163
+ if (typeof params.runId !== 'string')
1164
+ throw invalid('A side-question run id is required.');
1165
+ if (call === 'terminal.sideQuestions.read') {
1166
+ const afterSequence = params.afterSequence === undefined ? -1 : params.afterSequence;
1167
+ if (typeof afterSequence !== 'number')
1168
+ throw invalid('A side-question event sequence is required.');
1169
+ return service.read(params.runId, live.input.sessionId, afterSequence);
1170
+ }
1171
+ if (call === 'terminal.sideQuestions.interrupt')
1172
+ return service.interrupt(params.runId, live.input.sessionId);
1173
+ throw new HarnessError('unknown_method', `Unknown call ${call}.`);
1174
+ }
1175
+ /** The only terminal frontend: the Bun-run OpenTUI application. */
1176
+ frontendCommand() {
1177
+ return {
1178
+ program: process.env.XINGULAR_HARNESS_BUN || 'bun',
1179
+ // Solid must resolve to its client build for OpenTUI's effects, wherever it is imported.
1180
+ argsPrefix: ['--conditions=browser', this.assets.openTui]
1181
+ };
1182
+ }
1183
+ /**
1184
+ * Prepares an OpenTUI terminal session and returns what to spawn. The caller must report the
1185
+ * process's end with `dispose`, also when it never starts.
1186
+ */
1187
+ async prepare(input) {
1188
+ input = structuredClone(input);
1189
+ const { env, accounts, apps, bus } = this.options;
1190
+ const { root } = env;
1191
+ if (!input?.sessionId || !input.cwd || !input.sessionFolder)
1192
+ throw invalid('A terminal session needs an id, a working folder, and its own folder.');
1193
+ const carried = input.attach ? this.detachedClients.get(input.sessionId) ?? null : null;
1194
+ if (input.attach) {
1195
+ if (!carried)
1196
+ throw new HarnessError('not_found', 'There is no detached terminal session with this id.');
1197
+ if (carried.info.cwd !== input.cwd || carried.info.sessionFolder !== input.sessionFolder || (input.accountId && input.accountId !== carried.info.accountId)) {
1198
+ throw invalid('Reattach a terminal session in its own folder, on its own account.');
1199
+ }
1200
+ this.detachedClients.delete(input.sessionId);
1201
+ carried.stop();
1202
+ input.accountId = carried.info.accountId;
1203
+ input.nativeSessionId = carried.client.sessionFile ?? input.nativeSessionId ?? null;
1204
+ }
1205
+ await this.dispose(input.sessionId);
1206
+ const sessionId = input.sessionId;
1207
+ let acquiredWorktreeId = null;
1208
+ let account = null;
1209
+ let accountStarted = false;
1210
+ let token = null;
1211
+ try {
1212
+ const worktreeId = input.run?.worktreeId ?? null;
1213
+ const worktreePath = worktreeId ? this.options.worktrees?.pathOf(worktreeId) ?? null : null;
1214
+ if (worktreeId && worktreePath) {
1215
+ await this.options.worktrees.acquire(worktreeId, sessionId);
1216
+ acquiredWorktreeId = worktreeId;
1217
+ this.worktreeLeases.set(sessionId, worktreeId);
1218
+ input.cwd = worktreePath;
1219
+ }
1220
+ const command = this.frontendCommand();
1221
+ // A pinned account is used as it is; it is never exchanged for another one.
1222
+ const preparedAccount = input.accountId ? accounts.find(input.accountId) : accounts.resolve(null, null);
1223
+ account = preparedAccount;
1224
+ const launch = launchEnv(accounts.dir, preparedAccount);
1225
+ const loading = (await this.options.plugins?.loading({ cwd: input.cwd, agentDir: configDir(accounts.dir, preparedAccount), install: true, caller: { projectId: input.projectId ?? null } })) ?? null;
1226
+ await this.socket.listen();
1227
+ await accounts.terminalBegin(preparedAccount);
1228
+ accountStarted = true;
1229
+ const caller = { sessionId, projectId: input.projectId ?? null, actor: 'Code', surface: 'cli' };
1230
+ const broker = input.connections === false ? null : apps.grant({ sessionId, runId: null, caller, readOnly: !!input.readOnly, depth: 0 });
1231
+ // The session keeps these instructions; each of its runs records a copy, and a memory
1232
+ // block that changes later is sent along as a note.
1233
+ const variant = input.variant ? materialise(root, input.variant, { trust: this.options.trust ?? null }) : null;
1234
+ const browser = new BrowserControl({ root, cwd: input.cwd, pinned: variant,
1235
+ writable: Boolean(input.variant && !('dir' in input.variant)), apps, hub: this.options.hub,
1236
+ plugins: () => this.options.pluginControl?.list({ cwd: input.cwd, accountId: preparedAccount.id, projectId: input.projectId ?? null }) ?? Promise.resolve([]) });
1237
+ if (variant)
1238
+ this.variantDirs.set(sessionId, variant.dir);
1239
+ this.packageProfiles.set(sessionId, loading?.profilePaths ?? []);
1240
+ const worldTools = await this.worldToolNames(input, preparedAccount);
1241
+ const ownTools = await this.ownToolNames(input, preparedAccount);
1242
+ const launchTools = [...new Set([...(input.launch?.tools ?? []), TODO_WRITE, SESSION_NAME, ...ownTools, ...(broker ? apps.toolNames : []), ...worldTools])];
1243
+ // The profiles replace the variant's own subagent list, which they include.
1244
+ const profiles = this.options.subagents && input.subagents !== null ? profileSection(root, input.cwd, variant?.dir ?? null, false, undefined, loading?.profilePaths ?? []) : null;
1245
+ const launchSections = profiles ? [...(input.launch?.sections ?? []).filter((section) => section.id !== profiles.id), profiles] : input.launch?.sections;
1246
+ // A terminal is a person's own session: the same standard sections a host's session has.
1247
+ const standard = input.launch?.standard === null ? null : input.launch?.standard ?? { cwd: input.cwd };
1248
+ const pack = compile(env, caller, { ...input.launch, model: input.model ?? input.launch?.model ?? null, standard, sections: launchSections, tools: launchTools }, variant && profiles ? { ...variant, sections: variant.sections.filter((section) => section.id !== profiles.id) } : variant);
1249
+ record(root, pack);
1250
+ mkdirSync(this.historyDir(input), { recursive: true });
1251
+ saveForSession(input.sessionFolder, pack);
1252
+ const live = {
1253
+ input,
1254
+ variant,
1255
+ account: preparedAccount,
1256
+ broker,
1257
+ caller,
1258
+ token: '',
1259
+ controller: new TerminalController({
1260
+ sessionId,
1261
+ nativeSessionId: input.nativeSessionId ?? null,
1262
+ begin: ({ reservedRunId, text, model, effort }) => this.begin(live, reservedRunId, text, { model, effort }),
1263
+ afterBegin: (run, text) => this.snapshotTurn(live, run, text),
1264
+ hydrate: (owners) => this.hydrateMonitor(live, owners),
1265
+ policy: (run, tool, args) => this.decide(live, run, tool, args),
1266
+ workspaceRoot: run => {
1267
+ const record = loadRun(this.options.env.root, run.runId);
1268
+ return record.workspaceRoot ?? resolveWorkspaceRoot(record.workingDirectory ?? live.input.cwd);
1269
+ },
1270
+ workspaceEscape: (run, tool, args, reason) => this.workspaceEscape(live, run, tool, args, reason),
1271
+ workspaceBoundaryBypass: (run, tool, args) => this.workspaceBoundaryBypass(live, run, tool, args),
1272
+ allowToolGlobally: target => { this.options.approvals.allowTool('pi', target); },
1273
+ native: (nativeSessionId) => bus.emit({ type: 'terminal.native', sessionId, nativeSessionId }),
1274
+ telemetry: (event) => bus.emit({ type: 'terminal.telemetry', sessionId, event }),
1275
+ interrupt: (runId, stop) => this.options.runs.interrupt(runId, stop),
1276
+ resolveApproval: (runId, id, decision) => this.options.runs.resolveApproval(runId, id, decision),
1277
+ answerQuestion: (runId, id, answer) => this.options.runs.answerQuestion(runId, id, answer),
1278
+ answerQuestions: (runId, batchId, answers) => this.options.runs.answerQuestions(runId, batchId, answers)
1279
+ })
1280
+ };
1281
+ if (carried)
1282
+ this.clients.set(sessionId, carried.client);
1283
+ token = this.socket.issue({
1284
+ sessionId,
1285
+ caller: () => ({ ...caller, runId: live.controller.activeRun?.runId ?? null }),
1286
+ broker,
1287
+ terminal: { accept: (event, data) => live.controller.accept(event, data), disconnected: () => live.controller.disconnected(), control: async (call, params) => {
1288
+ if (call === 'terminal.browser.config') {
1289
+ const world = this.options.world;
1290
+ if (!world)
1291
+ throw invalid('Browser settings are unavailable in this host.');
1292
+ const config = world.browserConfig();
1293
+ return { backend: config.backend, allowPrivate: config.allowPrivate };
1294
+ }
1295
+ if (call === 'terminal.browser.configure') {
1296
+ const world = this.options.world;
1297
+ if (!world)
1298
+ throw invalid('Browser settings are unavailable in this host.');
1299
+ const change = {};
1300
+ if ('allowPrivate' in params) {
1301
+ if (typeof params.allowPrivate !== 'boolean')
1302
+ throw invalid('allowPrivate must be a boolean.');
1303
+ change.allowPrivate = params.allowPrivate;
1304
+ }
1305
+ if ('backend' in params) {
1306
+ if (params.backend !== 'isolated' && params.backend !== 'brave')
1307
+ throw invalid('backend must be isolated or brave.');
1308
+ change.backend = params.backend;
1309
+ }
1310
+ if (!Object.keys(change).length)
1311
+ throw invalid('Set a browser backend or allowPrivate value.');
1312
+ const config = await world.configureBrowser(change);
1313
+ return { backend: config.backend, allowPrivate: config.allowPrivate };
1314
+ }
1315
+ if (call === 'terminal.browser.extension.status') {
1316
+ const world = this.options.world;
1317
+ if (!world)
1318
+ throw invalid('Browser settings are unavailable in this host.');
1319
+ return world.browserExtensionStatus();
1320
+ }
1321
+ if (call === 'terminal.browser.extension.start') {
1322
+ const world = this.options.world;
1323
+ if (!world)
1324
+ throw invalid('Browser settings are unavailable in this host.');
1325
+ return world.browserExtensionStart();
1326
+ }
1327
+ if (call === 'terminal.browser.extension.stop') {
1328
+ const world = this.options.world;
1329
+ if (!world)
1330
+ throw invalid('Browser settings are unavailable in this host.');
1331
+ return world.browserExtensionStop();
1332
+ }
1333
+ if (call === 'terminal.browser.snapshot')
1334
+ return browser.snapshot(params.refresh === true);
1335
+ if (call === 'terminal.browser.read') {
1336
+ if (typeof params.id !== 'string')
1337
+ throw invalid('A resource id is required.');
1338
+ return browser.read(params.id, params.latest === true);
1339
+ }
1340
+ if (call === 'terminal.browser.importSkill') {
1341
+ if (typeof params.path !== 'string' || !params.path.trim())
1342
+ throw invalid('A skill folder is required.');
1343
+ browser.importSkill(params.path);
1344
+ bus.emit({ type: 'plugins.changed' });
1345
+ return {};
1346
+ }
1347
+ if (call.startsWith('terminal.browser.connections.'))
1348
+ return browser.connection(call.slice('terminal.browser.connections.'.length), params);
1349
+ if (call === 'terminal.browser.hubPreview') {
1350
+ if (!this.options.hub || typeof params.id !== 'string' || typeof params.version !== 'string')
1351
+ throw invalid('A catalog entry and version are required.');
1352
+ return this.options.hub.preview(params.id, params.version, typeof params.offset === 'number' ? params.offset : 0);
1353
+ }
1354
+ if (call === 'terminal.browser.hubDocument') {
1355
+ if (!this.options.hub || typeof params.id !== 'string' || typeof params.version !== 'string')
1356
+ throw invalid('An installed document and version are required.');
1357
+ return this.options.hub.readDocument(params.id, params.version);
1358
+ }
1359
+ if (call.startsWith('terminal.doctor.')) {
1360
+ const doctor = this.options.doctor;
1361
+ if (!doctor)
1362
+ throw invalid('Diagnostics are unavailable in this host.');
1363
+ if (call === 'terminal.doctor.start') {
1364
+ const model = typeof params.model === 'string' ? params.model : live.input.model ?? null;
1365
+ const tools = [...new Set([
1366
+ ...(Array.isArray(params.tools) ? params.tools.filter((name) => typeof name === 'string') : []),
1367
+ ...await this.worldToolNames(live.input, live.account)
1368
+ ])];
1369
+ const variantDir = this.variantDirs.get(sessionId);
1370
+ const report = doctor.start({
1371
+ context: { caller: live.caller, cwd: live.input.cwd, accountId: live.account.id, model, tools,
1372
+ variant: variantDir ? { dir: variantDir, scope: live.input.variant?.scope } : null },
1373
+ mode: params.mode === 'live' ? 'live' : 'local',
1374
+ checks: Array.isArray(params.checks) ? params.checks.filter((id) => typeof id === 'string') : [],
1375
+ confirmed: params.confirmed === true
1376
+ });
1377
+ this.diagnosticOwners.set(report.id, sessionId);
1378
+ return report;
1379
+ }
1380
+ const id = typeof params.id === 'string' ? params.id : '';
1381
+ if (this.diagnosticOwners.get(id) !== sessionId)
1382
+ throw invalid('This diagnostic job belongs to another terminal.');
1383
+ if (call === 'terminal.doctor.read')
1384
+ return doctor.read(id);
1385
+ if (call === 'terminal.doctor.cancel')
1386
+ return doctor.cancel(id);
1387
+ throw invalid('Unknown diagnostic operation.');
1388
+ }
1389
+ if (call.startsWith('terminal.live.'))
1390
+ return this.liveTerminal(live).call(call, params);
1391
+ if (call.startsWith('terminal.agents.'))
1392
+ return this.agentsCall(live, call, params);
1393
+ if (call.startsWith('terminal.subagents.'))
1394
+ return this.subagentsCall(live, call, params);
1395
+ if (call.startsWith('terminal.sideQuestions.'))
1396
+ return this.sideQuestionCall(live, call, params);
1397
+ if (call.startsWith('terminal.worktrees.'))
1398
+ return this.worktreesCall(live, call, params);
1399
+ if (call.startsWith('terminal.annotations.'))
1400
+ return this.annotationsCall(live, call, params);
1401
+ if (call === 'terminal.logs.info')
1402
+ return harnessLogInfo(this.options.stateDir);
1403
+ if (call === 'terminal.logs.read') {
1404
+ const { snapshotSize, offset } = params;
1405
+ if (typeof snapshotSize !== 'number' || !Number.isSafeInteger(snapshotSize) || snapshotSize < 0 || typeof offset !== 'number' || !Number.isSafeInteger(offset) || offset < 0 || offset >= snapshotSize) {
1406
+ throw invalid('A valid diagnostics snapshot size and byte offset are required.');
1407
+ }
1408
+ const current = harnessLogInfo(this.options.stateDir).size;
1409
+ if (current === null || snapshotSize > current)
1410
+ throw invalid('The diagnostics snapshot is no longer available.');
1411
+ return readHarnessLogChunk(this.options.stateDir, snapshotSize, offset);
1412
+ }
1413
+ if (call.startsWith('terminal.variant.'))
1414
+ return variantCall(this.variantContext(live), call, params);
1415
+ if (call.startsWith('terminal.refine.'))
1416
+ return refineCall(this.variantContext(live), call, params);
1417
+ if (call.startsWith('terminal.components.'))
1418
+ return this.componentsCall(live, call, params);
1419
+ if (call.startsWith('terminal.trajectory.'))
1420
+ return this.trajectoryCall(live, call, params);
1421
+ if (call === 'terminal.todo')
1422
+ return latestTerminalTodo(this.options.env.root, live.input.sessionId, this.nativeOf(live));
1423
+ if (call === 'terminal.permissions.get') {
1424
+ const policy = this.options.approvals.read();
1425
+ return { full: this.options.approvals.isAuto('pi'), globalTools: policy.globalTools.pi ?? [] };
1426
+ }
1427
+ if (call === 'terminal.permissions.setFull') {
1428
+ if (typeof params.enabled !== 'boolean')
1429
+ throw invalid('A boolean permission state is required.');
1430
+ return { full: this.options.approvals.setAuto('pi', params.enabled).auto.pi === true };
1431
+ }
1432
+ if (call === 'terminal.permissions.setTool') {
1433
+ if (typeof params.tool !== 'string' || !params.tool.trim() || typeof params.allowed !== 'boolean')
1434
+ throw invalid('A tool name and boolean permission state are required.');
1435
+ const policy = params.allowed
1436
+ ? this.options.approvals.allowTool('pi', params.tool)
1437
+ : this.options.approvals.revokeTool('pi', params.tool);
1438
+ return { globalTools: policy.globalTools.pi ?? [] };
1439
+ }
1440
+ if (call.startsWith('terminal.usage.'))
1441
+ return this.usageCall(live, call, params);
1442
+ if (call === 'terminal.context')
1443
+ return this.contextCall(live, params);
1444
+ if (call.startsWith('terminal.remoteControl.'))
1445
+ return this.remoteCall(live, call, params);
1446
+ if (call !== 'terminal.resources')
1447
+ return this.controlFor(live)(call, params);
1448
+ const current = await this.options.plugins?.loading({ cwd: input.cwd, agentDir: configDir(accounts.dir, preparedAccount), install: false, caller: { projectId: input.projectId ?? null } }) ?? null;
1449
+ const currentVariant = variant ? materialise(root, { dir: variant.dir, scope: variant.scope }, { trust: this.options.trust ?? null }) : null;
1450
+ if (resourceIdentity(current) !== resourceIdentity(loading) || (variant && (currentVariant?.fingerprint !== variant.fingerprint || JSON.stringify(currentVariant.extensionPaths) !== JSON.stringify(variant.extensionPaths)))) {
1451
+ 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.');
1452
+ }
1453
+ const resourceOwners = (current?.found ?? []).filter(item => item.packageRoot && (item.kind === 'prompt' && current?.promptPaths.includes(item.path) || item.kind === 'extension' && current?.extensionPaths.includes(item.path)));
1454
+ return resourceOwners.length ? { resourceOwners } : {};
1455
+ } }
1456
+ });
1457
+ live.token = token;
1458
+ this.sessions.set(sessionId, live);
1459
+ const args = [...command.argsPrefix, '--append-system-prompt', render(env, pack)];
1460
+ if (input.model)
1461
+ args.push('--model', input.model);
1462
+ {
1463
+ // Pi's own discovery is off: the terminal loads what the harness found and nothing else.
1464
+ args.push('--no-extensions', '--no-skills', '--no-prompt-templates', '--no-themes');
1465
+ // The pi command's own extensions are not loaded: a host's session cannot have them (PROP-0039).
1466
+ for (const path of [...(loading?.extensionPaths ?? []), ...(variant?.extensionPaths ?? [])])
1467
+ args.push('--extension', path);
1468
+ for (const path of [...(variant?.skillPaths ?? []), ...defaultSkillPaths(variant?.dir), ...(loading?.skillPaths ?? [])])
1469
+ args.push('--skill', path);
1470
+ for (const path of loading?.promptPaths ?? [])
1471
+ args.push('--prompt-template', path);
1472
+ for (const path of loading?.themePaths ?? [])
1473
+ args.push('--theme', path);
1474
+ }
1475
+ // Last, so its gate sees a tool call as every other extension left it.
1476
+ args.push('--extension', this.assets.piExtension, '--session-dir', this.historyDir(input));
1477
+ if (input.nativeSessionId)
1478
+ args.push('--session', input.nativeSessionId);
1479
+ return {
1480
+ program: command.program,
1481
+ args,
1482
+ env: {
1483
+ ...launch,
1484
+ XINGULAR_HARNESS_NAME: env.productName,
1485
+ ...(process.platform === 'win32' ? { XINGULAR_HARNESS_NODE: this.assets.node, XINGULAR_HARNESS_CLI: this.assets.cli } : {}),
1486
+ ...prependSearchPath(this.ensureCommands(), searchPath()),
1487
+ [SOCKET_ENV]: this.socket.path,
1488
+ [TOKEN_ENV]: token
1489
+ },
1490
+ accountId: preparedAccount.id,
1491
+ nativeSessionId: input.nativeSessionId ?? null
1492
+ };
1493
+ }
1494
+ catch (error) {
1495
+ if (token)
1496
+ this.socket.revoke(token);
1497
+ this.sessions.delete(sessionId);
1498
+ this.variantDirs.delete(sessionId);
1499
+ this.packageProfiles.delete(sessionId);
1500
+ this.worldNames.delete(sessionId);
1501
+ if (this.worktreeLeases.get(sessionId) === acquiredWorktreeId)
1502
+ this.worktreeLeases.delete(sessionId);
1503
+ apps.revokeSession(sessionId);
1504
+ if (accountStarted && account)
1505
+ await accounts.terminalEnd(account).catch(() => undefined);
1506
+ if (acquiredWorktreeId)
1507
+ await this.options.worktrees?.release(acquiredWorktreeId, sessionId).catch(() => undefined);
1508
+ // A reattach that failed leaves the reply running without a window, as before.
1509
+ if (carried) {
1510
+ if (this.clients.get(sessionId) === carried.client)
1511
+ this.clients.delete(sessionId);
1512
+ const stop = carried.client.onSettled(() => {
1513
+ if (this.detachedClients.get(sessionId)?.client !== carried.client)
1514
+ return;
1515
+ this.detachedClients.delete(sessionId);
1516
+ stop();
1517
+ void carried.client.dispose().catch(() => undefined);
1518
+ });
1519
+ this.detachedClients.set(sessionId, { client: carried.client, info: carried.info, stop });
1520
+ }
1521
+ throw error;
1522
+ }
1523
+ }
1524
+ /** Hands the terminal an instruction as a new run. Resolves once the native session has taken it. */
1525
+ async send(input) {
1526
+ const { env } = this.options;
1527
+ const { root } = env;
1528
+ if (!input.text?.trim())
1529
+ throw invalid('Message is empty.');
1530
+ const live = this.live(input.sessionId);
1531
+ if (this.options.runs.activeForSession(input.sessionId) || live.controller.state().active)
1532
+ throw new HarnessError('session_busy', 'This session is still working.');
1533
+ const runId = input.runId || newId();
1534
+ if (findRun(root, runId))
1535
+ throw invalid('This run already exists.');
1536
+ saveRun(root, this.runRecord(live, runId, input.text));
1537
+ copyToRun(root, live.input.sessionFolder, runId);
1538
+ try {
1539
+ await live.controller.send(runId, input.text);
1540
+ }
1541
+ catch (error) {
1542
+ updateRun(root, runId, (run) => {
1543
+ if (!isActiveStatus(run.status))
1544
+ return;
1545
+ run.status = 'failed';
1546
+ run.endedAt = now();
1547
+ });
1548
+ throw error;
1549
+ }
1550
+ return { runId };
1551
+ }
1552
+ /** What the person typed into the terminal. */
1553
+ input(sessionId, data) {
1554
+ this.sessions.get(sessionId)?.controller.onInput(data);
1555
+ }
1556
+ state(sessionId) {
1557
+ return this.live(sessionId).controller.state();
1558
+ }
1559
+ has(sessionId) {
1560
+ return this.sessions.has(sessionId);
1561
+ }
1562
+ remote(live) {
1563
+ const sessionId = live.input.sessionId;
1564
+ let remote = this.remotes.get(sessionId);
1565
+ if (!remote) {
1566
+ remote = new RemoteControl(this.remotePort(live), { productName: this.options.env.productName, ...this.options.remoteControl });
1567
+ this.remotes.set(sessionId, remote);
1568
+ }
1569
+ return remote;
1570
+ }
1571
+ /**
1572
+ * What a paired phone reaches: this session's own runs and the same send, interrupt and
1573
+ * decision calls the terminal makes (INV-RC-3).
1574
+ */
1575
+ remotePort(live) {
1576
+ const { root } = this.options.env;
1577
+ const sessionId = live.input.sessionId;
1578
+ const own = new Map();
1579
+ const isOwn = (runId) => {
1580
+ let known = own.get(runId);
1581
+ if (known === undefined) {
1582
+ const record = findRun(root, runId);
1583
+ known = !!record && !record.relation && record.trigger !== 'side-question';
1584
+ if (own.size > 1000)
1585
+ own.clear();
1586
+ if (record)
1587
+ own.set(runId, known);
1588
+ }
1589
+ return known;
1590
+ };
1591
+ return {
1592
+ sessionId,
1593
+ title: () => basename(live.input.cwd) || this.options.env.productName,
1594
+ send: (text) => {
1595
+ const client = this.clients.get(sessionId);
1596
+ return client ? client.send(text).then((result) => ({ runId: 'runId' in result ? result.runId : client.lastRun ?? '' })) : this.send({ sessionId, text });
1597
+ },
1598
+ control: (call, params) => this.controlFor(live)(call, params),
1599
+ history: () => {
1600
+ const native = this.nativeOf(live);
1601
+ if (!native)
1602
+ return [];
1603
+ const runs = listRuns(root, { sessionId })
1604
+ .filter((run) => run.nativeSessionId === native && !run.relation && run.trigger !== 'side-question')
1605
+ .slice(0, 20)
1606
+ .reverse();
1607
+ return runs.flatMap((run) => readEvents(root, run.id));
1608
+ },
1609
+ listen: (listener) => this.options.runs.listen((event) => {
1610
+ if (event.sessionId === sessionId && isOwn(event.runId))
1611
+ listener(event);
1612
+ }),
1613
+ state: () => {
1614
+ const state = live.controller.state();
1615
+ return { ready: state.ready, active: state.active };
1616
+ }
1617
+ };
1618
+ }
1619
+ async remoteCall(live, call, params) {
1620
+ const remote = this.remote(live);
1621
+ if (call === 'terminal.remoteControl.status')
1622
+ return remote.status();
1623
+ if (call === 'terminal.remoteControl.status.wait')
1624
+ return remote.waitStatus(params.revision);
1625
+ if (call === 'terminal.remoteControl.start') {
1626
+ const choice = params.interface ?? 'auto';
1627
+ if (!['auto', 'tailscale', 'lan'].includes(choice))
1628
+ throw invalid('Choose auto, tailscale or lan.');
1629
+ return remote.start(choice);
1630
+ }
1631
+ if (call === 'terminal.remoteControl.stop')
1632
+ return remote.stop();
1633
+ if (call === 'terminal.remoteControl.rotate')
1634
+ return remote.rotate();
1635
+ if (call === 'terminal.remoteControl.confirm') {
1636
+ if (typeof params.requestId !== 'string' || typeof params.allow !== 'boolean')
1637
+ throw invalid('A pairing request id and a boolean choice are required.');
1638
+ return remote.confirm(params.requestId, params.allow);
1639
+ }
1640
+ throw new HarnessError('unknown_method', `Unknown terminal control ${call}.`);
1641
+ }
1642
+ /** The terminal process ended (or never started): its turn, tokens, and written credentials go with it. */
1643
+ /**
1644
+ * The native conversation a terminal's runs carry. A client terminal's runs are harness runs, which
1645
+ * record Pi's session id; an in-process terminal's record its history file (ADR-0083).
1646
+ */
1647
+ nativeOf(live) {
1648
+ const client = this.clients.get(live.input.sessionId);
1649
+ return client ? client.nativeSessionId : live.controller.nativeSessionId;
1650
+ }
1651
+ /** The history file a relaunch of this terminal resumes. */
1652
+ historyFileOf(live) {
1653
+ const client = this.clients.get(live.input.sessionId);
1654
+ return client ? client.sessionFile : live.controller.nativeSessionId;
1655
+ }
1656
+ /**
1657
+ * Where a terminal's decision and stop calls go: its controller, or, once the frontend is a client
1658
+ * of the harness, the live session's run (ADR-0083).
1659
+ */
1660
+ controlFor(live) {
1661
+ return (call, params) => {
1662
+ const client = this.clients.get(live.input.sessionId);
1663
+ const routed = client ? LIVE_DECISION_CALLS[call] : undefined;
1664
+ return routed ? client.call(routed, params) : live.controller.control(call, params);
1665
+ };
1666
+ }
1667
+ /** The client half of a terminal whose frontend runs no Pi of its own (ADR-0083). */
1668
+ liveTerminal(live) {
1669
+ const existing = this.clients.get(live.input.sessionId);
1670
+ if (existing)
1671
+ return existing;
1672
+ const sessions = this.options.sessionService;
1673
+ if (!sessions)
1674
+ throw invalid('This host does not run terminal turns in the harness.');
1675
+ const { input } = live;
1676
+ const client = new LiveTerminal({
1677
+ sessionId: input.sessionId,
1678
+ root: this.options.env.root,
1679
+ accountId: live.account.id,
1680
+ cwd: input.cwd,
1681
+ sessionDir: this.historyDir(input),
1682
+ caller: live.caller,
1683
+ // The version this terminal was prepared on stays pinned; a switch restarts the frontend (ADR-0065).
1684
+ variant: live.variant ? { dir: live.variant.dir, scope: live.variant.scope ?? undefined } : null,
1685
+ launch: { ...input.launch, standard: input.launch?.standard === null ? null : input.launch?.standard ?? { cwd: input.cwd } },
1686
+ connections: input.connections !== false,
1687
+ readOnly: !!input.readOnly,
1688
+ ...(input.subagents === null ? { subagents: null } : {}),
1689
+ allowTool: (target) => { this.options.approvals.allowTool('pi', target); },
1690
+ tools: async () => [...(input.launch?.tools ?? []), ...await this.worldToolNames(input, live.account)],
1691
+ commands: async () => {
1692
+ // The trusted extensions a turn would load, loaded only to read what they register.
1693
+ const pi = await loadPi();
1694
+ const account = this.options.accounts.context(live.account);
1695
+ const plugins = this.options.plugins;
1696
+ const loader = await loadResources(pi, {
1697
+ cwd: input.cwd, account, system: '', inline: [], variantExtensions: live.variant?.extensionPaths ?? [],
1698
+ resources: { ids: [], skillPaths: [], promptPaths: [] },
1699
+ ...(plugins ? { plugins: { loading: () => plugins.loading({ cwd: input.cwd, agentDir: account.configDir, install: false, caller: { projectId: input.projectId ?? null } }), loaded: () => undefined } } : {})
1700
+ }, () => undefined);
1701
+ // A plugin that cannot load stops the window, as it stops an in-process terminal's start.
1702
+ const { errors } = loader.getExtensions();
1703
+ if (errors.length)
1704
+ throw new Error(errors.map((error) => `${error.path}: ${error.error}`).join('\n'));
1705
+ return registeredCommands(loader.getExtensions().extensions);
1706
+ }
1707
+ }, sessions, this.options.runs);
1708
+ this.clients.set(input.sessionId, client);
1709
+ return client;
1710
+ }
1711
+ /**
1712
+ * The terminal's window closed. A client terminal whose reply is still running keeps it running in
1713
+ * the harness, without the window, until it settles; `prepare({ attach: true })` reconnects to it.
1714
+ * Anything else ends now, as `dispose` does.
1715
+ */
1716
+ async detach(sessionId) {
1717
+ const live = this.sessions.get(sessionId);
1718
+ const client = this.clients.get(sessionId);
1719
+ const activeRunId = client?.activeRun ?? null;
1720
+ if (!live || !client || !activeRunId) {
1721
+ await this.dispose(sessionId);
1722
+ return { detached: false, activeRunId: null };
1723
+ }
1724
+ // The window's binding goes; the client and its live session stay.
1725
+ this.clients.delete(sessionId);
1726
+ await this.dispose(sessionId);
1727
+ const info = {
1728
+ sessionId, cwd: live.input.cwd, sessionFolder: live.input.sessionFolder, accountId: live.account.id,
1729
+ activeRunId, sessionFile: client.sessionFile, detachedAt: new Date().toISOString()
1730
+ };
1731
+ const stop = client.onSettled(() => {
1732
+ const entry = this.detachedClients.get(sessionId);
1733
+ if (entry?.client !== client)
1734
+ return;
1735
+ this.detachedClients.delete(sessionId);
1736
+ entry.stop();
1737
+ void client.dispose().catch(() => undefined);
1738
+ });
1739
+ this.detachedClients.set(sessionId, { client, info, stop });
1740
+ return { detached: true, activeRunId };
1741
+ }
1742
+ /** Client terminals whose reply is running without a window. */
1743
+ detached() {
1744
+ return [...this.detachedClients.values()].map(({ client, info }) => ({ ...info, activeRunId: client.activeRun, sessionFile: client.sessionFile }));
1745
+ }
1746
+ async dispose(sessionId) {
1747
+ const live = this.sessions.get(sessionId);
1748
+ if (!live)
1749
+ return;
1750
+ const client = this.clients.get(sessionId);
1751
+ this.clients.delete(sessionId);
1752
+ await client?.dispose().catch(() => undefined);
1753
+ for (const [id, owner] of this.diagnosticOwners)
1754
+ if (owner === sessionId) {
1755
+ this.options.doctor?.cancel(id);
1756
+ this.diagnosticOwners.delete(id);
1757
+ }
1758
+ this.sessions.delete(sessionId);
1759
+ this.worldNames.delete(sessionId);
1760
+ this.refinements.delete(sessionId);
1761
+ const remote = this.remotes.get(sessionId);
1762
+ this.remotes.delete(sessionId);
1763
+ await remote?.dispose().catch(() => undefined);
1764
+ const worktreeId = this.worktreeLeases.get(sessionId);
1765
+ this.worktreeLeases.delete(sessionId);
1766
+ if (worktreeId)
1767
+ await this.options.worktrees?.release(worktreeId, sessionId).catch(() => undefined);
1768
+ void this.options.noteCapture?.release(sessionId).catch(() => undefined);
1769
+ void this.options.world?.release(sessionId).catch(() => undefined);
1770
+ live.controller.dispose();
1771
+ this.socket.revoke(live.token);
1772
+ this.options.apps.revokeSession(sessionId);
1773
+ await this.options.accounts.terminalEnd(live.account).catch((error) => log('terminal credentials', error instanceof Error ? error.message : String(error)));
1774
+ }
1775
+ async close() {
1776
+ const detached = [...this.detachedClients.values()];
1777
+ this.detachedClients.clear();
1778
+ await Promise.allSettled(detached.map(({ client, stop }) => { stop(); return client.dispose(); }));
1779
+ await Promise.allSettled([...this.sessions.keys()].map((sessionId) => this.dispose(sessionId)));
1780
+ await this.socket.close();
1781
+ }
1782
+ }