@xingular/harness 0.1.6 → 1.0.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 (593) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE.md +60 -0
  4. package/README.md +265 -71
  5. package/THIRD_PARTY_LICENSES.md +942 -0
  6. package/dist/cli.js +14842 -9445
  7. package/dist/sdk/accounts/accounts.d.ts +165 -0
  8. package/dist/sdk/accounts/accounts.js +730 -0
  9. package/dist/sdk/accounts/choices.d.ts +23 -0
  10. package/dist/sdk/accounts/choices.js +62 -0
  11. package/dist/sdk/accounts/env.d.ts +78 -0
  12. package/dist/sdk/accounts/env.js +294 -0
  13. package/dist/sdk/accounts/secrets.d.ts +16 -0
  14. package/dist/sdk/accounts/secrets.js +40 -0
  15. package/dist/sdk/accounts/store.d.ts +114 -0
  16. package/dist/sdk/accounts/store.js +255 -0
  17. package/dist/sdk/browser/config.d.ts +16 -0
  18. package/dist/sdk/browser/config.js +22 -0
  19. package/dist/sdk/browser/diagnostics.d.ts +4 -0
  20. package/dist/sdk/browser/diagnostics.js +38 -0
  21. package/dist/sdk/browser/index.d.ts +17 -0
  22. package/dist/sdk/browser/index.js +65 -0
  23. package/dist/sdk/browser/network.d.ts +10 -0
  24. package/dist/sdk/browser/network.js +59 -0
  25. package/dist/sdk/browser/session.d.ts +84 -0
  26. package/dist/sdk/browser/session.js +192 -0
  27. package/dist/sdk/browser/tools.d.ts +8 -0
  28. package/dist/sdk/browser/tools.js +329 -0
  29. package/dist/sdk/capabilities/api.d.ts +22 -0
  30. package/dist/sdk/capabilities/api.js +54 -0
  31. package/dist/sdk/capabilities/runSnapshot.d.ts +18 -0
  32. package/dist/sdk/capabilities/runSnapshot.js +47 -0
  33. package/dist/sdk/capabilities/types.d.ts +29 -0
  34. package/dist/sdk/capabilities/types.js +1 -0
  35. package/dist/sdk/cli.d.ts +2 -0
  36. package/dist/sdk/cli.js +142 -0
  37. package/dist/sdk/core/bus.d.ts +23 -0
  38. package/dist/sdk/core/bus.js +76 -0
  39. package/dist/sdk/core/caller.d.ts +22 -0
  40. package/dist/sdk/core/caller.js +16 -0
  41. package/dist/sdk/core/changes.d.ts +15 -0
  42. package/dist/sdk/core/changes.js +15 -0
  43. package/dist/sdk/core/effort.d.ts +2 -0
  44. package/dist/sdk/core/effort.js +1 -0
  45. package/dist/sdk/core/env.d.ts +18 -0
  46. package/dist/sdk/core/env.js +8 -0
  47. package/dist/sdk/core/errors.d.ts +15 -0
  48. package/dist/sdk/core/errors.js +24 -0
  49. package/dist/sdk/core/frontmatter.d.ts +12 -0
  50. package/dist/sdk/core/frontmatter.js +50 -0
  51. package/dist/sdk/core/fs.d.ts +18 -0
  52. package/dist/sdk/core/fs.js +106 -0
  53. package/dist/sdk/core/index.d.ts +24 -0
  54. package/dist/sdk/core/index.js +187 -0
  55. package/dist/sdk/core/path.d.ts +7 -0
  56. package/dist/sdk/core/path.js +79 -0
  57. package/dist/sdk/core/projects.d.ts +18 -0
  58. package/dist/sdk/core/projects.js +67 -0
  59. package/dist/sdk/core/secrets.d.ts +2 -0
  60. package/dist/sdk/core/secrets.js +5 -0
  61. package/dist/sdk/core/text.d.ts +7 -0
  62. package/dist/sdk/core/text.js +40 -0
  63. package/dist/sdk/doctor/checks.d.ts +5 -0
  64. package/dist/sdk/doctor/checks.js +208 -0
  65. package/dist/sdk/doctor/probe.d.ts +4 -0
  66. package/dist/sdk/doctor/probe.js +92 -0
  67. package/dist/sdk/doctor/service.d.ts +23 -0
  68. package/dist/sdk/doctor/service.js +144 -0
  69. package/dist/sdk/doctor/types.d.ts +141 -0
  70. package/dist/sdk/doctor/types.js +29 -0
  71. package/dist/sdk/harness.d.ts +133 -0
  72. package/dist/sdk/harness.js +299 -0
  73. package/dist/sdk/history/lessonPrompt.d.ts +2 -0
  74. package/dist/sdk/history/lessonPrompt.js +15 -0
  75. package/dist/sdk/history/lessons.d.ts +52 -0
  76. package/dist/sdk/history/lessons.js +173 -0
  77. package/dist/sdk/history/recall.d.ts +68 -0
  78. package/dist/sdk/history/recall.js +199 -0
  79. package/dist/sdk/history/trajectory.d.ts +7 -0
  80. package/dist/sdk/history/trajectory.js +216 -0
  81. package/dist/sdk/hub/catalog.json +123 -0
  82. package/dist/sdk/hub/cli.d.ts +7 -0
  83. package/dist/sdk/hub/cli.js +81 -0
  84. package/dist/sdk/hub/hub.d.ts +74 -0
  85. package/dist/sdk/hub/hub.js +474 -0
  86. package/dist/sdk/hub/schema.d.ts +280 -0
  87. package/dist/sdk/hub/schema.js +78 -0
  88. package/dist/sdk/index.d.ts +63 -0
  89. package/dist/sdk/index.js +48 -0
  90. package/dist/sdk/mcp/connections.d.ts +83 -0
  91. package/dist/sdk/mcp/connections.js +669 -0
  92. package/dist/sdk/mcp/manager.d.ts +26 -0
  93. package/dist/sdk/mcp/manager.js +124 -0
  94. package/dist/sdk/mcp/oauth.d.ts +35 -0
  95. package/dist/sdk/mcp/oauth.js +171 -0
  96. package/dist/sdk/mcp/scopeMigration.d.ts +19 -0
  97. package/dist/sdk/mcp/scopeMigration.js +198 -0
  98. package/dist/sdk/mcp/service.d.ts +160 -0
  99. package/dist/sdk/mcp/service.js +510 -0
  100. package/dist/sdk/mcp/types.d.ts +131 -0
  101. package/dist/sdk/mcp/types.js +49 -0
  102. package/dist/sdk/pi/runtime.d.ts +13 -0
  103. package/dist/sdk/pi/runtime.js +31 -0
  104. package/dist/sdk/plugins/api.d.ts +66 -0
  105. package/dist/sdk/plugins/api.js +155 -0
  106. package/dist/sdk/plugins/attachments.d.ts +46 -0
  107. package/dist/sdk/plugins/attachments.js +111 -0
  108. package/dist/sdk/plugins/builtin.d.ts +6 -0
  109. package/dist/sdk/plugins/builtin.js +68 -0
  110. package/dist/sdk/plugins/catalogue.d.ts +29 -0
  111. package/dist/sdk/plugins/catalogue.js +196 -0
  112. package/dist/sdk/plugins/cli.d.ts +13 -0
  113. package/dist/sdk/plugins/cli.js +187 -0
  114. package/dist/sdk/plugins/discover.d.ts +42 -0
  115. package/dist/sdk/plugins/discover.js +115 -0
  116. package/dist/sdk/plugins/firstParty.d.ts +50 -0
  117. package/dist/sdk/plugins/firstParty.js +113 -0
  118. package/dist/sdk/plugins/hooks.d.ts +94 -0
  119. package/dist/sdk/plugins/hooks.js +162 -0
  120. package/dist/sdk/plugins/identity.d.ts +19 -0
  121. package/dist/sdk/plugins/identity.js +48 -0
  122. package/dist/sdk/plugins/install.d.ts +20 -0
  123. package/dist/sdk/plugins/install.js +110 -0
  124. package/dist/sdk/plugins/inventory.d.ts +22 -0
  125. package/dist/sdk/plugins/inventory.js +110 -0
  126. package/dist/sdk/plugins/manifest.d.ts +13 -0
  127. package/dist/sdk/plugins/manifest.js +56 -0
  128. package/dist/sdk/plugins/plugins.d.ts +104 -0
  129. package/dist/sdk/plugins/plugins.js +164 -0
  130. package/dist/sdk/plugins/runtime.d.ts +9 -0
  131. package/dist/sdk/plugins/runtime.js +95 -0
  132. package/dist/sdk/plugins/trust.d.ts +44 -0
  133. package/dist/sdk/plugins/trust.js +120 -0
  134. package/dist/sdk/plugins/types.d.ts +159 -0
  135. package/dist/sdk/plugins/types.js +7 -0
  136. package/dist/sdk/prompt/compile.d.ts +174 -0
  137. package/dist/sdk/prompt/compile.js +396 -0
  138. package/dist/sdk/refine/cli.d.ts +4 -0
  139. package/dist/sdk/refine/cli.js +186 -0
  140. package/dist/sdk/refine/demo.d.ts +21 -0
  141. package/dist/sdk/refine/demo.js +114 -0
  142. package/dist/sdk/refine/evidence.d.ts +55 -0
  143. package/dist/sdk/refine/evidence.js +330 -0
  144. package/dist/sdk/refine/host.d.ts +51 -0
  145. package/dist/sdk/refine/host.js +76 -0
  146. package/dist/sdk/refine/ledger.d.ts +48 -0
  147. package/dist/sdk/refine/ledger.js +76 -0
  148. package/dist/sdk/refine/library.d.ts +55 -0
  149. package/dist/sdk/refine/library.js +177 -0
  150. package/dist/sdk/refine/pi.d.ts +72 -0
  151. package/dist/sdk/refine/pi.js +248 -0
  152. package/dist/sdk/refine/prompts.d.ts +45 -0
  153. package/dist/sdk/refine/prompts.js +390 -0
  154. package/dist/sdk/refine/recursion.d.ts +113 -0
  155. package/dist/sdk/refine/recursion.js +258 -0
  156. package/dist/sdk/refine/runner.d.ts +81 -0
  157. package/dist/sdk/refine/runner.js +150 -0
  158. package/dist/sdk/refine/selection.d.ts +57 -0
  159. package/dist/sdk/refine/selection.js +251 -0
  160. package/dist/sdk/refine/service.d.ts +84 -0
  161. package/dist/sdk/refine/service.js +165 -0
  162. package/dist/sdk/refine/suite.d.ts +67 -0
  163. package/dist/sdk/refine/suite.js +174 -0
  164. package/dist/sdk/refine/trajectory.d.ts +9 -0
  165. package/dist/sdk/refine/trajectory.js +68 -0
  166. package/dist/sdk/refine/types.d.ts +77 -0
  167. package/dist/sdk/refine/types.js +10 -0
  168. package/dist/sdk/server/methods.d.ts +3 -0
  169. package/dist/sdk/server/methods.js +370 -0
  170. package/dist/sdk/server/remote.d.ts +19 -0
  171. package/dist/sdk/server/remote.js +84 -0
  172. package/dist/sdk/server/rpc.d.ts +20 -0
  173. package/dist/sdk/server/rpc.js +102 -0
  174. package/dist/sdk/server/serve.d.ts +10 -0
  175. package/dist/sdk/server/serve.js +18 -0
  176. package/dist/sdk/sessions/approvals.d.ts +47 -0
  177. package/dist/sdk/sessions/approvals.js +119 -0
  178. package/dist/sdk/sessions/continuation.d.ts +48 -0
  179. package/dist/sdk/sessions/continuation.js +85 -0
  180. package/dist/sdk/sessions/contracts.d.ts +534 -0
  181. package/dist/sdk/sessions/contracts.js +299 -0
  182. package/dist/sdk/sessions/extensionUi.d.ts +19 -0
  183. package/dist/sdk/sessions/extensionUi.js +64 -0
  184. package/dist/sdk/sessions/history.d.ts +16 -0
  185. package/dist/sdk/sessions/history.js +68 -0
  186. package/dist/sdk/sessions/lineageJoin.d.ts +2 -0
  187. package/dist/sdk/sessions/lineageJoin.js +61 -0
  188. package/dist/sdk/sessions/planMode.d.ts +36 -0
  189. package/dist/sdk/sessions/planMode.js +84 -0
  190. package/dist/sdk/sessions/policy.d.ts +43 -0
  191. package/dist/sdk/sessions/policy.js +1 -0
  192. package/dist/sdk/sessions/pricing.d.ts +34 -0
  193. package/dist/sdk/sessions/pricing.js +40 -0
  194. package/dist/sdk/sessions/reasoningSummaries.d.ts +11 -0
  195. package/dist/sdk/sessions/reasoningSummaries.js +61 -0
  196. package/dist/sdk/sessions/registry.d.ts +102 -0
  197. package/dist/sdk/sessions/registry.js +383 -0
  198. package/dist/sdk/sessions/runIndex.d.ts +21 -0
  199. package/dist/sdk/sessions/runIndex.js +65 -0
  200. package/dist/sdk/sessions/runStore.d.ts +66 -0
  201. package/dist/sdk/sessions/runStore.js +335 -0
  202. package/dist/sdk/sessions/runner.d.ts +213 -0
  203. package/dist/sdk/sessions/runner.js +589 -0
  204. package/dist/sdk/sessions/sessions.d.ts +140 -0
  205. package/dist/sdk/sessions/sessions.js +394 -0
  206. package/dist/sdk/sessions/subagentService.d.ts +41 -0
  207. package/dist/sdk/sessions/subagentService.js +247 -0
  208. package/dist/sdk/sessions/subagents.d.ts +50 -0
  209. package/dist/sdk/sessions/subagents.js +68 -0
  210. package/dist/sdk/terminal/accountsMenu.d.ts +46 -0
  211. package/dist/sdk/terminal/accountsMenu.js +392 -0
  212. package/dist/sdk/terminal/cli.d.ts +20 -0
  213. package/dist/sdk/terminal/cli.js +121 -0
  214. package/dist/sdk/terminal/client.d.ts +15 -0
  215. package/dist/sdk/terminal/client.js +126 -0
  216. package/dist/sdk/terminal/control.d.ts +4 -0
  217. package/dist/sdk/terminal/control.js +25 -0
  218. package/dist/sdk/terminal/controller.d.ts +72 -0
  219. package/dist/sdk/terminal/controller.js +351 -0
  220. package/dist/sdk/terminal/hubMenu.d.ts +10 -0
  221. package/dist/sdk/terminal/hubMenu.js +34 -0
  222. package/dist/sdk/terminal/launch.d.ts +5 -0
  223. package/dist/sdk/terminal/launch.js +27 -0
  224. package/dist/sdk/terminal/logs.d.ts +14 -0
  225. package/dist/sdk/terminal/logs.js +59 -0
  226. package/dist/sdk/terminal/piExtension.d.ts +6 -0
  227. package/dist/sdk/terminal/piExtension.js +392 -0
  228. package/dist/sdk/terminal/pluginMenu.d.ts +34 -0
  229. package/dist/sdk/terminal/pluginMenu.js +101 -0
  230. package/dist/sdk/terminal/socket.d.ts +70 -0
  231. package/dist/sdk/terminal/socket.js +256 -0
  232. package/dist/sdk/terminal/standalone.d.ts +18 -0
  233. package/dist/sdk/terminal/standalone.js +146 -0
  234. package/dist/sdk/terminal/terminals.d.ts +176 -0
  235. package/dist/sdk/terminal/terminals.js +767 -0
  236. package/dist/sdk/terminal/trajectory.d.ts +10 -0
  237. package/dist/sdk/terminal/trajectory.js +30 -0
  238. package/dist/sdk/terminal/tui/chrome.d.ts +64 -0
  239. package/dist/sdk/terminal/tui/chrome.js +169 -0
  240. package/dist/sdk/terminal/tui/clipboardFiles.d.ts +11 -0
  241. package/dist/sdk/terminal/tui/clipboardFiles.js +74 -0
  242. package/dist/sdk/terminal/tui/commandPages.d.ts +7 -0
  243. package/dist/sdk/terminal/tui/commandPages.js +63 -0
  244. package/dist/sdk/terminal/tui/commands.d.ts +42 -0
  245. package/dist/sdk/terminal/tui/commands.js +276 -0
  246. package/dist/sdk/terminal/tui/dialogGeometry.d.ts +27 -0
  247. package/dist/sdk/terminal/tui/dialogGeometry.js +50 -0
  248. package/dist/sdk/terminal/tui/dialogs.d.ts +75 -0
  249. package/dist/sdk/terminal/tui/dialogs.js +363 -0
  250. package/dist/sdk/terminal/tui/keybindings.d.ts +11 -0
  251. package/dist/sdk/terminal/tui/keybindings.js +49 -0
  252. package/dist/sdk/terminal/tui/list.d.ts +73 -0
  253. package/dist/sdk/terminal/tui/list.js +196 -0
  254. package/dist/sdk/terminal/tui/modeChoices.d.ts +42 -0
  255. package/dist/sdk/terminal/tui/modeChoices.js +70 -0
  256. package/dist/sdk/terminal/tui/modelPicker.d.ts +55 -0
  257. package/dist/sdk/terminal/tui/modelPicker.js +155 -0
  258. package/dist/sdk/terminal/tui/mouse.d.ts +3 -0
  259. package/dist/sdk/terminal/tui/mouse.js +3 -0
  260. package/dist/sdk/terminal/tui/page.d.ts +14 -0
  261. package/dist/sdk/terminal/tui/page.js +1 -0
  262. package/dist/sdk/terminal/tui/pageOverlay.d.ts +27 -0
  263. package/dist/sdk/terminal/tui/pageOverlay.js +145 -0
  264. package/dist/sdk/terminal/tui/pages/accountsPage.d.ts +54 -0
  265. package/dist/sdk/terminal/tui/pages/accountsPage.js +181 -0
  266. package/dist/sdk/terminal/tui/pages/componentsPage.d.ts +47 -0
  267. package/dist/sdk/terminal/tui/pages/componentsPage.js +119 -0
  268. package/dist/sdk/terminal/tui/pages/doctorPage.d.ts +37 -0
  269. package/dist/sdk/terminal/tui/pages/doctorPage.js +145 -0
  270. package/dist/sdk/terminal/tui/pages/hubPage.d.ts +39 -0
  271. package/dist/sdk/terminal/tui/pages/hubPage.js +128 -0
  272. package/dist/sdk/terminal/tui/pages/logPage.d.ts +38 -0
  273. package/dist/sdk/terminal/tui/pages/logPage.js +189 -0
  274. package/dist/sdk/terminal/tui/pages/permissionsPage.d.ts +44 -0
  275. package/dist/sdk/terminal/tui/pages/permissionsPage.js +124 -0
  276. package/dist/sdk/terminal/tui/pages/pluginsPage.d.ts +57 -0
  277. package/dist/sdk/terminal/tui/pages/pluginsPage.js +243 -0
  278. package/dist/sdk/terminal/tui/pages/settingsPage.d.ts +55 -0
  279. package/dist/sdk/terminal/tui/pages/settingsPage.js +88 -0
  280. package/dist/sdk/terminal/tui/pages/trajectoryPage.d.ts +58 -0
  281. package/dist/sdk/terminal/tui/pages/trajectoryPage.js +377 -0
  282. package/dist/sdk/terminal/tui/piAdapter.d.ts +18 -0
  283. package/dist/sdk/terminal/tui/piAdapter.js +48 -0
  284. package/dist/sdk/terminal/tui/planMode.d.ts +2 -0
  285. package/dist/sdk/terminal/tui/planMode.js +2 -0
  286. package/dist/sdk/terminal/tui/questionPicker.d.ts +20 -0
  287. package/dist/sdk/terminal/tui/questionPicker.js +150 -0
  288. package/dist/sdk/terminal/tui/questions.d.ts +19 -0
  289. package/dist/sdk/terminal/tui/questions.js +42 -0
  290. package/dist/sdk/terminal/tui/runtime.d.ts +114 -0
  291. package/dist/sdk/terminal/tui/runtime.js +441 -0
  292. package/dist/sdk/terminal/tui/search.d.ts +22 -0
  293. package/dist/sdk/terminal/tui/search.js +60 -0
  294. package/dist/sdk/terminal/tui/selection.d.ts +16 -0
  295. package/dist/sdk/terminal/tui/selection.js +16 -0
  296. package/dist/sdk/terminal/tui/sessionUi.d.ts +3 -0
  297. package/dist/sdk/terminal/tui/sessionUi.js +30 -0
  298. package/dist/sdk/terminal/tui/settingsModel.d.ts +12 -0
  299. package/dist/sdk/terminal/tui/settingsModel.js +84 -0
  300. package/dist/sdk/terminal/tui/sideQuestion.d.ts +53 -0
  301. package/dist/sdk/terminal/tui/sideQuestion.js +197 -0
  302. package/dist/sdk/terminal/tui/skillInvocation.d.ts +9 -0
  303. package/dist/sdk/terminal/tui/skillInvocation.js +43 -0
  304. package/dist/sdk/terminal/tui/status.d.ts +18 -0
  305. package/dist/sdk/terminal/tui/status.js +67 -0
  306. package/dist/sdk/terminal/tui/theme.d.ts +45 -0
  307. package/dist/sdk/terminal/tui/theme.js +176 -0
  308. package/dist/sdk/terminal/tui/treeModel.d.ts +79 -0
  309. package/dist/sdk/terminal/tui/treeModel.js +483 -0
  310. package/dist/sdk/terminal/tui/treePicker.d.ts +49 -0
  311. package/dist/sdk/terminal/tui/treePicker.js +251 -0
  312. package/dist/sdk/terminal/tui/ui.d.ts +47 -0
  313. package/dist/sdk/terminal/tui/ui.js +148 -0
  314. package/dist/sdk/terminal/tui/workingStatus.d.ts +9 -0
  315. package/dist/sdk/terminal/tui/workingStatus.js +229 -0
  316. package/dist/sdk/tools/askUser.d.ts +6 -0
  317. package/dist/sdk/tools/askUser.js +74 -0
  318. package/dist/sdk/tools/checklist.d.ts +9 -0
  319. package/dist/sdk/tools/checklist.js +54 -0
  320. package/dist/sdk/tools/definitions.d.ts +30 -0
  321. package/dist/sdk/tools/definitions.js +117 -0
  322. package/dist/sdk/tools/dispatch.d.ts +16 -0
  323. package/dist/sdk/tools/dispatch.js +67 -0
  324. package/dist/sdk/tools/harnessTools.d.ts +6 -0
  325. package/dist/sdk/tools/harnessTools.js +20 -0
  326. package/dist/sdk/tools/sessionName.d.ts +7 -0
  327. package/dist/sdk/tools/sessionName.js +31 -0
  328. package/dist/sdk/tools/spawnSubagent.d.ts +4 -0
  329. package/dist/sdk/tools/spawnSubagent.js +49 -0
  330. package/dist/sdk/tools/types.d.ts +48 -0
  331. package/dist/sdk/tools/types.js +1 -0
  332. package/dist/sdk/variant/components.d.ts +34 -0
  333. package/dist/sdk/variant/components.js +230 -0
  334. package/dist/sdk/variant/materialise.d.ts +66 -0
  335. package/dist/sdk/variant/materialise.js +81 -0
  336. package/dist/sdk/variant/migrate.d.ts +4 -0
  337. package/dist/sdk/variant/migrate.js +41 -0
  338. package/dist/sdk/variant/seed.d.ts +20 -0
  339. package/dist/sdk/variant/seed.js +116 -0
  340. package/dist/sdk/variant/store.d.ts +82 -0
  341. package/dist/sdk/variant/store.js +151 -0
  342. package/dist/sdk/variant/surface.d.ts +56 -0
  343. package/dist/sdk/variant/surface.js +81 -0
  344. package/dist/sdk/variant/trust.d.ts +27 -0
  345. package/dist/sdk/variant/trust.js +64 -0
  346. package/dist/sdk/variant/variant.d.ts +71 -0
  347. package/dist/sdk/variant/variant.js +331 -0
  348. package/dist/sdk/web/markdown.d.ts +10 -0
  349. package/dist/sdk/web/markdown.js +66 -0
  350. package/dist/sdk/web/safeRequest.d.ts +34 -0
  351. package/dist/sdk/web/safeRequest.js +175 -0
  352. package/dist/sdk/web/search.d.ts +53 -0
  353. package/dist/sdk/web/search.js +168 -0
  354. package/dist/sdk/web/text.d.ts +2 -0
  355. package/dist/sdk/web/text.js +7 -0
  356. package/dist/sdk/web/tools.d.ts +22 -0
  357. package/dist/sdk/web/tools.js +195 -0
  358. package/dist/sdk/web/turndown-plugin-gfm.d.ts +4 -0
  359. package/dist/sdk/wiki/notes.d.ts +68 -0
  360. package/dist/sdk/wiki/notes.js +297 -0
  361. package/dist/sdk/world/actions.d.ts +23 -0
  362. package/dist/sdk/world/actions.js +31 -0
  363. package/dist/sdk/world/cli.d.ts +12 -0
  364. package/dist/sdk/world/cli.js +166 -0
  365. package/dist/sdk/world/images.d.ts +20 -0
  366. package/dist/sdk/world/images.js +48 -0
  367. package/dist/sdk/world/untrusted.d.ts +12 -0
  368. package/dist/sdk/world/untrusted.js +20 -0
  369. package/dist/sdk/world/world.d.ts +65 -0
  370. package/dist/sdk/world/world.js +80 -0
  371. package/dist/terminal-opentui.js +13612 -0
  372. package/dist/terminal-pi.js +1170 -62
  373. package/docs/README.md +34 -0
  374. package/docs/architecture/00-overview.md +159 -0
  375. package/docs/architecture/01-extension-points.md +78 -0
  376. package/docs/architecture/02-sessions-and-runs.md +146 -0
  377. package/docs/architecture/03-variants.md +95 -0
  378. package/docs/architecture/04-prompt.md +102 -0
  379. package/docs/architecture/05-refinement.md +107 -0
  380. package/docs/architecture/06-plugins-and-capabilities.md +93 -0
  381. package/docs/architecture/07-tools-and-gate.md +88 -0
  382. package/docs/architecture/08-memory-and-history.md +94 -0
  383. package/docs/architecture/09-accounts-and-connectors.md +90 -0
  384. package/docs/architecture/10-server-protocol.md +84 -0
  385. package/docs/architecture/11-terminal.md +81 -0
  386. package/docs/architecture/12-storage.md +61 -0
  387. package/docs/architecture/13-world-tools.md +23 -0
  388. package/docs/architecture/README.md +114 -0
  389. package/docs/architecture/proposals/PROP-0001-reporting-section.md +106 -0
  390. package/docs/architecture/proposals/PROP-0002-retain-rollout-workspaces.md +104 -0
  391. package/docs/architecture/proposals/PROP-0003-executor-settings.md +81 -0
  392. package/docs/architecture/proposals/PROP-0004-flexible-reporting-guidance.md +71 -0
  393. package/docs/architecture/proposals/PROP-0005-xharness-cli-name.md +78 -0
  394. package/docs/architecture/proposals/PROP-0006-run-checklist.md +68 -0
  395. package/docs/architecture/proposals/PROP-0007-run-continuation.md +80 -0
  396. package/docs/architecture/proposals/PROP-0008-lessons-from-runs.md +80 -0
  397. package/docs/architecture/proposals/PROP-0009-foundation-and-variants.md +78 -0
  398. package/docs/architecture/proposals/PROP-0010-session-names.md +73 -0
  399. package/docs/architecture/proposals/PROP-0011-doctor.md +63 -0
  400. package/docs/architecture/proposals/PROP-0012-release-baseline.md +56 -0
  401. package/docs/architecture/proposals/README.md +76 -0
  402. package/docs/decisions/ADR-0001-owned-terminal.md +87 -0
  403. package/docs/decisions/ADR-0002-native-subagent-lifecycle.md +49 -0
  404. package/docs/decisions/ADR-0003-shared-asynchronous-subagents.md +70 -0
  405. package/docs/decisions/ADR-0004-fullscreen-terminal-layout.md +50 -0
  406. package/docs/decisions/ADR-0005-app-provided-oauth-clients.md +54 -0
  407. package/docs/decisions/ADR-0006-opentui-only-terminal.md +47 -0
  408. package/docs/decisions/ADR-0007-reporting-section.md +33 -0
  409. package/docs/decisions/ADR-0008-flexible-reporting-guidance.md +38 -0
  410. package/docs/decisions/ADR-0009-xharness-command.md +38 -0
  411. package/docs/decisions/ADR-0010-lessons-in-the-wiki.md +31 -0
  412. package/docs/decisions/ADR-0011-foundation-and-variants.md +28 -0
  413. package/docs/decisions/ADR-0012-native-session-names.md +34 -0
  414. package/docs/decisions/ADR-0013-doctor.md +27 -0
  415. package/docs/decisions/ADR-0014-public-release-baseline.md +21 -0
  416. package/docs/doctor.md +74 -0
  417. package/docs/experiments/EXP-0001-terminal-input-latency/plan.md +81 -0
  418. package/docs/experiments/EXP-0001-terminal-input-latency/results.md +46 -0
  419. package/docs/experiments/EXP-0002-opentui-typing-latency/plan.md +70 -0
  420. package/docs/experiments/EXP-0002-opentui-typing-latency/results.md +92 -0
  421. package/docs/features/README.md +20 -0
  422. package/docs/features/automations/README.md +41 -0
  423. package/docs/features/cores/README.md +39 -0
  424. package/docs/features/google-harness2/README.md +52 -0
  425. package/docs/features/integrations/README.md +40 -0
  426. package/docs/features/interfaces/README.md +29 -0
  427. package/docs/features/memory/README.md +28 -0
  428. package/docs/releases/1.0.0.md +40 -0
  429. package/docs/releases/README.md +36 -0
  430. package/docs/study/00-index.md +78 -0
  431. package/docs/study/arms/00-matrix.md +20 -0
  432. package/docs/study/arms/01-system-prompt.md +211 -0
  433. package/docs/study/arms/02-tools.md +79 -0
  434. package/docs/study/arms/03-subagents.md +90 -0
  435. package/docs/study/arms/04-skills.md +75 -0
  436. package/docs/study/arms/05-context-management.md +90 -0
  437. package/docs/study/arms/06-memory.md +90 -0
  438. package/docs/study/arms/07-governance.md +71 -0
  439. package/docs/study/arms/08-execution.md +69 -0
  440. package/docs/study/arms/09-observability.md +60 -0
  441. package/docs/study/arms/10-refinement.md +110 -0
  442. package/docs/study/btw-comparison.md +37 -0
  443. package/docs/study/feature-gap-review.md +42 -0
  444. package/docs/study/insights.md +123 -0
  445. package/docs/study/roadmap.md +72 -0
  446. package/docs/study/sources/claude-code.md +44 -0
  447. package/docs/study/sources/deepseek-harness.md +102 -0
  448. package/docs/study/sources/harness2.md +50 -0
  449. package/docs/study/sources/kimi-cli.md +42 -0
  450. package/docs/study/sources/omp.md +93 -0
  451. package/docs/study/sources/opencode.md +43 -0
  452. package/docs/study/sources/openhands.md +46 -0
  453. package/docs/study/sources/smolagents.md +40 -0
  454. package/docs/study/sources/swe-agent.md +34 -0
  455. package/docs/system-prompt-design.md +196 -0
  456. package/docs/terminal-tui.md +671 -0
  457. package/package.json +48 -12
  458. package/src/accounts/accounts.test.ts +0 -223
  459. package/src/accounts/accounts.ts +0 -522
  460. package/src/accounts/env.test.ts +0 -95
  461. package/src/accounts/env.ts +0 -338
  462. package/src/accounts/secrets.ts +0 -50
  463. package/src/accounts/store.ts +0 -338
  464. package/src/capabilities/api.ts +0 -75
  465. package/src/capabilities/runSnapshot.ts +0 -59
  466. package/src/capabilities/types.ts +0 -34
  467. package/src/cli.ts +0 -114
  468. package/src/core/bus.ts +0 -34
  469. package/src/core/changes.ts +0 -33
  470. package/src/core/env.ts +0 -27
  471. package/src/core/errors.ts +0 -32
  472. package/src/core/frontmatter.ts +0 -47
  473. package/src/core/fs.test.ts +0 -38
  474. package/src/core/fs.ts +0 -92
  475. package/src/core/index.ts +0 -193
  476. package/src/core/path.test.ts +0 -14
  477. package/src/core/path.ts +0 -79
  478. package/src/core/projects.ts +0 -90
  479. package/src/core/text.ts +0 -39
  480. package/src/harness.ts +0 -299
  481. package/src/index.ts +0 -70
  482. package/src/mcp/connections.ts +0 -614
  483. package/src/mcp/manager.ts +0 -130
  484. package/src/mcp/oauth.ts +0 -195
  485. package/src/mcp/scopeMigration.test.ts +0 -79
  486. package/src/mcp/scopeMigration.ts +0 -204
  487. package/src/mcp/service.test.ts +0 -376
  488. package/src/mcp/service.ts +0 -549
  489. package/src/mcp/types.ts +0 -108
  490. package/src/memory/api.test.ts +0 -82
  491. package/src/memory/api.ts +0 -289
  492. package/src/memory/blocks.test.ts +0 -78
  493. package/src/memory/blocks.ts +0 -406
  494. package/src/memory/caller.ts +0 -57
  495. package/src/memory/dispatch.ts +0 -59
  496. package/src/memory/facts.test.ts +0 -41
  497. package/src/memory/facts.ts +0 -248
  498. package/src/memory/graph.ts +0 -103
  499. package/src/memory/health.test.ts +0 -130
  500. package/src/memory/health.ts +0 -74
  501. package/src/memory/maintainer.ts +0 -272
  502. package/src/memory/maintenance.test.ts +0 -176
  503. package/src/memory/maintenance.ts +0 -330
  504. package/src/memory/migrate.ts +0 -79
  505. package/src/memory/operations.ts +0 -138
  506. package/src/memory/proposals.test.ts +0 -168
  507. package/src/memory/proposals.ts +0 -576
  508. package/src/memory/recall.test.ts +0 -104
  509. package/src/memory/recall.ts +0 -271
  510. package/src/memory/references.ts +0 -77
  511. package/src/memory/runContext.ts +0 -93
  512. package/src/memory/secrets.ts +0 -6
  513. package/src/memory/store.test.ts +0 -66
  514. package/src/memory/store.ts +0 -385
  515. package/src/memory/wiki.test.ts +0 -47
  516. package/src/memory/wiki.ts +0 -422
  517. package/src/memory/wikiIndex.test.ts +0 -88
  518. package/src/memory/wikiIndex.ts +0 -116
  519. package/src/plugins/api.ts +0 -181
  520. package/src/plugins/attachments.test.ts +0 -84
  521. package/src/plugins/attachments.ts +0 -141
  522. package/src/plugins/builtin.ts +0 -82
  523. package/src/plugins/catalogue.test.ts +0 -254
  524. package/src/plugins/catalogue.ts +0 -251
  525. package/src/plugins/cli.ts +0 -184
  526. package/src/plugins/discover.ts +0 -151
  527. package/src/plugins/firstParty.test.ts +0 -143
  528. package/src/plugins/firstParty.ts +0 -129
  529. package/src/plugins/hooks.test.ts +0 -250
  530. package/src/plugins/hooks.ts +0 -239
  531. package/src/plugins/identity.ts +0 -37
  532. package/src/plugins/install.test.ts +0 -107
  533. package/src/plugins/install.ts +0 -106
  534. package/src/plugins/inventory.ts +0 -123
  535. package/src/plugins/manifest.ts +0 -76
  536. package/src/plugins/plugins.test.ts +0 -205
  537. package/src/plugins/plugins.ts +0 -189
  538. package/src/plugins/proposal.ts +0 -121
  539. package/src/plugins/runtime.test.ts +0 -40
  540. package/src/plugins/runtime.ts +0 -82
  541. package/src/plugins/trust.ts +0 -142
  542. package/src/plugins/types.ts +0 -149
  543. package/src/prompt/compile.test.ts +0 -261
  544. package/src/prompt/compile.ts +0 -732
  545. package/src/server/methods.ts +0 -314
  546. package/src/server/remote.ts +0 -77
  547. package/src/server/rpc.ts +0 -116
  548. package/src/server/serve.ts +0 -28
  549. package/src/server/server.test.ts +0 -174
  550. package/src/sessions/approvals.ts +0 -118
  551. package/src/sessions/contracts.ts +0 -310
  552. package/src/sessions/conversation.test.ts +0 -150
  553. package/src/sessions/extensionSession.test.ts +0 -74
  554. package/src/sessions/extensionUi.test.ts +0 -65
  555. package/src/sessions/extensionUi.ts +0 -102
  556. package/src/sessions/history.ts +0 -51
  557. package/src/sessions/lineageJoin.ts +0 -75
  558. package/src/sessions/policy.ts +0 -32
  559. package/src/sessions/pricing.ts +0 -60
  560. package/src/sessions/reasoningSummaries.ts +0 -59
  561. package/src/sessions/registry.ts +0 -294
  562. package/src/sessions/runIndex.ts +0 -99
  563. package/src/sessions/runStore.test.ts +0 -152
  564. package/src/sessions/runStore.ts +0 -332
  565. package/src/sessions/runner.ts +0 -569
  566. package/src/sessions/runtimeResources.test.ts +0 -61
  567. package/src/sessions/sessions.test.ts +0 -455
  568. package/src/sessions/sessions.ts +0 -295
  569. package/src/skills/registry.test.ts +0 -104
  570. package/src/skills/registry.ts +0 -407
  571. package/src/terminal/cli.ts +0 -149
  572. package/src/terminal/client.ts +0 -74
  573. package/src/terminal/controller.ts +0 -227
  574. package/src/terminal/launch.test.ts +0 -34
  575. package/src/terminal/launch.ts +0 -88
  576. package/src/terminal/piExtension.ts +0 -210
  577. package/src/terminal/pluginControl.test.ts +0 -146
  578. package/src/terminal/pluginMenu.ts +0 -112
  579. package/src/terminal/socket.test.ts +0 -71
  580. package/src/terminal/socket.ts +0 -221
  581. package/src/terminal/standalone.test.ts +0 -130
  582. package/src/terminal/standalone.ts +0 -144
  583. package/src/terminal/terminal.test.ts +0 -330
  584. package/src/terminal/terminals.ts +0 -443
  585. package/src/tools/askUser.ts +0 -45
  586. package/src/tools/definitions.ts +0 -187
  587. package/src/tools/memoryTools.ts +0 -22
  588. package/src/tools/pluginPropose.ts +0 -41
  589. package/src/tools/types.ts +0 -39
  590. package/src/web/safeRequest.ts +0 -171
  591. package/src/web/text.ts +0 -53
  592. package/src/web/tools.ts +0 -134
  593. package/src/web/web.test.ts +0 -141
