@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,103 @@
1
+ import type { Checkpoint } from "./contracts.js";
2
+ export declare const WORK_SLICE_EXTENSION = "work-slice";
3
+ export interface WorkSliceInput {
4
+ /** False turns the guard off for this run: no budget, no stall notes, no continuation. */
5
+ enabled?: boolean;
6
+ /** Tool calls in one reply; 0 disables this limit. */
7
+ maxToolCalls?: number;
8
+ /** Minutes in one reply; 0 disables this limit. */
9
+ maxMinutes?: number;
10
+ /** New tokens in one reply (input + cache read + output); 0 disables this limit. */
11
+ maxNewTokens?: number;
12
+ /** Slices one task may use before it is handed back to the person; 0 disables the cap. */
13
+ maxSlices?: number;
14
+ /** Start the next slice from the checkpoint without asking. */
15
+ continue?: boolean;
16
+ }
17
+ export interface SlicePolicy {
18
+ enabled: boolean;
19
+ maxToolCalls: number;
20
+ maxMinutes: number;
21
+ maxNewTokens: number;
22
+ maxSlices: number;
23
+ continue: boolean;
24
+ }
25
+ /** The foundation defaults: enough for one meaningful step of work, short enough to report. */
26
+ export declare const DEFAULT_SLICE_POLICY: SlicePolicy;
27
+ export declare class WorkSliceError extends Error {
28
+ }
29
+ /**
30
+ * The policy a run asked for, or the defaults. `XINGULAR_WORK_SLICE=off` at launch turns the guard
31
+ * off for every run in this process; nothing is persisted, so restarting without it restores the
32
+ * defaults.
33
+ */
34
+ export declare function parseWorkSlice(input: unknown): SlicePolicy;
35
+ export type SliceEnd = 'budget' | 'stuck' | 'compaction-thrash';
36
+ /** What a live reply tracks for its slice; the runner reads it after the reply. */
37
+ export interface SliceState {
38
+ slice: number;
39
+ steps: number;
40
+ tokens: number;
41
+ startedAt: number;
42
+ lastTool: string | null;
43
+ compactions: number;
44
+ checkpointWritten: boolean;
45
+ /** Set when the guard ended the reply instead of the model finishing it. */
46
+ endedBy: SliceEnd | null;
47
+ /** The limit that ended it, for the record. */
48
+ endedByLimit: string | null;
49
+ /** Steps past which every non-exempt tool call is refused, once one was refused. */
50
+ refusedAt: number | null;
51
+ }
52
+ export declare function createSliceState(_policy: SlicePolicy, slice?: number, now?: number): SliceState;
53
+ /** Counters start again for each reply; a terminal session keeps one guard across replies. */
54
+ export declare function resetSliceState(state: SliceState, now?: number): void;
55
+ /** The first limit this reply has passed, named as a person would say it. */
56
+ export declare function sliceLimitReached(policy: SlicePolicy, state: SliceState, now?: number): string | null;
57
+ /** Commands that read as a check, for "no progress" and "still failing" rules. */
58
+ export declare function looksLikeCheck(command: string): boolean;
59
+ interface ToolCall {
60
+ toolName: string;
61
+ input?: unknown;
62
+ }
63
+ interface ToolResult {
64
+ toolName: string;
65
+ isError?: boolean;
66
+ input?: unknown;
67
+ }
68
+ /** The part of Pi's extension API this guard uses. Structural, so the module needs no Pi import. */
69
+ interface GuardApi {
70
+ on(event: 'agent_start', handler: () => unknown): void;
71
+ on(event: 'tool_call', handler: (event: ToolCall) => unknown): void;
72
+ on(event: 'tool_result', handler: (event: ToolResult) => unknown): void;
73
+ on(event: 'message_end', handler: (event: {
74
+ message: {
75
+ role: string;
76
+ usage?: {
77
+ input?: number;
78
+ output?: number;
79
+ cacheRead?: number;
80
+ cacheWrite?: number;
81
+ };
82
+ };
83
+ }) => unknown): void;
84
+ }
85
+ export interface WorkSliceGuardOptions {
86
+ policy: SlicePolicy;
87
+ state: SliceState;
88
+ /** Called once the guard has decided the reply must stop; the runner ends it. */
89
+ onEnd?: (end: SliceEnd, limit: string) => void;
90
+ }
91
+ /**
92
+ * Bounds one reply and names repeated work. The first refusal asks for a checkpoint; a tool call
93
+ * after that refusal ends the reply. A repeated call or failing command is named once, then ends it.
94
+ */
95
+ export declare function workSliceGuard(options: WorkSliceGuardOptions): (pi: GuardApi) => void;
96
+ /**
97
+ * Which slice of its session a run is, from 1. Counts the recorded continuation chain, so it
98
+ * survives a restart and needs no extra field on the run record.
99
+ */
100
+ export declare function sliceOf(root: string, runId: string, limit?: number): number;
101
+ /** The reason a checkpoint the harness derives (no model handoff) records. */
102
+ export declare function derivedReason(end: SliceEnd | null): Checkpoint['reason'];
103
+ export {};
@@ -0,0 +1,297 @@
1
+ // A reply's work slice (PROP-0041): a bounded amount of work, then a handoff checkpoint, then a
2
+ // continuation. The guard here refuses the call that would run past the budget, once with a
3
+ // request for the checkpoint, and ends the reply if the model keeps calling tools. It also names
4
+ // repeated work before it becomes a loop. All decisions are deterministic; no model is consulted.
5
+ import { createHash } from 'node:crypto';
6
+ import { findRun } from "./runStore.js";
7
+ export const WORK_SLICE_EXTENSION = 'work-slice';
8
+ /** The foundation defaults: enough for one meaningful step of work, short enough to report. */
9
+ export const DEFAULT_SLICE_POLICY = {
10
+ enabled: true,
11
+ // Roughly 40 minutes of ordinary work (about 15 s a call), so a checkpoint is a chapter of a
12
+ // task, not an interruption every few edits. A task still gets eight of them before the cap.
13
+ maxToolCalls: 160,
14
+ maxMinutes: 40,
15
+ // A generous cost ceiling, counted on new input, cache writes and output only: heavy reading at
16
+ // a large context can add tens of thousands of tokens a request, so a small cap stopped useful
17
+ // work (see the "Confirm plan model support" thread). 320k is a safety net, not the bound.
18
+ maxNewTokens: 320_000,
19
+ maxSlices: 8,
20
+ continue: true
21
+ };
22
+ const LIMITS = {
23
+ maxToolCalls: { min: 1, max: 2_000 },
24
+ maxMinutes: { min: 1, max: 480 },
25
+ maxNewTokens: { min: 1_000, max: 5_000_000 },
26
+ maxSlices: { min: 1, max: 100 }
27
+ };
28
+ export class WorkSliceError extends Error {
29
+ }
30
+ /**
31
+ * The policy a run asked for, or the defaults. `XINGULAR_WORK_SLICE=off` at launch turns the guard
32
+ * off for every run in this process; nothing is persisted, so restarting without it restores the
33
+ * defaults.
34
+ */
35
+ export function parseWorkSlice(input) {
36
+ if (input === undefined || input === null) {
37
+ const launch = process.env.XINGULAR_WORK_SLICE?.trim().toLowerCase();
38
+ return { ...DEFAULT_SLICE_POLICY, ...(launch === 'off' || launch === '0' || launch === 'false' ? { enabled: false } : {}) };
39
+ }
40
+ if (typeof input !== 'object' || Array.isArray(input))
41
+ throw new WorkSliceError('workSlice must be an object.');
42
+ const record = input;
43
+ const policy = { ...DEFAULT_SLICE_POLICY };
44
+ for (const key of Object.keys(record)) {
45
+ if (key === 'enabled') {
46
+ if (typeof record.enabled !== 'boolean')
47
+ throw new WorkSliceError('workSlice.enabled must be true or false.');
48
+ policy.enabled = record.enabled;
49
+ continue;
50
+ }
51
+ if (key === 'continue') {
52
+ if (typeof record.continue !== 'boolean')
53
+ throw new WorkSliceError('workSlice.continue must be true or false.');
54
+ policy.continue = record.continue;
55
+ continue;
56
+ }
57
+ const limits = LIMITS[key];
58
+ if (!limits)
59
+ throw new WorkSliceError(`workSlice has no field ${key}.`);
60
+ const value = record[key];
61
+ if (typeof value !== 'number' || !Number.isInteger(value))
62
+ throw new WorkSliceError(`workSlice.${key} must be a whole number.`);
63
+ // 0 switches one limit off, as the settings page says.
64
+ if (value === 0) {
65
+ policy[key] = 0;
66
+ continue;
67
+ }
68
+ if (value < limits.min || value > limits.max)
69
+ throw new WorkSliceError(`workSlice.${key} must be between ${limits.min} and ${limits.max}, or 0 for no limit.`);
70
+ policy[key] = value;
71
+ }
72
+ return policy;
73
+ }
74
+ export function createSliceState(_policy, slice = 1, now = Date.now()) {
75
+ return { slice, steps: 0, tokens: 0, startedAt: now, lastTool: null, compactions: 0, checkpointWritten: false, endedBy: null, endedByLimit: null, refusedAt: null };
76
+ }
77
+ /** Counters start again for each reply; a terminal session keeps one guard across replies. */
78
+ export function resetSliceState(state, now = Date.now()) {
79
+ state.steps = 0;
80
+ state.tokens = 0;
81
+ state.startedAt = now;
82
+ state.lastTool = null;
83
+ state.compactions = 0;
84
+ state.checkpointWritten = false;
85
+ state.endedBy = null;
86
+ state.endedByLimit = null;
87
+ state.refusedAt = null;
88
+ }
89
+ /** The first limit this reply has passed, named as a person would say it. */
90
+ export function sliceLimitReached(policy, state, now = Date.now()) {
91
+ if (policy.maxToolCalls > 0 && state.steps >= policy.maxToolCalls)
92
+ return `${policy.maxToolCalls} tool calls`;
93
+ if (policy.maxMinutes > 0 && now - state.startedAt >= policy.maxMinutes * 60_000)
94
+ return `${policy.maxMinutes} minutes`;
95
+ if (policy.maxNewTokens > 0 && state.tokens >= policy.maxNewTokens)
96
+ return `${Math.round(policy.maxNewTokens / 1000)}k new tokens`;
97
+ return null;
98
+ }
99
+ /** Tools a reply may still call while it writes its handoff. A plan update waits for the next slice. */
100
+ const HANDOFF_TOOLS = new Set(['checkpoint_write', 'session_name']);
101
+ /** Tools whose repetition is how waiting works, not a loop. */
102
+ const POLLING_TOOLS = new Set(['wait_subagents', 'subagents.list', 'subagents.status']);
103
+ /** Commands that read as a check, for "no progress" and "still failing" rules. */
104
+ export function looksLikeCheck(command) {
105
+ return /\b(test|tests|typecheck|tsc|vitest|jest|cargo|check:|prove|lint|ci|build)\b/u.test(command);
106
+ }
107
+ /**
108
+ * A stable rendering of a value: object keys sorted, so argument order never changes the result.
109
+ * Depth is bounded so a cyclic or absurdly nested input cannot recurse forever.
110
+ */
111
+ function canonical(value, depth = 0) {
112
+ if (depth > 8)
113
+ return '…';
114
+ if (value === null || typeof value !== 'object')
115
+ return JSON.stringify(value) ?? 'null';
116
+ if (Array.isArray(value))
117
+ return `[${value.map((item) => canonical(item, depth + 1)).join(',')}]`;
118
+ const entries = Object.entries(value).filter(([, item]) => item !== undefined);
119
+ entries.sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0));
120
+ return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${canonical(item, depth + 1)}`).join(',')}}`;
121
+ }
122
+ /**
123
+ * A call's identity: its name and every argument, hashed so the key stays small even when an
124
+ * argument is a whole file. A changed argument is a different call; only an identical one repeats.
125
+ * Reading a fixed handful of fields made every `checklist_write` look identical, so a reply that
126
+ * reported its progress was told it was repeating work.
127
+ */
128
+ const signature = (name, input) => `${name}\u0000${createHash('sha256').update(canonical(input ?? null)).digest('hex')}`;
129
+ const clip = (value, limit) => (value.length <= limit ? value : `${value.slice(0, limit - 1)}…`);
130
+ function handoffRequest(limit, state) {
131
+ return clip(`This reply reached its work-slice limit (${limit}; step ${state.steps}). Call checkpoint_write now with what is done, the single next step, anything blocking, the files you touched and the checks that matter. Then tell the user in a few lines where you stopped. Do not call other tools.`, 600);
132
+ }
133
+ function stalledNote(reason) {
134
+ return clip(`You are repeating work: ${reason}. Do not repeat it. Choose a different approach, read what the earlier attempt returned, or call checkpoint_write with the blocker so the next slice can decide.`, 600);
135
+ }
136
+ /**
137
+ * Bounds one reply and names repeated work. The first refusal asks for a checkpoint; a tool call
138
+ * after that refusal ends the reply. A repeated call or failing command is named once, then ends it.
139
+ */
140
+ export function workSliceGuard(options) {
141
+ const { policy, state } = options;
142
+ const calls = new Map();
143
+ const edits = new Map();
144
+ const failures = new Map();
145
+ let stepsSinceProgress = 0;
146
+ let nudged = false;
147
+ /** Repetition is judged inside one reply; the next reply starts from a clean count. */
148
+ const resetRepetition = () => {
149
+ calls.clear();
150
+ edits.clear();
151
+ failures.clear();
152
+ stepsSinceProgress = 0;
153
+ nudged = false;
154
+ };
155
+ const end = (kind, limit) => {
156
+ state.endedBy = kind;
157
+ state.endedByLimit = limit;
158
+ options.onEnd?.(kind, limit);
159
+ const reason = kind === 'stuck'
160
+ ? stalledNote(limit)
161
+ : kind === 'compaction-thrash'
162
+ ? handoffRequest('three context compactions in one reply', state)
163
+ : handoffRequest(limit, state);
164
+ return { block: true, terminate: true, reason };
165
+ };
166
+ const repeated = (name, input) => {
167
+ if (POLLING_TOOLS.has(name))
168
+ return null;
169
+ const key = signature(name, input);
170
+ const count = (calls.get(key) ?? 0) + 1;
171
+ calls.set(key, count);
172
+ if (count < 3)
173
+ return null;
174
+ return name === 'bash' ? `the same Bash command ${count} times (steps ending at ${state.steps})` : `the same ${name} call ${count} times (steps ending at ${state.steps})`;
175
+ };
176
+ const failing = (name, input) => {
177
+ if (name !== 'bash')
178
+ return null;
179
+ const command = input && typeof input === 'object' ? String(input.command ?? '') : '';
180
+ if (!command || !looksLikeCheck(command))
181
+ return null;
182
+ const count = failures.get(command) ?? 0;
183
+ return count >= 2 ? `the check ${command} failed ${count} times` : null;
184
+ };
185
+ return (pi) => {
186
+ pi.on('agent_start', () => {
187
+ resetSliceState(state);
188
+ resetRepetition();
189
+ });
190
+ pi.on('message_end', (event) => {
191
+ const usage = event.message.usage;
192
+ if (event.message.role !== 'assistant' || !usage)
193
+ return;
194
+ // New tokens only: the cached prefix was sent before and is not new context or new cost.
195
+ state.tokens += (usage.input ?? 0) + (usage.cacheWrite ?? 0) + (usage.output ?? 0);
196
+ });
197
+ pi.on('tool_result', (event) => {
198
+ if (event.toolName === 'checkpoint_write' && !event.isError) {
199
+ state.checkpointWritten = true;
200
+ stepsSinceProgress = 0;
201
+ nudged = false;
202
+ }
203
+ if (event.toolName !== 'bash')
204
+ return;
205
+ const command = event.input && typeof event.input === 'object' ? String(event.input.command ?? '') : '';
206
+ if (!command || !looksLikeCheck(command))
207
+ return;
208
+ if (event.isError) {
209
+ failures.set(command, (failures.get(command) ?? 0) + 1);
210
+ return;
211
+ }
212
+ // A passing check is progress: the repetition rules start over.
213
+ failures.delete(command);
214
+ edits.clear();
215
+ calls.clear();
216
+ stepsSinceProgress = 0;
217
+ nudged = false;
218
+ });
219
+ pi.on('tool_call', (event) => {
220
+ const name = event.toolName;
221
+ state.steps += 1;
222
+ state.lastTool = name;
223
+ const handoff = HANDOFF_TOOLS.has(name);
224
+ // A handoff is always callable; after the slice ended it is the only thing that is.
225
+ if (state.endedBy) {
226
+ return handoff ? undefined : { block: true, terminate: true, reason: 'This reply is over. Its checkpoint is recorded; nothing else runs.' };
227
+ }
228
+ if (state.compactions >= 3) {
229
+ if (handoff) {
230
+ state.endedBy = 'compaction-thrash';
231
+ state.endedByLimit = 'three compactions';
232
+ return undefined;
233
+ }
234
+ return end('compaction-thrash', 'three compactions');
235
+ }
236
+ const limit = sliceLimitReached(policy, state);
237
+ if (limit) {
238
+ // The slice ends here; the model gets one wrap-up request to write the handoff. A handoff
239
+ // call is never refused, including when it is the very first call past the budget.
240
+ state.endedBy = 'budget';
241
+ state.endedByLimit = limit;
242
+ state.refusedAt ??= state.steps;
243
+ stepsSinceProgress = 0;
244
+ return handoff ? undefined : { block: true, reason: handoffRequest(limit, state) };
245
+ }
246
+ if (handoff)
247
+ return undefined;
248
+ const edit = name === 'edit' || name === 'write';
249
+ const path = edit && event.input && typeof event.input === 'object' ? String(event.input.path ?? '') : '';
250
+ if (edit && path) {
251
+ const count = (edits.get(path) ?? 0) + 1;
252
+ edits.set(path, count);
253
+ stepsSinceProgress = 0;
254
+ if (count >= 4) {
255
+ const reason = `${path} was edited ${count} times with no passing check between`;
256
+ if (nudged)
257
+ return end('stuck', reason);
258
+ nudged = true;
259
+ return { block: true, reason: stalledNote(reason) };
260
+ }
261
+ }
262
+ else {
263
+ stepsSinceProgress += 1;
264
+ }
265
+ const repeat = repeated(name, event.input);
266
+ const failure = failing(name, event.input);
267
+ const noProgress = stepsSinceProgress >= 25 ? `${stepsSinceProgress} steps with no file change and no passing check` : null;
268
+ const reason = repeat ?? failure ?? noProgress;
269
+ if (!reason)
270
+ return undefined;
271
+ if (nudged)
272
+ return end('stuck', reason);
273
+ nudged = true;
274
+ stepsSinceProgress = 0;
275
+ return { block: true, reason: stalledNote(reason) };
276
+ });
277
+ };
278
+ }
279
+ /**
280
+ * Which slice of its session a run is, from 1. Counts the recorded continuation chain, so it
281
+ * survives a restart and needs no extra field on the run record.
282
+ */
283
+ export function sliceOf(root, runId, limit = 100) {
284
+ let current = runId;
285
+ let count = 0;
286
+ const seen = new Set();
287
+ while (current && count < limit && !seen.has(current)) {
288
+ seen.add(current);
289
+ count += 1;
290
+ current = findRun(root, current)?.resumedFromRunId ?? null;
291
+ }
292
+ return Math.max(1, count);
293
+ }
294
+ /** The reason a checkpoint the harness derives (no model handoff) records. */
295
+ export function derivedReason(end) {
296
+ return end ?? 'requested';
297
+ }
@@ -0,0 +1,15 @@
1
+ import type { RunRecord } from "./contracts.js";
2
+ import type { ScopeDecision } from "./policy.js";
3
+ export declare const WORKSPACE_FILE_WRITE_TOOLS: Set<string>;
4
+ export interface WorkspaceWriteInspection {
5
+ input: unknown;
6
+ path: string | null;
7
+ outside: boolean;
8
+ reason: string | null;
9
+ }
10
+ /** Canonicalize native file-write arguments and identify an explicit workspace escape. */
11
+ export declare function inspectWorkspaceWrite(workspaceRoot: string, cwd: string, tool: string, input: unknown): WorkspaceWriteInspection;
12
+ /** Adds the mandatory per-operation boundary without allowing it to waive host hard denials. */
13
+ export declare function combineWorkspaceScope(scope: ScopeDecision, inspection: WorkspaceWriteInspection): ScopeDecision;
14
+ /** Resolve a root for pre-boundary run records, then inspect the write using the pinned directory. */
15
+ export declare function inspectRunWorkspaceWrite(run: RunRecord, tool: string, input: unknown): WorkspaceWriteInspection;
@@ -0,0 +1,46 @@
1
+ import { isWithinWorkspace, resolveWorkspaceRoot, resolveWritePath, verifyWorkspaceRoot } from "../core/workspace.js";
2
+ export const WORKSPACE_FILE_WRITE_TOOLS = new Set(['edit', 'write']);
3
+ /** Canonicalize native file-write arguments and identify an explicit workspace escape. */
4
+ export function inspectWorkspaceWrite(workspaceRoot, cwd, tool, input) {
5
+ if (!WORKSPACE_FILE_WRITE_TOOLS.has(tool.toLowerCase()) || !input || typeof input !== 'object' || Array.isArray(input)) {
6
+ return { input, path: null, outside: false, reason: null };
7
+ }
8
+ const args = input;
9
+ if (typeof args.path !== 'string')
10
+ return { input, path: null, outside: false, reason: null };
11
+ const root = verifyWorkspaceRoot(workspaceRoot);
12
+ const path = resolveWritePath(args.path, cwd);
13
+ const outside = !isWithinWorkspace(root, path);
14
+ return {
15
+ input: { ...args, path },
16
+ path,
17
+ outside,
18
+ reason: outside ? `Write target ${path} is outside the active project workspace ${root}.` : null
19
+ };
20
+ }
21
+ /** Adds the mandatory per-operation boundary without allowing it to waive host hard denials. */
22
+ export function combineWorkspaceScope(scope, inspection) {
23
+ if (!inspection.outside)
24
+ return scope;
25
+ if (!scope.allowed && (scope.automatic || !scope.requiresApproval))
26
+ return scope;
27
+ return {
28
+ ...scope,
29
+ allowed: false,
30
+ requiresApproval: true,
31
+ humanOnly: true,
32
+ allowGlobalApproval: scope.humanOnly ? scope.allowGlobalApproval : true,
33
+ reason: inspection.reason
34
+ };
35
+ }
36
+ /** Resolve a root for pre-boundary run records, then inspect the write using the pinned directory. */
37
+ export function inspectRunWorkspaceWrite(run, tool, input) {
38
+ const cwd = run.workingDirectory;
39
+ const root = run.workspaceRoot ?? (cwd ? resolveWorkspaceRoot(cwd) : null);
40
+ if (!root || !cwd) {
41
+ if (WORKSPACE_FILE_WRITE_TOOLS.has(tool.toLowerCase()))
42
+ throw new Error('The active project workspace could not be resolved; file writes are blocked.');
43
+ return { input, path: null, outside: false, reason: null };
44
+ }
45
+ return inspectWorkspaceWrite(root, cwd, tool, input);
46
+ }
@@ -0,0 +1,62 @@
1
+ import type { HubOverview } from "../hub/schema.js";
2
+ import type { Connection } from "../mcp/types.js";
3
+ import type { Plugin } from "../plugins/types.js";
4
+ export declare const BROWSER_SECTIONS: readonly ["overview", "foundation", "variant", "skills", "plugins", "connections", "tools", "library"];
5
+ export type BrowserSection = typeof BROWSER_SECTIONS[number];
6
+ export type BrowserSource = 'installed' | 'discover';
7
+ export type BrowserChannel = 'all' | 'community' | 'official';
8
+ export declare const BROWSER_COMMANDS: Set<string>;
9
+ export interface BrowserItem {
10
+ id: string;
11
+ section: BrowserSection;
12
+ name: string;
13
+ description: string;
14
+ owner: string;
15
+ origin: string;
16
+ status: string;
17
+ path?: string;
18
+ scope?: string | null;
19
+ pluginId?: string;
20
+ contributionId?: string;
21
+ componentId?: string;
22
+ connectionId?: string;
23
+ skillName?: string;
24
+ available?: boolean;
25
+ read?: boolean;
26
+ added?: boolean;
27
+ required?: string | null;
28
+ parameters?: unknown;
29
+ }
30
+ export interface BrowserSnapshot {
31
+ items: BrowserItem[];
32
+ plugins: Plugin[];
33
+ connections: Connection[];
34
+ variant: {
35
+ scope: string | null;
36
+ pinned: string | null;
37
+ latest: string | null;
38
+ writable: boolean;
39
+ };
40
+ hub: HubOverview | null;
41
+ }
42
+ export interface BrowserRuntime {
43
+ skills: {
44
+ name: string;
45
+ description: string;
46
+ filePath: string;
47
+ }[];
48
+ tools: {
49
+ name: string;
50
+ description: string;
51
+ parameters?: unknown;
52
+ sourceInfo?: {
53
+ path: string;
54
+ source: string;
55
+ };
56
+ }[];
57
+ activeTools: string[];
58
+ skillCommands: boolean;
59
+ }
60
+ /** Saved configuration never establishes what the current runtime has loaded. */
61
+ export declare function browserItems(snapshot: BrowserSnapshot, runtime: BrowserRuntime): BrowserItem[];
62
+ export declare function catalogSection(entry: NonNullable<BrowserSnapshot['hub']>['entries'][number]): BrowserSection;
@@ -0,0 +1,41 @@
1
+ export const BROWSER_SECTIONS = ['overview', 'foundation', 'variant', 'skills', 'plugins', 'connections', 'tools', 'library'];
2
+ export const BROWSER_COMMANDS = new Set(['harness', 'skills', 'plugins', 'connections', 'mcp', 'tools', 'hub', 'components']);
3
+ /** Saved configuration never establishes what the current runtime has loaded. */
4
+ export function browserItems(snapshot, runtime) {
5
+ const items = snapshot.items.map(item => ({ ...item }));
6
+ for (const skill of runtime.skills) {
7
+ const existing = items.find(item => item.section === 'skills' && item.path === skill.filePath);
8
+ if (existing)
9
+ Object.assign(existing, { available: true, skillName: skill.name, status: 'Available now' });
10
+ else
11
+ items.push({ id: `runtime:skill:${skill.filePath}`, section: 'skills', name: skill.name, description: skill.description,
12
+ owner: 'Session resource loader', origin: 'Loaded resource', status: 'Available now', available: true, skillName: skill.name, path: skill.filePath });
13
+ }
14
+ const active = new Set(runtime.activeTools);
15
+ for (const tool of runtime.tools) {
16
+ const candidates = items.filter(item => item.section === 'tools' && item.name === tool.name);
17
+ const sourcePath = tool.sourceInfo?.path.replaceAll('\\', '/');
18
+ const owner = sourcePath ? snapshot.plugins.filter(plugin => {
19
+ const path = plugin.path?.replaceAll('\\', '/');
20
+ return path && (sourcePath === path || sourcePath.startsWith(path + '/'));
21
+ }).sort((a, b) => (b.path?.length ?? 0) - (a.path?.length ?? 0))[0] : undefined;
22
+ const builtin = sourcePath?.startsWith('builtin:') ? candidates.filter(item => item.origin === 'builtin' || item.origin === 'pi') : [];
23
+ const existing = owner ? candidates.find(item => item.pluginId === owner.id) : builtin.length === 1 ? builtin[0] : candidates.length === 1 ? candidates[0] : undefined;
24
+ const state = { available: active.has(tool.name), status: active.has(tool.name) ? 'Available now' : 'Not active here', parameters: tool.parameters };
25
+ if (existing)
26
+ Object.assign(existing, state, { description: tool.description || existing.description });
27
+ else
28
+ items.push({ id: `runtime:tool:${tool.name}`, section: 'tools', name: tool.name, description: tool.description,
29
+ owner: 'Runtime (unattributed)', origin: tool.sourceInfo?.source ?? 'Session', path: tool.sourceInfo?.path, ...state });
30
+ }
31
+ return items.sort((a, b) => Number(Boolean(b.available)) - Number(Boolean(a.available)) || a.name.localeCompare(b.name) || a.id.localeCompare(b.id));
32
+ }
33
+ export function catalogSection(entry) {
34
+ if (entry.delivery.kind === 'registry')
35
+ return entry.delivery.registryKind === 'skills' ? 'skills' : 'library';
36
+ if (entry.category === 'connector')
37
+ return 'connections';
38
+ if (entry.delivery.kind === 'pi-package')
39
+ return 'plugins';
40
+ return 'library';
41
+ }
@@ -0,0 +1,29 @@
1
+ import * as path from 'node:path';
2
+ import type { Hub } from "../hub/hub.js";
3
+ import type { ConnectedApps } from "../mcp/service.js";
4
+ import type { Plugin } from "../plugins/types.js";
5
+ import type { Materialised } from "../variant/materialise.js";
6
+ import type { BrowserSnapshot } from "./browser.js";
7
+ type PathApi = Pick<typeof path, 'relative' | 'sep' | 'isAbsolute'>;
8
+ export declare function isWithinBoundary(boundary: string, file: string, pathApi?: PathApi): boolean;
9
+ export interface BrowserControlOptions {
10
+ root: string;
11
+ cwd: string;
12
+ pinned: Materialised | null;
13
+ writable: boolean;
14
+ plugins(): Promise<Plugin[]>;
15
+ hub?: Hub;
16
+ apps: ConnectedApps;
17
+ }
18
+ /** Only the protected terminal control route calls this adapter. It never loads discovered code. */
19
+ export declare class BrowserControl {
20
+ private readonly options;
21
+ constructor(options: BrowserControlOptions);
22
+ snapshot(refresh?: boolean): Promise<BrowserSnapshot>;
23
+ read(id: string, latestVersion?: boolean): Promise<{
24
+ content: string;
25
+ }>;
26
+ importSkill(input: string): void;
27
+ connection(action: string, params: Record<string, unknown>): Promise<unknown>;
28
+ }
29
+ export {};