@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
@@ -1,32 +1,61 @@
1
1
  // Starting a session turn: the account, the run record, the compiled prompt, the tools and
2
2
  // their gate, then Pi. Everything before Pi answers is synchronous with the caller; the reply
3
3
  // itself streams as run events.
4
+ import { randomUUID } from 'node:crypto';
4
5
  import { existsSync, mkdirSync } from 'node:fs';
5
- import { join } from 'node:path';
6
+ import { isAbsolute, join } from 'node:path';
6
7
  import { log } from "../core/bus.js";
7
8
  import { HarnessError, invalid } from "../core/errors.js";
9
+ import { formatBashCommandForApproval } from "../core/workspaceBashTool.js";
8
10
  import { capabilitySet as parseCapabilitySet, freezeRun } from "../capabilities/runSnapshot.js";
9
11
  import { now, writeJson } from "../core/fs.js";
12
+ import { resolveWorkspaceRoot, verifyWorkspaceRoot } from "../core/workspace.js";
10
13
  import { takeChars } from "../core/text.js";
14
+ import { BUILTIN_PLUGIN_REGISTRY } from "../plugins/builtin.js";
11
15
  import { sessionLaunch } from "../prompt/compile.js";
12
16
  import * as attachments from "../plugins/attachments.js";
13
17
  import { FIRST_PARTY_PLUGINS, firstPartyExtensions } from "../plugins/firstParty.js";
14
18
  import { imagePruning } from "../world/images.js";
15
19
  import { materialise } from "../variant/materialise.js";
16
- import { ASK_USER, askUserTool } from "../tools/askUser.js";
17
- import { CHECKLIST_WRITE, checklistTool } from "../tools/checklist.js";
20
+ import { ASK_USER } from "../tools/askUser.js";
21
+ import { CHECKLIST_WRITE } from "../tools/checklist.js";
22
+ import { CHECKPOINT_WRITE, checkpointOf } from "../tools/checkpoint.js";
18
23
  import { SESSION_NAME, sessionNameTool } from "../tools/sessionName.js";
19
- import { subagentTools } from "../tools/spawnSubagent.js";
20
24
  import { DEFAULT_TOOL_NAMES } from "../tools/definitions.js";
21
- import { harnessTools } from "../tools/harnessTools.js";
25
+ import { GET_CAPABILITIES, getCapabilitiesTool } from "../tools/getCapabilities.js";
26
+ import { isActiveStatus } from "./contracts.js";
22
27
  import { SPAWN_SUBAGENT, WAIT_SUBAGENTS, subagentDepth, subagentHistory, subagentNesting } from "./subagents.js";
23
28
  import { selectHistory } from "./history.js";
24
29
  import { runLineageJoin } from "./lineageJoin.js";
30
+ import { compactionCheckpoint, COMPACTION_CHECKPOINT_EXTENSION } from "./compactionCheckpoint.js";
31
+ import { createSliceState, parseWorkSlice, sliceOf, workSliceGuard, WorkSliceError, WORK_SLICE_EXTENSION } from "./workSlice.js";
25
32
  import { piSessionFactory, runTurn, summarize } from "./runner.js";
26
- import { loadRun, runDir, saveRun, updateRun } from "./runStore.js";
27
- import { planRefusal, planSection } from "./planMode.js";
33
+ import { loadRun, readEvents, runDir, saveRun, updateRun } from "./runStore.js";
34
+ import { combineWorkspaceScope, inspectRunWorkspaceWrite } from "./workspaceBoundary.js";
35
+ import { PLAN_INSTRUCTIONS, PLAN_RESEARCH_SUBAGENT_INSTRUCTIONS, planRefusal, planSection } from "./planMode.js";
28
36
  import { CONTINUATION_SECTION_ID, continuationOf, continuationSection } from "./continuation.js";
29
37
  import { deliveredSnapshot } from "../prompt/compile.js";
