@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,522 +0,0 @@
1
- // The accounts service: everything that needs the secret store, the Pi runtime, or a notice.
2
- // Credentials go to the embedding application's secret store; nothing here writes one to the
3
- // harness's own files, except a terminal session's `auth.json` while that terminal runs.
4
- import { chmodSync, rmSync } from 'node:fs'
5
- import { randomUUID } from 'node:crypto'
6
- import { join } from 'node:path'
7
-
8
- import type { ModelRuntime } from '@earendil-works/pi-coding-agent'
9
-
10
- import { log, type Bus } from '../core/bus'
11
- import { HarnessError, invalid } from '../core/errors'
12
- import { atomicWrite, now, readJson, safeId } from '../core/fs'
13
- import type { RunEvent, RunRecord } from '../sessions/contracts'
14
- import * as env from './env'
15
- import { secretKey, type SecretStore } from './secrets'
16
- import * as store from './store'
17
- import type { Account, AccountsOverview, Issue, Limit } from './store'
18
-
19
- export type IssueCategory = 'auth_expired' | 'rate_limited' | 'quota' | 'network' | 'unknown'
20
-
21
- /** Best-effort classification of a provider error message. */
22
- export function classifyMessage(message: string): IssueCategory | null {
23
- const text = message.toLowerCase()
24
- if (/\b401\b|unauthori[sz]ed|invalid (api )?key|invalid x-api-key|authentication|not logged in|log ?in again|expired token|token (has )?expired|refresh token|oauth/.test(text)) return 'auth_expired'
25
- if (/usage limit|quota|insufficient_quota|billing|credit balance|out of credits/.test(text)) return 'quota'
26
- if (/\b429\b|rate.?limit|too many requests/.test(text)) return 'rate_limited'
27
- if (/econnrefused|enotfound|etimedout|network|socket hang up|fetch failed/.test(text)) return 'network'
28
- return null
29
- }
30
-
31
- export interface ModelOption {
32
- id: string
33
- name: string
34
- provider: string
35
- supportsImages: boolean
36
- reasoning: boolean
37
- }
38
-
39
- type Credential = Record<string, unknown> & { type: 'api_key' | 'oauth' }
40
-
41
- function validProvider(provider: string): string {
42
- if (provider && provider.length <= 64 && /^[A-Za-z0-9._-]+$/.test(provider)) return provider
43
- throw invalid('Invalid provider name.')
44
- }
45
-
46
- interface PendingLogin {
47
- accountId: string
48
- cancel: () => void
49
- prompts: Map<string, { resolve: (value: string) => void; reject: (error: Error) => void }>
50
- }
51
-
52
- interface Probe {
53
- signedIn: boolean | null
54
- authMethod: string | null
55
- plan: string | null
56
- identity: string | null
57
- limits?: Limit[]
58
- capabilities: string[]
59
- }
60
-
61
- export interface AccountsOptions {
62
- /** `<state dir>/accounts`. */
63
- dir: string
64
- secrets: SecretStore
65
- bus: Bus
66
- }
67
-
68
- export class Accounts {
69
- readonly dir: string
70
- private readonly secrets: SecretStore
71
- private readonly bus: Bus
72
- private readonly runtimes = new Map<string, Promise<ModelRuntime>>()
73
- private readonly logins = new Map<string, PendingLogin>()
74
- private readonly terminals = new Map<string, number>()
75
-
76
- constructor(options: AccountsOptions) {
77
- this.dir = options.dir
78
- this.secrets = options.secrets
79
- this.bus = options.bus
80
- }
81
-
82
- private changed(payload: Record<string, unknown> = {}): void {
83
- this.bus.emit({ type: 'accounts.changed', ...payload })
84
- }
85
-
86
- overview(): AccountsOverview {
87
- return store.overview(this.dir)
88
- }
89
-
90
- find(accountId: string): Account {
91
- return store.find(this.dir, accountId)
92
- }
93
-
94
- /** The account a new session uses; a session that already has one passes it and keeps it. */
95
- resolve(accountId?: string | null, poolId?: string | null): Account {
96
- return store.resolve(this.dir, accountId, poolId)
97
- }
98
-
99
- context(account: Account): env.AccountContext {
100
- return env.accountContext(this.dir, account)
101
- }
102
-
103
- // ---- Stored credentials -----------------------------------------------------------------
104
-
105
- async readCredential(accountId: string, provider: string): Promise<Credential | null> {
106
- this.find(accountId)
107
- const text = await this.secrets.get(secretKey(accountId, `pi-auth/${validProvider(provider)}`))
108
- if (!text) return null
109
- try {
110
- return JSON.parse(text) as Credential
111
- } catch {
112
- return null
113
- }
114
- }
115
-
116
- /** Stores a provider's credential, or removes it when `credential` is `null`. */
117
- async writeCredential(accountId: string, provider: string, credential: Credential | null): Promise<void> {
118
- this.find(accountId)
119
- const name = validProvider(provider)
120
- if (credential === null) await this.secrets.delete(secretKey(accountId, `pi-auth/${name}`))
121
- else await this.secrets.set(secretKey(accountId, `pi-auth/${name}`), JSON.stringify(credential))
122
- store.updateState(this.dir, accountId, (state) => {
123
- state.piProviders = state.piProviders.filter((existing) => existing !== name)
124
- if (credential !== null) state.piProviders = [...state.piProviders, name].sort()
125
- })
126
- }
127
-
128
- async listCredentials(accountId: string): Promise<{ providerId: string; type: 'api_key' | 'oauth' }[]> {
129
- this.find(accountId)
130
- const listed: { providerId: string; type: 'api_key' | 'oauth' }[] = []
131
- for (const provider of store.loadState(this.dir, accountId).piProviders) {
132
- const credential = await this.readCredential(accountId, provider).catch(() => null)
133
- if (credential && typeof credential.type === 'string') listed.push({ providerId: provider, type: credential.type })
134
- }
135
- return listed
136
- }
137
-
138
- /** Pi's `CredentialStore` for one account. Writes are serialized, so a refresh never races another. */
139
- private credentialStore(accountId: string) {
140
- let chain: Promise<unknown> = Promise.resolve()
141
- const serialize = <T>(work: () => Promise<T>): Promise<T> => {
142
- const next = chain.then(work)
143
- chain = next.catch(() => undefined)
144
- return next
145
- }
146
- return {
147
- read: async (providerId: string) => (await this.readCredential(accountId, providerId)) ?? undefined,
148
- list: () => this.listCredentials(accountId),
149
- modify: (providerId: string, change: (current: Credential | undefined) => Promise<Credential | undefined>) =>
150
- serialize(async () => {
151
- const current = (await this.readCredential(accountId, providerId)) ?? undefined
152
- const updated = await change(current)
153
- if (updated !== undefined) await this.writeCredential(accountId, providerId, updated)
154
- return updated ?? current
155
- }),
156
- delete: (providerId: string) => serialize(() => this.writeCredential(accountId, providerId, null))
157
- }
158
- }
159
-
160
- // ---- The Pi runtime ---------------------------------------------------------------------
161
-
162
- /** The lazily loaded Pi runtime of one account: its models and its credentials. Pi's own `~/.pi` is never used. */
163
- runtime(account: env.AccountContext | Account): Promise<ModelRuntime> {
164
- const id = account.id
165
- let runtime = this.runtimes.get(id)
166
- if (!runtime) {
167
- const configDir = 'configDir' in account ? account.configDir : env.ensureHome(this.dir, account)
168
- runtime = import('@earendil-works/pi-coding-agent')
169
- .then(({ ModelRuntime }) => ModelRuntime.create({ credentials: this.credentialStore(id) as never, modelsPath: join(configDir, 'models.json') }))
170
- .catch((error: unknown) => {
171
- this.runtimes.delete(id)
172
- throw error
173
- })
174
- this.runtimes.set(id, runtime)
175
- }
176
- return runtime
177
- }
178
-
179
- /** Drops a cached runtime after its credentials changed outside it (sign-in, key edits, terminal sessions). */
180
- invalidate(accountId: string): void {
181
- this.runtimes.delete(accountId)
182
- }
183
-
184
- async models(accountId?: string | null): Promise<ModelOption[]> {
185
- const account = accountId ? this.find(accountId) : store.defaultAccount(this.dir)
186
- if (!account) return []
187
- const available = await (await this.runtime(account)).getAvailable()
188
- return available.map((model) => ({ id: `${model.provider}/${model.id}`, name: model.name || model.id, provider: String(model.provider), supportsImages: model.input.includes('image'), reasoning: model.reasoning }))
189
- }
190
-
191
- /** Whether sessions can run, and why not. */
192
- async status(accountId?: string | null): Promise<{ available: boolean; reason: string | null; models: number }> {
193
- const account = accountId ? this.find(accountId) : store.defaultAccount(this.dir)
194
- if (!account) return { available: false, reason: 'Add a Pi account in Accounts to use Chat.', models: 0 }
195
- try {
196
- const models = await this.models(account.id)
197
- return models.length ? { available: true, reason: null, models: models.length } : { available: false, reason: `The Pi account "${account.label}" has no signed-in provider. Sign in or add a key in Accounts.`, models: 0 }
198
- } catch (error) {
199
- log('Pi runtime failed to load', error instanceof Error ? error.message : String(error))
200
- return { available: false, reason: 'The Pi SDK could not be loaded.', models: 0 }
201
- }
202
- }
203
-
204
- /** Providers Pi can sign in to (OAuth) or take a key for, independent of any account. */
205
- async providers(): Promise<{ id: string; name: string; oauth: boolean; oauthLabel: string | null; subscription: boolean; apiKey: boolean }[]> {
206
- const { ModelRuntime } = await import('@earendil-works/pi-coding-agent')
207
- const none = { read: async () => undefined, list: async () => [], modify: async () => undefined, delete: async () => undefined }
208
- const runtime = await ModelRuntime.create({ credentials: none, modelsPath: null, refreshOnCreate: false })
209
- return runtime
210
- .getProviders()
211
- .map((provider) => ({ id: provider.id, name: provider.name, oauth: Boolean(provider.auth.oauth), oauthLabel: provider.auth.oauth?.loginLabel ?? provider.auth.oauth?.name ?? null, subscription: Boolean(provider.auth.oauth?.isSubscription), apiKey: Boolean(provider.auth.apiKey?.login) }))
212
- .filter((provider) => provider.oauth || provider.apiKey)
213
- .sort((left, right) => left.name.localeCompare(right.name))
214
- }
215
-
216
- // ---- Status checks ----------------------------------------------------------------------
217
-
218
- private async probe(account: Account): Promise<Probe> {
219
- const runtime = await this.runtime(account)
220
- const credentials = await runtime.listCredentials()
221
- const available = await runtime.getAvailable()
222
- const subscription = credentials.filter((credential) => runtime.isUsingSubscription(credential.providerId)).map((credential) => credential.providerId)
223
- return {
224
- signedIn: credentials.length > 0 && available.length > 0,
225
- authMethod: [...new Set(credentials.map((credential) => credential.type))].join(', ') || null,
226
- plan: subscription.length ? `Subscription: ${subscription.join(', ')}` : null,
227
- identity: credentials.map((credential) => credential.providerId).join(', ') || null,
228
- capabilities: [`models:${available.length}`, ...credentials.map((credential) => `provider:${credential.providerId}`)]
229
- }
230
- }
231
-
232
- /** Checks one account and records what Pi reports. */
233
- async refreshOne(account: Account): Promise<void> {
234
- let probe: Probe | null = null
235
- let failure: string | null = null
236
- try {
237
- probe = await this.probe(account)
238
- } catch (error) {
239
- failure = error instanceof Error ? error.message : String(error)
240
- }
241
- const { previous, state } = store.updateState(this.dir, account.id, (current) => {
242
- current.lastCheckedAt = now()
243
- if (probe) {
244
- current.signedIn = probe.signedIn
245
- current.authMethod = probe.authMethod
246
- current.plan = probe.plan ?? current.plan
247
- current.identity = probe.identity
248
- if (probe.limits) current.limits = probe.limits
249
- current.capabilities = probe.capabilities
250
- const keepLimitError = current.lastError !== null && store.isLimitCategory(current.lastError.category) && store.limitErrorActive(current.lastError)
251
- if (probe.signedIn === true && !keepLimitError) current.lastError = null
252
- } else {
253
- current.lastError = { category: 'unknown', message: store.scrub(failure ?? ''), at: now(), resetsAt: null }
254
- }
255
- })
256
- if (previous !== state.health) store.audit(this.dir, account.id, 'health.changed', `${previous} → ${state.health} (status check)`)
257
- this.changed({ accountId: account.id })
258
- }
259
-
260
- async refresh(accountId?: string | null): Promise<AccountsOverview> {
261
- const accounts = accountId ? [this.find(accountId)] : store.loadRegistry(this.dir).accounts.filter((account) => account.provider === store.PROVIDER)
262
- for (const account of accounts) await this.refreshOne(account)
263
- return this.overview()
264
- }
265
-
266
- // ---- Create, rename, remove -------------------------------------------------------------
267
-
268
- create(label: string, authKind: string): AccountsOverview {
269
- const account = store.addAccount(this.dir, label, authKind)
270
- env.ensureHome(this.dir, account)
271
- store.audit(this.dir, account.id, 'account.created', `Pi account "${account.label}" (${account.authKind})`)
272
- this.changed({ accountId: account.id })
273
- return this.overview()
274
- }
275
-
276
- rename(accountId: string, label: string): AccountsOverview {
277
- store.renameAccount(this.dir, accountId, label)
278
- store.audit(this.dir, accountId, 'account.renamed', store.cleanLabel(label))
279
- this.changed({ accountId })
280
- return this.overview()
281
- }
282
-
283
- setDefault(accountId: string): AccountsOverview {
284
- store.setDefault(this.dir, accountId)
285
- store.audit(this.dir, accountId, 'default.changed', 'Default Pi account for new sessions.')
286
- this.changed({ accountId })
287
- return this.overview()
288
- }
289
-
290
- private async forgetCredentials(account: Account): Promise<void> {
291
- for (const provider of store.loadState(this.dir, account.id).piProviders) await this.writeCredential(account.id, provider, null)
292
- this.invalidate(account.id)
293
- }
294
-
295
- /** Deletes the account: removes its stored credentials and its runtime folder. Sessions pinned to it ask for another account next time they start. */
296
- async remove(accountId: string): Promise<AccountsOverview> {
297
- const account = this.find(accountId)
298
- await this.forgetCredentials(account).catch(() => undefined)
299
- await this.secrets.delete(secretKey(account.id, 'apiKey')).catch(() => undefined)
300
- store.forgetAccount(this.dir, account)
301
- rmSync(join(this.dir, safeId(account.id)), { recursive: true, force: true })
302
- store.audit(this.dir, account.id, 'account.deleted', `"${account.label}" and its stored sign-in were removed.`)
303
- this.changed({ accountId: account.id })
304
- return this.overview()
305
- }
306
-
307
- savePool(pool: { id?: string; label: string; accountIds: string[] }): AccountsOverview {
308
- const saved = store.savePool(this.dir, pool)
309
- store.audit(this.dir, null, 'pool.saved', `${saved.label}: ${saved.accountIds.length} account(s)`)
310
- this.changed()
311
- return this.overview()
312
- }
313
-
314
- removePool(poolId: string): AccountsOverview {
315
- store.removePool(this.dir, poolId)
316
- store.audit(this.dir, null, 'pool.removed', poolId)
317
- this.changed()
318
- return this.overview()
319
- }
320
-
321
- // ---- Keys and sign-in -------------------------------------------------------------------
322
-
323
- async setApiKey(accountId: string, rawKey: string, provider: string): Promise<AccountsOverview> {
324
- const account = this.find(accountId)
325
- const key = rawKey.trim()
326
- if (!key || key.length > 4096 || /\s/.test(key)) throw invalid('Paste the key without spaces.')
327
- if (!provider) throw invalid('Choose which Pi provider the key is for.')
328
- await this.writeCredential(account.id, provider, { type: 'api_key', key })
329
- this.invalidate(account.id)
330
- store.audit(this.dir, account.id, 'key.set', 'Provider key stored in the Keychain.')
331
- await this.refreshOne(account)
332
- return this.overview()
333
- }
334
-
335
- async removeProvider(accountId: string, provider: string): Promise<AccountsOverview> {
336
- const account = this.find(accountId)
337
- await this.writeCredential(account.id, provider, null)
338
- this.invalidate(account.id)
339
- store.audit(this.dir, account.id, 'key.removed', provider)
340
- await this.refreshOne(account)
341
- return this.overview()
342
- }
343
-
344
- async signOut(accountId: string): Promise<AccountsOverview> {
345
- const account = this.find(accountId)
346
- await this.forgetCredentials(account)
347
- store.audit(this.dir, account.id, 'logout', 'Signed out; stored credentials removed.')
348
- store.updateState(this.dir, account.id, (current) => {
349
- current.signedIn = false
350
- current.identity = null
351
- current.lastError = null
352
- })
353
- this.changed({ accountId: account.id })
354
- return this.overview()
355
- }
356
-
357
- private finishLogin(loginId: string, success: boolean, error: string | null): void {
358
- const login = this.logins.get(loginId)
359
- if (!login) return
360
- this.logins.delete(loginId)
361
- for (const prompt of login.prompts.values()) prompt.reject(new Error('Sign-in ended.'))
362
- if (success) this.invalidate(login.accountId)
363
- store.audit(this.dir, login.accountId, success ? 'login.completed' : 'login.failed', error ?? '')
364
- // A fresh sign-in starts clean; a limit that still applies is reported again by the next run.
365
- if (success) store.updateState(this.dir, login.accountId, (current) => void (current.lastError = null))
366
- this.bus.emit({ type: 'accounts.login', accountId: login.accountId, loginId, success, error })
367
- try {
368
- void this.refreshOne(this.find(login.accountId)).catch(() => undefined)
369
- } catch {
370
- // The account was removed while its sign-in was running.
371
- }
372
- }
373
-
374
- /**
375
- * Starts a provider's own sign-in for this account. Progress, links to open, and prompts
376
- * arrive as `accounts.loginEvent` notices; the outcome as `accounts.login`.
377
- */
378
- async loginStart(accountId: string, provider: string, method?: string | null): Promise<{ loginId: string }> {
379
- const account = this.find(accountId)
380
- if (!provider) throw invalid('Choose which Pi provider to sign in to.')
381
- store.audit(this.dir, account.id, 'login.started', `${method ?? 'oauth'} · ${provider}`)
382
- const runtime = await this.runtime(account)
383
- const loginId = randomUUID()
384
- const controller = new AbortController()
385
- const login: PendingLogin = { accountId: account.id, prompts: new Map(), cancel: () => controller.abort() }
386
- this.logins.set(loginId, login)
387
- const event = (payload: Record<string, unknown>): void => this.bus.emit({ type: 'accounts.loginEvent', accountId: account.id, loginId, event: payload })
388
- const interaction = {
389
- signal: controller.signal,
390
- notify: (notification: Record<string, unknown>) => event(notification),
391
- prompt: (prompt: { type: string; message: string; placeholder?: string; options?: unknown; signal?: AbortSignal }) =>
392
- new Promise<string>((resolve, reject) => {
393
- const promptId = randomUUID()
394
- login.prompts.set(promptId, { resolve, reject })
395
- prompt.signal?.addEventListener('abort', () => {
396
- login.prompts.delete(promptId)
397
- event({ type: 'promptClosed', promptId })
398
- reject(new Error('Prompt closed.'))
399
- })
400
- event({ type: 'prompt', promptId, prompt: { type: prompt.type, message: prompt.message, placeholder: prompt.placeholder ?? null, options: prompt.options ?? null } })
401
- })
402
- }
403
- runtime
404
- .login(provider, method === 'api_key' ? 'api_key' : 'oauth', interaction as never)
405
- .then(() => this.finishLogin(loginId, true, null))
406
- .catch((error: unknown) => this.finishLogin(loginId, false, controller.signal.aborted ? 'Sign-in was cancelled.' : error instanceof Error ? error.message : String(error)))
407
- return { loginId }
408
- }
409
-
410
- loginRespond(loginId: string, promptId: string, value: string | null): void {
411
- const login = this.logins.get(loginId)
412
- const prompt = login?.prompts.get(promptId)
413
- if (!login || !prompt) throw new HarnessError('not_found', 'That sign-in step is no longer waiting.')
414
- login.prompts.delete(promptId)
415
- if (value === null) prompt.reject(new Error('Cancelled.'))
416
- else prompt.resolve(value)
417
- }
418
-
419
- loginCancel(accountId: string, loginId?: string | null): void {
420
- if (loginId) this.logins.get(loginId)?.cancel()
421
- store.audit(this.dir, accountId, 'login.cancelled', '')
422
- }
423
-
424
- // ---- Terminal sessions --------------------------------------------------------------------
425
-
426
- /**
427
- * The `pi` command reads `auth.json` from its agent folder. For a terminal session the
428
- * account's stored credentials are written there (mode 0600) while the terminal runs, then
429
- * read back and removed.
430
- */
431
- async terminalBegin(account: Account): Promise<void> {
432
- const count = (this.terminals.get(account.id) ?? 0) + 1
433
- this.terminals.set(account.id, count)
434
- if (count > 1) return
435
- const path = join(env.configDir(this.dir, account), 'auth.json')
436
- try {
437
- const data: Record<string, unknown> = {}
438
- for (const provider of store.loadState(this.dir, account.id).piProviders) {
439
- const credential = await this.readCredential(account.id, provider).catch(() => null)
440
- if (credential) data[provider] = credential
441
- }
442
- atomicWrite(path, JSON.stringify(data, null, 2))
443
- chmodSync(path, 0o600)
444
- } catch (error) {
445
- this.terminals.delete(account.id)
446
- rmSync(path, { force: true })
447
- throw error
448
- }
449
- }
450
-
451
- async terminalEnd(account: Account): Promise<void> {
452
- const remaining = Math.max(0, (this.terminals.get(account.id) ?? 1) - 1)
453
- if (remaining > 0) {
454
- this.terminals.set(account.id, remaining)
455
- return
456
- }
457
- this.terminals.delete(account.id)
458
- const path = join(env.configDir(this.dir, account), 'auth.json')
459
- let data: Record<string, Credential> | null = null
460
- try {
461
- const parsed = readJson<Record<string, Credential>>(path)
462
- data = parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null
463
- } catch {
464
- data = null
465
- }
466
- if (data) {
467
- for (const provider of store.loadState(this.dir, account.id).piProviders) if (!(provider in data)) await this.writeCredential(account.id, provider, null).catch(() => undefined)
468
- for (const [provider, credential] of Object.entries(data)) await this.writeCredential(account.id, provider, credential).catch(() => undefined)
469
- this.invalidate(account.id)
470
- store.audit(this.dir, account.id, 'credentials.synced', 'Pi terminal sign-in saved to the Keychain.')
471
- }
472
- rmSync(path, { force: true })
473
- }
474
-
475
- // ---- Health from runs ---------------------------------------------------------------------
476
-
477
- /** Tracks account health and limits from run events. `run` is the event's run record, when it is known. */
478
- onRunEvent(event: RunEvent, run: RunRecord | null): void {
479
- if (event.type === 'account.status') this.applyStatus(event)
480
- else if (event.type === 'completed' && run?.accountId) {
481
- const accountId = run.accountId
482
- const { previous, state } = store.updateState(this.dir, accountId, (current) => {
483
- // A finished run proves the sign-in works; limit windows and limit errors stay until they reset.
484
- if (current.lastError && (!store.isLimitCategory(current.lastError.category) || !store.limitErrorActive(current.lastError))) current.lastError = null
485
- current.signedIn = true
486
- })
487
- if (previous !== state.health) this.changed({ accountId, health: state.health, previous })
488
- }
489
- }
490
-
491
- private applyStatus(event: RunEvent): void {
492
- const payload = event.payload as { accountId?: unknown; category?: unknown; message?: unknown; limits?: unknown; plan?: unknown; resetsAt?: unknown }
493
- if (typeof payload.accountId !== 'string') return
494
- const accountId = payload.accountId
495
- const text = (value: unknown): string | null => (typeof value === 'string' ? value : null)
496
- const category = text(payload.category)
497
- const message = text(payload.message) === null ? null : store.scrub(payload.message as string)
498
- const limits = Array.isArray(payload.limits) ? (payload.limits as Limit[]) : null
499
- let result: ReturnType<typeof store.updateState>
500
- try {
501
- result = store.updateState(this.dir, accountId, (state) => {
502
- if (limits) {
503
- for (const limit of limits) state.limits = [...state.limits.filter((existing) => existing.id !== limit.id), limit]
504
- state.limits.sort((left, right) => (left.id < right.id ? -1 : left.id > right.id ? 1 : 0))
505
- }
506
- if (text(payload.plan) !== null) state.plan = text(payload.plan)
507
- if (category) {
508
- state.lastError = { category, message: message ?? '', at: now(), resetsAt: text(payload.resetsAt) } satisfies Issue
509
- if (category === 'auth_expired') state.signedIn = false
510
- }
511
- })
512
- } catch {
513
- return
514
- }
515
- if (result.previous !== result.state.health) {
516
- store.audit(this.dir, accountId, 'health.changed', `${result.previous} → ${result.state.health}${message ? `: ${message}` : ''}`)
517
- this.changed({ accountId, health: result.state.health, previous: result.previous, message, resetsAt: text(payload.resetsAt) })
518
- } else {
519
- this.changed({ accountId })
520
- }
521
- }
522
- }
@@ -1,95 +0,0 @@
1
- import { existsSync, lstatSync, linkSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, readlinkSync, symlinkSync, writeFileSync } from 'node:fs'
2
- import { tmpdir } from 'node:os'
3
- import { join } from 'node:path'
4
- import { expect, test } from 'vitest'
5
-
6
- import { configDir, ensureSharedDir, linkSharedEntries, sharedDir, type EntryLinker } from './env'
7
-
8
- const temp = () => mkdtempSync(join(tmpdir(), 'harness-env-'))
9
- const refused = (code = 'EPERM') => Object.assign(new Error(`${code}: operation not permitted, symlink`), { code })
10
- const backups = (folder: string) => readdirSync(folder).filter((name) => name.includes('xagent-backup') || name.includes('agentx-backup'))
11
-
12
- // A Windows account without the symlink privilege: file symlinks are refused, junctions and hard links work.
13
- const windowsLike: EntryLinker = {
14
- symlink: (target, path, isDir) => {
15
- if (!isDir) throw refused()
16
- symlinkSync(target, path)
17
- },
18
- hardLink: (target, path) => linkSync(target, path)
19
- }
20
-
21
- const refusesAll: EntryLinker = {
22
- symlink: () => {
23
- throw refused()
24
- },
25
- hardLink: () => {
26
- throw refused()
27
- }
28
- }
29
-
30
- test('without the symlink privilege, files are hard-linked and folders are junctioned', () => {
31
- const dir = temp()
32
- const home = configDir(dir, { id: 'acc1' })
33
- mkdirSync(home, { recursive: true })
34
- writeFileSync(join(home, 'settings.json'), '{"theme":"dark"}')
35
-
36
- const first = linkSharedEntries(dir, { id: 'acc1' }, windowsLike)
37
- const shared = join(sharedDir(dir), 'settings.json')
38
- expect(first.entries.find((entry) => entry.name === 'settings.json')).toEqual({ name: 'settings.json', state: 'linked' })
39
- expect(lstatSync(join(home, 'settings.json')).ino).toBe(lstatSync(shared).ino)
40
- expect(lstatSync(join(home, 'settings.json')).isSymbolicLink()).toBe(false)
41
- expect(lstatSync(join(home, 'skills')).isSymbolicLink()).toBe(true)
42
-
43
- // Entries with no shared copy yet cannot be hard-linked, so they stay the account's own.
44
- expect(first.entries.find((entry) => entry.name === 'AGENTS.md')).toMatchObject({ state: 'unshared' })
45
-
46
- // A second launch finds the links in place and moves nothing aside.
47
- const second = linkSharedEntries(dir, { id: 'acc1' }, windowsLike)
48
- expect(second.entries.find((entry) => entry.name === 'settings.json')?.state).toBe('linked')
49
- expect(second.entries.find((entry) => entry.name === 'skills')?.state).toBe('linked')
50
- expect(backups(home)).toEqual([])
51
- expect(backups(sharedDir(dir))).toEqual([])
52
- expect(readFileSync(join(home, 'settings.json'), 'utf8')).toBe('{"theme":"dark"}')
53
- })
54
-
55
- test('when links are refused, the account keeps its own settings', () => {
56
- const dir = temp()
57
- const home = configDir(dir, { id: 'acc1' })
58
- mkdirSync(home, { recursive: true })
59
- writeFileSync(join(home, 'settings.json'), '{"mine":true}')
60
-
61
- const { entries } = linkSharedEntries(dir, { id: 'acc1' }, refusesAll)
62
- expect(entries.find((entry) => entry.name === 'settings.json')).toEqual({ name: 'settings.json', state: 'unshared', reason: 'EPERM' })
63
- expect(readFileSync(join(home, 'settings.json'), 'utf8')).toBe('{"mine":true}')
64
- expect(existsSync(join(sharedDir(dir), 'settings.json'))).toBe(false)
65
- expect(backups(home)).toEqual([])
66
- })
67
-
68
- test('when links are refused, a shared file the account lacks is copied in and stays put', () => {
69
- const dir = temp()
70
- writeFileSync(join(ensureSharedDir(dir), 'settings.json'), '{"shared":true}')
71
- const home = configDir(dir, { id: 'acc2' })
72
-
73
- const first = linkSharedEntries(dir, { id: 'acc2' }, refusesAll)
74
- expect(readFileSync(join(home, 'settings.json'), 'utf8')).toBe('{"shared":true}')
75
- expect(first.entries.find((entry) => entry.name === 'settings.json')?.state).toBe('unshared')
76
-
77
- const second = linkSharedEntries(dir, { id: 'acc2' }, refusesAll)
78
- expect(second.entries.find((entry) => entry.name === 'settings.json')?.state).toBe('unshared')
79
- expect(readFileSync(join(home, 'settings.json'), 'utf8')).toBe('{"shared":true}')
80
- expect(backups(home)).toEqual([])
81
- expect(backups(sharedDir(dir))).toEqual([])
82
- })
83
-
84
- test('a refused link leaves the account\'s existing link as it was', () => {
85
- const dir = temp()
86
- const home = configDir(dir, { id: 'acc3' })
87
- mkdirSync(home, { recursive: true })
88
- const elsewhere = join(temp(), 'skills')
89
- mkdirSync(elsewhere)
90
- symlinkSync(elsewhere, join(home, 'skills'))
91
-
92
- const { entries } = linkSharedEntries(dir, { id: 'acc3' }, refusesAll)
93
- expect(entries.find((entry) => entry.name === 'skills')?.state).toBe('unshared')
94
- expect(readlinkSync(join(home, 'skills'))).toBe(elsewhere)
95
- })