@mrpatronz/nexusflow 2.9.0 → 2.10.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 (732) hide show
  1. package/README.md +172 -113
  2. package/dist/agent/AcpCliAdapter.d.ts +2 -0
  3. package/dist/agent/AcpCliAdapter.d.ts.map +1 -1
  4. package/dist/agent/AcpCliAdapter.js +50 -4
  5. package/dist/agent/AcpCliAdapter.js.map +1 -1
  6. package/dist/agent/AntigravityCliAdapter.d.ts +44 -2
  7. package/dist/agent/AntigravityCliAdapter.d.ts.map +1 -1
  8. package/dist/agent/AntigravityCliAdapter.js +363 -16
  9. package/dist/agent/AntigravityCliAdapter.js.map +1 -1
  10. package/dist/agent/ClaudeCliAdapter.d.ts +3 -0
  11. package/dist/agent/ClaudeCliAdapter.d.ts.map +1 -1
  12. package/dist/agent/ClaudeCliAdapter.js +37 -10
  13. package/dist/agent/ClaudeCliAdapter.js.map +1 -1
  14. package/dist/agent/ClaudeSdkAdapter.d.ts +1 -0
  15. package/dist/agent/ClaudeSdkAdapter.d.ts.map +1 -1
  16. package/dist/agent/ClaudeSdkAdapter.js +38 -11
  17. package/dist/agent/ClaudeSdkAdapter.js.map +1 -1
  18. package/dist/agent/CliAdapterBase.d.ts +2 -0
  19. package/dist/agent/CliAdapterBase.d.ts.map +1 -1
  20. package/dist/agent/CliAdapterBase.js +5 -1
  21. package/dist/agent/CliAdapterBase.js.map +1 -1
  22. package/dist/agent/CodexCliAdapter.d.ts +1 -1
  23. package/dist/agent/CodexCliAdapter.d.ts.map +1 -1
  24. package/dist/agent/CodexCliAdapter.js +5 -4
  25. package/dist/agent/CodexCliAdapter.js.map +1 -1
  26. package/dist/agent/CodexSdkAdapter.d.ts.map +1 -1
  27. package/dist/agent/CodexSdkAdapter.js +4 -1
  28. package/dist/agent/CodexSdkAdapter.js.map +1 -1
  29. package/dist/agent/CopilotAcpAdapter.d.ts.map +1 -1
  30. package/dist/agent/CopilotAcpAdapter.js +2 -2
  31. package/dist/agent/CopilotAcpAdapter.js.map +1 -1
  32. package/dist/agent/ProviderRegistry.d.ts +8 -1
  33. package/dist/agent/ProviderRegistry.d.ts.map +1 -1
  34. package/dist/agent/ProviderRegistry.js.map +1 -1
  35. package/dist/agent/TurnSessionManager.d.ts +65 -0
  36. package/dist/agent/TurnSessionManager.d.ts.map +1 -0
  37. package/dist/agent/TurnSessionManager.js +330 -0
  38. package/dist/agent/TurnSessionManager.js.map +1 -0
  39. package/dist/agent/adapters.d.ts.map +1 -1
  40. package/dist/agent/adapters.js +23 -6
  41. package/dist/agent/adapters.js.map +1 -1
  42. package/dist/agent/cliAvailability.d.ts.map +1 -1
  43. package/dist/agent/cliAvailability.js +12 -0
  44. package/dist/agent/cliAvailability.js.map +1 -1
  45. package/dist/agent/mcp-config.d.ts +0 -5
  46. package/dist/agent/mcp-config.d.ts.map +1 -1
  47. package/dist/agent/mcp-config.js +2 -1
  48. package/dist/agent/mcp-config.js.map +1 -1
  49. package/dist/agent/models.d.ts +13 -0
  50. package/dist/agent/models.d.ts.map +1 -1
  51. package/dist/agent/models.js +262 -8
  52. package/dist/agent/models.js.map +1 -1
  53. package/dist/agent/nativeTools.d.ts.map +1 -1
  54. package/dist/agent/nativeTools.js +1 -0
  55. package/dist/agent/nativeTools.js.map +1 -1
  56. package/dist/agent/orchestrator.d.ts.map +1 -1
  57. package/dist/agent/orchestrator.js +3 -2
  58. package/dist/agent/orchestrator.js.map +1 -1
  59. package/dist/agent/session.d.ts +1 -0
  60. package/dist/agent/session.d.ts.map +1 -1
  61. package/dist/agent/session.js.map +1 -1
  62. package/dist/analyzers/detect-deps.js +16 -1
  63. package/dist/analyzers/detect-deps.js.map +1 -1
  64. package/dist/analyzers/run-analyzer.d.ts.map +1 -1
  65. package/dist/analyzers/run-analyzer.js.map +1 -1
  66. package/dist/commands/adapter.d.ts.map +1 -1
  67. package/dist/commands/adapter.js +15 -14
  68. package/dist/commands/adapter.js.map +1 -1
  69. package/dist/commands/add-repo.d.ts.map +1 -1
  70. package/dist/commands/add-repo.js +6 -5
  71. package/dist/commands/add-repo.js.map +1 -1
  72. package/dist/commands/commit.js +6 -8
  73. package/dist/commands/commit.js.map +1 -1
  74. package/dist/commands/config.d.ts.map +1 -1
  75. package/dist/commands/config.js +2 -1
  76. package/dist/commands/config.js.map +1 -1
  77. package/dist/commands/create.d.ts +4 -4
  78. package/dist/commands/create.d.ts.map +1 -1
  79. package/dist/commands/create.js +8 -7
  80. package/dist/commands/create.js.map +1 -1
  81. package/dist/commands/desktop.d.ts +44 -3
  82. package/dist/commands/desktop.d.ts.map +1 -1
  83. package/dist/commands/desktop.js +246 -7
  84. package/dist/commands/desktop.js.map +1 -1
  85. package/dist/commands/diff.d.ts.map +1 -1
  86. package/dist/commands/diff.js +3 -2
  87. package/dist/commands/diff.js.map +1 -1
  88. package/dist/commands/doctor.d.ts.map +1 -1
  89. package/dist/commands/doctor.js +3 -2
  90. package/dist/commands/doctor.js.map +1 -1
  91. package/dist/commands/finish.js +2 -1
  92. package/dist/commands/finish.js.map +1 -1
  93. package/dist/commands/handoff.d.ts.map +1 -1
  94. package/dist/commands/handoff.js +12 -12
  95. package/dist/commands/handoff.js.map +1 -1
  96. package/dist/commands/init.d.ts +2 -2
  97. package/dist/commands/init.d.ts.map +1 -1
  98. package/dist/commands/init.js +8 -7
  99. package/dist/commands/init.js.map +1 -1
  100. package/dist/commands/isolate.d.ts.map +1 -1
  101. package/dist/commands/isolate.js +2 -1
  102. package/dist/commands/isolate.js.map +1 -1
  103. package/dist/commands/knowledge.d.ts.map +1 -1
  104. package/dist/commands/knowledge.js +3 -2
  105. package/dist/commands/knowledge.js.map +1 -1
  106. package/dist/commands/list.d.ts.map +1 -1
  107. package/dist/commands/list.js +3 -2
  108. package/dist/commands/list.js.map +1 -1
  109. package/dist/commands/logs.js +3 -3
  110. package/dist/commands/logs.js.map +1 -1
  111. package/dist/commands/mcp.d.ts +1 -2
  112. package/dist/commands/mcp.d.ts.map +1 -1
  113. package/dist/commands/mcp.js +19 -12
  114. package/dist/commands/mcp.js.map +1 -1
  115. package/dist/commands/migrate.d.ts +12 -0
  116. package/dist/commands/migrate.d.ts.map +1 -0
  117. package/dist/commands/migrate.js +128 -0
  118. package/dist/commands/migrate.js.map +1 -0
  119. package/dist/commands/open.d.ts.map +1 -1
  120. package/dist/commands/open.js +2 -1
  121. package/dist/commands/open.js.map +1 -1
  122. package/dist/commands/progress.d.ts.map +1 -1
  123. package/dist/commands/progress.js +2 -1
  124. package/dist/commands/progress.js.map +1 -1
  125. package/dist/commands/project.d.ts.map +1 -1
  126. package/dist/commands/project.js +7 -6
  127. package/dist/commands/project.js.map +1 -1
  128. package/dist/commands/refresh.d.ts.map +1 -1
  129. package/dist/commands/refresh.js +8 -11
  130. package/dist/commands/refresh.js.map +1 -1
  131. package/dist/commands/remove.d.ts.map +1 -1
  132. package/dist/commands/remove.js +2 -1
  133. package/dist/commands/remove.js.map +1 -1
  134. package/dist/commands/schedule.js +16 -18
  135. package/dist/commands/schedule.js.map +1 -1
  136. package/dist/commands/start.d.ts.map +1 -1
  137. package/dist/commands/start.js +9 -8
  138. package/dist/commands/start.js.map +1 -1
  139. package/dist/commands/status.d.ts.map +1 -1
  140. package/dist/commands/status.js +2 -1
  141. package/dist/commands/status.js.map +1 -1
  142. package/dist/commands/stop.d.ts.map +1 -1
  143. package/dist/commands/stop.js +2 -1
  144. package/dist/commands/stop.js.map +1 -1
  145. package/dist/commands/strategy.d.ts.map +1 -1
  146. package/dist/commands/strategy.js +6 -5
  147. package/dist/commands/strategy.js.map +1 -1
  148. package/dist/commands/sync.js +8 -10
  149. package/dist/commands/sync.js.map +1 -1
  150. package/dist/commands/tui.d.ts.map +1 -1
  151. package/dist/commands/tui.js +11 -10
  152. package/dist/commands/tui.js.map +1 -1
  153. package/dist/commands/ui.d.ts.map +1 -1
  154. package/dist/commands/ui.js +4 -3
  155. package/dist/commands/ui.js.map +1 -1
  156. package/dist/core/adapters/local-storage.d.ts +3 -1
  157. package/dist/core/adapters/local-storage.d.ts.map +1 -1
  158. package/dist/core/adapters/local-storage.js +62 -4
  159. package/dist/core/adapters/local-storage.js.map +1 -1
  160. package/dist/core/analysis-cache.d.ts.map +1 -1
  161. package/dist/core/analysis-cache.js +8 -7
  162. package/dist/core/analysis-cache.js.map +1 -1
  163. package/dist/core/brand-config.d.ts +261 -0
  164. package/dist/core/brand-config.d.ts.map +1 -0
  165. package/dist/core/brand-config.js +514 -0
  166. package/dist/core/brand-config.js.map +1 -0
  167. package/dist/core/config.d.ts +1 -1
  168. package/dist/core/config.d.ts.map +1 -1
  169. package/dist/core/config.js +18 -8
  170. package/dist/core/config.js.map +1 -1
  171. package/dist/core/constants.d.ts +88 -0
  172. package/dist/core/constants.d.ts.map +1 -0
  173. package/dist/core/constants.js +103 -0
  174. package/dist/core/constants.js.map +1 -0
  175. package/dist/core/doctor.d.ts.map +1 -1
  176. package/dist/core/doctor.js +11 -3
  177. package/dist/core/doctor.js.map +1 -1
  178. package/dist/core/generation-lock.d.ts +2 -2
  179. package/dist/core/generation-lock.d.ts.map +1 -1
  180. package/dist/core/generation-lock.js +49 -22
  181. package/dist/core/generation-lock.js.map +1 -1
  182. package/dist/core/knowledge.d.ts +19 -0
  183. package/dist/core/knowledge.d.ts.map +1 -1
  184. package/dist/core/knowledge.js +150 -24
  185. package/dist/core/knowledge.js.map +1 -1
  186. package/dist/core/migrate.d.ts +53 -0
  187. package/dist/core/migrate.d.ts.map +1 -0
  188. package/dist/core/migrate.js +607 -0
  189. package/dist/core/migrate.js.map +1 -0
  190. package/dist/core/new-repo.js +1 -1
  191. package/dist/core/new-repo.js.map +1 -1
  192. package/dist/core/projects.d.ts.map +1 -1
  193. package/dist/core/projects.js +13 -4
  194. package/dist/core/projects.js.map +1 -1
  195. package/dist/core/refresh.d.ts.map +1 -1
  196. package/dist/core/refresh.js +6 -4
  197. package/dist/core/refresh.js.map +1 -1
  198. package/dist/core/scheduler.d.ts +1 -1
  199. package/dist/core/scheduler.d.ts.map +1 -1
  200. package/dist/core/scheduler.js +11 -9
  201. package/dist/core/scheduler.js.map +1 -1
  202. package/dist/core/sync.js +1 -1
  203. package/dist/core/sync.js.map +1 -1
  204. package/dist/core/workspace-git.d.ts.map +1 -1
  205. package/dist/core/workspace-git.js +34 -10
  206. package/dist/core/workspace-git.js.map +1 -1
  207. package/dist/core/workspace-state.d.ts +4 -0
  208. package/dist/core/workspace-state.d.ts.map +1 -1
  209. package/dist/core/workspace-state.js +8 -8
  210. package/dist/core/workspace-state.js.map +1 -1
  211. package/dist/core/workspace.d.ts +3 -4
  212. package/dist/core/workspace.d.ts.map +1 -1
  213. package/dist/core/workspace.js +62 -30
  214. package/dist/core/workspace.js.map +1 -1
  215. package/dist/desktop-server.js +6 -3
  216. package/dist/desktop-server.js.map +1 -1
  217. package/dist/generators/base.d.ts.map +1 -1
  218. package/dist/generators/base.js +7 -5
  219. package/dist/generators/base.js.map +1 -1
  220. package/dist/generators/claude.d.ts.map +1 -1
  221. package/dist/generators/claude.js +2 -1
  222. package/dist/generators/claude.js.map +1 -1
  223. package/dist/generators/cursor.d.ts +1 -1
  224. package/dist/generators/cursor.d.ts.map +1 -1
  225. package/dist/generators/cursor.js +9 -4
  226. package/dist/generators/cursor.js.map +1 -1
  227. package/dist/generators/index.d.ts +4 -8
  228. package/dist/generators/index.d.ts.map +1 -1
  229. package/dist/generators/index.js +34 -69
  230. package/dist/generators/index.js.map +1 -1
  231. package/dist/generators/plan-generator.d.ts.map +1 -1
  232. package/dist/generators/plan-generator.js +20 -11
  233. package/dist/generators/plan-generator.js.map +1 -1
  234. package/dist/generators/skills-generator.d.ts.map +1 -1
  235. package/dist/generators/skills-generator.js +26 -11
  236. package/dist/generators/skills-generator.js.map +1 -1
  237. package/dist/gui/app_logo.png +0 -0
  238. package/dist/gui/assets/AgentsPage-CnXzZFo1.js +3 -0
  239. package/dist/gui/assets/DashboardPage-rm3hKgsw.js +1 -0
  240. package/dist/gui/assets/GettingStartedPage-BKeuSHZQ.js +1 -0
  241. package/dist/gui/assets/NullStore-DuSDhGOe.js +1 -0
  242. package/dist/gui/assets/ProjectsPage-CioaA4uf.js +1 -0
  243. package/dist/gui/assets/ScaffoldRepoInline-DKZ1rPmE.js +1 -0
  244. package/dist/gui/assets/Separator-BgWyHDtg.js +1 -0
  245. package/dist/gui/assets/SettingsPage-BevO8oge.js +1 -0
  246. package/dist/gui/assets/SkillsPage-D2stJRWy.js +6 -0
  247. package/dist/gui/assets/StartWorkPage-C8a6eN90.js +1 -0
  248. package/dist/gui/assets/{StrategiesPage-C36TZgDX.js → StrategiesPage-BRQMly_d.js} +2 -2
  249. package/dist/gui/assets/WorkroomsPage-KqBkRD-x.js +4 -0
  250. package/dist/gui/assets/WorkspacesPage-DcA2i5HN.js +2 -0
  251. package/dist/gui/assets/alert-QnMXzwpU.js +1 -0
  252. package/dist/gui/assets/alert-dialog-BHEUQVyS.js +1 -0
  253. package/dist/gui/assets/badge-DTBzXOWn.js +1 -0
  254. package/dist/gui/assets/brand-hHByyyzq.js +1 -0
  255. package/dist/gui/assets/button-CREn1mup.js +1 -0
  256. package/dist/gui/assets/card-yrg6ov8B.js +1 -0
  257. package/dist/gui/assets/chevron-down-B_Mwbim8.js +1 -0
  258. package/dist/gui/assets/{circle-plus-lmU1yNJd.js → circle-plus--zM3-3r3.js} +1 -1
  259. package/dist/gui/assets/{code-xml-jFAa3-TT.js → code-xml-DH-38Tt7.js} +1 -1
  260. package/dist/gui/assets/{git-branch-Cfj9D8yW.js → git-branch-Ce0I2663.js} +1 -1
  261. package/dist/gui/assets/index-BB4BuHBQ.css +2 -0
  262. package/dist/gui/assets/index-BTe-_1Mu.js +57 -0
  263. package/dist/gui/assets/isElementDisabled-BrwmCB1d.js +1 -0
  264. package/dist/gui/assets/label-DC730X8s.js +1 -0
  265. package/dist/gui/assets/layers-j_brOuoa.js +1 -0
  266. package/dist/gui/assets/menu-d_G9km1-.js +1 -0
  267. package/dist/gui/assets/shield-check-Dd1Yv3l2.js +1 -0
  268. package/dist/gui/assets/square-check-big-BSIiYXPI.js +1 -0
  269. package/dist/gui/assets/tabs-BHwTt4t1.js +1 -0
  270. package/dist/gui/assets/{trash-2-CtR35_eF.js → trash-2-CMdv3eD3.js} +1 -1
  271. package/dist/gui/assets/useAriaLabelledBy-BmJErI5a.js +1 -0
  272. package/dist/gui/assets/useValueChanged-WS99hTmm.js +1 -0
  273. package/dist/gui/assets/{vsc-DFh0-53h.js → vsc-eMRaEwN3.js} +1 -1
  274. package/dist/gui/favicon-aurora.svg +8 -0
  275. package/dist/gui/favicon.ico +0 -0
  276. package/dist/gui/favicon.svg +8 -1
  277. package/dist/gui/index.html +21 -12
  278. package/dist/harness/claude.d.ts.map +1 -1
  279. package/dist/harness/claude.js +97 -7
  280. package/dist/harness/claude.js.map +1 -1
  281. package/dist/harness/codex.d.ts +1 -0
  282. package/dist/harness/codex.d.ts.map +1 -1
  283. package/dist/harness/codex.js +51 -34
  284. package/dist/harness/codex.js.map +1 -1
  285. package/dist/index.js +57 -27
  286. package/dist/index.js.map +1 -1
  287. package/dist/mcp/server.d.ts +12 -1
  288. package/dist/mcp/server.d.ts.map +1 -1
  289. package/dist/mcp/server.js +24 -6
  290. package/dist/mcp/server.js.map +1 -1
  291. package/dist/mcp/tools.d.ts +4 -2
  292. package/dist/mcp/tools.d.ts.map +1 -1
  293. package/dist/mcp/tools.js +372 -17
  294. package/dist/mcp/tools.js.map +1 -1
  295. package/dist/orchestration/detect.d.ts.map +1 -1
  296. package/dist/orchestration/detect.js +12 -5
  297. package/dist/orchestration/detect.js.map +1 -1
  298. package/dist/orchestration/log-tail.js +17 -17
  299. package/dist/orchestration/log-tail.js.map +1 -1
  300. package/dist/orchestration/runner.d.ts +1 -1
  301. package/dist/orchestration/runner.d.ts.map +1 -1
  302. package/dist/orchestration/runner.js +13 -8
  303. package/dist/orchestration/runner.js.map +1 -1
  304. package/dist/resources/agents-catalog.d.ts +3 -1
  305. package/dist/resources/agents-catalog.d.ts.map +1 -1
  306. package/dist/resources/agents-catalog.js +12 -6
  307. package/dist/resources/agents-catalog.js.map +1 -1
  308. package/dist/resources/contracts.d.ts +7 -0
  309. package/dist/resources/contracts.d.ts.map +1 -1
  310. package/dist/resources/contracts.js +8 -1
  311. package/dist/resources/contracts.js.map +1 -1
  312. package/dist/resources/fs-safety.d.ts +7 -1
  313. package/dist/resources/fs-safety.d.ts.map +1 -1
  314. package/dist/resources/fs-safety.js +44 -1
  315. package/dist/resources/fs-safety.js.map +1 -1
  316. package/dist/resources/materializer.d.ts +1 -0
  317. package/dist/resources/materializer.d.ts.map +1 -1
  318. package/dist/resources/materializer.js +48 -17
  319. package/dist/resources/materializer.js.map +1 -1
  320. package/dist/resources/service.d.ts.map +1 -1
  321. package/dist/resources/service.js +3 -2
  322. package/dist/resources/service.js.map +1 -1
  323. package/dist/server.d.ts +2 -19
  324. package/dist/server.d.ts.map +1 -1
  325. package/dist/server.js +1158 -318
  326. package/dist/server.js.map +1 -1
  327. package/dist/static-resources/workflows/cost-aware-sol-luna.md +95 -0
  328. package/dist/storage/db.d.ts +94 -0
  329. package/dist/storage/db.d.ts.map +1 -0
  330. package/dist/storage/db.js +609 -0
  331. package/dist/storage/db.js.map +1 -0
  332. package/dist/types.d.ts +19 -0
  333. package/dist/types.d.ts.map +1 -1
  334. package/dist/utils/detect-ai.d.ts.map +1 -1
  335. package/dist/utils/detect-ai.js +1 -3
  336. package/dist/utils/detect-ai.js.map +1 -1
  337. package/dist/utils/multi-git.d.ts +0 -8
  338. package/dist/utils/multi-git.d.ts.map +1 -1
  339. package/dist/utils/multi-git.js +12 -4
  340. package/dist/utils/multi-git.js.map +1 -1
  341. package/dist/utils/pr.d.ts.map +1 -1
  342. package/dist/utils/pr.js +10 -5
  343. package/dist/utils/pr.js.map +1 -1
  344. package/dist/utils/resolve-workspace.d.ts +1 -1
  345. package/dist/utils/resolve-workspace.d.ts.map +1 -1
  346. package/dist/utils/resolve-workspace.js +10 -3
  347. package/dist/utils/resolve-workspace.js.map +1 -1
  348. package/dist/utils/session-finder.d.ts +12 -1
  349. package/dist/utils/session-finder.d.ts.map +1 -1
  350. package/dist/utils/session-finder.js +183 -0
  351. package/dist/utils/session-finder.js.map +1 -1
  352. package/dist/utils/skills-catalog.d.ts +5 -1
  353. package/dist/utils/skills-catalog.d.ts.map +1 -1
  354. package/dist/utils/skills-catalog.js +131 -75
  355. package/dist/utils/skills-catalog.js.map +1 -1
  356. package/dist/utils/terminal-launch.d.ts +4 -0
  357. package/dist/utils/terminal-launch.d.ts.map +1 -1
  358. package/dist/utils/terminal-launch.js +71 -12
  359. package/dist/utils/terminal-launch.js.map +1 -1
  360. package/dist/utils/update-check.d.ts +4 -1
  361. package/dist/utils/update-check.d.ts.map +1 -1
  362. package/dist/utils/update-check.js +66 -19
  363. package/dist/utils/update-check.js.map +1 -1
  364. package/dist/utils/workflows.d.ts +4 -1
  365. package/dist/utils/workflows.d.ts.map +1 -1
  366. package/dist/utils/workflows.js +51 -21
  367. package/dist/utils/workflows.js.map +1 -1
  368. package/dist/workrooms/client.d.ts +54 -0
  369. package/dist/workrooms/client.d.ts.map +1 -0
  370. package/dist/workrooms/client.js +191 -0
  371. package/dist/workrooms/client.js.map +1 -0
  372. package/dist/workrooms/contracts.d.ts +579 -0
  373. package/dist/workrooms/contracts.d.ts.map +1 -0
  374. package/dist/workrooms/contracts.js +231 -0
  375. package/dist/workrooms/contracts.js.map +1 -0
  376. package/dist/workrooms/crypto.d.ts +30 -0
  377. package/dist/workrooms/crypto.d.ts.map +1 -0
  378. package/dist/workrooms/crypto.js +72 -0
  379. package/dist/workrooms/crypto.js.map +1 -0
  380. package/dist/workrooms/host.d.ts +61 -0
  381. package/dist/workrooms/host.d.ts.map +1 -0
  382. package/dist/workrooms/host.js +747 -0
  383. package/dist/workrooms/host.js.map +1 -0
  384. package/dist/workrooms/local-resources.d.ts +26 -0
  385. package/dist/workrooms/local-resources.d.ts.map +1 -0
  386. package/dist/workrooms/local-resources.js +367 -0
  387. package/dist/workrooms/local-resources.js.map +1 -0
  388. package/dist/workrooms/manager.d.ts +228 -0
  389. package/dist/workrooms/manager.d.ts.map +1 -0
  390. package/dist/workrooms/manager.js +768 -0
  391. package/dist/workrooms/manager.js.map +1 -0
  392. package/dist/workrooms/milestones.d.ts +42 -0
  393. package/dist/workrooms/milestones.d.ts.map +1 -0
  394. package/dist/workrooms/milestones.js +94 -0
  395. package/dist/workrooms/milestones.js.map +1 -0
  396. package/dist/workrooms/portable.d.ts +21 -0
  397. package/dist/workrooms/portable.d.ts.map +1 -0
  398. package/dist/workrooms/portable.js +201 -0
  399. package/dist/workrooms/portable.js.map +1 -0
  400. package/dist/workrooms/resource-package.d.ts +7 -0
  401. package/dist/workrooms/resource-package.d.ts.map +1 -0
  402. package/dist/workrooms/resource-package.js +260 -0
  403. package/dist/workrooms/resource-package.js.map +1 -0
  404. package/dist/workrooms/service.d.ts +92 -0
  405. package/dist/workrooms/service.d.ts.map +1 -0
  406. package/dist/workrooms/service.js +788 -0
  407. package/dist/workrooms/service.js.map +1 -0
  408. package/dist/workrooms/sqlite-store.d.ts +31 -0
  409. package/dist/workrooms/sqlite-store.d.ts.map +1 -0
  410. package/dist/workrooms/sqlite-store.js +511 -0
  411. package/dist/workrooms/sqlite-store.js.map +1 -0
  412. package/dist/workrooms/store.d.ts +542 -0
  413. package/dist/workrooms/store.d.ts.map +1 -0
  414. package/dist/workrooms/store.js +71 -0
  415. package/dist/workrooms/store.js.map +1 -0
  416. package/package.json +16 -10
  417. package/resources/workflows/cost-aware-sol-luna.md +95 -0
  418. package/dist/agent/AcpCliAdapter.test.d.ts +0 -2
  419. package/dist/agent/AcpCliAdapter.test.d.ts.map +0 -1
  420. package/dist/agent/AcpCliAdapter.test.js +0 -235
  421. package/dist/agent/AcpCliAdapter.test.js.map +0 -1
  422. package/dist/agent/AntigravityCliAdapter.test.d.ts +0 -2
  423. package/dist/agent/AntigravityCliAdapter.test.d.ts.map +0 -1
  424. package/dist/agent/AntigravityCliAdapter.test.js +0 -222
  425. package/dist/agent/AntigravityCliAdapter.test.js.map +0 -1
  426. package/dist/agent/ClaudeCliAdapter.test.d.ts +0 -2
  427. package/dist/agent/ClaudeCliAdapter.test.d.ts.map +0 -1
  428. package/dist/agent/ClaudeCliAdapter.test.js +0 -320
  429. package/dist/agent/ClaudeCliAdapter.test.js.map +0 -1
  430. package/dist/agent/ClaudeSdkAdapter.test.d.ts +0 -2
  431. package/dist/agent/ClaudeSdkAdapter.test.d.ts.map +0 -1
  432. package/dist/agent/ClaudeSdkAdapter.test.js +0 -155
  433. package/dist/agent/ClaudeSdkAdapter.test.js.map +0 -1
  434. package/dist/agent/CliAdapterBase.test.d.ts +0 -2
  435. package/dist/agent/CliAdapterBase.test.d.ts.map +0 -1
  436. package/dist/agent/CliAdapterBase.test.js +0 -240
  437. package/dist/agent/CliAdapterBase.test.js.map +0 -1
  438. package/dist/agent/CodexCliAdapter.test.d.ts +0 -2
  439. package/dist/agent/CodexCliAdapter.test.d.ts.map +0 -1
  440. package/dist/agent/CodexCliAdapter.test.js +0 -331
  441. package/dist/agent/CodexCliAdapter.test.js.map +0 -1
  442. package/dist/agent/CodexSdkAdapter.test.d.ts +0 -2
  443. package/dist/agent/CodexSdkAdapter.test.d.ts.map +0 -1
  444. package/dist/agent/CodexSdkAdapter.test.js +0 -76
  445. package/dist/agent/CodexSdkAdapter.test.js.map +0 -1
  446. package/dist/agent/ProviderRegistry.test.d.ts +0 -2
  447. package/dist/agent/ProviderRegistry.test.d.ts.map +0 -1
  448. package/dist/agent/ProviderRegistry.test.js +0 -129
  449. package/dist/agent/ProviderRegistry.test.js.map +0 -1
  450. package/dist/agent/adapters.test.d.ts +0 -2
  451. package/dist/agent/adapters.test.d.ts.map +0 -1
  452. package/dist/agent/adapters.test.js +0 -71
  453. package/dist/agent/adapters.test.js.map +0 -1
  454. package/dist/agent/cliAvailability.test.d.ts +0 -2
  455. package/dist/agent/cliAvailability.test.d.ts.map +0 -1
  456. package/dist/agent/cliAvailability.test.js +0 -217
  457. package/dist/agent/cliAvailability.test.js.map +0 -1
  458. package/dist/agent/mcp-config.test.d.ts +0 -2
  459. package/dist/agent/mcp-config.test.d.ts.map +0 -1
  460. package/dist/agent/mcp-config.test.js +0 -22
  461. package/dist/agent/mcp-config.test.js.map +0 -1
  462. package/dist/agent/models.test.d.ts +0 -2
  463. package/dist/agent/models.test.d.ts.map +0 -1
  464. package/dist/agent/models.test.js +0 -40
  465. package/dist/agent/models.test.js.map +0 -1
  466. package/dist/agent/nativeAgent.test.d.ts +0 -2
  467. package/dist/agent/nativeAgent.test.d.ts.map +0 -1
  468. package/dist/agent/nativeAgent.test.js +0 -35
  469. package/dist/agent/nativeAgent.test.js.map +0 -1
  470. package/dist/agent/nativeTools.test.d.ts +0 -2
  471. package/dist/agent/nativeTools.test.d.ts.map +0 -1
  472. package/dist/agent/nativeTools.test.js +0 -67
  473. package/dist/agent/nativeTools.test.js.map +0 -1
  474. package/dist/agent/orchestrator.test.d.ts +0 -2
  475. package/dist/agent/orchestrator.test.d.ts.map +0 -1
  476. package/dist/agent/orchestrator.test.js +0 -284
  477. package/dist/agent/orchestrator.test.js.map +0 -1
  478. package/dist/agent/session.test.d.ts +0 -2
  479. package/dist/agent/session.test.d.ts.map +0 -1
  480. package/dist/agent/session.test.js +0 -74
  481. package/dist/agent/session.test.js.map +0 -1
  482. package/dist/agent/smoke-chat.test.d.ts +0 -2
  483. package/dist/agent/smoke-chat.test.d.ts.map +0 -1
  484. package/dist/agent/smoke-chat.test.js +0 -166
  485. package/dist/agent/smoke-chat.test.js.map +0 -1
  486. package/dist/commands/add-repo.test.d.ts +0 -2
  487. package/dist/commands/add-repo.test.d.ts.map +0 -1
  488. package/dist/commands/add-repo.test.js +0 -30
  489. package/dist/commands/add-repo.test.js.map +0 -1
  490. package/dist/commands/commands.test.d.ts +0 -2
  491. package/dist/commands/commands.test.d.ts.map +0 -1
  492. package/dist/commands/commands.test.js +0 -156
  493. package/dist/commands/commands.test.js.map +0 -1
  494. package/dist/commands/commit.test.d.ts +0 -2
  495. package/dist/commands/commit.test.d.ts.map +0 -1
  496. package/dist/commands/commit.test.js +0 -54
  497. package/dist/commands/commit.test.js.map +0 -1
  498. package/dist/commands/isolate.test.d.ts +0 -2
  499. package/dist/commands/isolate.test.d.ts.map +0 -1
  500. package/dist/commands/isolate.test.js +0 -59
  501. package/dist/commands/isolate.test.js.map +0 -1
  502. package/dist/commands/knowledge.test.d.ts +0 -2
  503. package/dist/commands/knowledge.test.d.ts.map +0 -1
  504. package/dist/commands/knowledge.test.js +0 -90
  505. package/dist/commands/knowledge.test.js.map +0 -1
  506. package/dist/commands/status.test.d.ts +0 -2
  507. package/dist/commands/status.test.d.ts.map +0 -1
  508. package/dist/commands/status.test.js +0 -31
  509. package/dist/commands/status.test.js.map +0 -1
  510. package/dist/commands/ui.test.d.ts +0 -2
  511. package/dist/commands/ui.test.d.ts.map +0 -1
  512. package/dist/commands/ui.test.js +0 -58
  513. package/dist/commands/ui.test.js.map +0 -1
  514. package/dist/core/adapters/storage.test.d.ts +0 -2
  515. package/dist/core/adapters/storage.test.d.ts.map +0 -1
  516. package/dist/core/adapters/storage.test.js +0 -65
  517. package/dist/core/adapters/storage.test.js.map +0 -1
  518. package/dist/core/analysis-cache.test.d.ts +0 -2
  519. package/dist/core/analysis-cache.test.d.ts.map +0 -1
  520. package/dist/core/analysis-cache.test.js +0 -163
  521. package/dist/core/analysis-cache.test.js.map +0 -1
  522. package/dist/core/config.test.d.ts +0 -2
  523. package/dist/core/config.test.d.ts.map +0 -1
  524. package/dist/core/config.test.js +0 -90
  525. package/dist/core/config.test.js.map +0 -1
  526. package/dist/core/finish.test.d.ts +0 -2
  527. package/dist/core/finish.test.d.ts.map +0 -1
  528. package/dist/core/finish.test.js +0 -110
  529. package/dist/core/finish.test.js.map +0 -1
  530. package/dist/core/generation-lock.test.d.ts +0 -2
  531. package/dist/core/generation-lock.test.d.ts.map +0 -1
  532. package/dist/core/generation-lock.test.js +0 -134
  533. package/dist/core/generation-lock.test.js.map +0 -1
  534. package/dist/core/isolate.test.d.ts +0 -2
  535. package/dist/core/isolate.test.d.ts.map +0 -1
  536. package/dist/core/isolate.test.js +0 -320
  537. package/dist/core/isolate.test.js.map +0 -1
  538. package/dist/core/knowledge.test.d.ts +0 -2
  539. package/dist/core/knowledge.test.d.ts.map +0 -1
  540. package/dist/core/knowledge.test.js +0 -287
  541. package/dist/core/knowledge.test.js.map +0 -1
  542. package/dist/core/locks.test.d.ts +0 -2
  543. package/dist/core/locks.test.d.ts.map +0 -1
  544. package/dist/core/locks.test.js +0 -143
  545. package/dist/core/locks.test.js.map +0 -1
  546. package/dist/core/new-repo.test.d.ts +0 -2
  547. package/dist/core/new-repo.test.d.ts.map +0 -1
  548. package/dist/core/new-repo.test.js +0 -78
  549. package/dist/core/new-repo.test.js.map +0 -1
  550. package/dist/core/progress.test.d.ts +0 -2
  551. package/dist/core/progress.test.d.ts.map +0 -1
  552. package/dist/core/progress.test.js +0 -52
  553. package/dist/core/progress.test.js.map +0 -1
  554. package/dist/core/projects.test.d.ts +0 -2
  555. package/dist/core/projects.test.d.ts.map +0 -1
  556. package/dist/core/projects.test.js +0 -165
  557. package/dist/core/projects.test.js.map +0 -1
  558. package/dist/core/refresh.test.d.ts +0 -2
  559. package/dist/core/refresh.test.d.ts.map +0 -1
  560. package/dist/core/refresh.test.js +0 -93
  561. package/dist/core/refresh.test.js.map +0 -1
  562. package/dist/core/scanner.test.d.ts +0 -2
  563. package/dist/core/scanner.test.d.ts.map +0 -1
  564. package/dist/core/scanner.test.js +0 -53
  565. package/dist/core/scanner.test.js.map +0 -1
  566. package/dist/core/scheduler.test.d.ts +0 -2
  567. package/dist/core/scheduler.test.d.ts.map +0 -1
  568. package/dist/core/scheduler.test.js +0 -169
  569. package/dist/core/scheduler.test.js.map +0 -1
  570. package/dist/core/sync.test.d.ts +0 -2
  571. package/dist/core/sync.test.d.ts.map +0 -1
  572. package/dist/core/sync.test.js +0 -128
  573. package/dist/core/sync.test.js.map +0 -1
  574. package/dist/core/workspace-git.test.d.ts +0 -2
  575. package/dist/core/workspace-git.test.d.ts.map +0 -1
  576. package/dist/core/workspace-git.test.js +0 -52
  577. package/dist/core/workspace-git.test.js.map +0 -1
  578. package/dist/core/workspace-skills-workflow.integration.test.d.ts +0 -2
  579. package/dist/core/workspace-skills-workflow.integration.test.d.ts.map +0 -1
  580. package/dist/core/workspace-skills-workflow.integration.test.js +0 -177
  581. package/dist/core/workspace-skills-workflow.integration.test.js.map +0 -1
  582. package/dist/core/workspace-state.test.d.ts +0 -2
  583. package/dist/core/workspace-state.test.d.ts.map +0 -1
  584. package/dist/core/workspace-state.test.js +0 -84
  585. package/dist/core/workspace-state.test.js.map +0 -1
  586. package/dist/core/workspace.create.test.d.ts +0 -2
  587. package/dist/core/workspace.create.test.d.ts.map +0 -1
  588. package/dist/core/workspace.create.test.js +0 -224
  589. package/dist/core/workspace.create.test.js.map +0 -1
  590. package/dist/core/workspace.test.d.ts +0 -2
  591. package/dist/core/workspace.test.d.ts.map +0 -1
  592. package/dist/core/workspace.test.js +0 -53
  593. package/dist/core/workspace.test.js.map +0 -1
  594. package/dist/core/worktree.test.d.ts +0 -2
  595. package/dist/core/worktree.test.d.ts.map +0 -1
  596. package/dist/core/worktree.test.js +0 -153
  597. package/dist/core/worktree.test.js.map +0 -1
  598. package/dist/generators/base.test.d.ts +0 -2
  599. package/dist/generators/base.test.d.ts.map +0 -1
  600. package/dist/generators/base.test.js +0 -287
  601. package/dist/generators/base.test.js.map +0 -1
  602. package/dist/generators/knowledge-migration.test.d.ts +0 -2
  603. package/dist/generators/knowledge-migration.test.d.ts.map +0 -1
  604. package/dist/generators/knowledge-migration.test.js +0 -22
  605. package/dist/generators/knowledge-migration.test.js.map +0 -1
  606. package/dist/generators/plan-generator.test.d.ts +0 -2
  607. package/dist/generators/plan-generator.test.d.ts.map +0 -1
  608. package/dist/generators/plan-generator.test.js +0 -220
  609. package/dist/generators/plan-generator.test.js.map +0 -1
  610. package/dist/generators/skills-generator.test.d.ts +0 -2
  611. package/dist/generators/skills-generator.test.d.ts.map +0 -1
  612. package/dist/generators/skills-generator.test.js +0 -79
  613. package/dist/generators/skills-generator.test.js.map +0 -1
  614. package/dist/gui/assets/AgentsPage-B4fx43rd.js +0 -3
  615. package/dist/gui/assets/CSPContext-DBUu9tE0.js +0 -1
  616. package/dist/gui/assets/DashboardPage-DH52G8IG.js +0 -1
  617. package/dist/gui/assets/GettingStartedPage-BlTrQhvi.js +0 -1
  618. package/dist/gui/assets/ProjectsPage-BPVSLPXv.js +0 -1
  619. package/dist/gui/assets/ScaffoldRepoInline-ywcpq9PX.js +0 -1
  620. package/dist/gui/assets/SettingsPage-RQr8SalV.js +0 -1
  621. package/dist/gui/assets/SkillsPage-CYoSRrwx.js +0 -6
  622. package/dist/gui/assets/StartWorkPage-DBKK4lFi.js +0 -1
  623. package/dist/gui/assets/WorkspacesPage-DxEONvSq.js +0 -6
  624. package/dist/gui/assets/alert-dialog-CQAX2yRx.js +0 -1
  625. package/dist/gui/assets/badge-CAohJGj6.js +0 -1
  626. package/dist/gui/assets/bot-C2HWrAft.js +0 -1
  627. package/dist/gui/assets/button-BDDZnIHx.js +0 -1
  628. package/dist/gui/assets/card-BKXzd_5L.js +0 -1
  629. package/dist/gui/assets/chevron-down-qTL8HPug.js +0 -1
  630. package/dist/gui/assets/cpu-Cdwd84Jz.js +0 -1
  631. package/dist/gui/assets/empty-BdlgbifD.js +0 -1
  632. package/dist/gui/assets/index-BF6pre6G.css +0 -2
  633. package/dist/gui/assets/index-C_rwnBzF.js +0 -52
  634. package/dist/gui/assets/isElementDisabled-uy_EOitH.js +0 -1
  635. package/dist/gui/assets/label-CG5exjag.js +0 -1
  636. package/dist/gui/assets/square-DlIYnlSq.js +0 -1
  637. package/dist/gui/assets/textarea-ByfoCb54.js +0 -1
  638. package/dist/gui/assets/useAriaLabelledBy-ClesgjMe.js +0 -1
  639. package/dist/gui/assets/useValueChanged-DeYvGnKs.js +0 -1
  640. package/dist/gui/assets/zap-CWG_Lvki.js +0 -1
  641. package/dist/harness/contract.test.d.ts +0 -2
  642. package/dist/harness/contract.test.d.ts.map +0 -1
  643. package/dist/harness/contract.test.js +0 -426
  644. package/dist/harness/contract.test.js.map +0 -1
  645. package/dist/harness/harness.test.d.ts +0 -2
  646. package/dist/harness/harness.test.d.ts.map +0 -1
  647. package/dist/harness/harness.test.js +0 -215
  648. package/dist/harness/harness.test.js.map +0 -1
  649. package/dist/mcp/tools.test.d.ts +0 -2
  650. package/dist/mcp/tools.test.d.ts.map +0 -1
  651. package/dist/mcp/tools.test.js +0 -194
  652. package/dist/mcp/tools.test.js.map +0 -1
  653. package/dist/orchestration/log-tail.test.d.ts +0 -2
  654. package/dist/orchestration/log-tail.test.d.ts.map +0 -1
  655. package/dist/orchestration/log-tail.test.js +0 -72
  656. package/dist/orchestration/log-tail.test.js.map +0 -1
  657. package/dist/orchestration/orchestrator.test.d.ts +0 -2
  658. package/dist/orchestration/orchestrator.test.d.ts.map +0 -1
  659. package/dist/orchestration/orchestrator.test.js +0 -85
  660. package/dist/orchestration/orchestrator.test.js.map +0 -1
  661. package/dist/orchestration/runner.test.d.ts +0 -2
  662. package/dist/orchestration/runner.test.d.ts.map +0 -1
  663. package/dist/orchestration/runner.test.js +0 -233
  664. package/dist/orchestration/runner.test.js.map +0 -1
  665. package/dist/resources/agents-catalog.test.d.ts +0 -2
  666. package/dist/resources/agents-catalog.test.d.ts.map +0 -1
  667. package/dist/resources/agents-catalog.test.js +0 -50
  668. package/dist/resources/agents-catalog.test.js.map +0 -1
  669. package/dist/resources/codex-agent.test.d.ts +0 -2
  670. package/dist/resources/codex-agent.test.d.ts.map +0 -1
  671. package/dist/resources/codex-agent.test.js +0 -27
  672. package/dist/resources/codex-agent.test.js.map +0 -1
  673. package/dist/resources/materializer.test.d.ts +0 -2
  674. package/dist/resources/materializer.test.d.ts.map +0 -1
  675. package/dist/resources/materializer.test.js +0 -149
  676. package/dist/resources/materializer.test.js.map +0 -1
  677. package/dist/resources/service.test.d.ts +0 -2
  678. package/dist/resources/service.test.d.ts.map +0 -1
  679. package/dist/resources/service.test.js +0 -58
  680. package/dist/resources/service.test.js.map +0 -1
  681. package/dist/server.test.d.ts +0 -2
  682. package/dist/server.test.d.ts.map +0 -1
  683. package/dist/server.test.js +0 -1697
  684. package/dist/server.test.js.map +0 -1
  685. package/dist/utils/detect-ai.test.d.ts +0 -2
  686. package/dist/utils/detect-ai.test.d.ts.map +0 -1
  687. package/dist/utils/detect-ai.test.js +0 -70
  688. package/dist/utils/detect-ai.test.js.map +0 -1
  689. package/dist/utils/detect-editors.test.d.ts +0 -2
  690. package/dist/utils/detect-editors.test.d.ts.map +0 -1
  691. package/dist/utils/detect-editors.test.js +0 -85
  692. package/dist/utils/detect-editors.test.js.map +0 -1
  693. package/dist/utils/git.test.d.ts +0 -2
  694. package/dist/utils/git.test.d.ts.map +0 -1
  695. package/dist/utils/git.test.js +0 -125
  696. package/dist/utils/git.test.js.map +0 -1
  697. package/dist/utils/multi-git.test.d.ts +0 -2
  698. package/dist/utils/multi-git.test.d.ts.map +0 -1
  699. package/dist/utils/multi-git.test.js +0 -330
  700. package/dist/utils/multi-git.test.js.map +0 -1
  701. package/dist/utils/pr.test.d.ts +0 -2
  702. package/dist/utils/pr.test.d.ts.map +0 -1
  703. package/dist/utils/pr.test.js +0 -47
  704. package/dist/utils/pr.test.js.map +0 -1
  705. package/dist/utils/session-finder.test.d.ts +0 -2
  706. package/dist/utils/session-finder.test.d.ts.map +0 -1
  707. package/dist/utils/session-finder.test.js +0 -304
  708. package/dist/utils/session-finder.test.js.map +0 -1
  709. package/dist/utils/skills-catalog.test.d.ts +0 -2
  710. package/dist/utils/skills-catalog.test.d.ts.map +0 -1
  711. package/dist/utils/skills-catalog.test.js +0 -281
  712. package/dist/utils/skills-catalog.test.js.map +0 -1
  713. package/dist/utils/system-scanner.test.d.ts +0 -2
  714. package/dist/utils/system-scanner.test.d.ts.map +0 -1
  715. package/dist/utils/system-scanner.test.js +0 -73
  716. package/dist/utils/system-scanner.test.js.map +0 -1
  717. package/dist/utils/terminal-launch.test.d.ts +0 -2
  718. package/dist/utils/terminal-launch.test.d.ts.map +0 -1
  719. package/dist/utils/terminal-launch.test.js +0 -117
  720. package/dist/utils/terminal-launch.test.js.map +0 -1
  721. package/dist/utils/update-check.test.d.ts +0 -2
  722. package/dist/utils/update-check.test.d.ts.map +0 -1
  723. package/dist/utils/update-check.test.js +0 -30
  724. package/dist/utils/update-check.test.js.map +0 -1
  725. package/dist/utils/workflows.test.d.ts +0 -2
  726. package/dist/utils/workflows.test.d.ts.map +0 -1
  727. package/dist/utils/workflows.test.js +0 -86
  728. package/dist/utils/workflows.test.js.map +0 -1
  729. package/dist/utils/workspace-launch.test.d.ts +0 -2
  730. package/dist/utils/workspace-launch.test.d.ts.map +0 -1
  731. package/dist/utils/workspace-launch.test.js +0 -128
  732. package/dist/utils/workspace-launch.test.js.map +0 -1
