@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,116 @@
1
+ import type { Browser } from 'playwright-core';
2
+ import { NetworkPolicy } from "../browser/network.js";
3
+ import { type Launcher } from "../browser/session.js";
4
+ import { NotesBridge, type BridgeInfo } from "./bridge.js";
5
+ import { type NoteApp, type NoteTarget } from "./store.js";
6
+ export interface CaptureEnvelope {
7
+ url: string;
8
+ title: string;
9
+ documentId: string;
10
+ target: NoteTarget;
11
+ }
12
+ export type CaptureTargetSpec = {
13
+ kind: 'web';
14
+ } | {
15
+ kind: 'cdp';
16
+ endpoint: string;
17
+ name?: string | null;
18
+ } | {
19
+ kind: 'bridge';
20
+ name?: string | null;
21
+ };
22
+ export interface CaptureWindow {
23
+ id: string;
24
+ url: string;
25
+ title: string;
26
+ current: boolean;
27
+ }
28
+ /** One comment the person submitted in the inspector's popover. */
29
+ export interface InspectResult extends CaptureEnvelope {
30
+ body: string;
31
+ action: 'save' | 'queue' | 'send';
32
+ }
33
+ export interface InspectState {
34
+ /** False once the person or the page ended it. */
35
+ active: boolean;
36
+ url: string;
37
+ title: string;
38
+ documentId: string;
39
+ result: InspectResult | null;
40
+ /** Whether the result came from a script world the page cannot reach. */
41
+ trusted: boolean;
42
+ /** The app the window belongs to, for the note's source. */
43
+ app: NoteApp | null;
44
+ }
45
+ /** Opens a DevTools connection to a running app; tests replace it. */
46
+ export type AppConnector = (endpoint: string) => Promise<Browser>;
47
+ export interface AnnotationCaptureOptions {
48
+ stateDir: string;
49
+ launch?: Launcher;
50
+ network?: NetworkPolicy;
51
+ connect?: AppConnector;
52
+ bridge?: NotesBridge;
53
+ }
54
+ export declare class AnnotationCapture {
55
+ private readonly options;
56
+ private readonly targets;
57
+ private readonly inspecting;
58
+ private readonly network;
59
+ readonly bridge: NotesBridge;
60
+ constructor(options: AnnotationCaptureOptions);
61
+ private web;
62
+ private target;
63
+ private replace;
64
+ private call;
65
+ /** What `key` captures in now. */
66
+ describe(key: string): {
67
+ kind: CaptureTargetSpec['kind'];
68
+ app: NoteApp | null;
69
+ inspecting: boolean;
70
+ };
71
+ /** Captures in an app's windows instead of the browser: over its DevTools port, or the page bridge. */
72
+ attach(key: string, spec: CaptureTargetSpec): Promise<{
73
+ kind: CaptureTargetSpec['kind'];
74
+ app: NoteApp | null;
75
+ windows: CaptureWindow[];
76
+ bridge?: BridgeInfo;
77
+ }>;
78
+ windows(key: string): Promise<CaptureWindow[]>;
79
+ /** Switches to another window or tab of the target; an inspector there starts again. */
80
+ selectWindow(key: string, id: string): Promise<void>;
81
+ /** Shows the capture window, at `url` when given. Captures in the browser again if an app was attached. */
82
+ open(key: string, url?: string | null): Promise<{
83
+ url: string;
84
+ title: string;
85
+ }>;
86
+ identity(key: string): Promise<{
87
+ url: string;
88
+ title: string;
89
+ documentId: string;
90
+ }>;
91
+ /** Starts listening for the person's selection or click. Returns the id `capture` takes. */
92
+ begin(key: string, kind: 'text' | 'element'): Promise<{
93
+ captureId: string;
94
+ }>;
95
+ /** What the person selected, or null when nothing is selected yet. */
96
+ capture(key: string, captureId: string): Promise<CaptureEnvelope | null>;
97
+ cancel(key: string, captureId?: string): Promise<void>;
98
+ /** Turns on the inspector in the current window: hover outlines, a click opens the comment popover. */
99
+ inspect(key: string): Promise<{
100
+ captureId: string;
101
+ }>;
102
+ stopInspect(key: string): Promise<void>;
103
+ /**
104
+ * The next comment the person submitted, if any. A new document (a reload, a link) loses the
105
+ * inspector; it is turned on again there, so the person can keep going.
106
+ */
107
+ next(key: string): Promise<InspectState>;
108
+ toast(key: string, text: string): Promise<void>;
109
+ /** Opens the note's page when another one is showing, then scrolls to and marks its target. */
110
+ reveal(key: string, url: string, target: NoteTarget): Promise<{
111
+ state: 'matched' | 'ambiguous' | 'unresolved';
112
+ reason?: string;
113
+ }>;
114
+ release(key: string): Promise<void>;
115
+ close(): Promise<void>;
116
+ }
@@ -0,0 +1,308 @@
1
+ // The terminal's capture surface for page notes (PROP-0017, PROP-0018). A front end captures in
2
+ // one target at a time:
3
+ // - `web`: a visible browser window the person browses in, separate from the agent's browser;
4
+ // - `cdp`: the windows of a desktop app started with a DevTools port (an Electron app, say);
5
+ // - `bridge`: app windows that loaded the page bridge script (a macOS web view has no DevTools port).
6
+ // The person selects text, clicks an element, or inspects with the popover; the front end pulls
7
+ // what they did through the page helper.
8
+ import { randomUUID } from 'node:crypto';
9
+ import { join } from 'node:path';
10
+ import { readBrowserConfig } from "../browser/config.js";
11
+ import { NetworkPolicy } from "../browser/network.js";
12
+ import { BrowserSession, fileName, launchBrowser } from "../browser/session.js";
13
+ import { NotesBridge } from "./bridge.js";
14
+ import { evaluateIsolated } from "./isolated.js";
15
+ import { pageCall, pageReply } from "./pageScript.js";
16
+ import { validateUrl } from "./store.js";
17
+ const IDLE_MS = 60 * 60_000;
18
+ const LOOPBACK = /^(?:https?|wss?):\/\/(?:127\.0\.0\.1|localhost|\[::1\])(?::\d+)?(?:\/|$)/;
19
+ const connectApp = async (endpoint) => {
20
+ const { chromium } = await import('playwright-core');
21
+ return chromium.connectOverCDP(endpoint);
22
+ };
23
+ /** An app's own windows over its DevTools port. Nothing is routed or opened, and the app is never closed. */
24
+ class CdpTarget {
25
+ browser;
26
+ app;
27
+ kind = 'cdp';
28
+ ids = new WeakMap();
29
+ nextId = 1;
30
+ current = null;
31
+ constructor(browser, app) {
32
+ this.browser = browser;
33
+ this.app = app;
34
+ }
35
+ pages() {
36
+ return this.browser.contexts().flatMap((context) => context.pages()).filter((page) => !/^(?:devtools|chrome-extension):/.test(page.url()));
37
+ }
38
+ id(page) {
39
+ let id = this.ids.get(page);
40
+ if (!id) {
41
+ id = `w${this.nextId++}`;
42
+ this.ids.set(page, id);
43
+ }
44
+ return id;
45
+ }
46
+ page() {
47
+ const pages = this.pages();
48
+ if (this.current && pages.includes(this.current))
49
+ return this.current;
50
+ this.current = pages[0] ?? null;
51
+ if (!this.current)
52
+ throw new Error('The app has no open window.');
53
+ return this.current;
54
+ }
55
+ async evaluate(operation) {
56
+ return evaluateIsolated(this.page(), pageCall(operation));
57
+ }
58
+ async windows() {
59
+ const current = this.pages().length ? this.page() : null;
60
+ return Promise.all(this.pages().map(async (page) => ({ id: this.id(page), url: page.url(), title: await page.title().catch(() => ''), current: page === current })));
61
+ }
62
+ select(id) {
63
+ const page = this.pages().find((candidate) => this.ids.get(candidate) === id);
64
+ if (!page)
65
+ throw new Error(`There is no app window ${id}.`);
66
+ this.current = page;
67
+ void page.bringToFront().catch(() => undefined);
68
+ }
69
+ async close() {
70
+ // Disconnects; an app we attached to keeps running.
71
+ await this.browser.close().catch(() => undefined);
72
+ }
73
+ }
74
+ /** Windows that loaded the page bridge script. Every answer is page data the page could forge. */
75
+ class BridgeTarget {
76
+ bridge;
77
+ app;
78
+ kind = 'bridge';
79
+ current = null;
80
+ constructor(bridge, app) {
81
+ this.bridge = bridge;
82
+ this.app = app;
83
+ }
84
+ window() {
85
+ const windows = this.bridge.list();
86
+ if (this.current && windows.some((window) => window.id === this.current))
87
+ return this.current;
88
+ this.current = windows.sort((left, right) => right.lastSeen - left.lastSeen)[0]?.id ?? null;
89
+ if (!this.current)
90
+ throw new Error('No app window has connected to the page bridge yet. Load the bridge script in the app and reload it.');
91
+ return this.current;
92
+ }
93
+ async evaluate(operation) {
94
+ return { reply: await this.bridge.call(this.window(), operation), trusted: false };
95
+ }
96
+ async windows() {
97
+ const current = this.bridge.list().length ? this.window() : null;
98
+ return this.bridge.list().map((window) => ({ id: window.id, url: window.url, title: window.title, current: window.id === current }));
99
+ }
100
+ select(id) {
101
+ if (!this.bridge.list().some((window) => window.id === id))
102
+ throw new Error(`There is no app window ${id}.`);
103
+ this.current = id;
104
+ }
105
+ async close() { }
106
+ }
107
+ /** The visible browser window, with the helper in an isolated world. */
108
+ class WebTarget {
109
+ session;
110
+ kind = 'web';
111
+ app = null;
112
+ constructor(session) {
113
+ this.session = session;
114
+ }
115
+ evaluate(operation) {
116
+ return this.session.run(async () => evaluateIsolated((await this.session.tab()).page, pageCall(operation)));
117
+ }
118
+ async windows() {
119
+ if (!this.session.started)
120
+ return [];
121
+ return Promise.all(this.session.list().map(async (tab) => ({ id: tab.id, url: tab.url, title: await (await this.session.tab(tab.id)).page.title().catch(() => ''), current: tab.current })));
122
+ }
123
+ select(id) {
124
+ void this.session.select(id).page.bringToFront().catch(() => undefined);
125
+ }
126
+ close() {
127
+ return this.session.close();
128
+ }
129
+ }
130
+ export class AnnotationCapture {
131
+ options;
132
+ targets = new Map();
133
+ inspecting = new Map();
134
+ network;
135
+ bridge;
136
+ constructor(options) {
137
+ this.options = options;
138
+ this.network = options.network ?? new NetworkPolicy(() => readBrowserConfig(options.stateDir));
139
+ this.bridge = options.bridge ?? new NotesBridge({ stateDir: options.stateDir });
140
+ }
141
+ web(key) {
142
+ const existing = this.targets.get(key);
143
+ if (existing instanceof WebTarget)
144
+ return existing;
145
+ const session = new BrowserSession({
146
+ key: `notes-${key}`,
147
+ profileDir: join(this.options.stateDir, 'browser', 'profiles', 'person'),
148
+ downloadsDir: join(this.options.stateDir, 'browser', 'downloads', fileName(`notes-${key}`)),
149
+ // Always a window the person can see and select in.
150
+ config: () => ({ ...readBrowserConfig(this.options.stateDir), headed: true }),
151
+ network: this.network,
152
+ launch: this.options.launch ?? launchBrowser,
153
+ idleMs: IDLE_MS,
154
+ onIdle: () => void this.release(key)
155
+ });
156
+ const target = new WebTarget(session);
157
+ this.targets.set(key, target);
158
+ return target;
159
+ }
160
+ target(key) {
161
+ return this.targets.get(key) ?? this.web(key);
162
+ }
163
+ async replace(key, target) {
164
+ const old = this.targets.get(key);
165
+ this.targets.set(key, target);
166
+ this.inspecting.delete(key);
167
+ if (old && old !== target)
168
+ await old.close().catch(() => undefined);
169
+ }
170
+ async call(key, operation) {
171
+ return pageReply((await this.target(key).evaluate(operation)).reply);
172
+ }
173
+ /** What `key` captures in now. */
174
+ describe(key) {
175
+ const target = this.targets.get(key);
176
+ return { kind: target?.kind ?? 'web', app: target?.app ?? null, inspecting: this.inspecting.has(key) };
177
+ }
178
+ /** Captures in an app's windows instead of the browser: over its DevTools port, or the page bridge. */
179
+ async attach(key, spec) {
180
+ if (spec.kind === 'web') {
181
+ if (this.targets.get(key)?.kind !== 'web')
182
+ await this.replace(key, this.web(key));
183
+ return { kind: 'web', app: null, windows: await this.target(key).windows() };
184
+ }
185
+ if (spec.kind === 'cdp') {
186
+ if (typeof spec.endpoint !== 'string' || !LOOPBACK.test(spec.endpoint))
187
+ throw new Error('An app\'s DevTools address must be on this computer, such as http://127.0.0.1:9229.');
188
+ const browser = await (this.options.connect ?? connectApp)(spec.endpoint);
189
+ const name = spec.name?.trim() || `App at ${new URL(spec.endpoint).host}`;
190
+ const target = new CdpTarget(browser, { kind: 'cdp', name: name.slice(0, 200) });
191
+ await this.replace(key, target);
192
+ return { kind: 'cdp', app: target.app, windows: await target.windows() };
193
+ }
194
+ if (spec.kind === 'bridge') {
195
+ const bridge = await this.bridge.start();
196
+ const target = new BridgeTarget(this.bridge, { kind: 'bridge', name: (spec.name?.trim() || 'App window').slice(0, 200) });
197
+ await this.replace(key, target);
198
+ return { kind: 'bridge', app: target.app, windows: await target.windows(), bridge };
199
+ }
200
+ throw new Error('A capture target is web, cdp or bridge.');
201
+ }
202
+ windows(key) {
203
+ return this.target(key).windows();
204
+ }
205
+ /** Switches to another window or tab of the target; an inspector there starts again. */
206
+ async selectWindow(key, id) {
207
+ const inspecting = this.inspecting.has(key);
208
+ if (inspecting)
209
+ await this.stopInspect(key).catch(() => undefined);
210
+ this.target(key).select(id);
211
+ if (inspecting)
212
+ await this.inspect(key);
213
+ }
214
+ /** Shows the capture window, at `url` when given. Captures in the browser again if an app was attached. */
215
+ async open(key, url) {
216
+ if (this.targets.get(key) && this.targets.get(key).kind !== 'web')
217
+ await this.replace(key, this.web(key));
218
+ const { session } = this.web(key);
219
+ return session.run(async () => {
220
+ const tab = await session.tab();
221
+ if (url) {
222
+ const address = validateUrl(url);
223
+ const refusal = await this.network.refusal(address);
224
+ if (refusal)
225
+ throw new Error(refusal);
226
+ await tab.page.goto(address, { waitUntil: 'domcontentloaded' });
227
+ }
228
+ await tab.page.bringToFront().catch(() => undefined);
229
+ return { url: tab.page.url(), title: await tab.page.title().catch(() => '') };
230
+ });
231
+ }
232
+ identity(key) {
233
+ return this.call(key, { operation: 'identity' });
234
+ }
235
+ /** Starts listening for the person's selection or click. Returns the id `capture` takes. */
236
+ async begin(key, kind) {
237
+ this.inspecting.delete(key);
238
+ const captureId = randomUUID();
239
+ await this.call(key, { operation: 'begin', kind, captureId });
240
+ return { captureId };
241
+ }
242
+ /** What the person selected, or null when nothing is selected yet. */
243
+ capture(key, captureId) {
244
+ return this.call(key, { operation: 'capture', captureId });
245
+ }
246
+ async cancel(key, captureId) {
247
+ if (!this.targets.has(key))
248
+ return;
249
+ await this.call(key, { operation: 'cancel', ...(captureId ? { captureId } : {}) });
250
+ }
251
+ /** Turns on the inspector in the current window: hover outlines, a click opens the comment popover. */
252
+ async inspect(key) {
253
+ const captureId = randomUUID();
254
+ await this.call(key, { operation: 'begin', kind: 'inspect', captureId });
255
+ const identity = await this.identity(key).catch(() => null);
256
+ this.inspecting.set(key, { captureId, documentId: identity?.documentId ?? null });
257
+ return { captureId };
258
+ }
259
+ async stopInspect(key) {
260
+ const state = this.inspecting.get(key);
261
+ this.inspecting.delete(key);
262
+ if (state && this.targets.has(key))
263
+ await this.call(key, { operation: 'cancel', captureId: state.captureId }).catch(() => undefined);
264
+ }
265
+ /**
266
+ * The next comment the person submitted, if any. A new document (a reload, a link) loses the
267
+ * inspector; it is turned on again there, so the person can keep going.
268
+ */
269
+ async next(key) {
270
+ const target = this.target(key);
271
+ const state = this.inspecting.get(key);
272
+ if (!state)
273
+ throw new Error('The inspector is off.');
274
+ const { reply, trusted } = await target.evaluate({ operation: 'next', captureId: state.captureId });
275
+ const value = pageReply(reply);
276
+ if (!value.active && this.inspecting.get(key) === state && value.documentId !== state.documentId) {
277
+ await target.evaluate({ operation: 'begin', kind: 'inspect', captureId: state.captureId });
278
+ state.documentId = value.documentId;
279
+ return { ...value, active: true, result: null, trusted, app: target.app };
280
+ }
281
+ if (!value.active)
282
+ this.inspecting.delete(key);
283
+ return { ...value, trusted, app: target.app };
284
+ }
285
+ async toast(key, text) {
286
+ await this.call(key, { operation: 'toast', text: text.slice(0, 200) });
287
+ }
288
+ /** Opens the note's page when another one is showing, then scrolls to and marks its target. */
289
+ async reveal(key, url, target) {
290
+ const current = this.targets.get(key);
291
+ if (!current || current.kind === 'web') {
292
+ const shown = current?.session.started ? await this.identity(key).catch(() => null) : null;
293
+ if (shown?.url !== url)
294
+ await this.open(key, url);
295
+ }
296
+ return this.call(key, { operation: 'reveal', target });
297
+ }
298
+ async release(key) {
299
+ const target = this.targets.get(key);
300
+ this.targets.delete(key);
301
+ this.inspecting.delete(key);
302
+ await target?.close();
303
+ }
304
+ async close() {
305
+ await Promise.all([...this.targets.keys()].map((key) => this.release(key)));
306
+ await this.bridge.close();
307
+ }
308
+ }
@@ -0,0 +1,72 @@
1
+ import type { Bus } from "../core/bus.js";
2
+ import * as store from "./store.js";
3
+ export interface TerminalSender {
4
+ has(sessionId: string): boolean;
5
+ send(input: {
6
+ sessionId: string;
7
+ text: string;
8
+ }): Promise<{
9
+ runId: string;
10
+ }>;
11
+ }
12
+ export type SendResult = {
13
+ state: 'sent';
14
+ runId: string;
15
+ ids: string[];
16
+ } | {
17
+ state: 'busy';
18
+ ids: string[];
19
+ } | {
20
+ state: 'host';
21
+ text: string;
22
+ ids: string[];
23
+ } | {
24
+ state: 'empty';
25
+ ids: [];
26
+ };
27
+ export type NoteAction = 'save' | 'queue' | 'send';
28
+ export interface NoteDeliveryOptions {
29
+ root: () => string;
30
+ bus: Bus;
31
+ terminals: () => TerminalSender | null;
32
+ }
33
+ export declare class NoteDeliveries {
34
+ private readonly options;
35
+ private readonly sending;
36
+ constructor(options: NoteDeliveryOptions);
37
+ private changed;
38
+ queue(id: string, expectedVersion: number | undefined, sessionId: string, options?: {
39
+ autoSend?: boolean;
40
+ confirm?: boolean;
41
+ }): store.Note;
42
+ dequeue(id: string, expectedVersion: number | undefined): store.Note;
43
+ /**
44
+ * Creates a note and acts on it in one step: what the popover's three buttons do. `trusted` is
45
+ * false when the comment came from a script the page could reach; a send then waits in the queue
46
+ * for the person to confirm it.
47
+ */
48
+ createAndAct(input: store.NoteInput, action: NoteAction, sessionId: string | null, trusted: boolean): Promise<{
49
+ note: store.Note;
50
+ send: SendResult | null;
51
+ }>;
52
+ /** The person confirms a note the page could have forged; it is then sent with the next send. */
53
+ confirm(id: string, expectedVersion: number | undefined): store.Note;
54
+ /** The instruction for the session's queued notes (or the given ones), for a host that runs it itself. */
55
+ compose(sessionId: string, ids?: string[] | null): {
56
+ text: string;
57
+ ids: string[];
58
+ };
59
+ /** A host started `runId` with composed notes. */
60
+ delivered(ids: string[], sessionId: string, runId: string): store.Note[];
61
+ /** Sends the session's queued notes (or the given ones) as one run. */
62
+ send(sessionId: string, filter?: {
63
+ ids?: string[] | null;
64
+ auto?: boolean;
65
+ }): Promise<SendResult>;
66
+ /** A run of the session ended: notes waiting for it to be idle go out now. */
67
+ runEnded(sessionId: string | null | undefined): Promise<void>;
68
+ markFixed(id: string, caller: {
69
+ sessionId?: string | null;
70
+ agentId?: string | null;
71
+ }, summary: string): store.Note;
72
+ }
@@ -0,0 +1,130 @@
1
+ import { HarnessError, invalid } from "../core/errors.js";
2
+ import { compose, MAX_SEND } from "./queue.js";
3
+ import * as store from "./store.js";
4
+ export class NoteDeliveries {
5
+ options;
6
+ sending = new Set();
7
+ constructor(options) {
8
+ this.options = options;
9
+ }
10
+ changed(sessionId, ids) {
11
+ this.options.bus.emit({ type: 'annotations.changed', sessionId, ids });
12
+ }
13
+ queue(id, expectedVersion, sessionId, options = {}) {
14
+ const note = store.queue(this.options.root(), id, expectedVersion, sessionId, options);
15
+ this.changed(sessionId, [id]);
16
+ return note;
17
+ }
18
+ dequeue(id, expectedVersion) {
19
+ const note = store.dequeue(this.options.root(), id, expectedVersion);
20
+ this.changed(null, [id]);
21
+ return note;
22
+ }
23
+ /**
24
+ * Creates a note and acts on it in one step: what the popover's three buttons do. `trusted` is
25
+ * false when the comment came from a script the page could reach; a send then waits in the queue
26
+ * for the person to confirm it.
27
+ */
28
+ async createAndAct(input, action, sessionId, trusted) {
29
+ if (!['save', 'queue', 'send'].includes(action))
30
+ throw invalid('action is save, queue or send.');
31
+ if (action !== 'save' && !sessionId)
32
+ throw invalid('Queueing or sending a note needs a session.');
33
+ const root = this.options.root();
34
+ let note = store.create(root, input);
35
+ if (action === 'save') {
36
+ this.changed(null, [note.id]);
37
+ return { note, send: null };
38
+ }
39
+ const deliveryOptions = {
40
+ ...(action === 'send' ? { autoSend: true } : {}),
41
+ ...(!trusted ? { confirm: true } : {})
42
+ };
43
+ note = store.queue(root, note.id, undefined, sessionId, deliveryOptions);
44
+ this.changed(sessionId, [note.id]);
45
+ if (action === 'queue' || !trusted)
46
+ return { note, send: null };
47
+ const send = await this.send(sessionId, { ids: [note.id] });
48
+ return { note: store.read(root, note.id), send };
49
+ }
50
+ /** The person confirms a note the page could have forged; it is then sent with the next send. */
51
+ confirm(id, expectedVersion) {
52
+ const root = this.options.root();
53
+ const note = store.read(root, id);
54
+ if (note.delivery?.state !== 'queued')
55
+ throw invalid('This note is not waiting to be sent.');
56
+ const confirmed = store.queue(root, id, expectedVersion, note.delivery.sessionId, { autoSend: note.delivery.autoSend === true, confirmed: true });
57
+ this.changed(confirmed.delivery.sessionId, [id]);
58
+ return confirmed;
59
+ }
60
+ /** The instruction for the session's queued notes (or the given ones), for a host that runs it itself. */
61
+ compose(sessionId, ids) {
62
+ const waiting = store.queued(this.options.root(), sessionId, { ids: ids ?? null });
63
+ if (ids != null && waiting.some((note) => note.delivery?.confirm))
64
+ throw invalid('Confirm page-origin notes before composing them.');
65
+ const notes = waiting.filter((note) => note.delivery?.confirm !== true).slice(0, MAX_SEND);
66
+ if (!notes.length)
67
+ throw invalid('No confirmed notes are waiting for this session.');
68
+ return { text: compose(notes), ids: notes.map((note) => note.id) };
69
+ }
70
+ /** A host started `runId` with composed notes. */
71
+ delivered(ids, sessionId, runId) {
72
+ const sent = store.markSent(this.options.root(), ids, sessionId, runId);
73
+ this.changed(sessionId, ids);
74
+ return sent;
75
+ }
76
+ /** Sends the session's queued notes (or the given ones) as one run. */
77
+ async send(sessionId, filter = {}) {
78
+ const root = this.options.root();
79
+ const waiting = store.queued(root, sessionId, { ids: filter.ids ?? null, auto: filter.auto === true });
80
+ if (filter.ids != null && waiting.some((note) => note.delivery?.confirm))
81
+ throw invalid('Confirm page-origin notes before sending them.');
82
+ const notes = waiting.filter((note) => note.delivery?.confirm !== true).slice(0, MAX_SEND);
83
+ if (!notes.length)
84
+ return { state: 'empty', ids: [] };
85
+ const ids = notes.map((note) => note.id);
86
+ const terminals = this.options.terminals();
87
+ if (!terminals?.has(sessionId)) {
88
+ // A host's session: the host starts the run and reports it with `delivered`.
89
+ if (filter.auto)
90
+ this.options.bus.emit({ type: 'annotations.ready', sessionId, ids });
91
+ return { state: 'host', text: compose(notes), ids };
92
+ }
93
+ if (this.sending.has(sessionId))
94
+ return { state: 'busy', ids };
95
+ this.sending.add(sessionId);
96
+ try {
97
+ const { runId } = await terminals.send({ sessionId, text: compose(notes) });
98
+ store.markSent(root, ids, sessionId, runId);
99
+ this.changed(sessionId, ids);
100
+ return { state: 'sent', runId, ids };
101
+ }
102
+ catch (error) {
103
+ if (error instanceof HarnessError && error.code === 'session_busy') {
104
+ // Send it when the run ends.
105
+ for (const note of notes)
106
+ if (!note.delivery?.autoSend)
107
+ store.queue(root, note.id, undefined, sessionId, { autoSend: true });
108
+ this.changed(sessionId, ids);
109
+ return { state: 'busy', ids };
110
+ }
111
+ throw error;
112
+ }
113
+ finally {
114
+ this.sending.delete(sessionId);
115
+ }
116
+ }
117
+ /** A run of the session ended: notes waiting for it to be idle go out now. */
118
+ async runEnded(sessionId) {
119
+ if (!sessionId)
120
+ return;
121
+ if (!store.queued(this.options.root(), sessionId, { auto: true }).length)
122
+ return;
123
+ await this.send(sessionId, { auto: true });
124
+ }
125
+ markFixed(id, caller, summary) {
126
+ const note = store.markFixed(this.options.root(), id, caller, summary);
127
+ this.changed(note.delivery?.sessionId ?? null, [id]);
128
+ return note;
129
+ }
130
+ }
@@ -0,0 +1,10 @@
1
+ import type { AnnotationCapture, InspectState } from "./capture.js";
2
+ import type { NoteDeliveries, SendResult } from "./delivery.js";
3
+ import type { Note } from "./store.js";
4
+ export interface InspectStep extends Omit<InspectState, 'result'> {
5
+ note: Note | null;
6
+ send: SendResult | null;
7
+ /** What the person was told in the page. */
8
+ message: string | null;
9
+ }
10
+ export declare function inspectStep(capture: AnnotationCapture, notes: NoteDeliveries, key: string, sessionId: string | null, surface: string): Promise<InspectStep>;
@@ -0,0 +1,33 @@
1
+ // One step of the inspector loop (PROP-0018), shared by the terminal and a host: read the next
2
+ // comment from the capture target, keep it as a note, act on the button the person pressed, and
3
+ // tell them in the page what happened. Whether a send goes straight to the agent depends on where
4
+ // the comment came from, never on what the page or a client claims.
5
+ import { now } from "../core/fs.js";
6
+ function message(action, note, send, sessionId) {
7
+ if (action === 'save' || !sessionId)
8
+ return 'Annotation saved';
9
+ if (action === 'queue')
10
+ return 'Added to the agent\'s queue';
11
+ if (note.delivery?.confirm)
12
+ return 'Queued — confirm sending it in the panel';
13
+ if (send?.state === 'sent')
14
+ return 'Sent to the agent';
15
+ if (send?.state === 'busy')
16
+ return 'Queued — it goes to the agent when it finishes';
17
+ return 'Sending to the agent';
18
+ }
19
+ export async function inspectStep(capture, notes, key, sessionId, surface) {
20
+ const state = await capture.next(key);
21
+ const { result, ...rest } = state;
22
+ if (!result)
23
+ return { ...rest, note: null, send: null, message: null };
24
+ const action = sessionId ? result.action : 'save';
25
+ const { note, send } = await notes.createAndAct({
26
+ source: { url: result.url, title: result.title, capturedAt: now(), surface, representationHint: result.documentId, ...(state.app ? { app: state.app } : {}) },
27
+ target: result.target,
28
+ body: result.body
29
+ }, action, sessionId, state.trusted);
30
+ const text = message(action, note, send, sessionId);
31
+ await capture.toast(key, text).catch(() => undefined);
32
+ return { ...rest, note, send, message: text };
33
+ }
@@ -0,0 +1,6 @@
1
+ import type { Page } from 'playwright-core';
2
+ /** Evaluates `expression` (which yields a JSON string) and says whether the page could have reached it. */
3
+ export declare function evaluateIsolated(page: Page, expression: string): Promise<{
4
+ reply: unknown;
5
+ trusted: boolean;
6
+ }>;