@xingular/harness 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (397) hide show
  1. package/CHANGELOG.md +46 -5
  2. package/README.md +75 -21
  3. package/dist/cli.js +17421 -8711
  4. package/dist/sdk/accounts/accounts.d.ts +27 -3
  5. package/dist/sdk/accounts/accounts.js +78 -24
  6. package/dist/sdk/accounts/secrets.d.ts +12 -0
  7. package/dist/sdk/accounts/secrets.js +114 -7
  8. package/dist/sdk/accounts/store.d.ts +26 -1
  9. package/dist/sdk/accounts/store.js +1 -1
  10. package/dist/sdk/agents/builtin.d.ts +2 -0
  11. package/dist/sdk/agents/builtin.js +48 -0
  12. package/dist/sdk/agents/narrow.d.ts +13 -0
  13. package/dist/sdk/agents/narrow.js +29 -0
  14. package/dist/sdk/agents/planResearch.d.ts +5 -0
  15. package/dist/sdk/agents/planResearch.js +19 -0
  16. package/dist/sdk/agents/profiles.d.ts +81 -0
  17. package/dist/sdk/agents/profiles.js +226 -0
  18. package/dist/sdk/annotations/bridge.d.ts +42 -0
  19. package/dist/sdk/annotations/bridge.js +264 -0
  20. package/dist/sdk/annotations/capture.d.ts +116 -0
  21. package/dist/sdk/annotations/capture.js +308 -0
  22. package/dist/sdk/annotations/delivery.d.ts +72 -0
  23. package/dist/sdk/annotations/delivery.js +130 -0
  24. package/dist/sdk/annotations/inspect.d.ts +10 -0
  25. package/dist/sdk/annotations/inspect.js +33 -0
  26. package/dist/sdk/annotations/isolated.d.ts +6 -0
  27. package/dist/sdk/annotations/isolated.js +51 -0
  28. package/dist/sdk/annotations/pageScript.d.ts +28 -0
  29. package/dist/sdk/annotations/pageScript.js +871 -0
  30. package/dist/sdk/annotations/part.d.ts +10 -0
  31. package/dist/sdk/annotations/part.js +155 -0
  32. package/dist/sdk/annotations/queue.d.ts +4 -0
  33. package/dist/sdk/annotations/queue.js +40 -0
  34. package/dist/sdk/annotations/serverMethods.d.ts +3 -0
  35. package/dist/sdk/annotations/serverMethods.js +62 -0
  36. package/dist/sdk/annotations/store.d.ts +167 -0
  37. package/dist/sdk/annotations/store.js +394 -0
  38. package/dist/sdk/browser/backend.d.ts +16 -0
  39. package/dist/sdk/browser/backend.js +17 -0
  40. package/dist/sdk/browser/contracts.d.ts +19 -0
  41. package/dist/sdk/browser/contracts.js +116 -0
  42. package/dist/sdk/browser/index.d.ts +14 -3
  43. package/dist/sdk/browser/index.js +11 -4
  44. package/dist/sdk/browser/serverMethods.d.ts +3 -0
  45. package/dist/sdk/browser/serverMethods.js +7 -0
  46. package/dist/sdk/browser/session.d.ts +1 -0
  47. package/dist/sdk/browser/session.js +45 -9
  48. package/dist/sdk/browser/tools.d.ts +1 -4
  49. package/dist/sdk/browser/tools.js +14 -91
  50. package/dist/sdk/cli.js +24 -15
  51. package/dist/sdk/commands/list.d.ts +3 -0
  52. package/dist/sdk/commands/list.js +75 -0
  53. package/dist/sdk/commands/registry.d.ts +250 -0
  54. package/dist/sdk/commands/registry.js +47 -0
  55. package/dist/sdk/commands/routes.d.ts +16 -0
  56. package/dist/sdk/commands/routes.js +33 -0
  57. package/dist/sdk/core/bus.js +1 -1
  58. package/dist/sdk/core/commandBudget.d.ts +23 -0
  59. package/dist/sdk/core/commandBudget.js +57 -0
  60. package/dist/sdk/core/env.d.ts +1 -1
  61. package/dist/sdk/core/env.js +1 -1
  62. package/dist/sdk/core/index.js +17 -1
  63. package/dist/sdk/core/interactiveProfile.d.ts +46 -0
  64. package/dist/sdk/core/interactiveProfile.js +143 -0
  65. package/dist/sdk/core/workspace.d.ts +13 -0
  66. package/dist/sdk/core/workspace.js +88 -0
  67. package/dist/sdk/core/workspaceBashTool.d.ts +4 -0
  68. package/dist/sdk/core/workspaceBashTool.js +36 -0
  69. package/dist/sdk/core/workspaceSandbox.d.ts +31 -0
  70. package/dist/sdk/core/workspaceSandbox.js +320 -0
  71. package/dist/sdk/core/workspaceWriteOperations.d.ts +22 -0
  72. package/dist/sdk/core/workspaceWriteOperations.js +206 -0
  73. package/dist/sdk/doctor/serverMethods.d.ts +3 -0
  74. package/dist/sdk/doctor/serverMethods.js +8 -0
  75. package/dist/sdk/doctor/types.d.ts +2 -2
  76. package/dist/sdk/harness.d.ts +22 -0
  77. package/dist/sdk/harness.js +84 -5
  78. package/dist/sdk/history/recall.js +8 -8
  79. package/dist/sdk/history/serverMethods.d.ts +3 -0
  80. package/dist/sdk/history/serverMethods.js +8 -0
  81. package/dist/sdk/hub/cli.d.ts +1 -1
  82. package/dist/sdk/hub/cli.js +1 -1
  83. package/dist/sdk/hub/schema.d.ts +1 -1
  84. package/dist/sdk/hub/serverMethods.d.ts +3 -0
  85. package/dist/sdk/hub/serverMethods.js +11 -0
  86. package/dist/sdk/index.d.ts +16 -1
  87. package/dist/sdk/index.js +10 -0
  88. package/dist/sdk/mcp/connections.js +14 -7
  89. package/dist/sdk/mcp/serverMethods.d.ts +3 -0
  90. package/dist/sdk/mcp/serverMethods.js +22 -0
  91. package/dist/sdk/pi/runtime.d.ts +2 -1
  92. package/dist/sdk/pi/runtime.js +2 -2
  93. package/dist/sdk/plugins/builtin.d.ts +1 -0
  94. package/dist/sdk/plugins/builtin.js +145 -33
  95. package/dist/sdk/plugins/builtinRegistry.d.ts +60 -0
  96. package/dist/sdk/plugins/builtinRegistry.js +71 -0
  97. package/dist/sdk/plugins/catalogue.js +2 -1
  98. package/dist/sdk/plugins/cli.d.ts +1 -1
  99. package/dist/sdk/plugins/cli.js +4 -3
  100. package/dist/sdk/plugins/commandPrefix.d.ts +8 -0
  101. package/dist/sdk/plugins/commandPrefix.js +21 -0
  102. package/dist/sdk/plugins/firstParty.d.ts +1 -1
  103. package/dist/sdk/plugins/firstParty.js +8 -2
  104. package/dist/sdk/plugins/inventory.js +4 -3
  105. package/dist/sdk/plugins/manifest.d.ts +2 -0
  106. package/dist/sdk/plugins/manifest.js +2 -1
  107. package/dist/sdk/plugins/surfaces.d.ts +5 -0
  108. package/dist/sdk/plugins/surfaces.js +11 -0
  109. package/dist/sdk/plugins/types.d.ts +15 -0
  110. package/dist/sdk/prompt/compile.d.ts +19 -2
  111. package/dist/sdk/prompt/compile.js +79 -26
  112. package/dist/sdk/prompt/standard.d.ts +17 -0
  113. package/dist/sdk/prompt/standard.js +21 -0
  114. package/dist/sdk/refine/cli.d.ts +1 -1
  115. package/dist/sdk/refine/cli.js +2 -2
  116. package/dist/sdk/refine/serverMethods.d.ts +3 -0
  117. package/dist/sdk/refine/serverMethods.js +20 -0
  118. package/dist/sdk/server/methods.js +27 -86
  119. package/dist/sdk/server/params.d.ts +4 -0
  120. package/dist/sdk/server/params.js +11 -0
  121. package/dist/sdk/server/remote.js +36 -11
  122. package/dist/sdk/sessions/compactionCheckpoint.d.ts +11 -0
  123. package/dist/sdk/sessions/compactionCheckpoint.js +79 -0
  124. package/dist/sdk/sessions/continuation.d.ts +3 -0
  125. package/dist/sdk/sessions/continuation.js +5 -1
  126. package/dist/sdk/sessions/contracts.d.ts +185 -0
  127. package/dist/sdk/sessions/contracts.js +101 -4
  128. package/dist/sdk/sessions/control.d.ts +72 -0
  129. package/dist/sdk/sessions/control.js +158 -0
  130. package/dist/sdk/sessions/initialBash.d.ts +7 -0
  131. package/dist/sdk/sessions/initialBash.js +7 -0
  132. package/dist/sdk/sessions/planMode.d.ts +18 -4
  133. package/dist/sdk/sessions/planMode.js +82 -16
  134. package/dist/sdk/sessions/reasoningSummaries.d.ts +2 -2
  135. package/dist/sdk/sessions/reasoningSummaries.js +3 -3
  136. package/dist/sdk/sessions/registry.d.ts +2 -1
  137. package/dist/sdk/sessions/registry.js +6 -3
  138. package/dist/sdk/sessions/runIndex.js +7 -7
  139. package/dist/sdk/sessions/runStore.d.ts +14 -0
  140. package/dist/sdk/sessions/runStore.js +38 -1
  141. package/dist/sdk/sessions/runner.d.ts +40 -2
  142. package/dist/sdk/sessions/runner.js +163 -15
  143. package/dist/sdk/sessions/sessions.d.ts +40 -8
  144. package/dist/sdk/sessions/sessions.js +279 -27
  145. package/dist/sdk/sessions/sideQuestionServerMethods.d.ts +3 -0
  146. package/dist/sdk/sessions/sideQuestionServerMethods.js +19 -0
  147. package/dist/sdk/sessions/sideQuestions.d.ts +66 -0
  148. package/dist/sdk/sessions/sideQuestions.js +290 -0
  149. package/dist/sdk/sessions/subagentServerMethods.d.ts +3 -0
  150. package/dist/sdk/sessions/subagentServerMethods.js +41 -0
  151. package/dist/sdk/sessions/subagentService.d.ts +22 -2
  152. package/dist/sdk/sessions/subagentService.js +93 -16
  153. package/dist/sdk/sessions/subagents.d.ts +9 -1
  154. package/dist/sdk/sessions/subagents.js +19 -0
  155. package/dist/sdk/sessions/workSlice.d.ts +103 -0
  156. package/dist/sdk/sessions/workSlice.js +297 -0
  157. package/dist/sdk/sessions/workspaceBoundary.d.ts +15 -0
  158. package/dist/sdk/sessions/workspaceBoundary.js +46 -0
  159. package/dist/sdk/terminal/browser.d.ts +62 -0
  160. package/dist/sdk/terminal/browser.js +41 -0
  161. package/dist/sdk/terminal/browserControl.d.ts +29 -0
  162. package/dist/sdk/terminal/browserControl.js +187 -0
  163. package/dist/sdk/terminal/commandHelp.d.ts +17 -0
  164. package/dist/sdk/terminal/commandHelp.js +20 -0
  165. package/dist/sdk/terminal/controller.d.ts +15 -1
  166. package/dist/sdk/terminal/controller.js +67 -8
  167. package/dist/sdk/terminal/opentui/subagentsWidget.d.ts +28 -0
  168. package/dist/sdk/terminal/opentui/subagentsWidget.js +52 -0
  169. package/dist/sdk/terminal/opentui/theme.d.ts +43 -0
  170. package/dist/sdk/terminal/opentui/theme.js +87 -0
  171. package/dist/sdk/terminal/piExtension.d.ts +6 -1
  172. package/dist/sdk/terminal/piExtension.js +169 -84
  173. package/dist/sdk/terminal/socket.d.ts +6 -3
  174. package/dist/sdk/terminal/socket.js +40 -9
  175. package/dist/sdk/terminal/standalone.d.ts +9 -0
  176. package/dist/sdk/terminal/standalone.js +118 -19
  177. package/dist/sdk/terminal/subagentMonitor.d.ts +47 -0
  178. package/dist/sdk/terminal/subagentMonitor.js +152 -0
  179. package/dist/sdk/terminal/terminals.d.ts +60 -7
  180. package/dist/sdk/terminal/terminals.js +598 -47
  181. package/dist/sdk/terminal/trajectory.js +2 -2
  182. package/dist/sdk/terminal/tui/annotateCommand.d.ts +3 -0
  183. package/dist/sdk/terminal/tui/annotateCommand.js +22 -0
  184. package/dist/sdk/terminal/tui/commandPages.d.ts +7 -1
  185. package/dist/sdk/terminal/tui/commandPages.js +57 -6
  186. package/dist/sdk/terminal/tui/commands.d.ts +10 -4
  187. package/dist/sdk/terminal/tui/commands.js +269 -65
  188. package/dist/sdk/terminal/tui/dialogGeometry.d.ts +2 -2
  189. package/dist/sdk/terminal/tui/dialogGeometry.js +4 -2
  190. package/dist/sdk/terminal/tui/dialogs.d.ts +6 -1
  191. package/dist/sdk/terminal/tui/dialogs.js +9 -2
  192. package/dist/sdk/terminal/tui/list.d.ts +14 -1
  193. package/dist/sdk/terminal/tui/list.js +96 -14
  194. package/dist/sdk/terminal/tui/modelPicker.js +1 -6
  195. package/dist/sdk/terminal/tui/page.d.ts +3 -0
  196. package/dist/sdk/terminal/tui/pageOverlay.js +27 -7
  197. package/dist/sdk/terminal/tui/pages/accountsPage.d.ts +26 -4
  198. package/dist/sdk/terminal/tui/pages/accountsPage.js +264 -36
  199. package/dist/sdk/terminal/tui/pages/agentsPage.d.ts +74 -0
  200. package/dist/sdk/terminal/tui/pages/agentsPage.js +190 -0
  201. package/dist/sdk/terminal/tui/pages/annotatePage.d.ts +94 -0
  202. package/dist/sdk/terminal/tui/pages/annotatePage.js +341 -0
  203. package/dist/sdk/terminal/tui/pages/browserPage.d.ts +87 -0
  204. package/dist/sdk/terminal/tui/pages/browserPage.js +738 -0
  205. package/dist/sdk/terminal/tui/pages/doctorPage.d.ts +2 -0
  206. package/dist/sdk/terminal/tui/pages/doctorPage.js +103 -15
  207. package/dist/sdk/terminal/tui/pages/notesPage.d.ts +75 -0
  208. package/dist/sdk/terminal/tui/pages/notesPage.js +201 -0
  209. package/dist/sdk/terminal/tui/pages/pluginsPage.d.ts +0 -5
  210. package/dist/sdk/terminal/tui/pages/pluginsPage.js +9 -3
  211. package/dist/sdk/terminal/tui/pages/settingsPage.d.ts +8 -0
  212. package/dist/sdk/terminal/tui/pages/settingsPage.js +16 -1
  213. package/dist/sdk/terminal/tui/pages/subagentsPage.d.ts +24 -0
  214. package/dist/sdk/terminal/tui/pages/subagentsPage.js +152 -0
  215. package/dist/sdk/terminal/tui/pages/trajectoryPage.d.ts +12 -5
  216. package/dist/sdk/terminal/tui/pages/trajectoryPage.js +156 -83
  217. package/dist/sdk/terminal/tui/pages/usagePage.d.ts +43 -0
  218. package/dist/sdk/terminal/tui/pages/usagePage.js +370 -0
  219. package/dist/sdk/terminal/tui/pages/worktreesPage.d.ts +72 -0
  220. package/dist/sdk/terminal/tui/pages/worktreesPage.js +203 -0
  221. package/dist/sdk/terminal/tui/piAdapter.d.ts +1 -2
  222. package/dist/sdk/terminal/tui/piAdapter.js +3 -5
  223. package/dist/sdk/terminal/tui/planMode.d.ts +1 -1
  224. package/dist/sdk/terminal/tui/planMode.js +1 -1
  225. package/dist/sdk/terminal/tui/pluginCommands.d.ts +16 -0
  226. package/dist/sdk/terminal/tui/pluginCommands.js +31 -0
  227. package/dist/sdk/terminal/tui/questions.d.ts +2 -0
  228. package/dist/sdk/terminal/tui/runtime.js +27 -5
  229. package/dist/sdk/terminal/tui/sessionPicker.d.ts +40 -0
  230. package/dist/sdk/terminal/tui/sessionPicker.js +141 -0
  231. package/dist/sdk/terminal/tui/settingsModel.d.ts +2 -0
  232. package/dist/sdk/terminal/tui/settingsModel.js +32 -2
  233. package/dist/sdk/terminal/tui/sideQuestion.d.ts +26 -26
  234. package/dist/sdk/terminal/tui/sideQuestion.js +131 -98
  235. package/dist/sdk/terminal/tui/ui.d.ts +2 -0
  236. package/dist/sdk/terminal/tui/ui.js +1 -0
  237. package/dist/sdk/tools/askUser.js +26 -8
  238. package/dist/sdk/tools/checklist.js +3 -2
  239. package/dist/sdk/tools/checkpoint.d.ts +27 -0
  240. package/dist/sdk/tools/checkpoint.js +154 -0
  241. package/dist/sdk/tools/getCapabilities.d.ts +31 -0
  242. package/dist/sdk/tools/getCapabilities.js +34 -0
  243. package/dist/sdk/tools/harnessTools.d.ts +2 -0
  244. package/dist/sdk/tools/harnessTools.js +1 -1
  245. package/dist/sdk/tools/spawnSubagent.js +14 -4
  246. package/dist/sdk/usage/analytics.d.ts +20 -0
  247. package/dist/sdk/usage/analytics.js +151 -0
  248. package/dist/sdk/usage/ledger.d.ts +38 -0
  249. package/dist/sdk/usage/ledger.js +142 -0
  250. package/dist/sdk/usage/limits/adapters.d.ts +11 -0
  251. package/dist/sdk/usage/limits/adapters.js +349 -0
  252. package/dist/sdk/usage/limits/capture.d.ts +8 -0
  253. package/dist/sdk/usage/limits/capture.js +47 -0
  254. package/dist/sdk/usage/limits/http.d.ts +12 -0
  255. package/dist/sdk/usage/limits/http.js +51 -0
  256. package/dist/sdk/usage/limits/normalize.d.ts +15 -0
  257. package/dist/sdk/usage/limits/normalize.js +211 -0
  258. package/dist/sdk/usage/limits/service.d.ts +30 -0
  259. package/dist/sdk/usage/limits/service.js +176 -0
  260. package/dist/sdk/usage/limits/types.d.ts +65 -0
  261. package/dist/sdk/usage/limits/types.js +1 -0
  262. package/dist/sdk/usage/serverMethods.d.ts +3 -0
  263. package/dist/sdk/usage/serverMethods.js +22 -0
  264. package/dist/sdk/usage/service.d.ts +32 -0
  265. package/dist/sdk/usage/service.js +66 -0
  266. package/dist/sdk/usage/settings.d.ts +7 -0
  267. package/dist/sdk/usage/settings.js +28 -0
  268. package/dist/sdk/usage/types.d.ts +60 -0
  269. package/dist/sdk/usage/types.js +4 -0
  270. package/dist/sdk/web/part.d.ts +10 -0
  271. package/dist/sdk/web/part.js +6 -0
  272. package/dist/sdk/web/serverMethods.d.ts +3 -0
  273. package/dist/sdk/web/serverMethods.js +7 -0
  274. package/dist/sdk/wiki/serverMethods.d.ts +3 -0
  275. package/dist/sdk/wiki/serverMethods.js +26 -0
  276. package/dist/sdk/worktrees/cli.d.ts +11 -0
  277. package/dist/sdk/worktrees/cli.js +146 -0
  278. package/dist/sdk/worktrees/copy.d.ts +9 -0
  279. package/dist/sdk/worktrees/copy.js +111 -0
  280. package/dist/sdk/worktrees/git.d.ts +16 -0
  281. package/dist/sdk/worktrees/git.js +81 -0
  282. package/dist/sdk/worktrees/serverMethods.d.ts +3 -0
  283. package/dist/sdk/worktrees/serverMethods.js +30 -0
  284. package/dist/sdk/worktrees/service.d.ts +73 -0
  285. package/dist/sdk/worktrees/service.js +483 -0
  286. package/dist/sdk/worktrees/setup.d.ts +23 -0
  287. package/dist/sdk/worktrees/setup.js +65 -0
  288. package/dist/sdk/worktrees/types.d.ts +91 -0
  289. package/dist/sdk/worktrees/types.js +1 -0
  290. package/dist/sdk/world/cli.d.ts +2 -2
  291. package/dist/sdk/world/cli.js +6 -6
  292. package/dist/sdk/world/world.d.ts +3 -1
  293. package/dist/sdk/world/world.js +8 -9
  294. package/dist/terminal-opentui.js +16456 -5966
  295. package/dist/terminal-pi.js +1579 -573
  296. package/docs/README.md +22 -2
  297. package/docs/architecture/00-overview.md +28 -16
  298. package/docs/architecture/02-sessions-and-runs.md +184 -25
  299. package/docs/architecture/03-variants.md +10 -2
  300. package/docs/architecture/04-prompt.md +65 -7
  301. package/docs/architecture/06-plugins-and-capabilities.md +57 -4
  302. package/docs/architecture/07-tools-and-gate.md +112 -17
  303. package/docs/architecture/08-memory-and-history.md +4 -2
  304. package/docs/architecture/09-accounts-and-connectors.md +39 -1
  305. package/docs/architecture/10-server-protocol.md +168 -4
  306. package/docs/architecture/11-terminal.md +251 -8
  307. package/docs/architecture/12-storage.md +45 -3
  308. package/docs/architecture/13-world-tools.md +49 -6
  309. package/docs/architecture/14-surfaces-and-parity.md +136 -0
  310. package/docs/architecture/README.md +17 -4
  311. package/docs/architecture/proposals/PROP-0006-run-checklist.md +4 -2
  312. package/docs/architecture/proposals/PROP-0013-resource-browser.md +58 -0
  313. package/docs/architecture/proposals/PROP-0014-fullscreen-browser-tabs.md +58 -0
  314. package/docs/architecture/proposals/PROP-0015-one-capability-surface.md +88 -0
  315. package/docs/architecture/proposals/PROP-0016-managed-worktrees.md +156 -0
  316. package/docs/architecture/proposals/PROP-0017-page-annotations.md +131 -0
  317. package/docs/architecture/proposals/PROP-0018-annotation-queue-and-app-targets.md +179 -0
  318. package/docs/architecture/proposals/PROP-0019-contextual-side-questions.md +122 -0
  319. package/docs/architecture/proposals/PROP-0020-builtin-feature-plugins.md +105 -0
  320. package/docs/architecture/proposals/PROP-0021-command-namespaces-and-duplicates.md +99 -0
  321. package/docs/architecture/proposals/PROP-0022-plan-only-questions.md +102 -0
  322. package/docs/architecture/proposals/PROP-0023-xagent-cli-name.md +61 -0
  323. package/docs/architecture/proposals/PROP-0024-question-sketch-previews.md +130 -0
  324. package/docs/architecture/proposals/PROP-0025-contextual-question-wireframes.md +49 -0
  325. package/docs/architecture/proposals/PROP-0026-connected-sketch-display.md +94 -0
  326. package/docs/architecture/proposals/PROP-0027-xingular-agent-product-identity.md +60 -0
  327. package/docs/architecture/proposals/PROP-0028-normalize-question-wireframes.md +55 -0
  328. package/docs/architecture/proposals/PROP-0029-agent-profiles.md +124 -0
  329. package/docs/architecture/proposals/PROP-0030-subagent-title.md +86 -0
  330. package/docs/architecture/proposals/PROP-0031-plugin-command-owner-namespaces.md +54 -0
  331. package/docs/architecture/proposals/PROP-0032-planning-quality.md +196 -0
  332. package/docs/architecture/proposals/PROP-0033-compaction-plan-checkpoints.md +75 -0
  333. package/docs/architecture/proposals/PROP-0034-conversation-fast-mode.md +120 -0
  334. package/docs/architecture/proposals/PROP-0035-retain-subagent-status.md +47 -0
  335. package/docs/architecture/proposals/PROP-0036-first-class-harness-commands.md +59 -0
  336. package/docs/architecture/proposals/PROP-0037-workspace-write-boundary.md +63 -0
  337. package/docs/architecture/proposals/PROP-0038-annotate-open-shadow-roots.md +73 -0
  338. package/docs/architecture/proposals/PROP-0039-workspace-boundary-hardening.md +139 -0
  339. package/docs/architecture/proposals/PROP-0040-one-launch-path.md +87 -0
  340. package/docs/architecture/proposals/PROP-0041-bounded-work-slices.md +247 -0
  341. package/docs/architecture/proposals/PROP-0042-usage-and-provider-limits.md +127 -0
  342. package/docs/architecture/proposals/PROP-0043-read-only-plan-bash.md +74 -0
  343. package/docs/architecture/proposals/README.md +39 -0
  344. package/docs/decisions/ADR-0001-owned-terminal.md +1 -1
  345. package/docs/decisions/ADR-0009-xharness-command.md +6 -5
  346. package/docs/decisions/ADR-0014-public-release-baseline.md +3 -2
  347. package/docs/decisions/ADR-0015-resource-browser.md +28 -0
  348. package/docs/decisions/ADR-0016-fullscreen-browser-tabs.md +31 -0
  349. package/docs/decisions/ADR-0017-one-capability-surface.md +34 -0
  350. package/docs/decisions/ADR-0018-builtin-feature-plugins.md +35 -0
  351. package/docs/decisions/ADR-0019-managed-git-worktrees.md +31 -0
  352. package/docs/decisions/ADR-0020-page-annotations.md +37 -0
  353. package/docs/decisions/ADR-0021-required-command-namespaces.md +40 -0
  354. package/docs/decisions/ADR-0022-plan-only-question-tools.md +34 -0
  355. package/docs/decisions/ADR-0023-contextual-side-question-runs.md +35 -0
  356. package/docs/decisions/ADR-0024-xagent-command.md +40 -0
  357. package/docs/decisions/ADR-0025-question-choice-sketches.md +49 -0
  358. package/docs/decisions/ADR-0026-contextual-question-wireframes.md +58 -0
  359. package/docs/decisions/ADR-0027-connected-sketch-display.md +60 -0
  360. package/docs/decisions/ADR-0028-xingular-agent-product-identity.md +56 -0
  361. package/docs/decisions/ADR-0029-normalized-question-wireframe-borders.md +33 -0
  362. package/docs/decisions/ADR-0030-agent-profiles.md +43 -0
  363. package/docs/decisions/ADR-0031-plugin-command-owner-namespaces.md +37 -0
  364. package/docs/decisions/ADR-0032-compaction-run-checkpoints.md +33 -0
  365. package/docs/decisions/ADR-0033-plan-mode-research-delegation.md +47 -0
  366. package/docs/decisions/ADR-0034-first-class-harness-terminal-commands.md +25 -0
  367. package/docs/decisions/ADR-0035-one-launch-path.md +40 -0
  368. package/docs/decisions/ADR-0036-workspace-write-operation-boundary.md +54 -0
  369. package/docs/decisions/ADR-0037-usage-ledger-and-provider-limits.md +44 -0
  370. package/docs/decisions/ADR-0038-plan-bash-os-read-only.md +29 -0
  371. package/docs/doctor.md +10 -4
  372. package/docs/experiments/EXP-0003-performance-audit/data/session-runtime-replication.json +420 -0
  373. package/docs/experiments/EXP-0003-performance-audit/data/session-runtime.json +420 -0
  374. package/docs/experiments/EXP-0003-performance-audit/data/terminal-compact.json +376 -0
  375. package/docs/experiments/EXP-0003-performance-audit/data/terminal-history.json +422 -0
  376. package/docs/experiments/EXP-0003-performance-audit/data/terminal-question.json +95 -0
  377. package/docs/experiments/EXP-0003-performance-audit/data/terminal-wide.json +1036 -0
  378. package/docs/experiments/EXP-0003-performance-audit/plan.md +109 -0
  379. package/docs/experiments/EXP-0003-performance-audit/results.md +151 -0
  380. package/docs/features/README.md +26 -0
  381. package/docs/features/interfaces/README.md +1 -1
  382. package/docs/future-work.md +48 -0
  383. package/docs/handoff-2026-10-25-app-parity-and-solo-tabs.md +202 -0
  384. package/docs/parity.md +134 -0
  385. package/docs/releases/1.0.0.md +2 -2
  386. package/docs/releases/1.1.1.md +28 -0
  387. package/docs/releases/1.2.0.md +49 -0
  388. package/docs/releases/4.0.0.md +47 -0
  389. package/docs/releases/README.md +37 -27
  390. package/docs/study/00-index.md +8 -0
  391. package/docs/study/STUDY-0003-long-horizon-runs.md +210 -0
  392. package/docs/study/autonomous-engineering-sources.md +277 -0
  393. package/docs/study/autonomy-sources.md +147 -0
  394. package/docs/study/proactive-autonomy.md +408 -0
  395. package/docs/study/tui-resource-browser.md +78 -0
  396. package/docs/terminal-tui.md +386 -113
  397. package/package.json +6 -5