@@ -0,0 +1,788 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { WORKROOM_AUDIT_LIMIT, WORKROOM_DOCUMENT_HISTORY_LIMIT, WORKROOM_INVITE_TTL_MS, WORKROOM_MAX_DOCUMENT_BYTES, WORKROOM_MAX_PARTICIPANTS, WORKROOM_MAX_PENDING_JOINS, WORKROOM_MAX_RETAINED_INVITES, WORKROOM_MAX_RETAINED_JOIN_REQUESTS, WORKROOM_MAX_RESOURCES, WORKROOM_MAX_RESOURCE_CATALOG_JSON_BYTES, WORKROOM_MAX_WORKFLOW_EVIDENCE_BYTES, WORKROOM_MAX_WORKFLOW_STEP_EVIDENCE_BYTES, WORKROOM_SCHEMA_VERSION, WorkroomAuthorizationError, WorkroomRevisionError, WorkroomValidationError, digestWorkflowPackage, documentNameSchema, portableFeatureBundleSchema, portableRepoSchema, workflowPackageSchema, workflowProgressSchema, workflowStepProgressSchema, } from './contracts.js';
3
+ import { hashPassword, randomToken, tokenDigest, verifyPassword } from './crypto.js';
4
+ import { digestResourcePackage, validateResourcePackage } from './resource-package.js';
5
+ const FAILED_ATTEMPT_WINDOW_MS = 10 * 60 * 1000;
6
+ const FAILED_ATTEMPT_LOCK_MS = 15 * 60 * 1000;
7
+ const MAX_FAILED_ATTEMPTS = 5;
8
+ function nowIso(now = new Date()) {
9
+ return now.toISOString();
10
+ }
11
+ export function assertResourceCatalogStorageSize(currentBytes, addedBytes) {
12
+ if (!Number.isSafeInteger(currentBytes) || !Number.isSafeInteger(addedBytes) || currentBytes < 0 || addedBytes < 0
13
+ || currentBytes + addedBytes > WORKROOM_MAX_RESOURCE_CATALOG_JSON_BYTES) {
14
+ throw new WorkroomValidationError('This Workroom has reached its 40 MiB shared resource storage limit.');
15
+ }
16
+ }
17
+ function shortId(prefix) {
18
+ return `${prefix}-${randomUUID().replace(/-/g, '').slice(0, 20)}`;
19
+ }
20
+ function appendEvent(state, event, createdAt = nowIso()) {
21
+ const next = {
22
+ ...event,
23
+ sequence: (state.activity.at(-1)?.sequence ?? 0) + 1,
24
+ createdAt,
25
+ };
26
+ return {
27
+ ...state,
28
+ activity: [...state.activity, next].slice(-WORKROOM_AUDIT_LIMIT),
29
+ };
30
+ }
31
+ function bump(state) {
32
+ return { ...state, revision: state.revision + 1 };
33
+ }
34
+ function publicParticipant(participant) {
35
+ const { deviceTokenHash: _deviceSecret, agentTokenHash: _agentSecret, ...safe } = participant;
36
+ return safe;
37
+ }
38
+ function compactParticipants(participants) {
39
+ const active = participants.filter((participant) => !participant.revokedAt);
40
+ const revokedCapacity = WORKROOM_MAX_PARTICIPANTS - active.length;
41
+ if (revokedCapacity <= 0)
42
+ return active;
43
+ const retainedRevoked = participants
44
+ .filter((participant) => participant.revokedAt)
45
+ .slice(-revokedCapacity);
46
+ return [...active, ...retainedRevoked];
47
+ }
48
+ function compactJoinRequests(requests) {
49
+ const pending = requests.filter((request) => request.status === 'pending');
50
+ const retainedDecided = requests
51
+ .filter((request) => request.status !== 'pending')
52
+ .slice(-(WORKROOM_MAX_RETAINED_JOIN_REQUESTS - pending.length));
53
+ return [...retainedDecided, ...pending];
54
+ }
55
+ function liveInvites(invites, now = Date.now()) {
56
+ return invites.filter((invite) => !invite.usedAt && Date.parse(invite.expiresAt) > now);
57
+ }
58
+ function rolePermits(actual, allowed) {
59
+ return allowed.includes(actual);
60
+ }
61
+ function findPrincipal(state, rawToken, allowed = ['host', 'publisher', 'member'], allowedScopes = ['human']) {
62
+ const digest = tokenDigest(rawToken);
63
+ const actor = state.participants.find((participant) => (participant.deviceTokenHash === digest || participant.agentTokenHash === digest) && !participant.revokedAt);
64
+ if (!actor || !rolePermits(actor.role, allowed))
65
+ throw new WorkroomAuthorizationError();
66
+ const scope = actor.deviceTokenHash === digest ? 'human' : 'agent';
67
+ if (!allowedScopes.includes(scope))
68
+ throw new WorkroomAuthorizationError();
69
+ return { actor, scope };
70
+ }
71
+ function findActor(state, rawToken, allowed = ['host', 'publisher', 'member']) {
72
+ return findPrincipal(state, rawToken, allowed, ['human']).actor;
73
+ }
74
+ function sanitizeDocuments(documents, hostMemberId, createdAt) {
75
+ const makeDocument = (name) => {
76
+ const content = documents[name] ?? '';
77
+ if (Buffer.byteLength(content, 'utf8') > WORKROOM_MAX_DOCUMENT_BYTES) {
78
+ throw new WorkroomValidationError('Shared documents are limited to 256 KiB.');
79
+ }
80
+ return {
81
+ name,
82
+ revision: 0,
83
+ content,
84
+ updatedAt: createdAt,
85
+ updatedBy: hostMemberId,
86
+ history: [],
87
+ };
88
+ };
89
+ return {
90
+ plan: makeDocument('plan'),
91
+ decisions: makeDocument('decisions'),
92
+ handoff: makeDocument('handoff'),
93
+ };
94
+ }
95
+ export async function createInitialWorkroomState(input) {
96
+ if (input.password.length < 12 || input.password.length > 128) {
97
+ throw new WorkroomValidationError('Workroom passwords must contain 12–128 characters.');
98
+ }
99
+ const bundle = portableFeatureBundleSchema.parse(input.bundle);
100
+ const createdAt = nowIso();
101
+ const hostMemberId = shortId('member');
102
+ const hostToken = randomToken();
103
+ const hostAgentToken = randomToken();
104
+ const host = {
105
+ id: hostMemberId,
106
+ displayName: input.hostDisplayName.trim().slice(0, 80) || 'Host',
107
+ role: 'host',
108
+ joinedAt: createdAt,
109
+ lastSeenAt: createdAt,
110
+ deviceTokenHash: tokenDigest(hostToken),
111
+ agentTokenHash: tokenDigest(hostAgentToken),
112
+ };
113
+ let state = {
114
+ schemaVersion: WORKROOM_SCHEMA_VERSION,
115
+ roomId: input.roomId,
116
+ name: input.name.trim().slice(0, 160),
117
+ workspaceId: input.workspaceId,
118
+ address: input.address,
119
+ port: input.port,
120
+ certificateFingerprint: input.certificateFingerprint,
121
+ createdAt,
122
+ revision: 1,
123
+ password: await hashPassword(input.password),
124
+ hostMemberId,
125
+ bundle,
126
+ documents: sanitizeDocuments(input.documents, hostMemberId, createdAt),
127
+ participants: [host],
128
+ invites: [],
129
+ joinRequests: [],
130
+ attemptWindows: [],
131
+ resources: [],
132
+ activity: [],
133
+ };
134
+ state = appendEvent(state, {
135
+ type: 'room.created',
136
+ actorId: hostMemberId,
137
+ summary: 'Workroom created.',
138
+ }, createdAt);
139
+ return { state, hostToken, hostAgentToken };
140
+ }
141
+ export class WorkroomService {
142
+ store;
143
+ verifyRoomPassword;
144
+ passwordAttemptsByInvite = new Map();
145
+ passwordVerificationsInProgress = new Set();
146
+ constructor(store, verifyRoomPassword = verifyPassword) {
147
+ this.store = store;
148
+ this.verifyRoomPassword = verifyRoomPassword;
149
+ }
150
+ admitInvitePasswordAttempt(inviteHash, at) {
151
+ const current = this.passwordAttemptsByInvite.get(inviteHash);
152
+ const live = current && at - current.startedAt < FAILED_ATTEMPT_WINDOW_MS
153
+ ? current
154
+ : { startedAt: at, count: 0 };
155
+ if (live.count >= 20) {
156
+ throw new WorkroomValidationError('This invitation is temporarily limiting password attempts. Ask the host for a new invitation.');
157
+ }
158
+ this.passwordAttemptsByInvite.set(inviteHash, { startedAt: live.startedAt, count: live.count + 1 });
159
+ }
160
+ async authorizeHuman(rawToken, allowed = ['host', 'publisher', 'member']) {
161
+ findPrincipal(await this.store.read(), rawToken, allowed, ['human']);
162
+ }
163
+ async authorizeAgent(rawToken) {
164
+ findPrincipal(await this.store.read(), rawToken, undefined, ['agent']);
165
+ }
166
+ async authorizeRead(rawToken) {
167
+ findPrincipal(await this.store.read(), rawToken, undefined, ['human', 'agent']);
168
+ }
169
+ async verifyHostRecoveryPassword(password) {
170
+ if (password.length < 12 || password.length > 128)
171
+ return false;
172
+ return this.verifyRoomPassword(password, (await this.store.read()).password);
173
+ }
174
+ async snapshot(rawToken) {
175
+ const state = await this.store.read();
176
+ const { actor } = findPrincipal(state, rawToken, undefined, ['human', 'agent']);
177
+ return {
178
+ schemaVersion: WORKROOM_SCHEMA_VERSION,
179
+ roomId: state.roomId,
180
+ name: state.name,
181
+ address: state.address,
182
+ port: state.port,
183
+ certificateFingerprint: state.certificateFingerprint,
184
+ revision: state.revision,
185
+ createdAt: state.createdAt,
186
+ bundle: state.bundle,
187
+ documents: {
188
+ plan: { ...state.documents.plan, history: [] },
189
+ decisions: { ...state.documents.decisions, history: [] },
190
+ handoff: { ...state.documents.handoff, history: [] },
191
+ },
192
+ participants: state.participants.map(publicParticipant),
193
+ pendingJoins: actor.role === 'host'
194
+ ? state.joinRequests.filter((request) => request.status === 'pending').map((request) => ({
195
+ id: request.id,
196
+ displayName: request.displayName,
197
+ requestedAt: request.requestedAt,
198
+ }))
199
+ : [],
200
+ resources: state.resources,
201
+ workflowProgress: state.workflowProgress ? workflowProgressSchema.parse(state.workflowProgress) : undefined,
202
+ activity: state.activity.slice(-200),
203
+ };
204
+ }
205
+ async createInvite(actorToken) {
206
+ const token = randomToken();
207
+ return this.store.mutate((state) => {
208
+ const actor = findActor(state, actorToken, ['host']);
209
+ const createdAt = nowIso();
210
+ const expiresAt = new Date(Date.now() + WORKROOM_INVITE_TTL_MS).toISOString();
211
+ const invites = liveInvites(state.invites);
212
+ if (invites.length >= WORKROOM_MAX_RETAINED_INVITES) {
213
+ throw new WorkroomValidationError('This Workroom has reached its active invitation limit.');
214
+ }
215
+ let next = {
216
+ ...state,
217
+ invites: [...invites, {
218
+ id: shortId('invite'),
219
+ tokenHash: tokenDigest(token),
220
+ createdAt,
221
+ expiresAt,
222
+ createdBy: actor.id,
223
+ }],
224
+ };
225
+ next = bump(appendEvent(next, {
226
+ type: 'invite.created',
227
+ actorId: actor.id,
228
+ summary: 'Created an expiring one-use invitation.',
229
+ }, createdAt));
230
+ return { state: next, result: { token, expiresAt } };
231
+ });
232
+ }
233
+ async requestJoin(input) {
234
+ const displayName = input.displayName.trim();
235
+ if (!displayName || displayName.length > 80)
236
+ throw new WorkroomValidationError('Display name must contain 1–80 characters.');
237
+ if (!/^[a-z0-9][a-z0-9._-]{0,127}$/.test(input.roomId))
238
+ throw new WorkroomValidationError('Invalid Workroom ID.');
239
+ if (!/^[A-Za-z0-9_-]{40,256}$/.test(input.inviteToken))
240
+ throw new WorkroomValidationError('Invalid invitation credential.');
241
+ if (input.password.length < 12 || input.password.length > 128)
242
+ throw new WorkroomValidationError('Invalid Workroom password.');
243
+ if (!/^[A-Za-z0-9_-]{40,256}$/.test(input.deviceToken))
244
+ throw new WorkroomValidationError('Invalid device credential.');
245
+ if (!/^[A-Za-z0-9_-]{40,256}$/.test(input.agentToken) || input.agentToken === input.deviceToken) {
246
+ throw new WorkroomValidationError('Invalid agent credential.');
247
+ }
248
+ if (!input.sourceKey || input.sourceKey.length > 200)
249
+ throw new WorkroomValidationError('Invalid join source.');
250
+ // Invitation identifiers are high-entropy capabilities. Reject unknown,
251
+ // expired, consumed, or wrong-room capabilities before entering the
252
+ // mutation queue or paying the password KDF cost. They also must not
253
+ // contribute to a room-wide lock that arbitrary unauthenticated callers
254
+ // could use to deny access to legitimate invitees.
255
+ const initialState = await this.store.read();
256
+ const initialInviteHash = tokenDigest(input.inviteToken);
257
+ const initialInvite = initialState.invites.find((candidate) => candidate.tokenHash === initialInviteHash);
258
+ if (input.roomId !== initialState.roomId || !initialInvite || initialInvite.usedAt
259
+ || Date.parse(initialInvite.expiresAt) <= Date.now()) {
260
+ throw new WorkroomValidationError('Invalid or expired invitation or password.');
261
+ }
262
+ if (initialState.joinRequests.filter((request) => request.status === 'pending').length >= WORKROOM_MAX_PENDING_JOINS) {
263
+ throw new WorkroomValidationError('This Workroom has too many pending join requests.');
264
+ }
265
+ if (initialState.participants.filter((participant) => !participant.revokedAt).length >= WORKROOM_MAX_PARTICIPANTS) {
266
+ throw new WorkroomValidationError('This Workroom has reached its member limit.');
267
+ }
268
+ const initialNow = Date.now();
269
+ const initialCutoff = initialNow - FAILED_ATTEMPT_WINDOW_MS;
270
+ const initialAttempt = initialState.attemptWindows.find((window) => window.key === input.sourceKey && ((window.lockedUntil && Date.parse(window.lockedUntil) > initialNow)
271
+ || window.failures.some((value) => Date.parse(value) >= initialCutoff)));
272
+ if (initialAttempt?.lockedUntil && Date.parse(initialAttempt.lockedUntil) > initialNow) {
273
+ throw new WorkroomValidationError('Too many failed join attempts. Try again later.');
274
+ }
275
+ if (this.passwordVerificationsInProgress.has(initialInviteHash)) {
276
+ throw new WorkroomValidationError('This invitation already has a password verification in progress. Try again shortly.');
277
+ }
278
+ this.admitInvitePasswordAttempt(initialInviteHash, initialNow);
279
+ this.passwordVerificationsInProgress.add(initialInviteHash);
280
+ const initialPasswordRevision = JSON.stringify(initialState.password);
281
+ let outcome;
282
+ try {
283
+ const passwordMatches = await this.verifyRoomPassword(input.password, initialState.password);
284
+ outcome = await this.store.mutate((state) => {
285
+ const at = new Date();
286
+ const sourceKey = input.sourceKey;
287
+ const invite = state.invites.find((candidate) => candidate.tokenHash === initialInviteHash);
288
+ const inviteMatches = input.roomId === state.roomId
289
+ && invite !== undefined
290
+ && !invite.usedAt
291
+ && Date.parse(invite.expiresAt) > at.getTime()
292
+ && JSON.stringify(state.password) === initialPasswordRevision;
293
+ if (!inviteMatches)
294
+ return { state, result: { ok: false } };
295
+ if (state.joinRequests.filter((request) => request.status === 'pending').length >= WORKROOM_MAX_PENDING_JOINS) {
296
+ throw new WorkroomValidationError('This Workroom has too many pending join requests.');
297
+ }
298
+ if (state.participants.filter((participant) => !participant.revokedAt).length >= WORKROOM_MAX_PARTICIPANTS) {
299
+ throw new WorkroomValidationError('This Workroom has reached its member limit.');
300
+ }
301
+ const cutoff = at.getTime() - FAILED_ATTEMPT_WINDOW_MS;
302
+ const liveWindows = state.attemptWindows.filter((window) => ((window.lockedUntil && Date.parse(window.lockedUntil) > at.getTime())
303
+ || window.failures.some((value) => Date.parse(value) >= cutoff)));
304
+ const attempt = liveWindows.find((window) => window.key === sourceKey);
305
+ if (attempt?.lockedUntil && Date.parse(attempt.lockedUntil) > at.getTime()) {
306
+ throw new WorkroomValidationError('Too many failed join attempts. Try again later.');
307
+ }
308
+ if (!passwordMatches) {
309
+ const failures = [...(attempt?.failures ?? []).filter((value) => Date.parse(value) >= cutoff), at.toISOString()];
310
+ const window = {
311
+ key: sourceKey,
312
+ failures,
313
+ lockedUntil: failures.length >= MAX_FAILED_ATTEMPTS
314
+ ? new Date(at.getTime() + FAILED_ATTEMPT_LOCK_MS).toISOString()
315
+ : undefined,
316
+ };
317
+ const next = {
318
+ ...state,
319
+ attemptWindows: [
320
+ ...liveWindows.filter((candidate) => candidate.key !== sourceKey).slice(-999),
321
+ window,
322
+ ],
323
+ };
324
+ return { state: next, result: { ok: false } };
325
+ }
326
+ const requestId = shortId('join');
327
+ const requestedAt = at.toISOString();
328
+ let next = {
329
+ ...state,
330
+ invites: liveInvites(state.invites, at.getTime()).filter((candidate) => candidate.id !== invite.id),
331
+ joinRequests: compactJoinRequests([...state.joinRequests, {
332
+ id: requestId,
333
+ displayName,
334
+ deviceTokenHash: tokenDigest(input.deviceToken),
335
+ agentTokenHash: tokenDigest(input.agentToken),
336
+ requestedAt,
337
+ sourceKey,
338
+ status: 'pending',
339
+ }]),
340
+ attemptWindows: liveWindows.filter((candidate) => candidate.key !== sourceKey),
341
+ };
342
+ next = bump(appendEvent(next, {
343
+ type: 'join.requested',
344
+ actorId: state.hostMemberId,
345
+ summary: `${displayName} requested to join.`,
346
+ }, requestedAt));
347
+ return { state: next, result: { ok: true, requestId, status: 'pending' } };
348
+ });
349
+ }
350
+ finally {
351
+ this.passwordVerificationsInProgress.delete(initialInviteHash);
352
+ }
353
+ if (!outcome.ok)
354
+ throw new WorkroomValidationError('Invalid or expired invitation or password.');
355
+ this.passwordAttemptsByInvite.delete(initialInviteHash);
356
+ return { requestId: outcome.requestId, status: outcome.status };
357
+ }
358
+ async getJoinStatus(requestId, deviceToken) {
359
+ const state = await this.store.read();
360
+ const request = state.joinRequests.find((candidate) => candidate.id === requestId);
361
+ if (!request || request.deviceTokenHash !== tokenDigest(deviceToken))
362
+ throw new WorkroomAuthorizationError();
363
+ return { status: request.status, memberId: request.memberId };
364
+ }
365
+ async decideJoin(actorToken, requestId, accept) {
366
+ await this.store.mutate((state) => {
367
+ const actor = findActor(state, actorToken, ['host']);
368
+ const request = state.joinRequests.find((candidate) => candidate.id === requestId);
369
+ if (!request || request.status !== 'pending')
370
+ throw new WorkroomValidationError('Pending join request not found.');
371
+ if (accept && state.participants.filter((participant) => !participant.revokedAt).length >= WORKROOM_MAX_PARTICIPANTS) {
372
+ throw new WorkroomValidationError('This Workroom has reached its member limit.');
373
+ }
374
+ const decidedAt = nowIso();
375
+ const memberId = accept ? shortId('member') : undefined;
376
+ let next = {
377
+ ...state,
378
+ joinRequests: compactJoinRequests(state.joinRequests.map((candidate) => candidate.id === requestId ? {
379
+ ...candidate,
380
+ status: accept ? 'accepted' : 'rejected',
381
+ memberId,
382
+ decidedAt,
383
+ decidedBy: actor.id,
384
+ } : candidate)),
385
+ participants: accept ? compactParticipants([...state.participants, {
386
+ id: memberId,
387
+ displayName: request.displayName,
388
+ role: 'member',
389
+ joinedAt: decidedAt,
390
+ lastSeenAt: decidedAt,
391
+ deviceTokenHash: request.deviceTokenHash,
392
+ agentTokenHash: request.agentTokenHash,
393
+ }]) : compactParticipants(state.participants),
394
+ };
395
+ next = bump(appendEvent(next, {
396
+ type: accept ? 'member.joined' : 'member.rejected',
397
+ actorId: actor.id,
398
+ summary: accept ? `${request.displayName} joined the Workroom.` : `${request.displayName}'s join request was rejected.`,
399
+ }, decidedAt));
400
+ return { state: next, result: undefined };
401
+ });
402
+ }
403
+ async setParticipantRole(actorToken, memberId, role) {
404
+ await this.store.mutate((state) => {
405
+ const actor = findActor(state, actorToken, ['host']);
406
+ const target = state.participants.find((participant) => participant.id === memberId && !participant.revokedAt);
407
+ if (!target || target.role === 'host')
408
+ throw new WorkroomValidationError('Active member not found.');
409
+ let next = {
410
+ ...state,
411
+ participants: state.participants.map((participant) => participant.id === memberId ? { ...participant, role } : participant),
412
+ };
413
+ next = bump(appendEvent(next, {
414
+ type: 'member.role_changed',
415
+ actorId: actor.id,
416
+ summary: `${target.displayName} is now a ${role}.`,
417
+ }));
418
+ return { state: next, result: undefined };
419
+ });
420
+ }
421
+ async revokeParticipant(actorToken, memberId) {
422
+ await this.store.mutate((state) => {
423
+ const actor = findActor(state, actorToken, ['host']);
424
+ const target = state.participants.find((participant) => participant.id === memberId && !participant.revokedAt);
425
+ if (!target || target.role === 'host')
426
+ throw new WorkroomValidationError('Active member not found.');
427
+ const revokedAt = nowIso();
428
+ let next = {
429
+ ...state,
430
+ participants: compactParticipants(state.participants.map((participant) => participant.id === memberId ? { ...participant, revokedAt } : participant)),
431
+ };
432
+ next = bump(appendEvent(next, {
433
+ type: 'member.revoked',
434
+ actorId: actor.id,
435
+ summary: `${target.displayName}'s device access was revoked.`,
436
+ }, revokedAt));
437
+ return { state: next, result: undefined };
438
+ });
439
+ }
440
+ async rotatePassword(actorToken, password, revokeDevices) {
441
+ if (password.length < 12 || password.length > 128)
442
+ throw new WorkroomValidationError('Workroom passwords must contain 12–128 characters.');
443
+ const digest = await hashPassword(password);
444
+ await this.store.mutate((state) => {
445
+ const actor = findActor(state, actorToken, ['host']);
446
+ const rotatedAt = nowIso();
447
+ const participants = revokeDevices
448
+ ? state.participants.map((participant) => participant.id === actor.id ? participant : { ...participant, revokedAt: rotatedAt })
449
+ : state.participants;
450
+ const next = bump({
451
+ ...state,
452
+ password: digest,
453
+ participants,
454
+ invites: [],
455
+ joinRequests: compactJoinRequests(state.joinRequests.filter((request) => request.status !== 'pending')),
456
+ });
457
+ return { state: next, result: undefined };
458
+ });
459
+ }
460
+ async updateDocument(actorToken, nameInput, content, expectedRevision) {
461
+ const name = documentNameSchema.parse(nameInput);
462
+ if (Buffer.byteLength(content, 'utf8') > WORKROOM_MAX_DOCUMENT_BYTES)
463
+ throw new WorkroomValidationError('Shared documents are limited to 256 KiB.');
464
+ return this.store.mutate((state) => {
465
+ const actor = findActor(state, actorToken);
466
+ const current = state.documents[name];
467
+ if (current.revision !== expectedRevision)
468
+ throw new WorkroomRevisionError(expectedRevision, current.revision);
469
+ const updatedAt = nowIso();
470
+ const updated = {
471
+ ...current,
472
+ revision: current.revision + 1,
473
+ content,
474
+ updatedAt,
475
+ updatedBy: actor.id,
476
+ history: [...current.history, {
477
+ revision: current.revision,
478
+ content: current.content,
479
+ updatedAt: current.updatedAt,
480
+ updatedBy: current.updatedBy,
481
+ }].slice(-WORKROOM_DOCUMENT_HISTORY_LIMIT),
482
+ };
483
+ let next = { ...state, documents: { ...state.documents, [name]: updated } };
484
+ next = bump(appendEvent(next, {
485
+ type: name === 'handoff' ? 'handoff.published' : 'document.updated',
486
+ actorId: actor.id,
487
+ summary: `${name} updated to revision ${updated.revision}.`,
488
+ }, updatedAt));
489
+ return { state: next, result: updated };
490
+ });
491
+ }
492
+ async publishHandoff(actorToken, content, reposInput, expectedRevision) {
493
+ const repos = reposInput.map((repo) => portableRepoSchema.parse(repo));
494
+ if (Buffer.byteLength(content, 'utf8') > WORKROOM_MAX_DOCUMENT_BYTES)
495
+ throw new WorkroomValidationError('Shared documents are limited to 256 KiB.');
496
+ return this.store.mutate((state) => {
497
+ const actor = findActor(state, actorToken);
498
+ const current = state.documents.handoff;
499
+ if (current.revision !== expectedRevision)
500
+ throw new WorkroomRevisionError(expectedRevision, current.revision);
501
+ const updatedAt = nowIso();
502
+ const updated = {
503
+ ...current,
504
+ revision: current.revision + 1,
505
+ content,
506
+ updatedAt,
507
+ updatedBy: actor.id,
508
+ history: [...current.history, {
509
+ revision: current.revision,
510
+ content: current.content,
511
+ updatedAt: current.updatedAt,
512
+ updatedBy: current.updatedBy,
513
+ }].slice(-WORKROOM_DOCUMENT_HISTORY_LIMIT),
514
+ };
515
+ const handoffs = new Map(repos.map((repo) => [repo.remoteUrl, repo.handoff]));
516
+ const bundle = {
517
+ ...state.bundle,
518
+ repos: state.bundle.repos.map((repo) => {
519
+ const handoff = handoffs.get(repo.remoteUrl);
520
+ return handoff ? { ...repo, handoff } : repo;
521
+ }),
522
+ };
523
+ let next = {
524
+ ...state,
525
+ bundle,
526
+ documents: { ...state.documents, handoff: updated },
527
+ };
528
+ next = bump(appendEvent(next, {
529
+ type: 'handoff.published',
530
+ actorId: actor.id,
531
+ summary: `Published a Git metadata handoff at revision ${updated.revision}.`,
532
+ }, updatedAt));
533
+ return { state: next, result: updated };
534
+ });
535
+ }
536
+ async publishResource(actorToken, input) {
537
+ const parsed = validateResourcePackage(input);
538
+ await this.store.pruneUnreferencedPackages();
539
+ let writtenDigest;
540
+ try {
541
+ return await this.store.mutate(async (state) => {
542
+ const actor = findActor(state, actorToken, ['host', 'publisher']);
543
+ if (state.resources.length >= WORKROOM_MAX_RESOURCES)
544
+ throw new WorkroomValidationError('This Workroom has reached its resource version limit.');
545
+ if (state.resources.some((resource) => resource.kind === parsed.manifest.kind && resource.id === parsed.manifest.id && resource.version === parsed.manifest.version)) {
546
+ throw new WorkroomValidationError('Published resource versions are immutable; choose a new version.');
547
+ }
548
+ const family = state.resources.filter((resource) => resource.kind === parsed.manifest.kind && resource.id === parsed.manifest.id);
549
+ const ownerMemberId = family[0]?.ownerMemberId ?? actor.id;
550
+ const maintainerMemberIds = family[0]?.maintainerMemberIds ?? [];
551
+ if (family.length > 0 && actor.role !== 'host' && actor.id !== ownerMemberId && !maintainerMemberIds.includes(actor.id)) {
552
+ throw new WorkroomAuthorizationError('Only the resource owner, a maintainer, or the host can publish a new version.');
553
+ }
554
+ const pkg = {
555
+ manifest: {
556
+ ...parsed.manifest,
557
+ digest: digestResourcePackage(parsed),
558
+ ownerMemberId,
559
+ maintainerMemberIds,
560
+ createdAt: nowIso(),
561
+ quarantinedAt: undefined,
562
+ },
563
+ files: parsed.files,
564
+ };
565
+ validateResourcePackage(pkg);
566
+ const storedBytes = await this.store.packageStorageBytes(state.resources.map((resource) => resource.digest));
567
+ const packageBytes = Buffer.byteLength(`${JSON.stringify(pkg, null, 2)}\n`, 'utf8');
568
+ assertResourceCatalogStorageSize(storedBytes, packageBytes);
569
+ await this.store.writePackage(pkg);
570
+ writtenDigest = pkg.manifest.digest;
571
+ let next = { ...state, resources: [...state.resources, pkg.manifest] };
572
+ next = bump(appendEvent(next, {
573
+ type: 'resource.published',
574
+ actorId: actor.id,
575
+ summary: `Published ${pkg.manifest.kind} ${pkg.manifest.id}@${pkg.manifest.version}.`,
576
+ }, pkg.manifest.createdAt));
577
+ return { state: next, result: pkg };
578
+ });
579
+ }
580
+ catch (error) {
581
+ // The package blob is written before the SQLite state commit so readers can never see
582
+ // a catalog entry without its content. If the atomic state commit fails,
583
+ // remove that now-unreferenced blob before surfacing the failure.
584
+ if (writtenDigest)
585
+ await this.store.removePackage(writtenDigest).catch(() => { });
586
+ throw error;
587
+ }
588
+ }
589
+ async getResource(actorToken, digest) {
590
+ const state = await this.store.read();
591
+ findActor(state, actorToken);
592
+ const resource = state.resources.find((candidate) => candidate.digest === digest && !candidate.quarantinedAt);
593
+ if (!resource)
594
+ throw new WorkroomValidationError('Resource version not found or quarantined.');
595
+ return validateResourcePackage(await this.store.readPackage(digest));
596
+ }
597
+ async quarantineResource(actorToken, digest) {
598
+ await this.store.mutate((state) => {
599
+ const actor = findActor(state, actorToken, ['host']);
600
+ const target = state.resources.find((resource) => resource.digest === digest);
601
+ if (!target)
602
+ throw new WorkroomValidationError('Resource version not found.');
603
+ const quarantinedAt = nowIso();
604
+ let next = {
605
+ ...state,
606
+ resources: state.resources.map((resource) => resource.digest === digest ? { ...resource, quarantinedAt } : resource),
607
+ };
608
+ next = bump(appendEvent(next, {
609
+ type: 'resource.quarantined',
610
+ actorId: actor.id,
611
+ summary: `Quarantined ${target.kind} ${target.id}@${target.version}.`,
612
+ }, quarantinedAt));
613
+ return { state: next, result: undefined };
614
+ });
615
+ }
616
+ async purgeResource(actorToken, digest) {
617
+ await this.store.mutate((state) => {
618
+ const actor = findActor(state, actorToken, ['host']);
619
+ const target = state.resources.find((resource) => resource.digest === digest);
620
+ if (!target?.quarantinedAt) {
621
+ throw new WorkroomValidationError('Only a quarantined resource version can be permanently purged.');
622
+ }
623
+ const purgedAt = nowIso();
624
+ let next = {
625
+ ...state,
626
+ resources: state.resources.filter((resource) => resource.digest !== digest),
627
+ };
628
+ next = bump(appendEvent(next, {
629
+ type: 'resource.purged',
630
+ actorId: actor.id,
631
+ summary: `Permanently purged ${target.kind} ${target.id}@${target.version}.`,
632
+ }, purgedAt));
633
+ return {
634
+ state: next,
635
+ result: undefined,
636
+ // Keep deletion inside the store's serialized mutation. A new publish
637
+ // cannot reuse this identity until the old blob has been removed.
638
+ // Durable room state is the source of truth. A transient unlink
639
+ // failure must not turn a committed purge into a false API failure;
640
+ // publication retries orphan cleanup under the same store queue.
641
+ afterCommit: () => this.store.removePackage(digest).catch(() => { }),
642
+ };
643
+ });
644
+ }
645
+ async selectWorkflow(actorToken, workflowInput, expectedRevision) {
646
+ if (!Number.isInteger(expectedRevision) || expectedRevision < 0) {
647
+ throw new WorkroomValidationError('A nonnegative workflow expectedRevision is required.');
648
+ }
649
+ const workflow = workflowPackageSchema.parse(workflowInput);
650
+ const ref = {
651
+ kind: 'workflow',
652
+ id: workflow.id,
653
+ version: workflow.version,
654
+ digest: digestWorkflowPackage(workflow),
655
+ };
656
+ await this.store.mutate((state) => {
657
+ const actor = findActor(state, actorToken, ['host']);
658
+ const currentRevision = state.workflowProgress?.revision ?? 0;
659
+ if (currentRevision !== expectedRevision)
660
+ throw new WorkroomRevisionError(expectedRevision, currentRevision);
661
+ const updatedAt = nowIso();
662
+ const steps = workflow.steps.map((step) => ({
663
+ stepId: step.id,
664
+ status: 'pending',
665
+ revision: 0,
666
+ updatedBy: actor.id,
667
+ updatedAt,
668
+ }));
669
+ let next = {
670
+ ...state,
671
+ workflowProgress: { workflow: ref, package: workflow, revision: currentRevision + 1, steps },
672
+ };
673
+ next = bump(appendEvent(next, {
674
+ type: 'workflow.updated',
675
+ actorId: actor.id,
676
+ summary: `Selected workflow ${workflow.name}@${workflow.version}.`,
677
+ }, updatedAt));
678
+ return { state: next, result: undefined };
679
+ });
680
+ }
681
+ async transitionWorkflowStep(actorToken, stepId, status, expectedRevision, evidence) {
682
+ const parsedStatus = workflowStepProgressSchema.shape.status.safeParse(status);
683
+ if (!parsedStatus.success)
684
+ throw new WorkroomValidationError('Invalid workflow step status.');
685
+ return this.store.mutate((state) => {
686
+ const actor = findActor(state, actorToken);
687
+ const progress = state.workflowProgress;
688
+ if (!progress)
689
+ throw new WorkroomValidationError('No structured workflow is selected.');
690
+ const current = progress.steps.find((step) => step.stepId === stepId);
691
+ if (!current)
692
+ throw new WorkroomValidationError('Workflow step not found.');
693
+ const definition = progress.package.steps.find((step) => step.id === stepId);
694
+ if (!definition)
695
+ throw new WorkroomValidationError('Workflow progress does not match its retained package.');
696
+ if (current.revision !== expectedRevision)
697
+ throw new WorkroomRevisionError(expectedRevision, current.revision);
698
+ if (parsedStatus.data === 'completion_proposed')
699
+ throw new WorkroomValidationError('Completion proposals must use the scoped agent credential.');
700
+ const nextEvidence = evidence === undefined ? current.evidence : evidence;
701
+ if (parsedStatus.data === 'completed' && definition.requiresEvidence && !nextEvidence?.trim()) {
702
+ throw new WorkroomValidationError('This workflow step requires evidence before completion.');
703
+ }
704
+ if (nextEvidence !== undefined && Buffer.byteLength(nextEvidence, 'utf8') > WORKROOM_MAX_WORKFLOW_STEP_EVIDENCE_BYTES) {
705
+ throw new WorkroomValidationError('Workflow step evidence is limited to 16 KiB.');
706
+ }
707
+ const aggregateEvidenceBytes = progress.steps.reduce((total, step) => (total + Buffer.byteLength(step.stepId === stepId ? (nextEvidence ?? '') : (step.evidence ?? ''), 'utf8')), 0);
708
+ if (aggregateEvidenceBytes > WORKROOM_MAX_WORKFLOW_EVIDENCE_BYTES) {
709
+ throw new WorkroomValidationError('Workflow evidence exceeds the 512 KiB room limit.');
710
+ }
711
+ const updatedAt = nowIso();
712
+ const updated = {
713
+ ...current,
714
+ status: parsedStatus.data,
715
+ revision: current.revision + 1,
716
+ evidence: nextEvidence,
717
+ proposedBy: undefined,
718
+ updatedBy: actor.id,
719
+ updatedAt,
720
+ };
721
+ let next = {
722
+ ...state,
723
+ workflowProgress: {
724
+ ...progress,
725
+ revision: progress.revision + 1,
726
+ steps: progress.steps.map((step) => step.stepId === stepId ? updated : step),
727
+ },
728
+ };
729
+ next = bump(appendEvent(next, {
730
+ type: 'workflow.updated',
731
+ actorId: actor.id,
732
+ summary: `Workflow step ${stepId} moved to ${parsedStatus.data}.`,
733
+ }, updatedAt));
734
+ return { state: next, result: updated };
735
+ });
736
+ }
737
+ async proposeWorkflowStep(agentToken, stepId, expectedRevision, evidence) {
738
+ const trimmedEvidence = evidence.trim();
739
+ if (!trimmedEvidence)
740
+ throw new WorkroomValidationError('Completion proposals require evidence.');
741
+ if (Buffer.byteLength(trimmedEvidence, 'utf8') > WORKROOM_MAX_WORKFLOW_STEP_EVIDENCE_BYTES) {
742
+ throw new WorkroomValidationError('Workflow step evidence is limited to 16 KiB.');
743
+ }
744
+ return this.store.mutate((state) => {
745
+ const { actor } = findPrincipal(state, agentToken, undefined, ['agent']);
746
+ const progress = state.workflowProgress;
747
+ if (!progress)
748
+ throw new WorkroomValidationError('No structured workflow is selected.');
749
+ const current = progress.steps.find((step) => step.stepId === stepId);
750
+ if (!current)
751
+ throw new WorkroomValidationError('Workflow step not found.');
752
+ if (current.revision !== expectedRevision)
753
+ throw new WorkroomRevisionError(expectedRevision, current.revision);
754
+ if (current.status === 'completed' || current.status === 'skipped') {
755
+ throw new WorkroomValidationError('Agents cannot replace a terminal human workflow decision. A developer must reopen the step first.');
756
+ }
757
+ const aggregateEvidenceBytes = progress.steps.reduce((total, step) => (total + Buffer.byteLength(step.stepId === stepId ? trimmedEvidence : (step.evidence ?? ''), 'utf8')), 0);
758
+ if (aggregateEvidenceBytes > WORKROOM_MAX_WORKFLOW_EVIDENCE_BYTES) {
759
+ throw new WorkroomValidationError('Workflow evidence exceeds the 512 KiB room limit.');
760
+ }
761
+ const updatedAt = nowIso();
762
+ const updated = {
763
+ ...current,
764
+ status: 'completion_proposed',
765
+ revision: current.revision + 1,
766
+ evidence: trimmedEvidence,
767
+ proposedBy: actor.id,
768
+ updatedBy: actor.id,
769
+ updatedAt,
770
+ };
771
+ let next = {
772
+ ...state,
773
+ workflowProgress: {
774
+ ...progress,
775
+ revision: progress.revision + 1,
776
+ steps: progress.steps.map((step) => step.stepId === stepId ? updated : step),
777
+ },
778
+ };
779
+ next = bump(appendEvent(next, {
780
+ type: 'workflow.updated',
781
+ actorId: actor.id,
782
+ summary: `Agent proposed completion of workflow step ${stepId}.`,
783
+ }, updatedAt));
784
+ return { state: next, result: updated };
785
+ });
786
+ }
787
+ }
788
+ //# sourceMappingURL=service.js.map