@@ -1,455 +0,0 @@
1
- import { mkdtempSync, realpathSync, utimesSync, writeFileSync } from 'node:fs'
2
- import { tmpdir } from 'node:os'
3
- import { join } from 'node:path'
4
- import { afterEach, describe, expect, it } from 'vitest'
5
-
6
- import { memorySecretStore } from '../accounts/secrets'
7
- import { createHarness, type Harness } from '../harness'
8
- import type { RunEvent } from './contracts'
9
- import { grant, type ApprovalFacts } from './approvals'
10
- import { selectHistory } from './history'
11
- import type { ToolPolicy } from './policy'
12
- import type { PiEvent, PiSessionLike, SessionFactory } from './runner'
13
- import { loadRun, readEvents } from './runStore'
14
- import type { SessionRunInput } from './sessions'
15
-
16
- const tick = (ms = 20) => new Promise((resolve) => setTimeout(resolve, ms))
17
- const open: Harness[] = []
18
- afterEach(async () => {
19
- for (const harness of open.splice(0)) await harness.close()
20
- })
21
-
22
- function setup(factory: SessionFactory, policy: ToolPolicy = {}) {
23
- const harness = createHarness({ dataRoot: mkdtempSync(join(tmpdir(), 'harness-data-')), stateDir: mkdtempSync(join(tmpdir(), 'harness-state-')), secrets: memorySecretStore(), sessionFactory: factory, policy })
24
- open.push(harness)
25
- harness.accounts.create('Main', 'apiKey')
26
- const events: RunEvent[] = []
27
- harness.runs.listen((event) => void events.push(event))
28
- const sessionDir = join(harness.env.root, 'sessions', 'agent-1')
29
- const run = (id: string, extra: Partial<SessionRunInput> = {}) =>
30
- harness.sessions.run({ run: { id, sessionId: 'agent-1' }, cwd: harness.env.root, sessionDir, text: 'hello', caller: { agentId: 'agent-1', agentName: 'Helper', actor: 'Chat' }, tools: { allow: ['read', 'bash'], gated: ['bash'] }, ...extra })
31
- return { harness, events, run, sessionDir }
32
- }
33
-
34
- function fakeSession(script: (emit: (event: PiEvent) => void, gateCheck: () => Promise<string | null>) => Promise<void>, gate: { check: (name: string, input: unknown) => Promise<string | null> }): PiSessionLike & { aborted: boolean } {
35
- let listener: (event: PiEvent) => void = () => undefined
36
- const session = {
37
- sessionId: 'native-1',
38
- aborted: false,
39
- subscribe: (fn: (event: PiEvent) => void) => {
40
- listener = fn
41
- return () => undefined
42
- },
43
- prompt: () => script((event) => listener(event), () => gate.check('bash', { command: 'ls' })),
44
- sendCustomMessage: async () => undefined,
45
- abort: async () => {
46
- session.aborted = true
47
- },
48
- dispose: () => undefined
49
- }
50
- return session
51
- }
52
-
53
- describe('session turns on Pi', () => {
54
- it('rejects image-required turns before any prompt reaches a text-only model', async () => {
55
- let requested = false
56
- let disposed = false
57
- const { run, harness } = setup(async ({ gate }) => ({
58
- ...fakeSession(async () => { requested = true }, gate),
59
- model: { input: ['text'] },
60
- dispose: () => { disposed = true }
61
- }))
62
- expect(await run('image-unsupported', { requiresImageInput: true })).toEqual({ runId: 'image-unsupported', nativeSessionId: null })
63
- expect(requested).toBe(false)
64
- expect(disposed).toBe(true)
65
- expect(loadRun(harness.env.root, 'image-unsupported')).toMatchObject({ status: 'failed', terminalReason: expect.stringContaining('supports images') })
66
- })
67
-
68
- it('passes image pixels using Pi image content fields with the existing stable prefix', async () => {
69
- let observed: unknown
70
- const { run } = setup(async ({ gate }) => ({
71
- ...fakeSession(async () => undefined, gate),
72
- model: { input: ['text', 'image'] },
73
- prompt: async (_text, options) => { observed = options }
74
- }))
75
- await run('image-supported', { requiresImageInput: true, images: [{ mediaType: 'image/png', data: 'cGl4ZWxz' }] })
76
- await tick()
77
- expect(observed).toEqual({ images: [{ type: 'image', mimeType: 'image/png', data: 'cGl4ZWxz' }] })
78
- })
79
-
80
- it('saves the context update in native history before the request, without another model turn', async () => {
81
- const delivered: unknown[] = []
82
- const { run, events, harness } = setup(async ({ gate, params }) => ({
83
- ...fakeSession(async () => undefined, gate),
84
- sendCustomMessage: async (message, options) => void delivered.push({ type: message.customType, display: message.display, options, snapshot: Object.keys(message.details as object) }),
85
- prompt: async (text) => void delivered.push({ text, system: params.system.startsWith('# Xingular Harness context\n') })
86
- }))
87
- const started = await run('context-run', { preamble: '[Group context]', text: 'The user request' })
88
- await tick()
89
- expect(started).toEqual({ runId: 'context-run', nativeSessionId: 'native-1' })
90
- expect(delivered).toEqual([
91
- { type: 'xingular-context', display: false, options: { triggerTurn: false }, snapshot: ['xingularContext'] },
92
- { text: '[Group context]\n\nThe user request', system: true }
93
- ])
94
- expect(events.filter((event) => event.type === 'usage.updated')).toHaveLength(0)
95
- expect(events.at(-1)?.type).toBe('completed')
96
- expect(loadRun(harness.env.root, 'context-run')).toMatchObject({ status: 'completed', nativeSessionId: 'native-1', mode: 'chat', harness: 'pi', goal: 'The user request', agentId: 'agent-1' })
97
- expect(readEvents(harness.env.root, 'context-run').map((event) => event.sequence)).toEqual(events.map((_, index) => index))
98
- })
99
-
100
- it('streams Pi events as contract events and completes', async () => {
101
- const { events, run } = setup(async ({ gate }) =>
102
- fakeSession(async (emit) => {
103
- emit({ type: 'message_start', message: { role: 'assistant' } })
104
- emit({ type: 'message_update', message: { role: 'assistant' }, assistantMessageEvent: { type: 'text_delta', delta: 'Hi' } })
105
- emit({ type: 'message_end', message: { role: 'assistant', content: [{ type: 'text', text: 'Hi' }], usage: { input: 3, output: 1 } } })
106
- }, gate)
107
- )
108
- await run('run-1')
109
- await tick()
110
- expect(events.map((event) => event.type)).toEqual(['started', 'status.changed', 'context.snapshot', 'message.delta', 'message.completed', 'usage.updated', 'completed'])
111
- expect(events.map((event) => event.sequence)).toEqual([0, 1, 2, 3, 4, 5, 6])
112
- })
113
-
114
- it('reports cache, reasoning, cost and context size across the requests of a reply', async () => {
115
- const { events, run } = setup(async ({ gate }) => ({
116
- ...fakeSession(async (emit) => {
117
- emit({ type: 'message_end', message: { role: 'assistant', content: [], model: 'sonnet', provider: 'anthropic', usage: { input: 10, output: 4, cacheRead: 100, cacheWrite: 20, reasoning: 1, cost: { total: 0.01 } } } })
118
- emit({ type: 'message_end', message: { role: 'assistant', content: [{ type: 'text', text: 'Done' }], model: 'sonnet', provider: 'anthropic', usage: { input: 5, output: 6, cacheRead: 130, cacheWrite: 0, reasoning: 2, cost: { total: 0.02 } } } })
119
- emit({ type: 'message_end', message: { role: 'assistant', content: [], usage: { input: 1, output: 1 } } })
120
- }, gate),
121
- model: { input: ['text'], contextWindow: 200_000 }
122
- }))
123
- await run('run-1')
124
- await tick()
125
- const usage = events.filter((event) => event.type === 'usage.updated').map((event) => event.payload as Record<string, unknown>)
126
- expect(usage[1]).toMatchObject({ inputTokens: 15, outputTokens: 10, cacheReadTokens: 230, cacheWriteTokens: 20, reasoningTokens: 3, requests: 2, contextTokens: 135, contextWindow: 200_000, model: 'sonnet', provider: 'anthropic' })
127
- expect(usage[1].costUsd).toBeCloseTo(0.03)
128
- // A request without the optional figures leaves the running totals intact.
129
- expect(usage[2]).toMatchObject({ inputTokens: 16, cacheReadTokens: 230, reasoningTokens: 3, requests: 3, contextTokens: 1 })
130
- expect(usage[2]).not.toHaveProperty('model')
131
- })
132
-
133
- it('reports each model request on its own, with its model, cost and timing', async () => {
134
- const { events, run } = setup(async ({ gate }) =>
135
- fakeSession(async (emit) => {
136
- emit({ type: 'turn_start' })
137
- emit({ type: 'message_update', message: { role: 'assistant' }, assistantMessageEvent: { type: 'thinking_delta' } })
138
- emit({ type: 'message_end', message: { role: 'assistant', content: [], model: 'sonnet', provider: 'anthropic', responseId: 'resp-1', stopReason: 'toolUse', usage: { input: 10, output: 4, cacheRead: 100, cacheWrite: 20, reasoning: 1, cost: { total: 0.01 } } } })
139
- emit({ type: 'tool_execution_start', toolCallId: 't1', toolName: 'read', args: { path: 'a.md' } })
140
- emit({ type: 'tool_execution_end', toolCallId: 't1', toolName: 'read', isError: false, result: { content: [{ type: 'text', text: 'x'.repeat(1200) }, { type: 'image' }] } })
141
- emit({ type: 'turn_start' })
142
- emit({ type: 'message_end', message: { role: 'assistant', content: [{ type: 'text', text: 'Done' }], model: 'haiku', provider: 'anthropic', usage: { input: 5, output: 6 } } })
143
- }, gate)
144
- )
145
- await run('run-1')
146
- await tick()
147
- const requests = events.filter((event) => event.type === 'usage.updated').map((event) => (event.payload as { request: Record<string, unknown> }).request)
148
- expect(requests[0]).toMatchObject({ index: 1, responseId: 'resp-1', model: 'sonnet', provider: 'anthropic', inputTokens: 10, cacheReadTokens: 100, cacheWriteTokens: 20, outputTokens: 4, reasoningTokens: 1, costUsd: 0.01, costSource: 'harness', stopReason: 'toolUse' })
149
- expect(requests[0]).not.toHaveProperty('reasoningSummary')
150
- expect(requests[0].durationMs).toBeGreaterThanOrEqual(0)
151
- expect(requests[0].ttftMs).toBeGreaterThanOrEqual(0)
152
- // The second request reported no cost and produced no streamed output.
153
- expect(requests[1]).toMatchObject({ index: 2, model: 'haiku', inputTokens: 5, outputTokens: 6, cacheReadTokens: 0 })
154
- for (const key of ['costUsd', 'costSource', 'ttftMs', 'reasoningTokens', 'responseId']) expect(requests[1]).not.toHaveProperty(key)
155
- const tool = events.find((event) => event.type === 'tool.completed')!.payload as Record<string, unknown>
156
- // Stored evidence is redacted, which rewrites JSON carried as text in its compact form.
157
- expect(events.find((event) => event.type === 'tool.started')?.payload).toMatchObject({ toolCallId: 't1', input: '{"path":"a.md"}' })
158
- expect(tool).toMatchObject({ toolCallId: 't1', ok: true, resultChars: 1200 })
159
- expect(tool.output).toContain('x'.repeat(1200))
160
- })
161
-
162
- it('persists only an explicitly captured provider summary without inventing usage figures', async () => {
163
- const { events, run } = setup(async ({ gate }) =>
164
- fakeSession(async (emit) => {
165
- emit({ type: 'turn_start' })
166
- emit({ type: 'message_end', message: { role: 'assistant', api: 'openai-codex-responses', content: [
167
- { type: 'thinking', thinkingSignature: JSON.stringify({ type: 'reasoning', summary: [{ type: 'summary_text', text: 'I will check the saved state before changing the file.' }], content: [{ text: 'private reasoning' }], encrypted_content: 'private replay payload' }) },
168
- { type: 'text', text: 'Done.' }
169
- ], model: 'gpt-6', provider: 'openai', responseId: 'response-1' } })
170
- }, gate)
171
- )
172
- await run('summary-run')
173
- await tick()
174
- const usage = events.find((event) => event.type === 'usage.updated')!.payload as Record<string, unknown>
175
- expect(usage).toMatchObject({ requests: 1, model: 'gpt-6', provider: 'openai' })
176
- expect(usage).not.toHaveProperty('inputTokens')
177
- expect(usage.request).toMatchObject({ index: 1, responseId: 'response-1', reasoningSummary: 'I will check the saved state before changing the file.' })
178
- expect(usage.request).not.toHaveProperty('outputTokens')
179
- expect(JSON.stringify(events)).not.toContain('private reasoning')
180
- expect(JSON.stringify(events)).not.toContain('private replay payload')
181
- })
182
-
183
- it('records the compiled prompt and schemas for the tools actually declared to the model', async () => {
184
- const { events, run } = setup(async ({ gate }) => ({
185
- ...fakeSession(async () => undefined, gate),
186
- sendCustomMessage: async () => undefined,
187
- getActiveToolNames: () => ['read'],
188
- getAllTools: () => [
189
- { name: 'read', description: 'Read a file', parameters: { type: 'object', properties: { path: { type: 'string' } } } },
190
- { name: 'write', description: 'Write a file', parameters: { type: 'object' } }
191
- ]
192
- }))
193
- await run('run-1')
194
- await tick()
195
- const snapshot = events.find((event) => event.type === 'context.snapshot')!.payload as { systemPrompt: string; contextUpdate: string; tools: unknown[] }
196
- expect(snapshot.systemPrompt).toContain('## Context updates')
197
- expect(snapshot.contextUpdate).toContain('<block label="persona"')
198
- expect(snapshot.tools).toEqual([{ name: 'read', description: 'Read a file', parameters: { type: 'object', properties: { path: { type: 'string' } } } }])
199
- })
200
-
201
- it('caps saved tool inputs and outputs at 24,000 characters with truncation flags', async () => {
202
- const large = 'x'.repeat(25_000)
203
- const { events, run } = setup(async ({ gate }) =>
204
- fakeSession(async (emit) => {
205
- emit({ type: 'tool_execution_start', toolCallId: 't1', toolName: 'computer_type_text', args: { app: 'Editor', text: large } })
206
- emit({ type: 'tool_execution_end', toolCallId: 't1', toolName: 'computer_type_text', isError: false, result: large })
207
- }, gate)
208
- )
209
- await run('run-1')
210
- await tick()
211
- const started = events.find((event) => event.type === 'tool.started')!.payload as { input: string; inputTruncated: boolean }
212
- const completed = events.find((event) => event.type === 'tool.completed')!.payload as { output: string; outputTruncated: boolean }
213
- expect([started.inputTruncated, completed.outputTruncated]).toEqual([true, true])
214
- expect(started.input.length).toBeLessThanOrEqual(24_000)
215
- expect(completed.output.length).toBeLessThanOrEqual(24_000)
216
- })
217
-
218
- it('records generic tool attachment references without storing their content bytes', async () => {
219
- const { events, run } = setup(async ({ gate }) => fakeSession(async (emit) => {
220
- emit({ type: 'tool_execution_end', toolCallId: 'image-1', toolName: 'capture', isError: false, result: {
221
- content: [{ type: 'text', text: 'Captured screen.' }, { type: 'image', data: 'model-image-bytes' }],
222
- details: { attachments: [{ id: 'attachment-1', name: 'Screen capture', mimeType: 'image/jpeg' }] }
223
- } })
224
- emit({ type: 'tool_execution_end', toolCallId: 'image-2', toolName: 'capture', isError: false, result: {
225
- content: [{ type: 'text', text: 'Invalid attachment metadata was ignored.' }],
226
- details: { attachments: [{ id: 'attachment-2', name: 'Capture', mimeType: 'image/jpeg', data: 'must-not-persist' }] }
227
- } })
228
- }, gate))
229
-
230
- await run('attachments-run')
231
- await tick()
232
- const completed = events.filter((event) => event.type === 'tool.completed').map((event) => event.payload as Record<string, unknown>)
233
- expect(completed[0]?.attachments).toEqual([{ id: 'attachment-1', name: 'Screen capture', mimeType: 'image/jpeg' }])
234
- expect(completed[1]).not.toHaveProperty('attachments')
235
- expect(JSON.stringify(events)).not.toContain('model-image-bytes')
236
- expect(JSON.stringify(events)).not.toContain('must-not-persist')
237
- })
238
-
239
- it('records bounded retry and compaction events without exposing thinking text', async () => {
240
- const { events, run } = setup(async ({ gate }) =>
241
- fakeSession(async (emit) => {
242
- emit({ type: 'auto_retry_start', attempt: 1, maxAttempts: 3, delayMs: 250, errorMessage: 'Temporary failure' } as unknown as PiEvent)
243
- emit({ type: 'auto_retry_end', attempt: 1, success: true } as unknown as PiEvent)
244
- emit({ type: 'compaction_start', reason: 'threshold' } as unknown as PiEvent)
245
- emit({ type: 'compaction_end', reason: 'threshold', aborted: false, willRetry: true, result: { summary: 'kept the latest messages' } } as unknown as PiEvent)
246
- }, gate)
247
- )
248
- await run('run-1')
249
- await tick()
250
- expect(events.filter((event) => event.type.startsWith('retry.') || event.type.startsWith('compaction.')).map((event) => event.type)).toEqual(['retry.started', 'retry.ended', 'compaction.started', 'compaction.ended'])
251
- expect(events.find((event) => event.type === 'compaction.ended')?.payload).toMatchObject({ aborted: false, willRetry: true, summary: '{"summary":"kept the latest messages"}' })
252
- })
253
-
254
- it('leaves out the cost and reasoning figures nobody reported', async () => {
255
- const { events, run } = setup(async ({ gate }) => fakeSession(async (emit) => emit({ type: 'message_end', message: { role: 'assistant', content: [], usage: { input: 1, output: 1 } } }), gate))
256
- await run('run-1')
257
- await tick()
258
- const usage = events.find((event) => event.type === 'usage.updated')!.payload as Record<string, unknown>
259
- for (const key of ['costUsd', 'reasoningTokens', 'contextWindow']) expect(usage).not.toHaveProperty(key)
260
- expect(usage.request).not.toHaveProperty('durationMs')
261
- })
262
-
263
- it('pauses gated tools for approval and blocks on deny', async () => {
264
- let blockReason: string | null | undefined
265
- const { events, run, harness } = setup(async ({ gate }) => fakeSession(async (_emit, check) => void (blockReason = await check()), gate))
266
- await run('run-2', { text: 'list files' })
267
- await tick()
268
- const approval = events.find((event) => event.type === 'approval.requested')!.payload as { approvalId: string }
269
- expect(approval).toMatchObject({ target: 'bash', action: 'ls', rememberable: true })
270
- expect(loadRun(harness.env.root, 'run-2').status).toBe('waiting_for_approval')
271
- harness.runs.resolveApproval('run-2', approval.approvalId, 'denied')
272
- await tick()
273
- expect(blockReason).toMatch(/denied/)
274
- expect(events.some((event) => event.type === 'approval.resolved')).toBe(true)
275
- })
276
-
277
- it("asks before a tool an extension brought, and relays the extension's question and notice as run events", async () => {
278
- let blockReason: string | null | undefined
279
- let answered: unknown
280
- const { events, run, harness } = setup(async ({ gate, extensions }) => ({
281
- ...fakeSession(async () => undefined, gate),
282
- prompt: async () => {
283
- // What a loaded extension does: registers a tool nobody switched on by name, shows a line, and asks.
284
- gate.ask?.(['kit_scan'])
285
- extensions!.notice({ kind: 'status', key: 'kit', text: 'Scanning' })
286
- answered = await extensions!.ask({ question: 'Scan the whole folder?', options: [{ label: 'Yes' }, { label: 'No' }], multiSelect: false, allowText: false })
287
- blockReason = await gate.check('kit_scan', { path: '.' })
288
- }
289
- }))
290
- await run('run-ext', { text: 'scan' })
291
- await tick()
292
- expect(events.find((event) => event.type === 'extension.notice')?.payload).toEqual({ kind: 'status', key: 'kit', text: 'Scanning' })
293
- const question = events.find((event) => event.type === 'question.requested')!.payload as { questionId: string }
294
- expect(question).toMatchObject({ question: 'Scan the whole folder?' })
295
- harness.runs.answerQuestion('run-ext', question.questionId, { selected: ['Yes'] })
296
- await tick()
297
- expect(answered).toEqual({ selected: ['Yes'] })
298
- const approval = events.find((event) => event.type === 'approval.requested')!.payload as { approvalId: string }
299
- expect(approval).toMatchObject({ target: 'kit_scan' })
300
- harness.runs.resolveApproval('run-ext', approval.approvalId, 'approved')
301
- await tick()
302
- expect(blockReason).toBeNull()
303
- })
304
-
305
- it('gives an unattended run no extension interface', async () => {
306
- let offered: unknown = 'unset'
307
- const { run } = setup(async ({ gate, extensions }) => {
308
- offered = extensions
309
- return fakeSession(async () => undefined, gate)
310
- })
311
- await run('run-auto-ext', { text: 'tidy', automatic: true })
312
- await tick()
313
- expect(offered).toBeUndefined()
314
- })
315
-
316
- it('skips the prompt for a tool the user already allowed in this session', async () => {
317
- let blockReason: string | null | undefined
318
- const { events, run, harness } = setup(async ({ gate }) => fakeSession(async (_emit, check) => void (blockReason = await check()), gate))
319
- harness.approvals.remember('agent-1', 'bash')
320
- await run('run-auto', { text: 'list files' })
321
- await tick()
322
- expect(blockReason).toBeNull()
323
- expect(events.find((event) => event.type === 'approval.requested')?.payload).toMatchObject({ target: 'bash', rememberable: true, automatic: 'remembered' })
324
- expect(events.find((event) => event.type === 'approval.resolved')?.payload).toMatchObject({ decision: 'approved', automatic: 'remembered' })
325
- expect(events.some((event) => event.type === 'status.changed' && (event.payload as { status: string }).status === 'waiting_for_approval')).toBe(false)
326
- expect(events.at(-1)?.type).toBe('completed')
327
- })
328
-
329
- it('still asks when the application cannot say what is allowed', async () => {
330
- const { events, run } = setup(async ({ gate }) => fakeSession(async (_emit, check) => void (await check()), gate), { approval: async () => { throw new Error('down') } })
331
- await run('run-ask', { text: 'list files' })
332
- await tick()
333
- expect(events.some((event) => event.type === 'status.changed' && (event.payload as { status: string }).status === 'waiting_for_approval')).toBe(true)
334
- expect(events.some((event) => event.type === 'approval.resolved')).toBe(false)
335
- })
336
-
337
- it('lets the application refuse a call that is outside an unattended run\'s scope, and switch a tool off', async () => {
338
- const reasons: (string | null)[] = []
339
- let authorized = 0
340
- const policy: ToolPolicy = {
341
- scope: async ({ tool }) => ({ automatic: true, allowed: tool !== 'bash', reason: 'Scheduled workers may use scoped file tools only.' }),
342
- authorize: async () => {
343
- authorized += 1
344
- if (authorized > 1) throw new Error('Tool unavailable in the app settings.')
345
- }
346
- }
347
- const { run } = setup(async ({ gate }) => fakeSession(async () => {
348
- reasons.push(await gate.check('bash', {}), await gate.check('read', {}), await gate.check('read', {}))
349
- }, gate), policy)
350
- await run('run-scope')
351
- await tick()
352
- expect(reasons).toEqual(['Scheduled workers may use scoped file tools only.', null, 'Tool unavailable in the app settings.'])
353
- })
354
-
355
- it('aborts the Pi session on interrupt', async () => {
356
- let session: (PiSessionLike & { aborted: boolean }) | null = null
357
- const { events, run, harness } = setup(async ({ gate }) => (session = fakeSession(() => new Promise((resolve) => setTimeout(resolve, 50)), gate)))
358
- await run('run-3')
359
- await tick()
360
- await harness.runs.interrupt('run-3')
361
- await tick(80)
362
- expect(session!.aborted).toBe(true)
363
- expect(events.at(-1)).toMatchObject({ type: 'status.changed', payload: { status: 'interrupted' } })
364
- expect(loadRun(harness.env.root, 'run-3').status).toBe('interrupted')
365
- })
366
-
367
- it('gives every session the memory, recall and Wiki tools, a question tool, and the application\'s tools in order', async () => {
368
- let received: Parameters<SessionFactory>[0] | null = null
369
- const { run, harness } = setup(async (input) => {
370
- received = input
371
- return fakeSession(async () => undefined, input.gate)
372
- })
373
- harness.sessions.provide(() => [{ name: 'react', label: 'React', description: 'React', parameters: (Type) => Type.Object({}), execute: async () => 'ok' }])
374
- await run('run-5', { tools: { allow: ['react'] } })
375
- await tick()
376
- expect(received!.allow.slice(0, 3)).toEqual(['react', 'ask_user', 'plugin_propose'])
377
- expect(received!.allow).toEqual(expect.arrayContaining(['memory_append', 'history_search', 'wiki_search', 'wiki_read', 'wiki_propose']))
378
- expect(received!.tools.map((tool) => tool.name)).toEqual(['memory_append', 'memory_replace', 'memory_rethink', 'history_search', 'history_read', 'wiki_search', 'wiki_read', 'wiki_list', 'skill_load', 'memory_facts', 'wiki_propose', 'ask_user', 'plugin_propose', 'react'])
379
- expect(await received!.tools[0].execute({ block: 'notes', text: 'Remember this.' })).toContain('Updated the notes block')
380
- expect(received!.params.system).toContain('# Xingular Harness context')
381
- })
382
-
383
- it('refuses missing continuation history before creating a run or opening a session', async () => {
384
- let called = false
385
- const { run, events, harness } = setup(async ({ gate }) => {
386
- called = true
387
- return fakeSession(async () => undefined, gate)
388
- })
389
- await expect(run('resume-run', { resume: { sessionFile: '/tmp/missing-history/saved.jsonl', nativeSessionId: 'native-1' } })).rejects.toMatchObject({ code: 'native_resume_unavailable' })
390
- expect(called).toBe(false)
391
- expect(events).toEqual([])
392
- expect(() => loadRun(harness.env.root, 'resume-run')).toThrow()
393
- })
394
-
395
- it('fails the run when the session cannot start', async () => {
396
- const { events, run, harness } = setup(async () => {
397
- throw new Error('No provider')
398
- })
399
- expect(await run('run-4')).toEqual({ runId: 'run-4', nativeSessionId: null })
400
- expect(events.at(-1)).toMatchObject({ type: 'failed', payload: { message: 'No provider' } })
401
- expect(loadRun(harness.env.root, 'run-4')).toMatchObject({ status: 'failed', terminalReason: 'No provider' })
402
- })
403
-
404
- it('needs an account before it records anything', async () => {
405
- const harness = createHarness({ dataRoot: mkdtempSync(join(tmpdir(), 'harness-data-')), stateDir: mkdtempSync(join(tmpdir(), 'harness-state-')), secrets: memorySecretStore(), sessionFactory: async () => { throw new Error('unreachable') } })
406
- open.push(harness)
407
- await expect(harness.sessions.run({ run: { id: 'r', sessionId: 's' }, cwd: '/tmp', sessionDir: join(harness.env.root, 's'), text: 'x', caller: { agentId: 'a' }, tools: { allow: [] } })).rejects.toMatchObject({ code: 'account_required' })
408
- expect(() => loadRun(harness.env.root, 'r')).toThrow()
409
- })
410
- })
411
-
412
- describe('history selection', () => {
413
- it('opens the newest history, or exactly the recorded one for a continuation', () => {
414
- const dir = mkdtempSync(join(tmpdir(), 'context-history-'))
415
- expect(selectHistory({ sessionDir: dir })).toBeNull()
416
- expect(selectHistory({ sessionDir: join(dir, 'missing') })).toBeNull()
417
- const first = join(dir, 'first.jsonl')
418
- const newer = join(dir, 'newer.jsonl')
419
- writeFileSync(first, `${JSON.stringify({ type: 'session', id: 'first' })}\n`)
420
- writeFileSync(newer, `${JSON.stringify({ type: 'session', id: 'newer' })}\n`)
421
- writeFileSync(join(dir, 'notes.txt'), '')
422
- utimesSync(newer, new Date(Date.now() + 1000), new Date(Date.now() + 1000))
423
- utimesSync(join(dir, 'notes.txt'), new Date(Date.now() + 5000), new Date(Date.now() + 5000))
424
- expect(selectHistory({ sessionDir: dir })).toBe(realpathSync(newer))
425
- expect(selectHistory({ sessionDir: dir, resume: { sessionFile: first, nativeSessionId: 'first' } })).toBe(realpathSync(first))
426
- expect(() => selectHistory({ sessionDir: dir, resume: { sessionFile: first, nativeSessionId: 'newer' } })).toThrow(/saved conversation/)
427
- expect(() => selectHistory({ sessionDir: dir, resume: { sessionFile: join(dir, 'missing'), nativeSessionId: 'first' } })).toThrow(/saved conversation/)
428
- expect(() => selectHistory({ sessionDir: dir, resume: { sessionFile: first } })).toThrow(/saved conversation/)
429
- const outside = join(mkdtempSync(join(tmpdir(), 'other-history-')), 'outside.jsonl')
430
- writeFileSync(outside, `${JSON.stringify({ type: 'session', id: 'outside' })}\n`)
431
- expect(() => selectHistory({ sessionDir: dir, resume: { sessionFile: outside, nativeSessionId: 'outside' } })).toThrow(/saved conversation/)
432
- })
433
-
434
- it('never opens a newest file that is not a native history', () => {
435
- const dir = mkdtempSync(join(tmpdir(), 'context-history-'))
436
- writeFileSync(join(dir, 'broken.jsonl'), 'not json\n')
437
- expect(() => selectHistory({ sessionDir: dir })).toThrow(/saved conversation/)
438
- })
439
- })
440
-
441
- describe('standing approvals', () => {
442
- const policy = { auto: { pi: true }, remembered: { 'pane-1': ['shell'] } }
443
- const facts = (unattended: boolean, readOnly: boolean): ApprovalFacts => ({ unattended, readOnly })
444
-
445
- it('the switch reaches attended sessions but not unattended or read-only ones', () => {
446
- expect(grant(policy, 'pi', 'pane-1', 'edit files', facts(false, false))).toBe('always')
447
- expect(grant(policy, 'pi', 'pane-1', 'edit files', facts(true, false))).toBeNull()
448
- expect(grant(policy, 'pi', 'pane-1', 'edit files', facts(false, true))).toBeNull()
449
- expect(grant(policy, 'pi', 'pane-1', 'shell', facts(false, true))).toBe('remembered')
450
- expect(grant(policy, 'pi', 'pane-1', 'shell', facts(true, false))).toBeNull()
451
- expect(grant(policy, 'chat', 'pane-1', 'edit files', facts(false, false))).toBeNull()
452
- expect(grant(policy, 'pi', 'pane-2', 'shell', {})).toBe('always')
453
- expect(grant({ auto: {}, remembered: policy.remembered }, 'pi', 'pane-2', 'shell', {})).toBeNull()
454
- })
455
- })