@@ -0,0 +1,152 @@
1
+ import { matchesKey, stripTerminalSequences, truncateToWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';
2
+ import { CHROME_PAD, headerRow, neutralRule, padded } from "../chrome.js";
3
+ import { FilterList } from "../list.js";
4
+ import { ACTIVE_SUBAGENT_STATUSES, terminalSubagentDuration } from "../../subagentMonitor.js";
5
+ import { profileColor, opentuiTheme } from "../../opentui/theme.js";
6
+ import { subagentTasks } from "../../opentui/subagentsWidget.js";
7
+ import { formatTokenCount } from "../status.js";
8
+ import { getWorkingSpinnerFrames, SPINNER_INTERVAL_MS, spinnerFrameAt } from "../workingStatus.js";
9
+ const TABS = [{ id: 'subagents', title: 'Subagents' }];
10
+ function clean(value) {
11
+ return stripTerminalSequences(value).replace(/[\x00-\x08\x0b-\x1f\x7f]/g, ' ').replace(/\s+/g, ' ').trim();
12
+ }
13
+ function statusGlyph(item, now) {
14
+ if (item.status === 'running') {
15
+ const started = Date.parse(item.startedAt);
16
+ return spinnerFrameAt(now - (Number.isFinite(started) ? started : now), getWorkingSpinnerFrames(process.env.TERM), SPINNER_INTERVAL_MS);
17
+ }
18
+ switch (item.status) {
19
+ case 'queued': return '○';
20
+ case 'waiting_for_approval':
21
+ case 'waiting_for_input': return '●';
22
+ case 'completed': return '✓';
23
+ case 'failed': return '✕';
24
+ case 'interrupted': return '!';
25
+ }
26
+ }
27
+ function statusGlyphTone(item) {
28
+ if (item.status === 'completed')
29
+ return 'success';
30
+ if (item.status === 'failed')
31
+ return 'error';
32
+ if (item.status === 'waiting_for_approval' || item.status === 'waiting_for_input')
33
+ return 'accent';
34
+ if (item.status === 'running')
35
+ return 'warning';
36
+ return 'muted';
37
+ }
38
+ function effectiveModel(item) {
39
+ const model = item.usage?.model;
40
+ if (model)
41
+ return `${item.usage?.provider ? `${item.usage.provider}/` : ''}${model}`;
42
+ return item.model ?? '—';
43
+ }
44
+ function usageSummary(item) {
45
+ const usage = item.usage;
46
+ if (!usage)
47
+ return 'usage —';
48
+ const cache = [usage.cacheReadTokens, usage.cacheWriteTokens]
49
+ .filter((value) => typeof value === 'number' && value > 0)
50
+ .map(formatTokenCount);
51
+ const cost = typeof usage.costUsd === 'number'
52
+ ? `${usage.costSource === 'estimate' ? '~' : ''}$${usage.costUsd.toFixed(usage.costUsd > 0 && usage.costUsd < 0.001 ? 5 : usage.costUsd < 0.01 ? 4 : 2).replace(/0+$/u, '').replace(/\.$/u, '')}`
53
+ : '';
54
+ const fields = [
55
+ typeof usage.inputTokens === 'number' ? `↑${formatTokenCount(usage.inputTokens)}` : '',
56
+ typeof usage.outputTokens === 'number' ? `↓${formatTokenCount(usage.outputTokens)}` : '',
57
+ cache.some(Boolean) ? `c${cache.join('/')}` : '',
58
+ cost,
59
+ typeof usage.contextTokens === 'number' ? `ctx${formatTokenCount(usage.contextTokens)}${typeof usage.contextWindow === 'number' ? `/${formatTokenCount(usage.contextWindow)}` : ''}` : '',
60
+ typeof usage.requests === 'number' ? `req${usage.requests}` : ''
61
+ ].filter(Boolean);
62
+ return fields.length ? fields.join(' ') : 'usage —';
63
+ }
64
+ function rowSubtitle(item, width) {
65
+ const available = Math.max(1, width - 2);
66
+ const verbose = width >= 96;
67
+ const account = clean(item.accountLabel ?? '') || '—';
68
+ const model = truncateToWidth(effectiveModel(item), 24, '…');
69
+ const details = verbose
70
+ ? [`acct ${account}`, `model ${model}`, `eff ${item.effort ?? '—'}`, usageSummary(item)].join(' · ')
71
+ : [`a:${account}`, `m:${model}`, `e:${item.effort ?? '—'}`, usageSummary(item)].join(' · ');
72
+ return truncateToWidth(details, available, '…');
73
+ }
74
+ export class SubagentsPage {
75
+ options;
76
+ presentation = 'fullscreen';
77
+ list;
78
+ constructor(options) {
79
+ this.options = options;
80
+ this.list = new FilterList({
81
+ theme: options.theme, search: 'always', noun: ['subagent', 'subagents'], rowGap: 1,
82
+ empty: 'No subagents have started in the latest run'
83
+ });
84
+ }
85
+ get focused() { return this.list.focused; }
86
+ set focused(value) { this.list.focused = value; }
87
+ tabs() { return TABS; }
88
+ activeTab() { return 'subagents'; }
89
+ hint() { return '↑↓ select · type to search'; }
90
+ rows(width = 80, now = this.options.now?.() ?? Date.now()) {
91
+ const ordered = [...this.options.snapshot()].sort((left, right) => {
92
+ const active = Number(ACTIVE_SUBAGENT_STATUSES.has(right.status)) - Number(ACTIVE_SUBAGENT_STATUSES.has(left.status));
93
+ return active || left.startedAt.localeCompare(right.startedAt) || left.id.localeCompare(right.id);
94
+ });
95
+ return ordered.map(item => {
96
+ const profile = clean(item.profile?.name ?? '') || 'subagent';
97
+ const title = clean(item.title) || 'Subagent';
98
+ const color = item.profile?.color ? profileColor(opentuiTheme, item.profile.color) : null;
99
+ const glyph = statusGlyph(item, now);
100
+ const labelParts = [
101
+ { text: `${glyph} `, ...(item.status === 'running' && color ? { color } : { tone: statusGlyphTone(item) }) },
102
+ { text: profile, ...(color ? { color } : { tone: 'muted' }) },
103
+ { text: ` ${title}` }
104
+ ];
105
+ return {
106
+ id: item.id,
107
+ label: labelParts.map(part => part.text).join(''),
108
+ labelParts,
109
+ subtitle: rowSubtitle(item, width),
110
+ searchText: `${profile} ${title} ${item.goal ?? ''} ${item.status} ${item.activity} ${item.accountLabel ?? ''} ${effectiveModel(item)} ${item.effort ?? ''} ${usageSummary(item)}`,
111
+ data: item
112
+ };
113
+ });
114
+ }
115
+ render(width, rows) {
116
+ const items = this.rows(width, this.options.now?.() ?? Date.now());
117
+ this.list.setRows(items);
118
+ const active = items.filter(item => item.data && ACTIVE_SUBAGENT_STATUSES.has(item.data.status)).length;
119
+ const selected = this.list.selected?.data;
120
+ const header = headerRow('Subagents', `${active} active · ${items.length} total`, width, this.options.theme());
121
+ const bodyRows = Math.max(0, rows - 7);
122
+ const listRows = Math.max(1, Math.min(this.list.height, Math.floor(bodyRows * 0.6)));
123
+ const output = [header, '', ...this.list.render(width, listRows), ''];
124
+ if (selected) {
125
+ const theme = this.options.theme();
126
+ const detailIndent = ' '.repeat(CHROME_PAD * 2);
127
+ const detailLines = (text) => wrapTextWithAnsi(theme.fg('text', clean(text)), Math.max(1, width - detailIndent.length))
128
+ .map(line => `${detailIndent}${line}`);
129
+ output.push(neutralRule(width, theme));
130
+ output.push(padded(theme.fg('muted', `Profile: ${clean(selected.profile?.name ?? '') || 'copy of the parent'}`), width));
131
+ output.push(padded(theme.fg('accent', 'Task'), width));
132
+ output.push(...detailLines(selected.goal || 'Subagent'));
133
+ output.push(padded(theme.fg('muted', `Status: ${[selected.status.replaceAll('_', ' '), subagentTasks(selected.checklist), terminalSubagentDuration(selected)].filter(Boolean).join(' · ')}`), width));
134
+ output.push(padded(theme.fg('accent', 'Current activity'), width));
135
+ output.push(...detailLines(selected.activity));
136
+ }
137
+ else {
138
+ output.push(padded(this.options.theme().fg('muted', 'The selected subagent’s task and current activity appear here.'), width));
139
+ }
140
+ return output.slice(0, Math.max(0, rows)).map(line => truncateToWidth(line, width, ''));
141
+ }
142
+ handleInput(data) {
143
+ this.list.setRows(this.rows());
144
+ if (matchesKey(data, 'enter'))
145
+ return this.list.handleInput(data);
146
+ if (this.list.handleInput(data)) {
147
+ this.options.changed();
148
+ return true;
149
+ }
150
+ return false;
151
+ }
152
+ }
@@ -5,7 +5,7 @@ import type { RunEvent } from "../../../sessions/contracts.js";
5
5
  import type { TerminalTrajectoryRun, TerminalTrajectorySnapshot } from "../../trajectory.js";
6
6
  import { type TabInfo } from "../chrome.js";
7
7
  import type { TerminalPage } from "../page.js";
8
- import { type TerminalMouseInput } from "../mouse.js";
8
+ import type { TerminalMouseInput } from "../mouse.js";
9
9
  export interface TrajectoryPageOptions {
10
10
  theme: () => Theme;
11
11
  runs(): Promise<TerminalTrajectoryRun[]>;
@@ -18,17 +18,20 @@ export interface TrajectoryPageOptions {
18
18
  }
19
19
  export declare class TrajectoryPage implements TerminalPage {
20
20
  private readonly options;
21
+ readonly presentation: "fullscreen";
21
22
  private runs;
22
23
  private section;
23
24
  private readonly runList;
25
+ private readonly eventList;
24
26
  private runIndex;
25
27
  private run;
26
28
  private events;
27
- private selected;
28
29
  private detailOffset;
29
- private eventRowsStart;
30
- private eventRowsFirst;
31
- private eventRowsCount;
30
+ private listStart;
31
+ private listEnd;
32
+ private listWidth;
33
+ private detailsStart;
34
+ private wideLayout;
32
35
  private loading;
33
36
  private loaded;
34
37
  private disposed;
@@ -49,10 +52,14 @@ export declare class TrajectoryPage implements TerminalPage {
49
52
  private refresh;
50
53
  /** `[` is the older run, `]` the newer one. */
51
54
  private runRows;
55
+ private eventRows;
56
+ private eventJson;
52
57
  private selectRun;
53
58
  private changeRun;
54
59
  hint(): string;
55
60
  handleMouse(event: TerminalMouseInput): TuiMouseEventResult | undefined;
56
61
  handleInput(data: string): boolean;
62
+ private runSummary;
63
+ private detailLines;
57
64
  render(width: number, rows: number): string[];
58
65
  }
@@ -1,7 +1,5 @@
1
- import { matchesKey, stripTerminalSequences, truncateToWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';
2
- import { selectedRow, selectionText } from "../chrome.js";
1
+ import { matchesKey, stripTerminalSequences, truncateToWidth, visibleWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';
3
2
  import { FilterList } from "../list.js";
4
- import { isPrimaryClick } from "../mouse.js";
5
3
  const ACTIVE = new Set(['queued', 'running', 'waiting_for_approval', 'waiting_for_input']);
6
4
  const payloadOf = (event) => event.payload && typeof event.payload === 'object' ? event.payload : {};
7
5
  const stringOf = (value) => typeof value === 'string' ? value : undefined;
@@ -68,17 +66,20 @@ const SECTIONS = [
68
66
  ];
69
67
  export class TrajectoryPage {
70
68
  options;
69
+ presentation = 'fullscreen';
71
70
  runs = [];
72
71
  section = 'runs';
73
72
  runList;
73
+ eventList;
74
74
  runIndex = 0;
75
75
  run;
76
76
  events = [];
77
- selected = 0;
78
77
  detailOffset = 0;
79
- eventRowsStart = -1;
80
- eventRowsFirst = 0;
81
- eventRowsCount = 0;
78
+ listStart = 0;
79
+ listEnd = 0;
80
+ listWidth = 0;
81
+ detailsStart = -1;
82
+ wideLayout = false;
82
83
  loading = false;
83
84
  loaded = false;
84
85
  disposed = false;
@@ -87,15 +88,19 @@ export class TrajectoryPage {
87
88
  constructor(options) {
88
89
  this.options = options;
89
90
  this.runList = new FilterList({
90
- theme: options.theme, search: 'never', noun: ['run', 'runs'], empty: 'No runs recorded for this native session yet',
91
+ theme: options.theme, search: 'always', noun: ['run', 'runs'], empty: 'No runs recorded for this native session yet',
91
92
  onActivate: row => { if (row.data)
92
93
  void this.selectRun(row.data); }
93
94
  });
95
+ this.eventList = new FilterList({
96
+ theme: options.theme, search: 'always', noun: ['event', 'events'], empty: 'No recorded events',
97
+ onActivate: () => { this.detailOffset = 0; this.options.changed(); }
98
+ });
94
99
  }
95
100
  get selectedRunId() { return this.run?.id; }
96
- get selectedEvent() { return this.events[this.selected]; }
97
- get focused() { return this.runList.focused; }
98
- set focused(value) { this.runList.focused = value; }
101
+ get selectedEvent() { return this.eventList.selected?.data; }
102
+ get focused() { return this.section === 'runs' ? this.runList.focused : this.eventList.focused; }
103
+ set focused(value) { this.runList.focused = value; this.eventList.focused = value; }
99
104
  tabs() { return SECTIONS; }
100
105
  activeTab() { return this.section; }
101
106
  activate() {
@@ -153,8 +158,11 @@ export class TrajectoryPage {
153
158
  return;
154
159
  this.run = snapshot.run;
155
160
  this.events = snapshot.events.slice().sort(bySequence);
161
+ this.eventList.setRows(this.eventRows());
162
+ const latest = this.events.at(-1);
163
+ if (latest)
164
+ this.eventList.select(String(latest.sequence));
156
165
  this.runList.select(run.id);
157
- this.selected = Math.max(0, this.events.length - 1);
158
166
  this.detailOffset = 0;
159
167
  }
160
168
  async refresh() {
@@ -162,7 +170,8 @@ export class TrajectoryPage {
162
170
  return;
163
171
  this.loading = true;
164
172
  const lastSequence = this.events.at(-1)?.sequence;
165
- const followedLatest = this.selected === this.events.length - 1;
173
+ const selectedBefore = this.selectedEvent;
174
+ const followedLatest = !selectedBefore || selectedBefore.sequence === lastSequence;
166
175
  try {
167
176
  const snapshot = await this.options.read(this.run.id, lastSequence);
168
177
  if (this.disposed)
@@ -176,8 +185,14 @@ export class TrajectoryPage {
176
185
  const existing = new Set(this.events.map(event => event.sequence));
177
186
  this.events = [...this.events, ...snapshot.events.filter(event => !existing.has(event.sequence))].sort(bySequence);
178
187
  }
179
- if (followedLatest && this.events.length)
180
- this.selected = this.events.length - 1;
188
+ this.eventList.setRows(this.eventRows());
189
+ if (followedLatest) {
190
+ const latest = this.events.at(-1);
191
+ if (latest)
192
+ this.eventList.select(String(latest.sequence));
193
+ }
194
+ else if (selectedBefore)
195
+ this.eventList.select(String(selectedBefore.sequence));
181
196
  this.error = undefined;
182
197
  }
183
198
  catch (error) {
@@ -201,6 +216,18 @@ export class TrajectoryPage {
201
216
  data: run
202
217
  }));
203
218
  }
219
+ eventRows() {
220
+ return this.events.map(event => ({
221
+ id: String(event.sequence),
222
+ label: `${timeOf(event.timestamp)} ${event.type}`,
223
+ detail: eventDescription(event),
224
+ searchText: `${event.timestamp} ${event.type} ${eventDescription(event)} ${safe(JSON.stringify(payloadOf(event)))}`,
225
+ data: event
226
+ }));
227
+ }
228
+ eventJson(event) {
229
+ return JSON.stringify({ payload: event.payload, type: event.type, sequence: event.sequence, timestamp: event.timestamp }, null, 2);
230
+ }
204
231
  async selectRun(run) {
205
232
  const at = this.runs.findIndex(candidate => candidate.id === run.id);
206
233
  if (at < 0 || this.loading)
@@ -209,7 +236,7 @@ export class TrajectoryPage {
209
236
  this.runList.select(run.id);
210
237
  this.run = run;
211
238
  this.events = [];
212
- this.selected = 0;
239
+ this.eventList.setRows([]);
213
240
  this.detailOffset = 0;
214
241
  this.error = undefined;
215
242
  this.loading = true;
@@ -236,7 +263,7 @@ export class TrajectoryPage {
236
263
  this.run = this.runs[next];
237
264
  this.runList.select(this.run.id);
238
265
  this.events = [];
239
- this.selected = 0;
266
+ this.eventList.setRows([]);
240
267
  this.detailOffset = 0;
241
268
  this.error = undefined;
242
269
  this.loading = true;
@@ -255,21 +282,38 @@ export class TrajectoryPage {
255
282
  }
256
283
  }
257
284
  hint() {
258
- return this.section === 'runs' ? '↑/↓ runs · Enter inspect' : '[ ] runs · ↑/↓ events · PgUp/PgDn details · c copy';
285
+ return this.section === 'runs'
286
+ ? '↑/↓ runs · type to search · Enter inspect'
287
+ : '[ ] runs · ↑/↓ events · type to search · PgUp/PgDn details · Shift+C copy';
259
288
  }
260
289
  handleMouse(event) {
290
+ if (event.type === 'wheel') {
291
+ const down = (event.wheelDelta ?? 1) >= 0;
292
+ if (this.section === 'events' && this.detailsStart >= 0 && (this.wideLayout ? event.x >= this.listWidth + 3 : event.y >= this.detailsStart)) {
293
+ this.detailOffset = Math.max(0, this.detailOffset + (down ? 4 : -4));
294
+ this.options.changed();
295
+ return { handled: true, render: true };
296
+ }
297
+ if (this.section === 'runs')
298
+ this.runList.handleInput(down ? '\x1b[B' : '\x1b[A');
299
+ else
300
+ this.eventList.handleInput(down ? '\x1b[B' : '\x1b[A');
301
+ this.detailOffset = 0;
302
+ this.options.changed();
303
+ return { handled: true, render: true };
304
+ }
261
305
  if (event.type === 'click' && event.button !== 'left')
262
306
  return undefined;
263
- if (this.section === 'runs')
264
- return this.runList.handleMouse({ ...event, y: event.y - 1 });
265
- const offset = event.y - this.eventRowsStart;
266
- if (this.eventRowsStart < 0 || offset < 0 || offset >= this.eventRowsCount)
307
+ if (event.type !== 'click' && event.type !== 'move')
267
308
  return undefined;
268
- const next = this.eventRowsFirst + offset;
269
- const changed = this.selected !== next;
270
- this.selected = next;
271
- this.detailOffset = 0;
272
- return { handled: true, render: changed || isPrimaryClick(event) };
309
+ if (this.section === 'runs') {
310
+ if (event.y < this.listStart || event.y >= this.listEnd)
311
+ return undefined;
312
+ return this.runList.handleMouse({ ...event, y: event.y - this.listStart });
313
+ }
314
+ if (event.y < this.listStart || event.y >= this.listEnd || (this.wideLayout && event.x >= this.listWidth + 3))
315
+ return undefined;
316
+ return this.eventList.handleMouse({ ...event, y: event.y - this.listStart });
273
317
  }
274
318
  handleInput(data) {
275
319
  if (matchesKey(data, 'tab') || matchesKey(data, 'shift+tab')) {
@@ -288,16 +332,6 @@ export class TrajectoryPage {
288
332
  void this.changeRun(-1);
289
333
  return true;
290
334
  }
291
- if (matchesKey(data, 'up')) {
292
- this.selected = Math.max(0, this.selected - 1);
293
- this.detailOffset = 0;
294
- return true;
295
- }
296
- if (matchesKey(data, 'down')) {
297
- this.selected = this.events.length ? Math.min(this.events.length - 1, this.selected + 1) : 0;
298
- this.detailOffset = 0;
299
- return true;
300
- }
301
335
  if (matchesKey(data, 'pageUp')) {
302
336
  this.detailOffset = Math.max(0, this.detailOffset - 8);
303
337
  return true;
@@ -306,71 +340,110 @@ export class TrajectoryPage {
306
340
  this.detailOffset += 8;
307
341
  return true;
308
342
  }
309
- if (data.toLowerCase() === 'c' && this.selectedEvent) {
310
- const text = JSON.stringify(this.selectedEvent, null, 2);
343
+ if (data === 'C' && this.selectedEvent) {
344
+ const text = this.eventJson(this.selectedEvent);
311
345
  void this.options.copy(text).then(() => this.options.notify('Copied selected trajectory event')).catch(error => this.options.notify(String(error), 'error'));
312
346
  return true;
313
347
  }
314
- return false;
348
+ const selected = this.selectedEvent?.sequence;
349
+ if (!this.eventList.handleInput(data))
350
+ return false;
351
+ if (this.selectedEvent?.sequence !== selected)
352
+ this.detailOffset = 0;
353
+ return true;
354
+ }
355
+ runSummary(width, rows) {
356
+ if (!this.run || rows < 1)
357
+ return [];
358
+ const runPosition = `${this.runIndex + 1}/${this.runs.length}`;
359
+ const modelEvent = [...this.events].reverse().find(event => event.type === 'usage.updated');
360
+ const usage = modelEvent ? payloadOf(modelEvent) : {};
361
+ const model = stringOf(usage.model) ?? 'model unavailable';
362
+ const theme = this.options.theme();
363
+ const lines = [theme.bold(theme.fg('accent', `Run ${runPosition} · ${this.run.id} · ${this.run.status}${this.loading ? ' · loading' : ''}`))];
364
+ if (rows >= 3)
365
+ lines.push(theme.fg('muted', `${model} · ${durationLine(this.run)} · ${this.run.mode}`));
366
+ if (rows >= 4)
367
+ lines.push(theme.fg('muted', usageLine(this.events)));
368
+ if (rows >= 6)
369
+ lines.push(theme.fg('dim', this.run.goal ? `Prompt: ${this.run.goal.replace(/\s+/g, ' ').slice(0, 140)}` : 'Prompt text unavailable'));
370
+ if (this.error)
371
+ lines.push(theme.fg('error', `Read error: ${this.error}`));
372
+ return lines.slice(0, Math.min(rows, 5)).map(line => truncateToWidth(line, width, ''));
373
+ }
374
+ detailLines(width, rows) {
375
+ if (rows < 1)
376
+ return [];
377
+ const event = this.selectedEvent;
378
+ const theme = this.options.theme();
379
+ const heading = theme.bold(theme.fg('accent', event ? `Event JSON · ${event.type} · #${event.sequence}` : 'Event JSON'));
380
+ const json = event
381
+ ? wrapTextWithAnsi(safe(this.eventJson(event)), Math.max(1, width))
382
+ : [this.events.length ? 'No event selected' : this.loading ? 'Loading recorded events…' : 'No recorded events'];
383
+ const visibleRows = Math.max(0, rows - 1);
384
+ this.detailOffset = Math.min(this.detailOffset, Math.max(0, json.length - visibleRows));
385
+ return [heading, ...json.slice(this.detailOffset, this.detailOffset + visibleRows)
386
+ .map(line => this.options.theme().fg('dim', line))].slice(0, rows).map(line => truncateToWidth(line, width, ''));
315
387
  }
316
388
  render(width, rows) {
317
389
  const theme = this.options.theme();
318
390
  if (width < 1 || rows < 1)
319
391
  return [];
320
392
  if (this.section === 'runs') {
321
- this.eventRowsStart = -1;
322
- this.eventRowsCount = 0;
393
+ this.wideLayout = false;
394
+ this.detailsStart = -1;
323
395
  if (this.error && !this.runs.length)
324
396
  return [theme.fg('error', 'Read error: ' + this.error)];
325
- const lines = [theme.fg('dim', !this.loaded ? 'Loading recorded runs…' : this.runs.length + ' recorded runs')];
326
- lines.push(...this.runList.render(width, Math.max(0, rows - lines.length)));
397
+ const lines = [theme.fg('dim', !this.loaded ? 'Loading recorded runs…' : `${this.runs.length} recorded runs`)];
398
+ this.listStart = lines.length;
399
+ const list = this.runList.render(width, Math.max(0, rows - lines.length));
400
+ this.listEnd = this.listStart + list.length;
401
+ lines.push(...list);
327
402
  return lines.slice(0, rows).map(line => truncateToWidth(line, width, ''));
328
403
  }
329
404
  if (!this.run) {
330
- this.eventRowsStart = -1;
331
- this.eventRowsCount = 0;
405
+ this.listStart = this.listEnd = 0;
406
+ this.detailsStart = -1;
332
407
  if (this.error)
333
408
  return [theme.fg('error', 'Read error: ' + this.error)];
334
409
  return [theme.fg('dim', this.loading || !this.loaded ? 'Loading recorded runs…' : 'No run selected. Choose a run in the Runs tab.')];
335
410
  }
336
- const runPosition = (this.runIndex + 1) + '/' + this.runs.length;
337
- const modelEvent = [...this.events].reverse().find(event => event.type === 'usage.updated');
338
- const usage = modelEvent ? payloadOf(modelEvent) : {};
339
- const model = stringOf(usage.model) ?? 'model unavailable';
340
- const lines = [theme.bold(theme.fg('accent', 'Run ' + runPosition + ' · ' + this.run.id + ' · ' + this.run.status + (this.loading ? ' · loading' : '')))];
341
- if (rows >= 9) {
342
- lines.push(theme.fg('muted', model + ' · ' + durationLine(this.run) + ' · ' + this.run.mode));
343
- lines.push(theme.fg('muted', usageLine(this.events)));
344
- lines.push(this.run.goal ? theme.fg('dim', 'Prompt: ' + this.run.goal.replace(/\s+/g, ' ').slice(0, 140)) : theme.fg('dim', 'Prompt text unavailable'));
345
- }
346
- else if (rows >= 6) {
347
- lines.push(theme.fg('muted', model + ' · ' + durationLine(this.run)));
348
- }
349
- lines.push(theme.bold('Events'));
350
- if (this.error)
351
- lines.push(theme.fg('error', 'Read error: ' + this.error));
352
- if (!this.events.length)
353
- lines.push(theme.fg('dim', this.loading ? 'Loading recorded events…' : 'No recorded events'));
354
- const available = Math.max(0, rows - lines.length - 1);
355
- const ledgerBudget = this.events.length ? Math.min(this.events.length, Math.max(1, Math.floor(available * 0.4))) : 0;
356
- const detailBudget = Math.max(0, available - ledgerBudget);
357
- const firstEvent = Math.max(0, Math.min(this.events.length - ledgerBudget, this.selected - Math.floor(ledgerBudget / 2)));
358
- this.eventRowsStart = lines.length;
359
- this.eventRowsFirst = firstEvent;
360
- this.eventRowsCount = ledgerBudget;
361
- for (let offset = 0; offset < ledgerBudget; offset++) {
362
- const index = firstEvent + offset;
363
- const event = this.events[index];
364
- const prefix = index === this.selected ? '❯' : ' ';
365
- const label = truncateToWidth(prefix + ' ' + timeOf(event.timestamp) + ' ' + event.type + ' ' + eventDescription(event), width, '');
366
- lines.push(index === this.selected ? selectedRow(selectionText(theme, label), width, theme) : theme.fg('muted', label));
411
+ const lines = this.runSummary(width, rows);
412
+ const bodyRows = Math.max(0, rows - lines.length);
413
+ const wide = width >= 72;
414
+ this.wideLayout = wide;
415
+ if (wide) {
416
+ const separatorWidth = 3;
417
+ const leftWidth = Math.max(24, Math.floor((width - separatorWidth) * 0.42));
418
+ const rightWidth = Math.max(1, width - leftWidth - separatorWidth);
419
+ const paneStart = lines.length;
420
+ this.listWidth = leftWidth;
421
+ const events = [theme.bold(theme.fg('accent', 'Events')), ...this.eventList.render(leftWidth, Math.max(0, bodyRows - 1))];
422
+ const details = this.detailLines(rightWidth, bodyRows);
423
+ this.listStart = paneStart + 1;
424
+ this.listEnd = this.listStart + events.length - 1;
425
+ this.detailsStart = paneStart;
426
+ const pane = Array.from({ length: Math.max(events.length, details.length) }, (_, index) => {
427
+ const left = truncateToWidth(events[index] ?? '', leftWidth, '');
428
+ const gap = ' '.repeat(Math.max(0, leftWidth - visibleWidth(left)));
429
+ return left + gap + theme.fg('dim', ' │ ') + (details[index] ?? '');
430
+ });
431
+ lines.push(...pane);
367
432
  }
368
- const selected = this.selectedEvent;
369
- lines.push(theme.bold(selected ? 'Selected event · ' + selected.type + ' · #' + selected.sequence : 'Selected event'));
370
- if (selected) {
371
- const details = wrapTextWithAnsi(safe(JSON.stringify({ payload: selected.payload, type: selected.type, sequence: selected.sequence, timestamp: selected.timestamp }, null, 2)), width);
372
- this.detailOffset = Math.min(this.detailOffset, Math.max(0, details.length - detailBudget));
373
- lines.push(...details.slice(this.detailOffset, this.detailOffset + detailBudget).map(line => theme.fg('dim', line)));
433
+ else {
434
+ lines.push(theme.bold(theme.fg('accent', 'Events')));
435
+ this.listStart = lines.length;
436
+ this.listWidth = width;
437
+ const listRows = Math.min(Math.max(0, bodyRows - 1), Math.max(1, Math.floor(bodyRows * 0.58)));
438
+ const events = this.eventList.render(width, listRows);
439
+ this.listEnd = this.listStart + events.length;
440
+ lines.push(...events);
441
+ const hasRoomForDetails = lines.length < rows;
442
+ if (hasRoomForDetails)
443
+ lines.push('');
444
+ this.detailsStart = lines.length;
445
+ const detailRows = Math.max(0, rows - lines.length);
446
+ lines.push(...this.detailLines(width, detailRows));
374
447
  }
375
448
  return lines.slice(0, rows).map(line => truncateToWidth(line, width, ''));
376
449
  }
@@ -0,0 +1,43 @@
1
+ import type { Theme } from '@earendil-works/pi-coding-agent';
2
+ import type { UsagePeriod } from "../../../usage/types.js";
3
+ import type { TabInfo } from "../chrome.js";
4
+ import type { TerminalPage } from "../page.js";
5
+ type UsageTab = 'analysis' | 'limits';
6
+ interface UsagePageOptions {
7
+ theme(): Theme;
8
+ call(method: string, params?: Record<string, unknown>): Promise<unknown>;
9
+ changed(): void;
10
+ initialTab?: UsageTab;
11
+ initialPeriod?: UsagePeriod;
12
+ }
13
+ export declare class UsagePage implements TerminalPage {
14
+ private readonly options;
15
+ presentation: "centered";
16
+ focused: boolean;
17
+ private tab;
18
+ private period;
19
+ private scope;
20
+ private summary?;
21
+ private limitsSummary?;
22
+ private limits?;
23
+ private busy;
24
+ private error?;
25
+ private refreshLimits;
26
+ private offset;
27
+ private scrollRows;
28
+ private maxOffset;
29
+ private disposed;
30
+ constructor(options: UsagePageOptions);
31
+ tabs(): readonly TabInfo[];
32
+ activeTab(): string;
33
+ selectTab(id: string): void;
34
+ hint(): string;
35
+ activate(): void;
36
+ dispose(): void;
37
+ private load;
38
+ handleInput(data: string): boolean;
39
+ private analysisContent;
40
+ private limitsContent;
41
+ render(width: number, rows: number): string[];
42
+ }
43
+ export {};