@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,206 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import { spawn } from 'node:child_process';
3
+ import { createHash } from 'node:crypto';
4
+ import { constants as fsConstants, realpathSync, statSync } from 'node:fs';
5
+ import { access, readFile } from 'node:fs/promises';
6
+ import { dirname, resolve } from 'node:path';
7
+ import { isWithinWorkspace, resolveWritePath, verifyWorkspaceRoot } from "./workspace.js";
8
+ import { workspaceSandboxCommand } from "./workspaceSandbox.js";
9
+ const writeAuthorization = new AsyncLocalStorage();
10
+ const atomicWriteScript = `
11
+ const fs = require('node:fs')
12
+ const path = require('node:path')
13
+ const crypto = require('node:crypto')
14
+ const input = JSON.parse(fs.readFileSync(0, 'utf8'))
15
+ const target = input.path
16
+ const parent = path.dirname(target)
17
+ fs.mkdirSync(parent, { recursive: true })
18
+ let mode = 0o666
19
+ let preserveMode = false
20
+ try {
21
+ const existing = fs.lstatSync(target)
22
+ if (existing.isSymbolicLink() || !existing.isFile()) throw new Error('The write target is not a regular file.')
23
+ fs.accessSync(target, fs.constants.W_OK)
24
+ if (input.expectedContentHash) {
25
+ const current = fs.readFileSync(target)
26
+ if (crypto.createHash('sha256').update(current).digest('hex') !== input.expectedContentHash) {
27
+ throw new Error('The file changed after the edit tool read it; retry against the current contents.')
28
+ }
29
+ }
30
+ mode = existing.mode & 0o7777
31
+ preserveMode = true
32
+ } catch (error) {
33
+ if (error && error.code !== 'ENOENT') throw error
34
+ }
35
+ const temporary = path.join(parent, '.' + path.basename(target) + '.' + process.pid + '.' + crypto.randomBytes(8).toString('hex') + '.tmp')
36
+ let descriptor
37
+ try {
38
+ descriptor = fs.openSync(temporary, 'wx', mode)
39
+ fs.writeFileSync(descriptor, input.content, 'utf8')
40
+ if (preserveMode) fs.fchmodSync(descriptor, mode)
41
+ fs.fsyncSync(descriptor)
42
+ fs.closeSync(descriptor)
43
+ descriptor = undefined
44
+ fs.renameSync(temporary, target)
45
+ try {
46
+ const directory = fs.openSync(parent, 'r')
47
+ try { fs.fsyncSync(directory) } finally { fs.closeSync(directory) }
48
+ } catch {}
49
+ } catch (error) {
50
+ if (descriptor !== undefined) fs.closeSync(descriptor)
51
+ try { fs.unlinkSync(temporary) } catch {}
52
+ throw error
53
+ }
54
+ `;
55
+ function shellQuote(value) {
56
+ return `'${value.replaceAll("'", "'\\''")}'`;
57
+ }
58
+ function nearestExistingDirectory(path) {
59
+ let current = dirname(path);
60
+ while (true) {
61
+ try {
62
+ const canonical = realpathSync(current);
63
+ if (!statSync(canonical).isDirectory())
64
+ throw new Error(`Write scope is not a directory: ${canonical}`);
65
+ return canonical;
66
+ }
67
+ catch (error) {
68
+ if (error.code !== 'ENOENT')
69
+ throw error;
70
+ const parent = dirname(current);
71
+ if (parent === current)
72
+ throw new Error(`Could not find a writable parent for ${path}.`);
73
+ current = parent;
74
+ }
75
+ }
76
+ }
77
+ function killProcess(child) {
78
+ if (child.pid && process.platform !== 'win32') {
79
+ try {
80
+ process.kill(-child.pid, 'SIGKILL');
81
+ return;
82
+ }
83
+ catch {
84
+ // Fall back to the direct child if it has already left its process group.
85
+ }
86
+ }
87
+ child.kill('SIGKILL');
88
+ }
89
+ async function runAtomicWrite(authorization, path, content) {
90
+ const root = verifyWorkspaceRoot(authorization.workspaceRoot);
91
+ const canonicalPath = resolveWritePath(path, authorization.cwd);
92
+ if (canonicalPath !== authorization.path)
93
+ throw new Error('The write target changed after workspace approval; the write was blocked.');
94
+ const outside = !isWithinWorkspace(root, canonicalPath);
95
+ if (outside !== authorization.outside || (outside && !authorization.authorized)) {
96
+ throw new Error('The write authorization no longer matches the active workspace target.');
97
+ }
98
+ const additionalRoots = outside ? [nearestExistingDirectory(canonicalPath)] : [];
99
+ const command = `exec ${shellQuote(process.execPath)} -e ${shellQuote(atomicWriteScript)}`;
100
+ const spec = workspaceSandboxCommand(process.platform, root, root, command, additionalRoots);
101
+ const env = {
102
+ PATH: '/usr/bin:/bin',
103
+ ...(process.env.HOME ? { HOME: process.env.HOME } : {})
104
+ };
105
+ await new Promise((resolvePromise, rejectPromise) => {
106
+ const child = spawn(spec.executable, spec.args, {
107
+ cwd: root,
108
+ detached: process.platform !== 'win32',
109
+ env,
110
+ stdio: ['pipe', 'pipe', 'pipe'],
111
+ windowsHide: true
112
+ });
113
+ const output = [];
114
+ let outputBytes = 0;
115
+ let settled = false;
116
+ const timeout = setTimeout(() => {
117
+ killProcess(child);
118
+ finish(new Error('Workspace-contained atomic write timed out.'));
119
+ }, 30_000);
120
+ const capture = (chunk) => {
121
+ if (outputBytes >= 8_192)
122
+ return;
123
+ const remaining = 8_192 - outputBytes;
124
+ output.push(chunk.subarray(0, remaining));
125
+ outputBytes += Math.min(chunk.length, remaining);
126
+ };
127
+ const finish = (error) => {
128
+ if (settled)
129
+ return;
130
+ settled = true;
131
+ clearTimeout(timeout);
132
+ authorization.signal?.removeEventListener('abort', abort);
133
+ if (error)
134
+ rejectPromise(error);
135
+ else
136
+ resolvePromise();
137
+ };
138
+ const abort = () => {
139
+ killProcess(child);
140
+ finish(new Error('Workspace-contained atomic write was aborted.'));
141
+ };
142
+ authorization.signal?.addEventListener('abort', abort, { once: true });
143
+ if (authorization.signal?.aborted) {
144
+ abort();
145
+ return;
146
+ }
147
+ child.stdout?.on('data', capture);
148
+ child.stderr?.on('data', capture);
149
+ child.once('error', (error) => finish(error));
150
+ child.once('close', (code) => {
151
+ if (code === 0) {
152
+ finish();
153
+ return;
154
+ }
155
+ const detail = Buffer.concat(output).toString('utf8').trim();
156
+ finish(new Error(`Workspace-contained atomic write failed${detail ? `: ${detail.slice(-2000)}` : ` with exit code ${code ?? 'unknown'}`}.`));
157
+ });
158
+ child.stdin?.on('error', () => { });
159
+ child.stdin?.end(JSON.stringify({
160
+ path: canonicalPath,
161
+ content,
162
+ ...(authorization.expectedContentHash ? { expectedContentHash: authorization.expectedContentHash } : {})
163
+ }));
164
+ });
165
+ }
166
+ async function writeWorkspaceFile(path, content) {
167
+ const authorization = writeAuthorization.getStore();
168
+ if (!authorization?.authorized)
169
+ throw new Error('The file write has no current workspace authorization.');
170
+ await runAtomicWrite(authorization, path, content);
171
+ }
172
+ export const workspaceWriteOperations = {
173
+ mkdir: async (_directory) => { },
174
+ writeFile: writeWorkspaceFile
175
+ };
176
+ export const workspaceEditOperations = {
177
+ access: async (path) => access(path, fsConstants.R_OK | fsConstants.W_OK),
178
+ readFile: async (path) => {
179
+ const content = await readFile(path);
180
+ const authorization = writeAuthorization.getStore();
181
+ if (authorization && resolve(path) === authorization.path) {
182
+ authorization.expectedContentHash = createHash('sha256').update(content).digest('hex');
183
+ }
184
+ return content;
185
+ },
186
+ writeFile: writeWorkspaceFile
187
+ };
188
+ /** Binds one policy-approved path and abort signal to Pi's native write/edit operation. */
189
+ export function withWorkspaceWriteAuthorization(tool, authorizations) {
190
+ const native = tool;
191
+ const execute = native.execute.bind(tool);
192
+ return {
193
+ ...tool,
194
+ execute: (...args) => {
195
+ const toolCallId = String(args[0] ?? '');
196
+ const authorization = authorizations.get(toolCallId);
197
+ authorizations.delete(toolCallId);
198
+ if (!authorization?.authorized)
199
+ throw new Error('The file write has no current workspace authorization.');
200
+ const signal = args[2];
201
+ if (signal && typeof signal === 'object' && 'aborted' in signal)
202
+ authorization.signal = signal;
203
+ return writeAuthorization.run(authorization, () => execute(...args));
204
+ }
205
+ };
206
+ }
@@ -0,0 +1,3 @@
1
+ import type { Harness } from "../harness.js";
2
+ import type { Handler } from "../server/rpc.js";
3
+ export declare function doctorServerMethods(harness: Harness): Record<string, Handler>;
@@ -0,0 +1,8 @@
1
+ import { text } from "../server/params.js";
2
+ export function doctorServerMethods(harness) {
3
+ return {
4
+ 'doctor.start': (params) => harness.doctor.start(params),
5
+ 'doctor.read': (params) => harness.doctor.read(text(params, 'id')),
6
+ 'doctor.cancel': (params) => harness.doctor.cancel(text(params, 'id'))
7
+ };
8
+ }
@@ -11,9 +11,9 @@ export declare const findingSchema: z.ZodObject<{
11
11
  status: z.ZodEnum<{
12
12
  disabled: "disabled";
13
13
  warning: "warning";
14
+ blocked: "blocked";
14
15
  pass: "pass";
15
16
  fail: "fail";
16
- blocked: "blocked";
17
17
  unverified: "unverified";
18
18
  }>;
19
19
  verification: z.ZodEnum<{
@@ -119,9 +119,9 @@ export declare const doctorReportSchema: z.ZodObject<{
119
119
  status: z.ZodEnum<{
120
120
  disabled: "disabled";
121
121
  warning: "warning";
122
+ blocked: "blocked";
122
123
  pass: "pass";
123
124
  fail: "fail";
124
- blocked: "blocked";
125
125
  unverified: "unverified";
126
126
  }>;
127
127
  verification: z.ZodEnum<{
@@ -21,7 +21,14 @@ import type { ToolPolicy } from "./sessions/policy.js";
21
21
  import { RunRegistry } from "./sessions/registry.js";
22
22
  import type { SessionFactory } from "./sessions/runner.js";
23
23
  import { Sessions } from "./sessions/sessions.js";
24
+ import { SideQuestions } from "./sessions/sideQuestions.js";
24
25
  import { Terminals, type TerminalAssets } from "./terminal/terminals.js";
26
+ import { Worktrees } from "./worktrees/service.js";
27
+ import { Usage } from "./usage/service.js";
28
+ import { UsageLimits } from "./usage/limits/service.js";
29
+ import { AnnotationCapture } from "./annotations/capture.js";
30
+ import { NoteDeliveries } from "./annotations/delivery.js";
31
+ import type { WorktreesOptions } from "./worktrees/types.js";
25
32
  export interface HarnessOptions {
26
33
  /** Disable the generic backend when a host supplies its own browser tools. */
27
34
  browser?: boolean;
@@ -60,6 +67,8 @@ export interface HarnessOptions {
60
67
  oauthClients?: AppOAuthClients;
61
68
  /** Replaces how a plugin's hooks file is read as a module, for tests. */
62
69
  hookImporter?: (address: string) => Promise<unknown>;
70
+ /** Where managed worktrees are made, the prefix of branches they get, and how many `prune` keeps. */
71
+ worktrees?: WorktreesOptions;
63
72
  }
64
73
  /** What starting on a data root found and repaired. */
65
74
  export interface Recovery {
@@ -82,6 +91,8 @@ declare class Env implements HarnessEnv {
82
91
  get root(): string;
83
92
  }
84
93
  export declare class Harness {
94
+ /** The periodic liveness check, started once with recovery (PROP-0041). */
95
+ private staleRuns?;
85
96
  private readonly browserEnabled;
86
97
  readonly hub: Hub;
87
98
  readonly bus: Bus;
@@ -100,6 +111,8 @@ export declare class Harness {
100
111
  applicationPlugins: ReadonlySet<string>;
101
112
  readonly subagents: Subagents;
102
113
  readonly sessions: Sessions;
114
+ /** Tool-free, run-backed questions over a redacted projection of the main conversation. */
115
+ readonly sideQuestions: SideQuestions;
103
116
  /** Web, browser and computer tools (page 13). */
104
117
  readonly world: World;
105
118
  /** Which plugin code in variants a person trusted. */
@@ -111,6 +124,15 @@ export declare class Harness {
111
124
  refineHost: RefineHost | null;
112
125
  readonly terminals: Terminals;
113
126
  readonly doctor: Doctor;
127
+ /** Usage analytics over the derived ledger, and the provider limits last reported. */
128
+ readonly usage: Usage;
129
+ readonly usageLimits: UsageLimits;
130
+ /** Managed git worktrees sessions can work in (PROP-0016). */
131
+ readonly worktrees: Worktrees;
132
+ /** The terminal's visible browser for capturing page notes (PROP-0017). */
133
+ readonly noteCapture: AnnotationCapture;
134
+ /** Page notes on their way to a session (PROP-0018). */
135
+ readonly pageNotes: NoteDeliveries;
114
136
  /** Resolves when what was loaded in the background at construction is in place. */
115
137
  readonly ready: Promise<void>;
116
138
  recovery: Recovery;
@@ -25,6 +25,7 @@ import { migrateVariantLayout } from "./variant/migrate.js";
25
25
  import { VariantTrust } from "./variant/trust.js";
26
26
  import * as notes from "./wiki/notes.js";
27
27
  import * as pluginApi from "./plugins/api.js";
28
+ import * as pluginInstall from "./plugins/install.js";
28
29
  import * as attachments from "./plugins/attachments.js";
29
30
  import { HARNESS_PLUGINS } from "./plugins/builtin.js";
30
31
  import { World } from "./world/world.js";
@@ -35,9 +36,16 @@ import { Hooks } from "./plugins/hooks.js";
35
36
  import { packageFolders, Plugins } from "./plugins/plugins.js";
36
37
  import { approvalKey, Approvals } from "./sessions/approvals.js";
37
38
  import { RunRegistry } from "./sessions/registry.js";
38
- import { markInterruptedRuns } from "./sessions/runStore.js";
39
+ import { loadRun, markInterruptedRuns, reconcileStaleRuns } from "./sessions/runStore.js";
39
40
  import { Sessions } from "./sessions/sessions.js";
41
+ import { SideQuestions } from "./sessions/sideQuestions.js";
40
42
  import { Terminals } from "./terminal/terminals.js";
43
+ import { Worktrees } from "./worktrees/service.js";
44
+ import { Usage } from "./usage/service.js";
45
+ import { UsageLimits } from "./usage/limits/service.js";
46
+ import { indexRunUsage, syncUsage } from "./usage/ledger.js";
47
+ import { AnnotationCapture } from "./annotations/capture.js";
48
+ import { NoteDeliveries } from "./annotations/delivery.js";
41
49
  class Env {
42
50
  directory;
43
51
  productName;
@@ -56,6 +64,8 @@ class Env {
56
64
  }
57
65
  }
58
66
  export class Harness {
67
+ /** The periodic liveness check, started once with recovery (PROP-0041). */
68
+ staleRuns;
59
69
  browserEnabled;
60
70
  hub;
61
71
  bus = new Bus();
@@ -74,6 +84,8 @@ export class Harness {
74
84
  applicationPlugins = new Set();
75
85
  subagents;
76
86
  sessions;
87
+ /** Tool-free, run-backed questions over a redacted projection of the main conversation. */
88
+ sideQuestions;
77
89
  /** Web, browser and computer tools (page 13). */
78
90
  world;
79
91
  /** Which plugin code in variants a person trusted. */
@@ -85,6 +97,15 @@ export class Harness {
85
97
  refineHost;
86
98
  terminals;
87
99
  doctor;
100
+ /** Usage analytics over the derived ledger, and the provider limits last reported. */
101
+ usage;
102
+ usageLimits;
103
+ /** Managed git worktrees sessions can work in (PROP-0016). */
104
+ worktrees;
105
+ /** The terminal's visible browser for capturing page notes (PROP-0017). */
106
+ noteCapture;
107
+ /** Page notes on their way to a session (PROP-0018). */
108
+ pageNotes;
88
109
  /** Resolves when what was loaded in the background at construction is in place. */
89
110
  ready;
90
111
  recovery = { interruptedRuns: 0, interruptedRunIds: [], reindexedPages: 0, migrationError: null };
@@ -103,6 +124,10 @@ export class Harness {
103
124
  const secrets = options.secrets ?? fileSecretStore(join(this.stateDir, 'secrets.json'));
104
125
  this.accounts = new Accounts({ dir: join(this.stateDir, 'accounts'), secrets, bus: this.bus });
105
126
  this.approvals = new Approvals(this.stateDir, this.bus);
127
+ this.usageLimits = new UsageLimits({ accounts: this.accounts, root: () => this.env.dataRoot });
128
+ // Response headers are the free, always-fresh limit signal; the account runtime reports them here.
129
+ this.accounts.observeModels = (models, accountId) => this.usageLimits.observeModels(models, accountId);
130
+ this.usage = new Usage({ root: () => this.env.root, accounts: this.accounts });
106
131
  this.runs = new RunRegistry({
107
132
  root: () => this.env.root,
108
133
  automatic: async (run, target, operation, standing) => {
@@ -146,7 +171,9 @@ export class Harness {
146
171
  reloadTimer.unref?.();
147
172
  });
148
173
  this.describe(options.plugins ?? []);
149
- this.subagents = new Subagents(this.env, this.runs, (input) => this.sessions.run(input));
174
+ this.worktrees = new Worktrees(this.stateDir, options.worktrees);
175
+ this.noteCapture = new AnnotationCapture({ stateDir: this.stateDir });
176
+ this.subagents = new Subagents(this.env, this.runs, (input) => this.sessions.run(input), { descriptors: () => HARNESS_PLUGINS });
150
177
  this.sessions = new Sessions({
151
178
  subagents: this.subagents,
152
179
  env: this.env,
@@ -156,10 +183,13 @@ export class Harness {
156
183
  broker: this.apps,
157
184
  plugins: this.plugins,
158
185
  resolveCapabilities: (caller, tools, cwd) => capabilities.resolve(this, { agentId: caller.agentId ?? null, projectId: caller.projectId ?? null, cwd, tools }),
186
+ fullPermission: (run) => this.approvals.isAuto(approvalKey(run)),
159
187
  factory: options.sessionFactory,
160
- trust: this.trust
188
+ trust: this.trust,
189
+ worktrees: this.worktrees
161
190
  });
162
- this.world = new World({ root: () => this.env.root, stateDir: this.stateDir, secrets, browser: this.browserEnabled ? undefined : false });
191
+ this.sideQuestions = new SideQuestions({ env: this.env, sessions: this.sessions, runs: this.runs });
192
+ this.world = new World({ root: () => this.env.root, stateDir: this.stateDir, secrets, browser: this.browserEnabled ? undefined : false, notesChanged: (sessionId, ids) => this.bus.emit({ type: 'annotations.changed', sessionId, ids }) });
163
193
  // The harness's own world tools come first; a tool an application provides under the same name is not added again.
164
194
  this.sessions.provide(this.world.provider());
165
195
  this.doctor = new Doctor({
@@ -169,7 +199,8 @@ export class Harness {
169
199
  const refineRuntime = { root: () => this.env.root, runs: this.runs, run: (input) => this.sessions.run(input) };
170
200
  this.refine = new Refinements({ root: () => this.env.root, bus: this.bus, trust: this.trust, host: () => this.refineHost, runtime: refineRuntime });
171
201
  this.lessons = new Lessons({ root: () => this.env.root, runtime: refineRuntime });
172
- this.terminals = new Terminals({ trust: this.trust, subagents: this.subagents, resolveCapabilities: (caller, tools, cwd) => capabilities.resolve(this, { agentId: caller.agentId ?? null, projectId: caller.projectId ?? null, cwd, tools }), env: this.env, stateDir: this.stateDir, accounts: this.accounts, runs: this.runs, approvals: this.approvals, apps: this.apps, policy: this.policy, bus: this.bus, plugins: this.plugins, pluginControl: { list: (scope) => pluginApi.list(this, scope), attach: (params, on) => pluginApi.attach(this, params, on), trust: (scope) => pluginApi.trust(this, scope), untrust: (scope) => pluginApi.untrust(this, scope), configure: (params) => pluginApi.configure(this, params) }, assets: options.assets, hub: this.hub, world: this.world, doctor: this.doctor });
202
+ this.pageNotes = new NoteDeliveries({ root: () => this.env.root, bus: this.bus, terminals: () => this.terminals ?? null });
203
+ this.terminals = new Terminals({ trust: this.trust, subagents: this.subagents, sideQuestions: this.sideQuestions, resolveCapabilities: (caller, tools, cwd) => capabilities.resolve(this, { agentId: caller.agentId ?? null, projectId: caller.projectId ?? null, cwd, tools }), env: this.env, stateDir: this.stateDir, accounts: this.accounts, runs: this.runs, approvals: this.approvals, apps: this.apps, policy: this.policy, bus: this.bus, plugins: this.plugins, usage: this.usage, usageLimits: this.usageLimits, pluginControl: { list: (scope) => pluginApi.list(this, scope), attach: (params, on) => pluginApi.attach(this, params, on), trust: (scope) => pluginApi.trust(this, scope), untrust: (scope) => pluginApi.untrust(this, scope), configure: (params) => pluginApi.configure(this, params), install: params => pluginInstall.install(this, params), remove: params => pluginInstall.remove(this, params) }, assets: options.assets, hub: this.hub, world: this.world, doctor: this.doctor, worktrees: this.worktrees, noteCapture: this.noteCapture, pageNotes: this.pageNotes });
173
204
  this.ready = this.apps.start().catch((error) => log('connected apps', error instanceof Error ? error.message : String(error)));
174
205
  this.runs.listen((event, run) => {
175
206
  this.accounts.onRunEvent(event, run);
@@ -178,6 +209,16 @@ export class Harness {
178
209
  recall.indexRun(this.env.root, event.runId);
179
210
  if (run?.trigger === 'doctor')
180
211
  return;
212
+ const runEnded = event.type === 'completed' || event.type === 'failed' || (event.type === 'status.changed' && event.payload.status === 'interrupted');
213
+ // The usage ledger is derived from the same recorded events; a failure never blocks the run.
214
+ // An interrupted run is indexed too, so its spent tokens are not lost until the next start.
215
+ if (runEnded)
216
+ indexRunUsage(this.env.root, event.runId);
217
+ if (runEnded && run?.sessionId) {
218
+ // Notes sent with "Proceed now" while this session was working go out now; the terminal
219
+ // needs a moment to see its run as ended.
220
+ setTimeout(() => void this.pageNotes.runEnded(run.sessionId).catch((error) => log('page notes', error instanceof Error ? error.message : String(error))), 250).unref?.();
221
+ }
181
222
  if ((event.type === 'completed' || event.type === 'failed') && run) {
182
223
  if (event.type === 'completed')
183
224
  this.lessons.consider(run);
@@ -246,6 +287,19 @@ export class Harness {
246
287
  let interruptedRunIds = [];
247
288
  try {
248
289
  interruptedRunIds = markInterruptedRuns(root, `${this.env.productName} stopped before the run settled.`);
290
+ const recoveredSessions = new Set();
291
+ for (const runId of interruptedRunIds) {
292
+ try {
293
+ const sessionId = loadRun(root, runId).sessionId;
294
+ if (!sessionId || recoveredSessions.has(sessionId))
295
+ continue;
296
+ recoveredSessions.add(sessionId);
297
+ setTimeout(() => void this.pageNotes.runEnded(sessionId).catch((error) => log('page notes', error instanceof Error ? error.message : String(error))), 250).unref?.();
298
+ }
299
+ catch {
300
+ // A malformed run record does not prevent the rest of recovery.
301
+ }
302
+ }
249
303
  }
250
304
  catch (error) {
251
305
  log('run recovery', error instanceof Error ? error.message : String(error));
@@ -257,23 +311,48 @@ export class Harness {
257
311
  catch (error) {
258
312
  log('recall', error instanceof Error ? error.message : String(error));
259
313
  }
314
+ try {
315
+ // Usage rows come from the same run events; the ledger can always be rebuilt from them.
316
+ syncUsage(root);
317
+ }
318
+ catch (error) {
319
+ log('usage ledger', error instanceof Error ? error.message : String(error));
320
+ }
260
321
  this.recovery = { interruptedRuns: interruptedRunIds.length, interruptedRunIds, reindexedPages: safeCount(root), migrationError, legacyWiki };
322
+ if (!this.staleRuns) {
323
+ // A reply that stops emitting heartbeats while its process lives is settled here, not left
324
+ // `running` for the next restart (PROP-0041).
325
+ this.staleRuns = setInterval(() => {
326
+ try {
327
+ reconcileStaleRuns(root);
328
+ }
329
+ catch (error) {
330
+ log('stale run check', error instanceof Error ? error.message : String(error));
331
+ }
332
+ }, 60_000);
333
+ this.staleRuns.unref?.();
334
+ }
261
335
  return this.recovery;
262
336
  }
263
337
  /** Rebuilds the history index from the run records. Returns the notes in the Wiki. */
264
338
  rebuildIndex() {
265
339
  recall.sync(this.env.root);
340
+ syncUsage(this.env.root);
266
341
  const count = safeCount(this.env.root);
267
342
  this.recovery = { ...this.recovery, reindexedPages: count };
268
343
  return count;
269
344
  }
270
345
  /** Stops live runs and refinements and releases the index. */
271
346
  async close() {
347
+ if (this.staleRuns)
348
+ clearInterval(this.staleRuns);
272
349
  await this.doctor.close();
273
350
  this.hub.close();
274
351
  await this.refine.close();
275
352
  await this.lessons.close();
276
353
  await this.terminals.close();
354
+ await this.noteCapture.close();
355
+ await this.sideQuestions.close();
277
356
  await this.runs.interruptAll();
278
357
  await this.world.close();
279
358
  await this.hooks.settled().catch(() => undefined);
@@ -32,11 +32,11 @@ export function passages(run, events) {
32
32
  return found;
33
33
  }
34
34
  function writeRun(database, run, found) {
35
- execute(database, 'DELETE FROM recall_fts WHERE run_id = ?1', run.id);
35
+ execute(database, 'DELETE FROM recall_fts WHERE run_id = ?', run.id);
36
36
  found.forEach((passage, seq) => {
37
- execute(database, 'INSERT INTO recall_fts (run_id, seq, role, at, mode, session_id, project_id, text) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)', run.id, seq, passage.role, passage.at, run.mode, run.sessionId, run.projectId, passage.text);
37
+ execute(database, 'INSERT INTO recall_fts (run_id, seq, role, at, mode, session_id, project_id, text) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', run.id, seq, passage.role, passage.at, run.mode, run.sessionId, run.projectId, passage.text);
38
38
  });
39
- execute(database, 'INSERT OR REPLACE INTO recall_runs (run_id, passages, status) VALUES (?1, ?2, ?3)', run.id, found.length, run.status);
39
+ execute(database, 'INSERT OR REPLACE INTO recall_runs (run_id, passages, status) VALUES (?, ?, ?)', run.id, found.length, run.status);
40
40
  }
41
41
  function eventsOf(root, runId) {
42
42
  try {
@@ -93,13 +93,13 @@ export function search(root, query, scope, limit) {
93
93
  if (!fts)
94
94
  return [];
95
95
  return withIndex(root, (database) => all(database, `SELECT run_id, seq, role, at, mode, session_id, project_id, snippet(recall_fts, 7, '', '', '…', 40) AS snippet
96
- FROM recall_fts WHERE recall_fts MATCH ?1
97
- AND (?2 IS NULL OR session_id = ?2) AND (?3 IS NULL OR mode = ?3)
98
- AND (?4 IS NULL OR project_id = ?4) AND (?5 IS NULL OR at >= ?5)
99
- ORDER BY bm25(recall_fts), at DESC LIMIT ?6`, fts, scope.sessionId ?? null, scope.mode ?? null, scope.projectId ?? null, scope.since ?? null, Math.min(Math.max(limit, 1), SEARCH_MAX)).map((row) => ({ runId: row.run_id, seq: Number(row.seq), role: row.role, at: row.at ?? '', mode: row.mode ?? '', sessionId: row.session_id ?? '', projectId: row.project_id, snippet: row.snippet })));
96
+ FROM recall_fts WHERE recall_fts MATCH ?
97
+ AND (? IS NULL OR session_id = ?) AND (? IS NULL OR mode = ?)
98
+ AND (? IS NULL OR project_id = ?) AND (? IS NULL OR at >= ?)
99
+ ORDER BY bm25(recall_fts), at DESC LIMIT ?`, fts, scope.sessionId ?? null, scope.sessionId ?? null, scope.mode ?? null, scope.mode ?? null, scope.projectId ?? null, scope.projectId ?? null, scope.since ?? null, scope.since ?? null, Math.min(Math.max(limit, 1), SEARCH_MAX)).map((row) => ({ runId: row.run_id, seq: Number(row.seq), role: row.role, at: row.at ?? '', mode: row.mode ?? '', sessionId: row.session_id ?? '', projectId: row.project_id, snippet: row.snippet })));
100
100
  }
101
101
  export function read(root, runId) {
102
- return withIndex(root, (database) => all(database, 'SELECT seq, role, at, text FROM recall_fts WHERE run_id = ?1 ORDER BY CAST(seq AS INTEGER)', runId).map((row) => ({ seq: Number(row.seq), role: row.role, at: row.at ?? '', text: row.text })));
102
+ return withIndex(root, (database) => all(database, 'SELECT seq, role, at, text FROM recall_fts WHERE run_id = ? ORDER BY CAST(seq AS INTEGER)', runId).map((row) => ({ seq: Number(row.seq), role: row.role, at: row.at ?? '', text: row.text })));
103
103
  }
104
104
  function textParam(params, key) {
105
105
  const value = params?.[key];
@@ -0,0 +1,3 @@
1
+ import type { Harness } from "../harness.js";
2
+ import type { Handler } from "../server/rpc.js";
3
+ export declare function historyServerMethods(harness: Harness): Record<string, Handler>;
@@ -0,0 +1,8 @@
1
+ import * as recall from "./recall.js";
2
+ import { text } from "../server/params.js";
3
+ export function historyServerMethods(harness) {
4
+ return {
5
+ 'recall.search': (params) => recall.searchAll(harness.env, typeof params.query === 'string' ? params.query : '', params.filter ?? {}),
6
+ 'recall.read': (params) => recall.read(harness.env.root, text(params, 'runId'))
7
+ };
8
+ }
@@ -1,5 +1,5 @@
1
1
  import { Harness, type HarnessOptions } from "../harness.js";
2
- export declare const HUB_USAGE = "Usage: xharness hub <list | show | install> [id@version] [options]\n\n list [--refresh] Browse the curated catalog and locally installed versions\n show <id@version> Show publisher, license, source, and delivery details\n install <id@version> Install the exact reviewed version, disabled and untrusted\n\n --data <dir> Target data library (default: ~/.xingular-harness/data)\n --state <dir> Target device state (default: ~/.xingular-harness/state)\n --refresh Refresh the public catalog; cached items remain usable offline\n";
2
+ export declare const HUB_USAGE = "Usage: xagent hub <list | show | install> [id@version] [options]\n\n list [--refresh] Browse the curated catalog and locally installed versions\n show <id@version> Show publisher, license, source, and delivery details\n install <id@version> Install the exact reviewed version, disabled and untrusted\n\n --data <dir> Target data library (default: ~/.xingular-harness/data)\n --state <dir> Target device state (default: ~/.xingular-harness/state)\n --refresh Refresh the public catalog; cached items remain usable offline\n";
3
3
  export declare function runHubCli(argv: string[], dependencies?: {
4
4
  home?: string;
5
5
  write?: (text: string) => void;
@@ -1,7 +1,7 @@
1
1
  import { homedir } from 'node:os';
2
2
  import { join, resolve } from 'node:path';
3
3
  import { Harness } from "../harness.js";
4
- export const HUB_USAGE = `Usage: xharness hub <list | show | install> [id@version] [options]
4
+ export const HUB_USAGE = `Usage: xagent hub <list | show | install> [id@version] [options]
5
5
 
6
6
  list [--refresh] Browse the curated catalog and locally installed versions
7
7
  show <id@version> Show publisher, license, source, and delivery details
@@ -263,9 +263,9 @@ export declare const hubOverviewSchema: z.ZodObject<{
263
263
  }>>;
264
264
  }, z.core.$strict>>;
265
265
  origin: z.ZodEnum<{
266
- remote: "remote";
267
266
  bundled: "bundled";
268
267
  cached: "cached";
268
+ remote: "remote";
269
269
  }>;
270
270
  refreshedAt: z.ZodNullable<z.ZodString>;
271
271
  error: z.ZodNullable<z.ZodString>;
@@ -0,0 +1,3 @@
1
+ import type { Harness } from "../harness.js";
2
+ import type { Handler } from "../server/rpc.js";
3
+ export declare function hubServerMethods(harness: Harness): Record<string, Handler>;
@@ -0,0 +1,11 @@
1
+ import { text } from "../server/params.js";
2
+ export function hubServerMethods(harness) {
3
+ return {
4
+ 'hub.list': (params) => harness.hub.list(params.refresh === true),
5
+ 'hub.get': (params) => harness.hub.get(text(params, 'id'), text(params, 'version'), params.preview === true),
6
+ 'hub.install': (params) => harness.hub.install(text(params, 'id'), text(params, 'version')),
7
+ 'hub.preview': (params) => harness.hub.preview(text(params, 'id'), text(params, 'version'), typeof params.offset === 'number' ? params.offset : 0),
8
+ 'hub.uninstall': (params) => harness.hub.uninstall(text(params, 'id'), text(params, 'version')),
9
+ 'hub.readDocument': (params) => harness.hub.readDocument(text(params, 'id'), text(params, 'version'))
10
+ };
11
+ }
@@ -21,11 +21,18 @@ export type { ApprovalFacts, ApprovalPolicy, AutomaticReason } from "./sessions/
21
21
  export type { ScopeDecision, ToolCall, ToolPolicy } from "./sessions/policy.js";
22
22
  export type { ActiveRun, ApprovalDecision, ApprovalInput, QuestionAnswer, QuestionInput } from "./sessions/registry.js";
23
23
  export type { RunFields, SessionRunInput } from "./sessions/sessions.js";
24
+ export type { SessionCommand } from "./sessions/runner.js";
25
+ export type { SessionRef, SessionTree, SessionTreeEntry } from "./sessions/control.js";
26
+ export { CORE_COMMANDS, coreCommand, type CoreCommand, type CoreCommandGroup, type CoreCommandKind, type CoreCommandName, type ListedCommand } from "./commands/registry.js";
27
+ export { STANDARD_CONNECTIONS, type StandardSession } from "./prompt/standard.js";
28
+ export { SideQuestions, SIDE_QUESTION_INSTRUCTION, buildSideQuestionText, type SideQuestionContext, type SideQuestionRead, type SideQuestionStartInput } from "./sessions/sideQuestions.js";
24
29
  export type { Effort } from "./sessions/runner.js";
25
30
  export { describeTool, HARNESS_TOOLS, DEFAULT_TOOL_NAMES, WIKI_WRITE, type HarnessCall, type HarnessToolDefinition } from "./tools/definitions.js";
26
31
  export type { ToolProvider, ToolResult, ToolSession, ToolSpec } from "./tools/types.js";
27
32
  export { SPAWN_SUBAGENT, SUBAGENT_RELATION } from "./tools/spawnSubagent.js";
28
33
  export { CHECKLIST_WRITE, checklistOf } from "./tools/checklist.js";
34
+ export { BUILTIN_PROFILES } from "./agents/builtin.js";
35
+ export { PROFILE_COLORS, createProfile, listProfiles, parseProfile, profileDirs, removeProfile, resolveProfile, type AgentProfile, type ProfileColor, type ProfileListing, type ProfileProblem, type ProfileScope, type ProfileSource, type ProfileVariant } from "./agents/profiles.js";
29
36
  export { continuationOf, type Continuation, type ContinuationBlock } from "./sessions/continuation.js";
30
37
  export { webTools, WEB_PLUGIN, WEB_SEARCH_KEY, type WebToolOptions } from "./web/tools.js";
31
38
  export { safeRequest, type WebRequest, type WebResponse } from "./web/safeRequest.js";
@@ -53,11 +60,19 @@ export { HARNESS_PLUGINS } from "./plugins/builtin.js";
53
60
  export { FIRST_PARTY_PLUGINS } from "./plugins/firstParty.js";
54
61
  export type { Found, Resolver } from "./plugins/discover.js";
55
62
  export type { Loading, Registered } from "./plugins/plugins.js";
56
- export { CONTRIBUTION_KINDS, PLUGIN_SOURCES, type ConnectorTemplate, type Contribution, type ContributionKind, type ContributionState, type Delivery, type Inventory, type InventoryItem, type Plugin, type PluginDescriptor, type PluginManage, type PluginSetting, type PluginSource } from "./plugins/types.js";
63
+ export { CONTRIBUTION_KINDS, PLUGIN_SOURCES, type ConnectorTemplate, type Contribution, type ContributionKind, type ContributionState, type Delivery, type Inventory, type InventoryItem, type Plugin, type PluginDescriptor, type PluginManage, type PluginSetting, type PluginSource, type PluginSurfaces } from "./plugins/types.js";
64
+ export { describePlugin, surfaceBadge } from "./plugins/surfaces.js";
57
65
  export { methods } from "./server/methods.js";
58
66
  export { remoteDirectory, remotePolicy, remoteSecrets, remoteTools } from "./server/remote.js";
59
67
  export { PROTOCOL_VERSION, RpcPeer, stdioTransport, type Handler, type Transport } from "./server/rpc.js";
60
68
  export { attach, serve, type Served } from "./server/serve.js";
61
69
  export { DEFAULT_SUBAGENT_DEPTH, MAX_SUBAGENT_DEPTH, type SubagentOptions, type SubagentInput } from "./sessions/subagents.js";
62
70
  export { Subagents } from "./sessions/subagentService.js";
71
+ export { Worktrees } from "./worktrees/service.js";
72
+ export type { CreateWorktreeInput, CreateWorktreeResult, HandoffResult, WorktreeRecord, WorktreeStatus, WorktreesOptions, WorktreeKind } from "./worktrees/types.js";
63
73
  export { WAIT_SUBAGENTS, type SubagentResult, type SubagentSelection } from "./sessions/subagents.js";
74
+ export * as pageNotes from "./annotations/store.js";
75
+ export type { Note as PageNote, NoteApp as PageNoteApp, NoteDelivery as PageNoteDelivery, NoteInput as PageNoteInput, NoteSource as PageNoteSource, NoteTarget as PageNoteTarget } from "./annotations/store.js";
76
+ export { PAGE_SCRIPT } from "./annotations/pageScript.js";
77
+ export { NoteDeliveries, type SendResult } from "./annotations/delivery.js";
78
+ export type { CaptureTargetSpec, CaptureWindow, InspectState } from "./annotations/capture.js";