38
+ import { profileSection } from "../agents/profiles.js";
39
+ /** A session command acts on a saved conversation and is never combined with a request of its own. */
40
+ function checkCommand(command, historyFile, input) {
41
+ if (!historyFile)
42
+ throw invalid('There is no saved conversation for this command yet.');
43
+ if (input.plan || input.continueFrom || input.automatic || input.initialBashCommand != null || input.images?.length) {
44
+ throw invalid('A session command runs on its own, without plan mode, a continuation, Bash or images.');
45
+ }
46
+ switch (command?.name) {
47
+ case 'compact': return;
48
+ case 'navigate':
49
+ if (typeof command.entryId !== 'string' || !command.entryId)
50
+ throw invalid('navigate needs the entry to go to.');
51
+ return;
52
+ case 'export':
53
+ if (typeof command.path !== 'string' || !isAbsolute(command.path) || !command.path.endsWith('.html'))
54
+ throw invalid('export needs an absolute .html path.');
55
+ return;
56
+ default: throw invalid('Unknown session command.');
57
+ }
58
+ }
30
59
  const CONTINUE_TEXT = 'Continue from where you stopped.';
31
60
  /** Fills a continuing run's input from the run it continues, refusing anything that would differ. */
32
61
  function continueInput(root, input) {
@@ -34,6 +63,10 @@ function continueInput(root, input) {
34
63
  if (!continuation.resumable || !continuation.resume)
35
64
  throw new HarnessError('continuation_unavailable', `Run ${input.continueFrom} cannot be continued (${continuation.reason ?? 'history_missing'}).`);
36
65
  const mismatch = (what) => new HarnessError('continuation_mismatch', `A continuation keeps the earlier run's ${what}.`);
66
+ if (continuation.capabilities?.tools.includes(ASK_USER)
67
+ && (input.automatic || ['probe', 'improver', 'reflection'].includes(input.run.trigger ?? ''))) {
68
+ throw mismatch('attended question capability');
69
+ }
37
70
  if (continuation.sessionId !== input.run.sessionId)
38
71
  throw mismatch('session');
39
72
  if (input.resume?.nativeSessionId && input.resume.nativeSessionId !== continuation.resume.nativeSessionId)
@@ -91,7 +124,7 @@ export class Sessions {
91
124
  diagnosticGate(run, caller) {
92
125
  return this.gate(run, caller, new Set(), true);
93
126
  }
94
- gate(run, caller, gated, diagnostic = false) {
127
+ gate(run, caller, gated, diagnostic = false, automatic = false) {
95
128
  const { policy } = this.options;
96
129
  return {
97
130
  ask: (names) => {
@@ -99,9 +132,19 @@ export class Sessions {
99
132
  gated.add(name);
100
133
  },
101
134
  check: async (tool, args) => {
102
- const call = { run: loadRun(this.env.root, run.runId), caller, tool, args };
103
- const scope = (await policy.scope?.(call)) ?? { allowed: true };
104
- if (!scope.allowed && (scope.automatic || !scope.requiresApproval))
135
+ let record;
136
+ let inspection;
137
+ try {
138
+ record = loadRun(this.env.root, run.runId);
139
+ inspection = inspectRunWorkspaceWrite(record, tool, args);
140
+ }
141
+ catch (error) {
142
+ return error instanceof Error ? error.message : 'The active project workspace could not be verified.';
143
+ }
144
+ const call = { run: record, caller, tool, args: inspection.input };
145
+ const hostScope = (await policy.scope?.(call)) ?? { allowed: true };
146
+ const scope = combineWorkspaceScope(hostScope, inspection);
147
+ if (!scope.allowed && (scope.automatic || (automatic && inspection.outside) || !scope.requiresApproval))
105
148
  return scope.reason ?? 'Outside the approved work scope.';
106
149
  try {
107
150
  await policy.authorize?.(call);
@@ -118,11 +161,11 @@ export class Sessions {
118
161
  return scope.reason ? `Refused in a refinement run: ${scope.reason}` : 'Refused in a refinement run: this call needs a person to approve it.';
119
162
  if (call.run.trigger === 'reflection')
120
163
  return scope.reason ? `Refused in a reflection run: ${scope.reason}` : 'Refused in a reflection run: this call needs a person to approve it.';
121
- // A call outside the scope is approved one at a time; a gated tool may be remembered, unless only a person may decide it.
164
+ const boundaryAction = inspection.outside ? `${inspection.reason} ${summarize(call.args, tool)}` : summarize(call.args);
122
165
  const decision = await run.requestApproval({
123
166
  target: tool,
124
- action: summarize(args),
125
- operation: { tool, args: (args && typeof args === 'object' ? args : {}) },
167
+ action: boundaryAction,
168
+ operation: { tool, args: (call.args && typeof call.args === 'object' ? call.args : {}) },
126
169
  rememberable: scope.allowed && !scope.humanOnly,
127
170
  standing: scope.humanOnly ? (scope.allowGlobalApproval ? 'switch' : 'none') : undefined
128
171
  });
@@ -135,6 +178,69 @@ export class Sessions {
135
178
  return error instanceof Error ? error.message : 'Tool was disabled while waiting.';
136
179
  }
137
180
  return null;
181
+ },
182
+ workspaceBoundaryBypass: async (tool, args) => {
183
+ if (diagnostic || automatic || tool.toLowerCase() !== 'bash')
184
+ return false;
185
+ const record = loadRun(this.env.root, run.runId);
186
+ if (record.trigger != null && record.trigger !== 'user')
187
+ return false;
188
+ if (!await this.options.fullPermission?.(record))
189
+ return false;
190
+ const call = { run: record, caller, tool, args };
191
+ const scope = (await policy.scope?.(call)) ?? { allowed: true };
192
+ if (scope.automatic || (!scope.allowed && !scope.requiresApproval) || (scope.humanOnly && !scope.allowGlobalApproval))
193
+ return false;
194
+ try {
195
+ await policy.authorize?.(call);
196
+ return true;
197
+ }
198
+ catch {
199
+ return false;
200
+ }
201
+ },
202
+ requestWorkspaceWriteScope: async (tool, args, reason) => {
203
+ if (diagnostic || automatic || tool.toLowerCase() !== 'bash')
204
+ return false;
205
+ const record = loadRun(this.env.root, run.runId);
206
+ if (!isActiveStatus(record.status) || (record.trigger != null && record.trigger !== 'user'))
207
+ return false;
208
+ const operationArgs = (args && typeof args === 'object' && !Array.isArray(args) ? args : {});
209
+ if (operationArgs.workspaceWriteScope !== 'outside' || typeof operationArgs.command !== 'string' || !operationArgs.command)
210
+ return false;
211
+ const call = { run: record, caller, tool, args: operationArgs };
212
+ const scope = (await policy.scope?.(call)) ?? { allowed: true };
213
+ if (scope.automatic || (!scope.allowed && !scope.requiresApproval))
214
+ return false;
215
+ try {
216
+ await policy.authorize?.(call);
217
+ }
218
+ catch {
219
+ return false;
220
+ }
221
+ const decision = await run.requestApproval({
222
+ target: 'workspace-write',
223
+ action: `${reason}\n\nCommand to be run once after approval:\n${formatBashCommandForApproval(operationArgs.command)}`,
224
+ operation: { tool, args: operationArgs },
225
+ rememberable: false,
226
+ standing: 'none'
227
+ });
228
+ if (decision !== 'approved')
229
+ return false;
230
+ const currentRecord = loadRun(this.env.root, run.runId);
231
+ if (!isActiveStatus(currentRecord.status) || (currentRecord.trigger != null && currentRecord.trigger !== 'user'))
232
+ return false;
233
+ const currentCall = { run: currentRecord, caller, tool, args: operationArgs };
234
+ const currentScope = (await policy.scope?.(currentCall)) ?? { allowed: true };
235
+ if (currentScope.automatic || (!currentScope.allowed && !currentScope.requiresApproval))
236
+ return false;
237
+ try {
238
+ await policy.authorize?.(currentCall);
239
+ return true;
240
+ }
241
+ catch {
242
+ return false;
243
+ }
138
244
  }
139
245
  };
140
246
  }
@@ -147,10 +253,96 @@ export class Sessions {
147
253
  * session that cannot be opened ends its run as failed and returns no native session id.
148
254
  */
149
255
  async run(input) {
150
- const { root } = this.env;
151
256
  if (!input?.run?.id || !input.run.sessionId || typeof input.text !== 'string')
152
257
  throw invalid('A session turn needs a run id, a session, and text.');
153
258
  input = structuredClone(input);
259
+ const worktree = await this.enterWorktree(input);
260
+ try {
261
+ return await this.start(input);
262
+ }
263
+ catch (error) {
264
+ await worktree?.release();
265
+ throw error;
266
+ }
267
+ }
268
+ /**
269
+ * A run that names a worktree the harness manages works in that worktree's folder and holds it
270
+ * until the run settles. An id the harness does not know is kept as the caller's own field.
271
+ */
272
+ async enterWorktree(input) {
273
+ const id = input.run.worktreeId;
274
+ const worktrees = this.options.worktrees;
275
+ if (!id || !worktrees)
276
+ return null;
277
+ const path = worktrees.pathOf(id);
278
+ if (!path)
279
+ return null;
280
+ const holder = input.run.sessionId;
281
+ await worktrees.acquire(id, holder);
282
+ input.cwd = path;
283
+ let released = false;
284
+ const release = async () => {
285
+ if (released)
286
+ return;
287
+ released = true;
288
+ stop();
289
+ await worktrees.release(id, holder).catch(() => undefined);
290
+ };
291
+ const stop = this.options.runs.listen((event) => {
292
+ if (event.runId !== input.run.id)
293
+ return;
294
+ const settled = event.type === 'completed' || event.type === 'failed' || (event.type === 'status.changed' && event.payload.status === 'interrupted');
295
+ if (settled)
296
+ void release();
297
+ });
298
+ return { release };
299
+ }
300
+ /**
301
+ * Starts the next slice when a reply stopped at its work-slice boundary (PROP-0041). Never for
302
+ * unattended, plan, subagent, command or worktree runs, after a blocked or fruitless slice, or
303
+ * past the policy's slice cap. The earlier record is never reopened; this is a new run with the
304
+ * same pins, exactly as an application-asked continuation.
305
+ */
306
+ async continueSlice(input) {
307
+ const { root, runId, source, policy } = input;
308
+ if (!policy.enabled || !policy.continue || policy.maxSlices <= 0)
309
+ return;
310
+ if (source.automatic || source.plan || source.command || source.run.relation || source.run.worktreeId)
311
+ return;
312
+ if ((source.depth ?? 0) > 0)
313
+ return;
314
+ const record = loadRun(root, runId);
315
+ if (record.terminalReason !== 'slice_complete')
316
+ return;
317
+ const events = readEvents(root, runId);
318
+ const checkpoint = checkpointOf(events);
319
+ if (checkpoint?.blocked)
320
+ return;
321
+ const progressed = events.some((event) => event.type === 'file.changed')
322
+ || events.some((event) => {
323
+ const payload = event.payload;
324
+ return event.type === 'tool.completed' && payload.name === 'bash' && payload.ok === true;
325
+ })
326
+ || (checkpoint?.done.length ?? 0) > 0;
327
+ if (!progressed)
328
+ return;
329
+ if (sliceOf(root, runId) >= policy.maxSlices)
330
+ return;
331
+ const next = {
332
+ ...source,
333
+ run: { ...source.run, id: randomUUID(), resumedFromRunId: null, parentRunId: null, relation: null },
334
+ continueFrom: runId,
335
+ text: CONTINUE_TEXT
336
+ };
337
+ next.initialBashCommand = undefined;
338
+ next.initialBashContext = undefined;
339
+ next.images = undefined;
340
+ next.resume = null;
341
+ next.command = undefined;
342
+ await this.run(next).catch((error) => log(`the next work slice did not start: ${error instanceof Error ? error.message : String(error)}`));
343
+ }
344
+ async start(input) {
345
+ const { root } = this.env;
154
346
  if ((input.initialBashCommand !== undefined && input.initialBashCommand !== null) || (input.initialBashContext !== undefined && input.initialBashContext !== null)) {
155
347
  const request = input.text.trimStart();
156
348
  const requestedCommand = /^!\s*([\s\S]+)$/u.exec(request)?.[1];
@@ -176,8 +368,23 @@ export class Sessions {
176
368
  throw invalid('An initial Bash command is available only for a new interactive user turn.');
177
369
  }
178
370
  }
371
+ let slicePolicy;
372
+ try {
373
+ slicePolicy = parseWorkSlice(input.workSlice);
374
+ }
375
+ catch (error) {
376
+ if (error instanceof WorkSliceError)
377
+ throw invalid(error.message);
378
+ throw error;
379
+ }
179
380
  const continuation = input.continueFrom ? continueInput(root, input) : null;
381
+ const previousRun = continuation ? loadRun(root, continuation.runId) : null;
382
+ const workspaceRoot = previousRun?.workspaceRoot
383
+ ? verifyWorkspaceRoot(previousRun.workspaceRoot)
384
+ : resolveWorkspaceRoot(previousRun?.workingDirectory ?? input.cwd);
180
385
  const historyFile = selectHistory({ sessionDir: input.sessionDir, resume: input.resume });
386
+ if (input.command)
387
+ checkCommand(input.command, historyFile, input);
181
388
  const account = this.options.accounts.resolve(input.accountId, input.poolId);
182
389
  const context = this.options.accounts.context(account);
183
390
  const runId = input.run.id;
@@ -185,9 +392,15 @@ export class Sessions {
185
392
  const broker = input.connections ? this.options.broker : null;
186
393
  const depth = input.depth ?? 0;
187
394
  const canSpawn = input.subagents !== null && (!input.automatic || input.subagents !== undefined) && subagentNesting(root, input.run) < subagentDepth(input.subagents ?? undefined);
395
+ const pinnedAskUser = continuation?.capabilities?.tools.includes(ASK_USER) ?? false;
396
+ const interactivePlan = input.plan === true
397
+ && !input.automatic
398
+ && (input.run.trigger === undefined || input.run.trigger === null || input.run.trigger === 'user');
399
+ const harnessToolsEnabled = input.harnessTools !== false || pinnedAskUser;
400
+ const canAskUser = pinnedAskUser || (interactivePlan && harnessToolsEnabled);
188
401
  const requestedTools = [...new Set([
189
- ...input.tools.allow.filter((name) => name !== SPAWN_SUBAGENT && name !== WAIT_SUBAGENTS),
190
- ...(input.harnessTools === false ? [] : [ASK_USER, CHECKLIST_WRITE, SESSION_NAME, ...DEFAULT_TOOL_NAMES]),
402
+ ...input.tools.allow.filter((name) => name !== SPAWN_SUBAGENT && name !== WAIT_SUBAGENTS && (name !== ASK_USER || canAskUser)),
403
+ ...(harnessToolsEnabled ? [...(canAskUser ? [ASK_USER] : []), CHECKLIST_WRITE, CHECKPOINT_WRITE, SESSION_NAME, GET_CAPABILITIES, ...DEFAULT_TOOL_NAMES] : []),
191
404
  ...(broker?.toolNames ?? []),
192
405
  ...(canSpawn ? [SPAWN_SUBAGENT, WAIT_SUBAGENTS] : [])
193
406
  ])];
@@ -224,6 +437,7 @@ export class Sessions {
224
437
  sessionId: input.run.sessionId,
225
438
  nativeSessionId: null,
226
439
  workingDirectory: input.cwd,
440
+ workspaceRoot,
227
441
  worktreeId: input.run.worktreeId ?? null,
228
442
  goal: input.run.goal ?? takeChars(input.text, 4000),
229
443
  attachmentCount: input.run.attachmentCount ?? 0,
@@ -236,7 +450,9 @@ export class Sessions {
236
450
  agentId: input.run.agentId ?? caller.agentId ?? null,
237
451
  trigger: input.run.trigger ?? null,
238
452
  variantScope: variant?.scope ?? null,
239
- variantVersion: variant?.fingerprint ?? null
453
+ variantVersion: variant?.fingerprint ?? null,
454
+ ...(input.run.profile ? { profile: input.run.profile } : {}),
455
+ ...(input.run.title ? { title: input.run.title } : {})
240
456
  });
241
457
  // What the session is, kept with the run so later consolidation knows which memory it had.
242
458
  try {
@@ -256,10 +472,14 @@ export class Sessions {
256
472
  if (!existsSync(input.sessionDir))
257
473
  mkdirSync(input.sessionDir, { recursive: true });
258
474
  const launch = input.launch ?? {};
259
- const plan = planSection(input.plan === true, historyFile);
475
+ const plan = planSection(input.plan === true, historyFile, input.run.relation === 'subagent');
260
476
  const delivered = historyFile ? deliveredSnapshot(historyFile) : null;
261
477
  const resumed = continuationSection(continuation, delivered?.some((entry) => entry.id === CONTINUATION_SECTION_ID) ?? false);
262
- const own = [plan, resumed].filter((section) => section !== null);
478
+ // The profiles replace the variant's own subagent list, which they include.
479
+ const profiles = capabilities.tools.includes(SPAWN_SUBAGENT) ? profileSection(root, input.cwd, variant?.dir ?? null, input.plan === true) : null;
480
+ if (profiles && variant)
481
+ variant = { ...variant, sections: variant.sections.filter((section) => section.id !== profiles.id) };
482
+ const own = [plan, resumed, profiles].filter((section) => section !== null);
263
483
  const effectiveLaunch = {
264
484
  ...launch,
265
485
  sections: own.length ? [...(launch.sections ?? []).filter((section) => !own.some((entry) => entry.id === section.id)), ...own] : launch.sections,
@@ -273,6 +493,9 @@ export class Sessions {
273
493
  fail(error);
274
494
  throw error;
275
495
  }
496
+ // A reply is bounded only when its checkpoint tool is part of the frozen capability set.
497
+ const sliceEnabled = slicePolicy.enabled && capabilities.tools.includes(CHECKPOINT_WRITE);
498
+ const sliceState = createSliceState(slicePolicy, sliceOf(root, runId));
276
499
  const run = this.options.runs.start(input.run.sessionId, runId);
277
500
  if (run.finished)
278
501
  return { runId, nativeSessionId: null };
@@ -295,9 +518,23 @@ export class Sessions {
295
518
  });
296
519
  try {
297
520
  const tools = [
298
- ...(broker ? broker.tools(session, { readOnly: typeof input.connections === 'object' && input.connections.readOnly === true }) : []),
299
- ...(input.harnessTools === false ? [] : [...harnessTools(this.options.env, caller, { variantDir: variant?.dir ?? null }), askUserTool(run), checklistTool(run), naming]),
300
- ...subagentTools(this.options.subagents, runId).filter(tool => allow.includes(tool.name))
521
+ ...(harnessToolsEnabled && allow.includes(GET_CAPABILITIES) ? [getCapabilitiesTool(() => ({
522
+ capabilities,
523
+ active: pi?.getActiveToolNames?.() ?? [],
524
+ registered: pi?.getAllTools?.() ?? []
525
+ }))] : []),
526
+ ...BUILTIN_PLUGIN_REGISTRY.tools({
527
+ env: this.options.env,
528
+ caller,
529
+ variantDir: variant?.dir ?? null,
530
+ run,
531
+ runId,
532
+ allowedTools: allow,
533
+ harnessTools: harnessToolsEnabled,
534
+ subagents: this.options.subagents,
535
+ sessionName: naming,
536
+ ...(broker ? { connectionTools: () => broker.tools(session, { readOnly: typeof input.connections === 'object' && input.connections.readOnly === true }) } : {})
537
+ })
301
538
  ];
302
539
  if (input.providers !== false) {
303
540
  const named = new Set(tools.map((tool) => tool.name));
@@ -312,12 +549,15 @@ export class Sessions {
312
549
  }
313
550
  }
314
551
  const readOnly = new Set(tools.filter((tool) => tool.readOnly).map((tool) => tool.name));
552
+ const checkpointPlanInstructions = input.plan
553
+ ? () => input.run.relation === 'subagent' ? PLAN_RESEARCH_SUBAGENT_INSTRUCTIONS : PLAN_INSTRUCTIONS
554
+ : undefined;
315
555
  const planModeSafety = new Map();
316
556
  for (const tool of tools)
317
557
  if (tool.planModeSafe)
318
558
  planModeSafety.set(tool.name, tool.planModeSafe);
319
559
  const gate = (() => {
320
- const baseGate = this.gate(run, caller, new Set((input.tools.gated ?? []).filter((name) => capabilities.tools.includes(name))));
560
+ const baseGate = this.gate(run, caller, new Set((input.tools.gated ?? []).filter((name) => capabilities.tools.includes(name))), false, input.automatic === true);
321
561
  return {
322
562
  ...baseGate,
323
563
  check: (name, args) => {
@@ -334,7 +574,9 @@ export class Sessions {
334
574
  ...(input.initialBashContext !== undefined && input.initialBashContext !== null ? { initialBashContext: input.initialBashContext } : {}),
335
575
  gate,
336
576
  restrictTools: input.run.relation === 'subagent',
577
+ ...(input.plan ? { plan: true } : {}),
337
578
  cwd: input.cwd,
579
+ workspaceRoot,
338
580
  sessionDir: input.sessionDir,
339
581
  historyFile,
340
582
  model: input.model ?? null,
@@ -347,10 +589,20 @@ export class Sessions {
347
589
  account: context,
348
590
  resources: { ids: [], skillPaths: variant?.skillPaths ?? [], promptPaths: [] },
349
591
  variantExtensions: variant?.extensionPaths ?? [],
350
- inline: [...firstPartyFor(root, capabilities.contributions), runLineageJoin(root, runId), this.options.subagents.extension(runId), { name: 'image-pruning', factory: imagePruning() }],
592
+ inline: [
593
+ ...firstPartyFor(root, capabilities.contributions),
594
+ runLineageJoin(root, runId),
595
+ this.options.subagents.extension(runId),
596
+ { name: 'image-pruning', factory: imagePruning() },
597
+ { name: COMPACTION_CHECKPOINT_EXTENSION, factory: compactionCheckpoint({ initialChecklist: continuation?.checklist, planInstructions: checkpointPlanInstructions }) },
598
+ ...(sliceEnabled ? [{ name: WORK_SLICE_EXTENSION, factory: workSliceGuard({ policy: slicePolicy, state: sliceState }) }] : [])
599
+ ],
351
600
  ...(plugins ? { plugins: { loading: () => plugins.loading({ cwd: input.cwd, agentDir: context.configDir, install: true, caller: { agentId: caller.agentId ?? null, projectId: caller.projectId ?? null }, contributions: capabilities.contributions }), loaded: (extensions) => plugins.loaded(extensions) } } : {}),
352
601
  system,
353
- update
602
+ ...(sliceEnabled ? { slice: { state: sliceState, maxToolCalls: slicePolicy.maxToolCalls } } : {}),
603
+ // A command sends no request of its own, so the context update waits for the next turn.
604
+ update: input.command ? null : update,
605
+ ...(input.command ? { command: input.command } : {})
354
606
  };
355
607
  pi = await this.factory({
356
608
  params,
@@ -378,7 +630,7 @@ export class Sessions {
378
630
  if (pi.model?.id && pi.model.provider)
379
631
  this.options.subagents.resolvedModel(runId, `${pi.model.provider}/${pi.model.id}`, pi.thinkingLevel);
380
632
  params.settle = () => this.options.subagents.verify(runId, opened.sessionManager ? subagentHistory(opened.sessionManager.getBranch()) : undefined);
381
- void runTurn(run, pi, params);
633
+ void runTurn(run, pi, params).then(() => (sliceEnabled && !input.command ? this.continueSlice({ root, runId, source: input, policy: slicePolicy }) : undefined));
382
634
  return { runId, nativeSessionId: pi.sessionId };
383
635
  }
384
636
  catch (error) {
@@ -0,0 +1,3 @@
1
+ import type { Harness } from "../harness.js";
2
+ import type { Handler } from "../server/rpc.js";
3
+ export declare function sideQuestionServerMethods(harness: Harness): Record<string, Handler>;
@@ -0,0 +1,19 @@
1
+ import { invalid } from "../core/errors.js";
2
+ export function sideQuestionServerMethods(harness) {
3
+ return {
4
+ 'sideQuestions.start': params => harness.sideQuestions.start(params),
5
+ 'sideQuestions.read': params => {
6
+ if (typeof params.runId !== 'string' || typeof params.sessionId !== 'string')
7
+ throw invalid('A side-question run and session id are required.');
8
+ const afterSequence = params.afterSequence === undefined ? -1 : params.afterSequence;
9
+ if (typeof afterSequence !== 'number')
10
+ throw invalid('A side-question event sequence is required.');
11
+ return harness.sideQuestions.read(params.runId, params.sessionId, afterSequence);
12
+ },
13
+ 'sideQuestions.interrupt': params => {
14
+ if (typeof params.runId !== 'string' || typeof params.sessionId !== 'string')
15
+ throw invalid('A side-question run and session id are required.');
16
+ return harness.sideQuestions.interrupt(params.runId, params.sessionId);
17
+ }
18
+ };
19
+ }
@@ -0,0 +1,66 @@
1
+ import type { RunEvent, RunRecord } from "./contracts.js";
2
+ import type { RunRegistry } from "./registry.js";
3
+ import type { SessionRunInput, Sessions } from "./sessions.js";
4
+ import type { HarnessEnv } from "../core/env.js";
5
+ import type { CallerContext } from "../core/caller.js";
6
+ import type { Reference } from "../prompt/compile.js";
7
+ export declare const SIDE_QUESTION_INSTRUCTION: string;
8
+ export interface SideQuestionContext {
9
+ /** Used when the source has no Harness run event containing its effective system prompt. */
10
+ instructions?: string;
11
+ /** Visible conversation text and public progress supplied by the surface. */
12
+ transcript?: string;
13
+ /** References selected specifically for this side question, supplied as untrusted context data. */
14
+ references?: Reference[];
15
+ }
16
+ export interface SideQuestionStartInput {
17
+ /** Optional caller-reserved identifiers; the Harness creates these when omitted. */
18
+ runId?: string;
19
+ sessionId?: string;
20
+ /** The main run whose persisted prompt and public run events should be mirrored. */
21
+ parentRunId?: string | null;
22
+ /** The conversation session that owns parentRunId when the side run uses a fresh session id. */
23
+ parentSessionId?: string | null;
24
+ accountId?: string | null;
25
+ model?: string | null;
26
+ effort?: SessionRunInput['effort'];
27
+ cwd: string;
28
+ caller: CallerContext;
29
+ question: string;
30
+ context?: SideQuestionContext;
31
+ }
32
+ export interface SideQuestionRead {
33
+ run: RunRecord;
34
+ events: RunEvent[];
35
+ }
36
+ export interface BuildSideQuestionTextInput {
37
+ question: string;
38
+ context?: SideQuestionContext;
39
+ references?: readonly string[];
40
+ parentEvents?: readonly RunEvent[];
41
+ parentStatus?: string | null;
42
+ }
43
+ /** Bounded, redacted context copied into the side run's user message. */
44
+ export declare function buildSideQuestionText(input: BuildSideQuestionTextInput): string;
45
+ export interface SideQuestionsOptions {
46
+ env: HarnessEnv;
47
+ sessions: Sessions;
48
+ runs: RunRegistry;
49
+ }
50
+ /** A single Harness-owned side-question capability shared by hosts and the standalone terminal. */
51
+ export declare class SideQuestions {
52
+ private readonly options;
53
+ private readonly sessionDirs;
54
+ private readonly unsubscribe;
55
+ constructor(options: SideQuestionsOptions);
56
+ start(raw: SideQuestionStartInput): Promise<{
57
+ runId: string;
58
+ sessionId: string;
59
+ nativeSessionId: string | null;
60
+ }>;
61
+ read(runId: string, sessionId: string, afterSequence?: number): SideQuestionRead;
62
+ interrupt(runId: string, sessionId: string): Promise<{
63
+ runId: string;
64
+ }>;
65
+ close(): Promise<void>;
66
+ }