studiograph 1.3.48-next.21 → 1.3.48-next.211

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 (966) hide show
  1. package/README.md +26 -16
  2. package/dist/agent/agent-pool.d.ts +87 -3
  3. package/dist/agent/agent-pool.js +188 -15
  4. package/dist/agent/agent-pool.js.map +1 -1
  5. package/dist/agent/followups.d.ts +34 -0
  6. package/dist/agent/followups.js +52 -0
  7. package/dist/agent/followups.js.map +1 -0
  8. package/dist/agent/orchestrator.d.ts +101 -4
  9. package/dist/agent/orchestrator.js +464 -66
  10. package/dist/agent/orchestrator.js.map +1 -1
  11. package/dist/agent/prompts/entity-types-section.d.ts +34 -0
  12. package/dist/agent/prompts/entity-types-section.js +76 -0
  13. package/dist/agent/prompts/entity-types-section.js.map +1 -0
  14. package/dist/agent/prompts/system.md +112 -54
  15. package/dist/agent/skill-loader.d.ts +30 -17
  16. package/dist/agent/skill-loader.js +63 -30
  17. package/dist/agent/skill-loader.js.map +1 -1
  18. package/dist/agent/skills/artifact-canvas/skill.md +152 -0
  19. package/dist/agent/skills/entity-schema.md +75 -431
  20. package/dist/agent/skills/interview/entity-templates.md +38 -37
  21. package/dist/agent/skills/interview/question-domains.md +54 -49
  22. package/dist/agent/skills/interview/skill.md +84 -16
  23. package/dist/agent/skills/schema-rules.md +28 -28
  24. package/dist/agent/tools/artifact-tools.d.ts +124 -0
  25. package/dist/agent/tools/artifact-tools.js +207 -0
  26. package/dist/agent/tools/artifact-tools.js.map +1 -0
  27. package/dist/agent/tools/capture-tools.d.ts +31 -0
  28. package/dist/agent/tools/capture-tools.js +40 -0
  29. package/dist/agent/tools/capture-tools.js.map +1 -0
  30. package/dist/agent/tools/folder-tools.d.ts +47 -0
  31. package/dist/agent/tools/folder-tools.js +249 -0
  32. package/dist/agent/tools/folder-tools.js.map +1 -0
  33. package/dist/agent/tools/fs-tools.d.ts +6 -5
  34. package/dist/agent/tools/fs-tools.js +5 -5
  35. package/dist/agent/tools/fs-tools.js.map +1 -1
  36. package/dist/agent/tools/graph-tools.d.ts +48 -72
  37. package/dist/agent/tools/graph-tools.js +852 -329
  38. package/dist/agent/tools/graph-tools.js.map +1 -1
  39. package/dist/agent/tools/history-tools.d.ts +95 -0
  40. package/dist/agent/tools/history-tools.js +461 -0
  41. package/dist/agent/tools/history-tools.js.map +1 -0
  42. package/dist/agent/tools/load-skill.d.ts +6 -5
  43. package/dist/agent/tools/load-skill.js +3 -3
  44. package/dist/agent/tools/load-skill.js.map +1 -1
  45. package/dist/agent/tools/ops-tools.d.ts +5 -4
  46. package/dist/agent/tools/ops-tools.js +130 -236
  47. package/dist/agent/tools/ops-tools.js.map +1 -1
  48. package/dist/agent/tools/permission-tools.d.ts +87 -17
  49. package/dist/agent/tools/permission-tools.js +233 -43
  50. package/dist/agent/tools/permission-tools.js.map +1 -1
  51. package/dist/agent/tools/tool-loader.js +5 -4
  52. package/dist/agent/tools/tool-loader.js.map +1 -1
  53. package/dist/agent/tools/web-tools.js +58 -9
  54. package/dist/agent/tools/web-tools.js.map +1 -1
  55. package/dist/cli/commands/about.d.ts +1 -0
  56. package/dist/cli/commands/about.js +55 -3
  57. package/dist/cli/commands/about.js.map +1 -1
  58. package/dist/cli/commands/admin/audit-workspace.d.ts +23 -0
  59. package/dist/cli/commands/admin/audit-workspace.js +133 -0
  60. package/dist/cli/commands/admin/audit-workspace.js.map +1 -0
  61. package/dist/cli/commands/admin/audit.d.ts +21 -0
  62. package/dist/cli/commands/admin/audit.js +174 -0
  63. package/dist/cli/commands/admin/audit.js.map +1 -0
  64. package/dist/cli/commands/admin/backup.d.ts +54 -0
  65. package/dist/cli/commands/admin/backup.js +207 -0
  66. package/dist/cli/commands/admin/backup.js.map +1 -0
  67. package/dist/cli/commands/admin/folder.d.ts +11 -0
  68. package/dist/cli/commands/{collection.js → admin/folder.js} +33 -32
  69. package/dist/cli/commands/admin/folder.js.map +1 -0
  70. package/dist/cli/commands/admin/index.d.ts +16 -0
  71. package/dist/cli/commands/admin/index.js +46 -0
  72. package/dist/cli/commands/admin/index.js.map +1 -0
  73. package/dist/cli/commands/admin/migrate-assets.d.ts +53 -0
  74. package/dist/cli/commands/admin/migrate-assets.js +184 -0
  75. package/dist/cli/commands/admin/migrate-assets.js.map +1 -0
  76. package/dist/cli/commands/admin/migrate.d.ts +56 -0
  77. package/dist/cli/commands/admin/migrate.js +263 -0
  78. package/dist/cli/commands/admin/migrate.js.map +1 -0
  79. package/dist/cli/commands/admin/restore.d.ts +24 -0
  80. package/dist/cli/commands/admin/restore.js +228 -0
  81. package/dist/cli/commands/admin/restore.js.map +1 -0
  82. package/dist/cli/commands/admin/secrets.d.ts +23 -0
  83. package/dist/cli/commands/admin/secrets.js +256 -0
  84. package/dist/cli/commands/admin/secrets.js.map +1 -0
  85. package/dist/cli/commands/admin/settings.d.ts +28 -0
  86. package/dist/cli/commands/admin/settings.js +284 -0
  87. package/dist/cli/commands/admin/settings.js.map +1 -0
  88. package/dist/cli/commands/admin/token.d.ts +42 -0
  89. package/dist/cli/commands/admin/token.js +126 -0
  90. package/dist/cli/commands/admin/token.js.map +1 -0
  91. package/dist/cli/commands/admin/transfer-ownership.d.ts +15 -0
  92. package/dist/cli/commands/admin/transfer-ownership.js +106 -0
  93. package/dist/cli/commands/admin/transfer-ownership.js.map +1 -0
  94. package/dist/cli/commands/admin/user.d.ts +11 -0
  95. package/dist/cli/commands/admin/user.js +187 -0
  96. package/dist/cli/commands/admin/user.js.map +1 -0
  97. package/dist/cli/commands/clone.d.ts +25 -3
  98. package/dist/cli/commands/clone.js +142 -36
  99. package/dist/cli/commands/clone.js.map +1 -1
  100. package/dist/cli/commands/config.d.ts +23 -107
  101. package/dist/cli/commands/config.js +52 -260
  102. package/dist/cli/commands/config.js.map +1 -1
  103. package/dist/cli/commands/connector.d.ts +10 -13
  104. package/dist/cli/commands/connector.js +16 -58
  105. package/dist/cli/commands/connector.js.map +1 -1
  106. package/dist/cli/commands/deploy.js +215 -68
  107. package/dist/cli/commands/deploy.js.map +1 -1
  108. package/dist/cli/commands/index.js +5 -1
  109. package/dist/cli/commands/index.js.map +1 -1
  110. package/dist/cli/commands/init.js +10 -30
  111. package/dist/cli/commands/init.js.map +1 -1
  112. package/dist/cli/commands/mcp.js +54 -6
  113. package/dist/cli/commands/mcp.js.map +1 -1
  114. package/dist/cli/commands/r2.d.ts +3 -0
  115. package/dist/cli/commands/r2.js +223 -204
  116. package/dist/cli/commands/r2.js.map +1 -1
  117. package/dist/cli/commands/redeploy.js +65 -12
  118. package/dist/cli/commands/redeploy.js.map +1 -1
  119. package/dist/cli/commands/reset.d.ts +22 -6
  120. package/dist/cli/commands/reset.js +132 -34
  121. package/dist/cli/commands/reset.js.map +1 -1
  122. package/dist/cli/commands/serve.js +231 -26
  123. package/dist/cli/commands/serve.js.map +1 -1
  124. package/dist/cli/commands/sync.d.ts +1 -1
  125. package/dist/cli/commands/sync.js +237 -227
  126. package/dist/cli/commands/sync.js.map +1 -1
  127. package/dist/cli/crypto-bundle.d.ts +39 -0
  128. package/dist/cli/crypto-bundle.js +94 -0
  129. package/dist/cli/crypto-bundle.js.map +1 -0
  130. package/dist/cli/index.js +24 -26
  131. package/dist/cli/index.js.map +1 -1
  132. package/dist/cli/railway-pin.d.ts +68 -0
  133. package/dist/cli/railway-pin.js +96 -0
  134. package/dist/cli/railway-pin.js.map +1 -0
  135. package/dist/cli/railway-provisioning.d.ts +53 -0
  136. package/dist/cli/railway-provisioning.js +85 -0
  137. package/dist/cli/railway-provisioning.js.map +1 -0
  138. package/dist/cli/setup-wizard.d.ts +30 -49
  139. package/dist/cli/setup-wizard.js +39 -40
  140. package/dist/cli/setup-wizard.js.map +1 -1
  141. package/dist/core/accent-palette.d.ts +22 -0
  142. package/dist/core/accent-palette.js +47 -0
  143. package/dist/core/accent-palette.js.map +1 -0
  144. package/dist/core/artifact-asset-urls.d.ts +51 -0
  145. package/dist/core/artifact-asset-urls.js +79 -0
  146. package/dist/core/artifact-asset-urls.js.map +1 -0
  147. package/dist/core/artifact-bundle.d.ts +69 -0
  148. package/dist/core/artifact-bundle.js +305 -0
  149. package/dist/core/artifact-bundle.js.map +1 -0
  150. package/dist/core/artifact-escape.d.ts +5 -0
  151. package/dist/core/artifact-escape.js +26 -0
  152. package/dist/core/artifact-escape.js.map +1 -0
  153. package/dist/core/artifact-export.d.ts +17 -0
  154. package/dist/core/artifact-export.js +529 -0
  155. package/dist/core/artifact-export.js.map +1 -0
  156. package/dist/core/cell-anchor.d.ts +29 -0
  157. package/dist/core/cell-anchor.js +53 -0
  158. package/dist/core/cell-anchor.js.map +1 -0
  159. package/dist/core/comment-anchor.d.ts +41 -0
  160. package/dist/core/comment-anchor.js +147 -0
  161. package/dist/core/comment-anchor.js.map +1 -0
  162. package/dist/core/commit-messages.d.ts +57 -0
  163. package/dist/core/commit-messages.js +85 -0
  164. package/dist/core/commit-messages.js.map +1 -0
  165. package/dist/core/embeds.d.ts +96 -0
  166. package/dist/core/embeds.js +196 -0
  167. package/dist/core/embeds.js.map +1 -0
  168. package/dist/core/entity-body-templates.d.ts +21 -0
  169. package/dist/core/entity-body-templates.js +67 -0
  170. package/dist/core/entity-body-templates.js.map +1 -0
  171. package/dist/core/entity-types-catalog.d.ts +65 -0
  172. package/dist/core/entity-types-catalog.js +142 -0
  173. package/dist/core/entity-types-catalog.js.map +1 -0
  174. package/dist/core/field-library.d.ts +62 -0
  175. package/dist/core/field-library.js +129 -0
  176. package/dist/core/field-library.js.map +1 -0
  177. package/dist/core/folder-paths.d.ts +41 -0
  178. package/dist/core/folder-paths.js +95 -0
  179. package/dist/core/folder-paths.js.map +1 -0
  180. package/dist/core/folder-sidecar.d.ts +36 -0
  181. package/dist/core/folder-sidecar.js +91 -0
  182. package/dist/core/folder-sidecar.js.map +1 -0
  183. package/dist/core/format-registry.d.ts +170 -0
  184. package/dist/core/format-registry.js +412 -0
  185. package/dist/core/format-registry.js.map +1 -0
  186. package/dist/core/graph.d.ts +773 -14
  187. package/dist/core/graph.js +2269 -308
  188. package/dist/core/graph.js.map +1 -1
  189. package/dist/core/history-diff.d.ts +35 -0
  190. package/dist/core/history-diff.js +114 -0
  191. package/dist/core/history-diff.js.map +1 -0
  192. package/dist/core/refs.d.ts +41 -0
  193. package/dist/core/refs.js +80 -0
  194. package/dist/core/refs.js.map +1 -0
  195. package/dist/core/schema-registry.d.ts +66 -0
  196. package/dist/core/schema-registry.js +198 -37
  197. package/dist/core/schema-registry.js.map +1 -1
  198. package/dist/core/schemas/connector.d.ts +67 -0
  199. package/dist/core/schemas/connector.js +100 -0
  200. package/dist/core/schemas/connector.js.map +1 -0
  201. package/dist/core/schemas/workspace.d.ts +122 -0
  202. package/dist/core/schemas/workspace.js +211 -0
  203. package/dist/core/schemas/workspace.js.map +1 -0
  204. package/dist/core/secrets/SecretStore.d.ts +77 -0
  205. package/dist/core/secrets/SecretStore.js +13 -0
  206. package/dist/core/secrets/SecretStore.js.map +1 -0
  207. package/dist/core/secrets/SettingsResolver.d.ts +54 -0
  208. package/dist/core/secrets/SettingsResolver.js +80 -0
  209. package/dist/core/secrets/SettingsResolver.js.map +1 -0
  210. package/dist/core/secrets/SettingsStore.d.ts +30 -0
  211. package/dist/core/secrets/SettingsStore.js +9 -0
  212. package/dist/core/secrets/SettingsStore.js.map +1 -0
  213. package/dist/core/secrets/SqliteEncryptedStore.d.ts +90 -0
  214. package/dist/core/secrets/SqliteEncryptedStore.js +598 -0
  215. package/dist/core/secrets/SqliteEncryptedStore.js.map +1 -0
  216. package/dist/core/secrets/audit.d.ts +36 -0
  217. package/dist/core/secrets/audit.js +60 -0
  218. package/dist/core/secrets/audit.js.map +1 -0
  219. package/dist/core/secrets/auth-db-migration.d.ts +129 -0
  220. package/dist/core/secrets/auth-db-migration.js +653 -0
  221. package/dist/core/secrets/auth-db-migration.js.map +1 -0
  222. package/dist/core/secrets/bundle.d.ts +141 -0
  223. package/dist/core/secrets/bundle.js +435 -0
  224. package/dist/core/secrets/bundle.js.map +1 -0
  225. package/dist/core/secrets/dual-write.d.ts +81 -0
  226. package/dist/core/secrets/dual-write.js +247 -0
  227. package/dist/core/secrets/dual-write.js.map +1 -0
  228. package/dist/core/secrets/encryption.d.ts +44 -0
  229. package/dist/core/secrets/encryption.js +97 -0
  230. package/dist/core/secrets/encryption.js.map +1 -0
  231. package/dist/core/secrets/index.d.ts +49 -0
  232. package/dist/core/secrets/index.js +74 -0
  233. package/dist/core/secrets/index.js.map +1 -0
  234. package/dist/core/secrets/master-key.d.ts +38 -0
  235. package/dist/core/secrets/master-key.js +122 -0
  236. package/dist/core/secrets/master-key.js.map +1 -0
  237. package/dist/core/secrets/probes/anthropic.d.ts +98 -0
  238. package/dist/core/secrets/probes/anthropic.js +300 -0
  239. package/dist/core/secrets/probes/anthropic.js.map +1 -0
  240. package/dist/core/secrets/probes/brave.d.ts +9 -0
  241. package/dist/core/secrets/probes/brave.js +15 -0
  242. package/dist/core/secrets/probes/brave.js.map +1 -0
  243. package/dist/core/secrets/probes/r2.d.ts +15 -0
  244. package/dist/core/secrets/probes/r2.js +14 -0
  245. package/dist/core/secrets/probes/r2.js.map +1 -0
  246. package/dist/core/secrets/probes/tavily.d.ts +18 -0
  247. package/dist/core/secrets/probes/tavily.js +58 -0
  248. package/dist/core/secrets/probes/tavily.js.map +1 -0
  249. package/dist/core/secrets/probes/voyage.d.ts +13 -0
  250. package/dist/core/secrets/probes/voyage.js +52 -0
  251. package/dist/core/secrets/probes/voyage.js.map +1 -0
  252. package/dist/core/secrets/r2-structural-migration.d.ts +39 -0
  253. package/dist/core/secrets/r2-structural-migration.js +109 -0
  254. package/dist/core/secrets/r2-structural-migration.js.map +1 -0
  255. package/dist/core/secrets/read-flip.d.ts +59 -0
  256. package/dist/core/secrets/read-flip.js +87 -0
  257. package/dist/core/secrets/read-flip.js.map +1 -0
  258. package/dist/core/secrets/registry.d.ts +188 -0
  259. package/dist/core/secrets/registry.js +616 -0
  260. package/dist/core/secrets/registry.js.map +1 -0
  261. package/dist/core/types.d.ts +77 -483
  262. package/dist/core/types.js +25 -3
  263. package/dist/core/types.js.map +1 -1
  264. package/dist/core/user-config.d.ts +75 -12
  265. package/dist/core/user-config.js +155 -14
  266. package/dist/core/user-config.js.map +1 -1
  267. package/dist/core/validation.d.ts +786 -1373
  268. package/dist/core/validation.js +458 -147
  269. package/dist/core/validation.js.map +1 -1
  270. package/dist/core/workspace-manager.d.ts +87 -14
  271. package/dist/core/workspace-manager.js +222 -94
  272. package/dist/core/workspace-manager.js.map +1 -1
  273. package/dist/core/workspace.d.ts +19 -5
  274. package/dist/core/workspace.js +67 -39
  275. package/dist/core/workspace.js.map +1 -1
  276. package/dist/mcp/comment-virtual-entity.d.ts +36 -0
  277. package/dist/mcp/comment-virtual-entity.js +71 -0
  278. package/dist/mcp/comment-virtual-entity.js.map +1 -0
  279. package/dist/mcp/connector-manager.d.ts +72 -2
  280. package/dist/mcp/connector-manager.js +211 -32
  281. package/dist/mcp/connector-manager.js.map +1 -1
  282. package/dist/mcp/oauth-provider.d.ts +0 -5
  283. package/dist/mcp/oauth-provider.js +10 -22
  284. package/dist/mcp/oauth-provider.js.map +1 -1
  285. package/dist/mcp/oauth-registry.d.ts +46 -8
  286. package/dist/mcp/oauth-registry.js +52 -13
  287. package/dist/mcp/oauth-registry.js.map +1 -1
  288. package/dist/mcp/server-discovery.d.ts +73 -0
  289. package/dist/mcp/server-discovery.js +164 -0
  290. package/dist/mcp/server-discovery.js.map +1 -0
  291. package/dist/mcp/server.d.ts +24 -3
  292. package/dist/mcp/server.js +53 -10
  293. package/dist/mcp/server.js.map +1 -1
  294. package/dist/mcp/state-signer.d.ts +62 -0
  295. package/dist/mcp/state-signer.js +205 -0
  296. package/dist/mcp/state-signer.js.map +1 -0
  297. package/dist/mcp/stdio-proxy.d.ts +67 -0
  298. package/dist/mcp/stdio-proxy.js +149 -0
  299. package/dist/mcp/stdio-proxy.js.map +1 -0
  300. package/dist/mcp/tools.d.ts +65 -2
  301. package/dist/mcp/tools.js +1772 -124
  302. package/dist/mcp/tools.js.map +1 -1
  303. package/dist/server/__tests__/helpers/graph-api.d.ts +16 -0
  304. package/dist/server/__tests__/helpers/graph-api.js +16 -0
  305. package/dist/server/__tests__/helpers/graph-api.js.map +1 -0
  306. package/dist/server/artifact-asset-mint.d.ts +41 -0
  307. package/dist/server/artifact-asset-mint.js +59 -0
  308. package/dist/server/artifact-asset-mint.js.map +1 -0
  309. package/dist/server/asset-index-queue.d.ts +98 -0
  310. package/dist/server/asset-index-queue.js +193 -0
  311. package/dist/server/asset-index-queue.js.map +1 -0
  312. package/dist/server/body-write-coordinator.d.ts +161 -0
  313. package/dist/server/body-write-coordinator.js +182 -0
  314. package/dist/server/body-write-coordinator.js.map +1 -0
  315. package/dist/server/cloud-billing-flow.d.ts +32 -0
  316. package/dist/server/cloud-billing-flow.js +75 -0
  317. package/dist/server/cloud-billing-flow.js.map +1 -0
  318. package/dist/server/commit-audit.d.ts +26 -0
  319. package/dist/server/commit-audit.js +30 -0
  320. package/dist/server/commit-audit.js.map +1 -0
  321. package/dist/server/index.d.ts +29 -3
  322. package/dist/server/index.js +657 -269
  323. package/dist/server/index.js.map +1 -1
  324. package/dist/server/middleware/sanitize.d.ts +46 -0
  325. package/dist/server/middleware/sanitize.js +182 -0
  326. package/dist/server/middleware/sanitize.js.map +1 -0
  327. package/dist/server/routes/artifact-export-api.d.ts +11 -0
  328. package/dist/server/routes/artifact-export-api.js +159 -0
  329. package/dist/server/routes/artifact-export-api.js.map +1 -0
  330. package/dist/server/routes/asset-api.d.ts +76 -1
  331. package/dist/server/routes/asset-api.js +761 -61
  332. package/dist/server/routes/asset-api.js.map +1 -1
  333. package/dist/server/routes/audit-api.d.ts +24 -0
  334. package/dist/server/routes/audit-api.js +117 -0
  335. package/dist/server/routes/audit-api.js.map +1 -0
  336. package/dist/server/routes/auth-api.js +532 -63
  337. package/dist/server/routes/auth-api.js.map +1 -1
  338. package/dist/server/routes/capture-api.d.ts +10 -3
  339. package/dist/server/routes/capture-api.js +213 -25
  340. package/dist/server/routes/capture-api.js.map +1 -1
  341. package/dist/server/routes/chat-sessions-api.d.ts +13 -0
  342. package/dist/server/routes/chat-sessions-api.js +455 -0
  343. package/dist/server/routes/chat-sessions-api.js.map +1 -0
  344. package/dist/server/routes/chat.d.ts +35 -1
  345. package/dist/server/routes/chat.js +699 -54
  346. package/dist/server/routes/chat.js.map +1 -1
  347. package/dist/server/routes/comments-api.d.ts +15 -0
  348. package/dist/server/routes/comments-api.js +444 -0
  349. package/dist/server/routes/comments-api.js.map +1 -0
  350. package/dist/server/routes/config-api.d.ts +3 -2
  351. package/dist/server/routes/config-api.js +179 -49
  352. package/dist/server/routes/config-api.js.map +1 -1
  353. package/dist/server/routes/connectors-api.js +177 -42
  354. package/dist/server/routes/connectors-api.js.map +1 -1
  355. package/dist/server/routes/event-ingest-api.d.ts +15 -0
  356. package/dist/server/routes/{meeting-ingest-api.js → event-ingest-api.js} +35 -20
  357. package/dist/server/routes/event-ingest-api.js.map +1 -0
  358. package/dist/server/routes/field-library-api.d.ts +28 -0
  359. package/dist/server/routes/field-library-api.js +126 -0
  360. package/dist/server/routes/field-library-api.js.map +1 -0
  361. package/dist/server/routes/git-http.d.ts +2 -2
  362. package/dist/server/routes/git-http.js +86 -39
  363. package/dist/server/routes/git-http.js.map +1 -1
  364. package/dist/server/routes/graph-api-access.d.ts +57 -0
  365. package/dist/server/routes/graph-api-access.js +112 -0
  366. package/dist/server/routes/graph-api-access.js.map +1 -0
  367. package/dist/server/routes/graph-api.d.ts +1 -1
  368. package/dist/server/routes/graph-api.js +1455 -325
  369. package/dist/server/routes/graph-api.js.map +1 -1
  370. package/dist/server/routes/health.d.ts +18 -0
  371. package/dist/server/routes/health.js +74 -0
  372. package/dist/server/routes/health.js.map +1 -0
  373. package/dist/server/routes/import-batch.d.ts +24 -0
  374. package/dist/server/routes/import-batch.js +33 -0
  375. package/dist/server/routes/import-batch.js.map +1 -0
  376. package/dist/server/routes/insights-api.d.ts +9 -2
  377. package/dist/server/routes/insights-api.js +355 -61
  378. package/dist/server/routes/insights-api.js.map +1 -1
  379. package/dist/server/routes/mcp.d.ts +7 -3
  380. package/dist/server/routes/mcp.js +24 -8
  381. package/dist/server/routes/mcp.js.map +1 -1
  382. package/dist/server/routes/oauth-api.d.ts +67 -0
  383. package/dist/server/routes/oauth-api.js +421 -0
  384. package/dist/server/routes/oauth-api.js.map +1 -0
  385. package/dist/server/routes/permissions-api.d.ts +9 -2
  386. package/dist/server/routes/permissions-api.js +87 -31
  387. package/dist/server/routes/permissions-api.js.map +1 -1
  388. package/dist/server/routes/r2-api.d.ts +25 -0
  389. package/dist/server/routes/r2-api.js +276 -0
  390. package/dist/server/routes/r2-api.js.map +1 -0
  391. package/dist/server/routes/secrets-api.d.ts +36 -0
  392. package/dist/server/routes/secrets-api.js +308 -0
  393. package/dist/server/routes/secrets-api.js.map +1 -0
  394. package/dist/server/routes/settings-api.d.ts +29 -0
  395. package/dist/server/routes/settings-api.js +180 -0
  396. package/dist/server/routes/settings-api.js.map +1 -0
  397. package/dist/server/routes/share-api.d.ts +32 -0
  398. package/dist/server/routes/share-api.js +234 -0
  399. package/dist/server/routes/share-api.js.map +1 -0
  400. package/dist/server/routes/views-api.js +51 -0
  401. package/dist/server/routes/views-api.js.map +1 -1
  402. package/dist/server/routes/workspace-api.d.ts +2 -1
  403. package/dist/server/routes/workspace-api.js +142 -23
  404. package/dist/server/routes/workspace-api.js.map +1 -1
  405. package/dist/server/routes/ws.d.ts +3 -2
  406. package/dist/server/routes/ws.js +68 -17
  407. package/dist/server/routes/ws.js.map +1 -1
  408. package/dist/server/session-manager.d.ts +48 -5
  409. package/dist/server/session-manager.js +182 -14
  410. package/dist/server/session-manager.js.map +1 -1
  411. package/dist/server/ws-hub.d.ts +124 -37
  412. package/dist/server/ws-hub.js +0 -0
  413. package/dist/server/ws-hub.js.map +1 -1
  414. package/dist/server/yjs-manager.d.ts +278 -7
  415. package/dist/server/yjs-manager.js +830 -38
  416. package/dist/server/yjs-manager.js.map +1 -1
  417. package/dist/server/yjs-persistence.d.ts +165 -0
  418. package/dist/server/yjs-persistence.js +557 -0
  419. package/dist/server/yjs-persistence.js.map +1 -0
  420. package/dist/server/yjs-text-diff.d.ts +32 -0
  421. package/dist/server/yjs-text-diff.js +61 -0
  422. package/dist/server/yjs-text-diff.js.map +1 -0
  423. package/dist/services/access-control.d.ts +73 -0
  424. package/dist/services/access-control.js +165 -0
  425. package/dist/services/access-control.js.map +1 -0
  426. package/dist/services/artifact-asset-token.d.ts +69 -0
  427. package/dist/services/artifact-asset-token.js +94 -0
  428. package/dist/services/artifact-asset-token.js.map +1 -0
  429. package/dist/services/artifact-library-sources.d.ts +3 -0
  430. package/dist/services/artifact-library-sources.js +44 -0
  431. package/dist/services/artifact-library-sources.js.map +1 -0
  432. package/dist/services/artifact-render-export.d.ts +97 -0
  433. package/dist/services/artifact-render-export.js +467 -0
  434. package/dist/services/artifact-render-export.js.map +1 -0
  435. package/dist/services/asset-caption-service.d.ts +103 -0
  436. package/dist/services/asset-caption-service.js +186 -0
  437. package/dist/services/asset-caption-service.js.map +1 -0
  438. package/dist/services/asset-delete-authz.d.ts +31 -0
  439. package/dist/services/asset-delete-authz.js +61 -0
  440. package/dist/services/asset-delete-authz.js.map +1 -0
  441. package/dist/services/asset-finalize-service.d.ts +58 -0
  442. package/dist/services/asset-finalize-service.js +216 -0
  443. package/dist/services/asset-finalize-service.js.map +1 -0
  444. package/dist/services/asset-import-service.d.ts +111 -0
  445. package/dist/services/asset-import-service.js +394 -0
  446. package/dist/services/asset-import-service.js.map +1 -0
  447. package/dist/services/asset-index-sweep.d.ts +95 -0
  448. package/dist/services/asset-index-sweep.js +249 -0
  449. package/dist/services/asset-index-sweep.js.map +1 -0
  450. package/dist/services/asset-lifecycle-check.d.ts +22 -0
  451. package/dist/services/asset-lifecycle-check.js +80 -0
  452. package/dist/services/asset-lifecycle-check.js.map +1 -0
  453. package/dist/services/asset-listing.d.ts +72 -0
  454. package/dist/services/asset-listing.js +321 -0
  455. package/dist/services/asset-listing.js.map +1 -0
  456. package/dist/services/asset-presign-service.d.ts +68 -0
  457. package/dist/services/asset-presign-service.js +124 -0
  458. package/dist/services/asset-presign-service.js.map +1 -0
  459. package/dist/services/asset-sidecar-service.d.ts +28 -0
  460. package/dist/services/asset-sidecar-service.js +79 -0
  461. package/dist/services/asset-sidecar-service.js.map +1 -0
  462. package/dist/services/asset-upload-errors.d.ts +41 -0
  463. package/dist/services/asset-upload-errors.js +45 -0
  464. package/dist/services/asset-upload-errors.js.map +1 -0
  465. package/dist/services/asset-upload-service.d.ts +56 -0
  466. package/dist/services/asset-upload-service.js +200 -0
  467. package/dist/services/asset-upload-service.js.map +1 -0
  468. package/dist/services/asset-upload-token.d.ts +58 -0
  469. package/dist/services/asset-upload-token.js +75 -0
  470. package/dist/services/asset-upload-token.js.map +1 -0
  471. package/dist/services/assets/asset-index-service.d.ts +127 -0
  472. package/dist/services/assets/asset-index-service.js +227 -0
  473. package/dist/services/assets/asset-index-service.js.map +1 -0
  474. package/dist/services/assets/base.d.ts +128 -2
  475. package/dist/services/assets/base.js +98 -1
  476. package/dist/services/assets/base.js.map +1 -1
  477. package/dist/services/assets/index.d.ts +114 -1
  478. package/dist/services/assets/index.js +221 -0
  479. package/dist/services/assets/index.js.map +1 -1
  480. package/dist/services/assets/local.d.ts +16 -1
  481. package/dist/services/assets/local.js +110 -1
  482. package/dist/services/assets/local.js.map +1 -1
  483. package/dist/services/assets/r2-sync.d.ts +88 -0
  484. package/dist/services/assets/r2-sync.js +412 -0
  485. package/dist/services/assets/r2-sync.js.map +1 -0
  486. package/dist/services/assets/r2.d.ts +87 -1
  487. package/dist/services/assets/r2.js +203 -1
  488. package/dist/services/assets/r2.js.map +1 -1
  489. package/dist/services/auth-service.d.ts +312 -45
  490. package/dist/services/auth-service.js +1011 -195
  491. package/dist/services/auth-service.js.map +1 -1
  492. package/dist/services/chat-session-service.d.ts +188 -0
  493. package/dist/services/chat-session-service.js +512 -0
  494. package/dist/services/chat-session-service.js.map +1 -0
  495. package/dist/services/cloud-inference-billing.d.ts +64 -0
  496. package/dist/services/cloud-inference-billing.js +313 -0
  497. package/dist/services/cloud-inference-billing.js.map +1 -0
  498. package/dist/services/comment-service.d.ts +97 -0
  499. package/dist/services/comment-service.js +341 -0
  500. package/dist/services/comment-service.js.map +1 -0
  501. package/dist/services/comment-virtual-entity.d.ts +36 -0
  502. package/dist/services/comment-virtual-entity.js +71 -0
  503. package/dist/services/comment-virtual-entity.js.map +1 -0
  504. package/dist/services/convert-service.d.ts +64 -0
  505. package/dist/services/convert-service.js +68 -0
  506. package/dist/services/convert-service.js.map +1 -0
  507. package/dist/services/csv-service.d.ts +22 -17
  508. package/dist/services/csv-service.js +55 -92
  509. package/dist/services/csv-service.js.map +1 -1
  510. package/dist/services/entity-mutations.d.ts +213 -0
  511. package/dist/services/entity-mutations.js +380 -0
  512. package/dist/services/entity-mutations.js.map +1 -0
  513. package/dist/services/{meeting-processor.d.ts → event-processor.d.ts} +15 -8
  514. package/dist/services/{meeting-processor.js → event-processor.js} +124 -65
  515. package/dist/services/event-processor.js.map +1 -0
  516. package/dist/services/extract/docx.d.ts +1 -0
  517. package/dist/services/extract/docx.js +233 -0
  518. package/dist/services/extract/docx.js.map +1 -0
  519. package/dist/services/extract/index.d.ts +9 -0
  520. package/dist/services/extract/index.js +10 -0
  521. package/dist/services/extract/index.js.map +1 -0
  522. package/dist/services/extract/pdf.d.ts +13 -0
  523. package/dist/services/extract/pdf.js +69 -0
  524. package/dist/services/extract/pdf.js.map +1 -0
  525. package/dist/services/folder-creation.d.ts +33 -0
  526. package/dist/services/folder-creation.js +103 -0
  527. package/dist/services/folder-creation.js.map +1 -0
  528. package/dist/services/git.d.ts +58 -0
  529. package/dist/services/git.js +194 -55
  530. package/dist/services/git.js.map +1 -1
  531. package/dist/services/graph-maintenance.js +4 -4
  532. package/dist/services/graph-maintenance.js.map +1 -1
  533. package/dist/services/import-service.d.ts +51 -2
  534. package/dist/services/import-service.js +254 -107
  535. package/dist/services/import-service.js.map +1 -1
  536. package/dist/services/lint-service.js +10 -17
  537. package/dist/services/lint-service.js.map +1 -1
  538. package/dist/services/markdown.d.ts +12 -1
  539. package/dist/services/markdown.js +77 -9
  540. package/dist/services/markdown.js.map +1 -1
  541. package/dist/services/mention-detector.d.ts +23 -0
  542. package/dist/services/mention-detector.js +47 -0
  543. package/dist/services/mention-detector.js.map +1 -0
  544. package/dist/services/model-capabilities.d.ts +41 -0
  545. package/dist/services/model-capabilities.js +107 -0
  546. package/dist/services/model-capabilities.js.map +1 -0
  547. package/dist/services/notification-broadcast.d.ts +32 -0
  548. package/dist/services/notification-broadcast.js +38 -0
  549. package/dist/services/notification-broadcast.js.map +1 -0
  550. package/dist/services/notification-service.d.ts +41 -0
  551. package/dist/services/notification-service.js +100 -0
  552. package/dist/services/notification-service.js.map +1 -0
  553. package/dist/services/oauth-server-store.d.ts +147 -0
  554. package/dist/services/oauth-server-store.js +319 -0
  555. package/dist/services/oauth-server-store.js.map +1 -0
  556. package/dist/services/orphan-service.js +2 -2
  557. package/dist/services/orphan-service.js.map +1 -1
  558. package/dist/services/personal-folder.d.ts +40 -0
  559. package/dist/services/personal-folder.js +101 -0
  560. package/dist/services/personal-folder.js.map +1 -0
  561. package/dist/services/scratch-asset-promote.d.ts +57 -0
  562. package/dist/services/scratch-asset-promote.js +95 -0
  563. package/dist/services/scratch-asset-promote.js.map +1 -0
  564. package/dist/services/scratch-attachment-service.d.ts +196 -0
  565. package/dist/services/scratch-attachment-service.js +347 -0
  566. package/dist/services/scratch-attachment-service.js.map +1 -0
  567. package/dist/services/share-link-service.d.ts +57 -0
  568. package/dist/services/share-link-service.js +142 -0
  569. package/dist/services/share-link-service.js.map +1 -0
  570. package/dist/services/user-person-bridge.d.ts +136 -0
  571. package/dist/services/user-person-bridge.js +340 -0
  572. package/dist/services/user-person-bridge.js.map +1 -0
  573. package/dist/services/vector-service.d.ts +282 -5
  574. package/dist/services/vector-service.js +585 -29
  575. package/dist/services/vector-service.js.map +1 -1
  576. package/dist/services/workspace-access.d.ts +108 -0
  577. package/dist/services/workspace-access.js +149 -0
  578. package/dist/services/workspace-access.js.map +1 -0
  579. package/dist/services/workspace-assets-folder.d.ts +46 -0
  580. package/dist/services/workspace-assets-folder.js +75 -0
  581. package/dist/services/workspace-assets-folder.js.map +1 -0
  582. package/dist/utils/git.d.ts +17 -2
  583. package/dist/utils/git.js +23 -7
  584. package/dist/utils/git.js.map +1 -1
  585. package/dist/utils/log.d.ts +42 -0
  586. package/dist/utils/log.js +137 -0
  587. package/dist/utils/log.js.map +1 -0
  588. package/dist/utils/preflight.js +2 -2
  589. package/dist/utils/preflight.js.map +1 -1
  590. package/dist/utils/version-checker.d.ts +12 -19
  591. package/dist/utils/version-checker.js +14 -104
  592. package/dist/utils/version-checker.js.map +1 -1
  593. package/dist/web/_app/immutable/assets/0.Bs_z3Z86.css +2 -0
  594. package/dist/web/_app/immutable/assets/12.DlIf1mKh.css +1 -0
  595. package/dist/web/_app/immutable/assets/14.7d-Q37wc.css +1 -0
  596. package/dist/web/_app/immutable/assets/15.BwBFdc1D.css +1 -0
  597. package/dist/web/_app/immutable/assets/16.CmLrbzZp.css +1 -0
  598. package/dist/web/_app/immutable/assets/17.DcuK5ZVm.css +1 -0
  599. package/dist/web/_app/immutable/assets/2.DATnR31c.css +1 -0
  600. package/dist/web/_app/immutable/assets/3.Cz4H8n_O.css +1 -0
  601. package/dist/web/_app/immutable/assets/4.aDHteTDy.css +1 -0
  602. package/dist/web/_app/immutable/assets/6.CF1i5Bon.css +1 -0
  603. package/dist/web/_app/immutable/assets/ArtifactEntityView.C4ooklFD.css +1 -0
  604. package/dist/web/_app/immutable/assets/AssetLibraryPicker.BOFW0MvF.css +1 -0
  605. package/dist/web/_app/immutable/assets/BulkActionsBar.cmKvHzRK.css +1 -0
  606. package/dist/web/_app/immutable/assets/CalendarView.lBCvS3X5.css +1 -0
  607. package/dist/web/_app/immutable/assets/CodeMirrorEditor.D6hf2pNJ.css +1 -0
  608. package/dist/web/_app/immutable/assets/CopyField.DVGZtw4U.css +1 -0
  609. package/dist/web/_app/immutable/assets/FolderMembersPanel.kiYnDFHu.css +1 -0
  610. package/dist/web/_app/immutable/assets/FolderMutationDialogs.BMRF6Z3z.css +1 -0
  611. package/dist/web/_app/immutable/assets/FolderPicker.Cp46GHe2.css +1 -0
  612. package/dist/web/_app/immutable/assets/GalleryView.WBGxxUQc.css +1 -0
  613. package/dist/web/_app/immutable/assets/GraphView.BJtGL9py.css +1 -0
  614. package/dist/web/_app/immutable/assets/KanbanView.BOaI5KFL.css +1 -0
  615. package/dist/web/_app/immutable/assets/Link.vV0ne105.css +1 -0
  616. package/dist/web/_app/immutable/assets/Markdown.BZnsSOSf.css +1 -0
  617. package/dist/web/_app/immutable/assets/Rail.ilusFgD4.css +1 -0
  618. package/dist/web/_app/immutable/assets/SettingsDialog.D7-BO6S_.css +1 -0
  619. package/dist/web/_app/immutable/assets/Spinner.UBfl0pab.css +1 -0
  620. package/dist/web/_app/immutable/assets/StopFilledAlt.fNlDN65D.css +1 -0
  621. package/dist/web/_app/immutable/assets/TimelineView.xAZBJhHw.css +1 -0
  622. package/dist/web/_app/immutable/assets/WorkspaceView.C8ErUJAY.css +1 -0
  623. package/dist/web/_app/immutable/assets/button.BlLm4Dg1.css +1 -0
  624. package/dist/web/_app/immutable/assets/history-summary.LpmHXasl.css +1 -0
  625. package/dist/web/_app/immutable/assets/ibm-plex-mono-latin-400-normal.CvHOgSBP.woff +0 -0
  626. package/dist/web/_app/immutable/assets/ibm-plex-mono-latin-400-normal.DMJ8VG8y.woff2 +0 -0
  627. package/dist/web/_app/immutable/assets/ibm-plex-mono-latin-500-normal.CB9ihrfo.woff +0 -0
  628. package/dist/web/_app/immutable/assets/ibm-plex-mono-latin-500-normal.DSY6xOcd.woff2 +0 -0
  629. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-400-italic.CZTNEAuW.woff2 +0 -0
  630. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-400-italic.CsGl1sm0.woff +0 -0
  631. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-400-normal.CDDApCn2.woff2 +0 -0
  632. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-400-normal.CYLoc0-x.woff +0 -0
  633. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-500-italic.BNK2_mGO.woff2 +0 -0
  634. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-500-italic.DpEwFAQM.woff +0 -0
  635. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-500-normal.6ng42L7E.woff2 +0 -0
  636. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-500-normal.BgVn5rGT.woff +0 -0
  637. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-600-normal.Cu4Hd6ag.woff +0 -0
  638. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-600-normal.CuJfVYMP.woff2 +0 -0
  639. package/dist/web/_app/immutable/assets/inline-list.cqga56xT.css +1 -0
  640. package/dist/web/_app/immutable/assets/list-columns.BgnafZ4K.css +1 -0
  641. package/dist/web/_app/immutable/assets/realtime.DDCWxn3B.css +1 -0
  642. package/dist/web/_app/immutable/assets/select.B90KUqR4.css +1 -0
  643. package/dist/web/_app/immutable/assets/sheet.RF9RBmml.css +1 -0
  644. package/dist/web/_app/immutable/chunks/22qYtvr82.js +1 -0
  645. package/dist/web/_app/immutable/chunks/2f1VclTm.js +1 -0
  646. package/dist/web/_app/immutable/chunks/4xgUPoGj2.js +2 -0
  647. package/dist/web/_app/immutable/chunks/5CYQk4xR.js +1 -0
  648. package/dist/web/_app/immutable/chunks/6S9WOky52.js +1 -0
  649. package/dist/web/_app/immutable/chunks/9K8VREGP.js +1 -0
  650. package/dist/web/_app/immutable/chunks/B-n5q6ku.js +1 -0
  651. package/dist/web/_app/immutable/chunks/B0p2fSEV2.js +1 -0
  652. package/dist/web/_app/immutable/chunks/B2oi87jG.js +2 -0
  653. package/dist/web/_app/immutable/chunks/B5Rb52QU.js +1 -0
  654. package/dist/web/_app/immutable/chunks/B6Lt-qaJ.js +1 -0
  655. package/dist/web/_app/immutable/chunks/B6O-q2CN.js +1 -0
  656. package/dist/web/_app/immutable/chunks/B6qeIQZz.js +2 -0
  657. package/dist/web/_app/immutable/chunks/B9KfEsQw2.js +1 -0
  658. package/dist/web/_app/immutable/chunks/BB-RSeXQ.js +1 -0
  659. package/dist/web/_app/immutable/chunks/BCQQzpbj.js +1 -0
  660. package/dist/web/_app/immutable/chunks/BGdzd-Tc.js +1 -0
  661. package/dist/web/_app/immutable/chunks/BLs1h0Rh2.js +1 -0
  662. package/dist/web/_app/immutable/chunks/BPzV_xOS2.js +1 -0
  663. package/dist/web/_app/immutable/chunks/BT2C6q8n.js +1 -0
  664. package/dist/web/_app/immutable/chunks/BUesbLq2.js +1 -0
  665. package/dist/web/_app/immutable/chunks/BUrnwOw4.js +2 -0
  666. package/dist/web/_app/immutable/chunks/BV-iCKqa.js +1 -0
  667. package/dist/web/_app/immutable/chunks/BVCJtXyc.js +1 -0
  668. package/dist/web/_app/immutable/chunks/BY9a3GSt.js +6 -0
  669. package/dist/web/_app/immutable/chunks/BZi3uKF4.js +1 -0
  670. package/dist/web/_app/immutable/chunks/BbfHGU9n2.js +1 -0
  671. package/dist/web/_app/immutable/chunks/Bbqu2-5G2.js +1 -0
  672. package/dist/web/_app/immutable/chunks/BbsX1Slr.js +3 -0
  673. package/dist/web/_app/immutable/chunks/BcwjqHaZ2.js +1 -0
  674. package/dist/web/_app/immutable/chunks/BizeEpTx.js +2 -0
  675. package/dist/web/_app/immutable/chunks/BlgjVmFM2.js +14 -0
  676. package/dist/web/_app/immutable/chunks/BocSRgv12.js +1 -0
  677. package/dist/web/_app/immutable/chunks/BojBYqc3.js +1 -0
  678. package/dist/web/_app/immutable/chunks/BpCG9m962.js +1 -0
  679. package/dist/web/_app/immutable/chunks/BqvnBg_y.js +1 -0
  680. package/dist/web/_app/immutable/chunks/BsT0J1QD.js +1 -0
  681. package/dist/web/_app/immutable/chunks/BsW72fyR2.js +1 -0
  682. package/dist/web/_app/immutable/chunks/Bti_XKwx.js +2692 -0
  683. package/dist/web/_app/immutable/chunks/Bua97Had2.js +2 -0
  684. package/dist/web/_app/immutable/chunks/BuhW5Fr0.js +689 -0
  685. package/dist/web/_app/immutable/chunks/Bx74EEyn2.js +1 -0
  686. package/dist/web/_app/immutable/chunks/BxPN0T61.js +1 -0
  687. package/dist/web/_app/immutable/chunks/Bz9lrgGs.js +1 -0
  688. package/dist/web/_app/immutable/chunks/BzaxZ6j12.js +1 -0
  689. package/dist/web/_app/immutable/chunks/C1B4xDs0.js +1 -0
  690. package/dist/web/_app/immutable/chunks/C1trcC8M.js +7 -0
  691. package/dist/web/_app/immutable/chunks/C9tvkk7Y2.js +22 -0
  692. package/dist/web/_app/immutable/chunks/CBQjdDZf.js +1 -0
  693. package/dist/web/_app/immutable/chunks/CDeazMFW2.js +2 -0
  694. package/dist/web/_app/immutable/chunks/CEahbtnP.js +1 -0
  695. package/dist/web/_app/immutable/chunks/CGjVCd0e.js +8 -0
  696. package/dist/web/_app/immutable/chunks/CICK9maP.js +1 -0
  697. package/dist/web/_app/immutable/chunks/CIoNEHQ82.js +184 -0
  698. package/dist/web/_app/immutable/chunks/CP8_U450.js +1 -0
  699. package/dist/web/_app/immutable/chunks/CQ1eQ93t2.js +6 -0
  700. package/dist/web/_app/immutable/chunks/CUFQMEBn2.js +1 -0
  701. package/dist/web/_app/immutable/chunks/CYEYDNwO2.js +1 -0
  702. package/dist/web/_app/immutable/chunks/CYyIP7kP2.js +66 -0
  703. package/dist/web/_app/immutable/chunks/CZgFaimi2.js +83 -0
  704. package/dist/web/_app/immutable/chunks/CZqvCTCv.js +1 -0
  705. package/dist/web/_app/immutable/chunks/CcI9jYBV.js +1 -0
  706. package/dist/web/_app/immutable/chunks/CcWaWbrI2.js +1 -0
  707. package/dist/web/_app/immutable/chunks/CdtzRVZK.js +1 -0
  708. package/dist/web/_app/immutable/chunks/CfYG72Hc.js +1 -0
  709. package/dist/web/_app/immutable/chunks/Ch1kmm3J2.js +1 -0
  710. package/dist/web/_app/immutable/chunks/CmSSN61R2.js +1 -0
  711. package/dist/web/_app/immutable/chunks/Cqm5wAin.js +1 -0
  712. package/dist/web/_app/immutable/chunks/CuYPXpWU.js +1 -0
  713. package/dist/web/_app/immutable/chunks/CvSEodTA.js +1 -0
  714. package/dist/web/_app/immutable/chunks/CvqqrTrj.js +1 -0
  715. package/dist/web/_app/immutable/chunks/CwAmWt6h.js +1 -0
  716. package/dist/web/_app/immutable/chunks/CwP3Oa7F.js +1 -0
  717. package/dist/web/_app/immutable/chunks/CyTluew1.js +2 -0
  718. package/dist/web/_app/immutable/chunks/CzXdOgVl2.js +7 -0
  719. package/dist/web/_app/immutable/chunks/D0IAituJ.js +1 -0
  720. package/dist/web/_app/immutable/chunks/D0W7c6Sg.js +185 -0
  721. package/dist/web/_app/immutable/chunks/D0rBhnqB2.js +1 -0
  722. package/dist/web/_app/immutable/chunks/D1-GZSN1.js +1 -0
  723. package/dist/web/_app/immutable/chunks/D1yvtriI.js +1 -0
  724. package/dist/web/_app/immutable/chunks/D25NMRpl.js +1 -0
  725. package/dist/web/_app/immutable/chunks/D8wZ2xul.js +4 -0
  726. package/dist/web/_app/immutable/chunks/DA3HEX3X.js +1 -0
  727. package/dist/web/_app/immutable/chunks/DD9wj4ca2.js +1 -0
  728. package/dist/web/_app/immutable/chunks/DJ1YeiJL2.js +1 -0
  729. package/dist/web/_app/immutable/chunks/DKFZnpVC.js +5 -0
  730. package/dist/web/_app/immutable/chunks/DKJl40hl.js +1 -0
  731. package/dist/web/_app/immutable/chunks/DPUVu0T0.js +1 -0
  732. package/dist/web/_app/immutable/chunks/DQMWa2t7.js +1 -0
  733. package/dist/web/_app/immutable/chunks/DXsFrDer2.js +1 -0
  734. package/dist/web/_app/immutable/chunks/DYc5KW3F.js +1 -0
  735. package/dist/web/_app/immutable/chunks/DZcTwXBW.js +1 -0
  736. package/dist/web/_app/immutable/chunks/DcPrzUMJ.js +1 -0
  737. package/dist/web/_app/immutable/chunks/DdPO8kRu2.js +2 -0
  738. package/dist/web/_app/immutable/chunks/DeOXyJZw2.js +1 -0
  739. package/dist/web/_app/immutable/chunks/DfufUFR1.js +2 -0
  740. package/dist/web/_app/immutable/chunks/DiQg3ing.js +1 -0
  741. package/dist/web/_app/immutable/chunks/DjIqa1_y.js +2 -0
  742. package/dist/web/_app/immutable/chunks/DkJ1dkxV2.js +1 -0
  743. package/dist/web/_app/immutable/chunks/Dl63IUnH2.js +224 -0
  744. package/dist/web/_app/immutable/chunks/DmFdD6zT.js +1 -0
  745. package/dist/web/_app/immutable/chunks/Dn3R8S-U.js +1 -0
  746. package/dist/web/_app/immutable/chunks/DpNr1GQ2.js +1 -0
  747. package/dist/web/_app/immutable/chunks/DpVNDHXz.js +2 -0
  748. package/dist/web/_app/immutable/chunks/DuxziNnw.js +1 -0
  749. package/dist/web/_app/immutable/chunks/Dv3A3Wcj.js +1 -0
  750. package/dist/web/_app/immutable/chunks/FuiJnZG0.js +1 -0
  751. package/dist/web/_app/immutable/chunks/Izkulod7.js +1 -0
  752. package/dist/web/_app/immutable/chunks/KZY6ongq2.js +2 -0
  753. package/dist/web/_app/immutable/chunks/LYL8znYR2.js +23 -0
  754. package/dist/web/_app/immutable/chunks/PhN6tXuJ2.js +1 -0
  755. package/dist/web/_app/immutable/chunks/PyLyCpuL.js +1 -0
  756. package/dist/web/_app/immutable/chunks/RK4gCWN0.js +2 -0
  757. package/dist/web/_app/immutable/chunks/VsBngTf4.js +2 -0
  758. package/dist/web/_app/immutable/chunks/XcWejFm_.js +5 -0
  759. package/dist/web/_app/immutable/chunks/al_nidE9.js +1 -0
  760. package/dist/web/_app/immutable/chunks/e50-izZO2.js +1 -0
  761. package/dist/web/_app/immutable/chunks/hG-QCarB.js +1 -0
  762. package/dist/web/_app/immutable/chunks/hStvCHkX.js +1 -0
  763. package/dist/web/_app/immutable/chunks/iJVY9p0y.js +3 -0
  764. package/dist/web/_app/immutable/chunks/kNaey6uv.js +1 -0
  765. package/dist/web/_app/immutable/chunks/pIXNe0C1.js +1 -0
  766. package/dist/web/_app/immutable/chunks/pNSRq_1B.js +2 -0
  767. package/dist/web/_app/immutable/chunks/qwr5boV8.js +1 -0
  768. package/dist/web/_app/immutable/chunks/rjjyuGhb.js +1 -0
  769. package/dist/web/_app/immutable/chunks/sg-T-FoN.js +1 -0
  770. package/dist/web/_app/immutable/chunks/uBgoVlsx.js +3 -0
  771. package/dist/web/_app/immutable/chunks/ulshNz6x.js +2 -0
  772. package/dist/web/_app/immutable/chunks/xY0c-UfG.js +3 -0
  773. package/dist/web/_app/immutable/chunks/xeNvx4Bl2.js +1 -0
  774. package/dist/web/_app/immutable/chunks/xihTtKlq.js +1 -0
  775. package/dist/web/_app/immutable/entry/app.pNUW-q4U.js +2 -0
  776. package/dist/web/_app/immutable/entry/start.DxGtIGG_.js +1 -0
  777. package/dist/web/_app/immutable/nodes/0.D6ZCY_R6.js +2 -0
  778. package/dist/web/_app/immutable/nodes/1.BryH3TNr.js +1 -0
  779. package/dist/web/_app/immutable/nodes/10.CMZMvR17.js +1 -0
  780. package/dist/web/_app/immutable/nodes/11.QHx_JShx.js +1 -0
  781. package/dist/web/_app/immutable/nodes/12.CiRWbb4X.js +1 -0
  782. package/dist/web/_app/immutable/nodes/13.CYrHXCA8.js +1 -0
  783. package/dist/web/_app/immutable/nodes/14.K18eIE5Z.js +1 -0
  784. package/dist/web/_app/immutable/nodes/15.DxgnZ61h.js +1 -0
  785. package/dist/web/_app/immutable/nodes/16.Dn6iuWIo.js +1 -0
  786. package/dist/web/_app/immutable/nodes/17.viwS7vTR.js +77 -0
  787. package/dist/web/_app/immutable/nodes/2.BQkQ_uUM.js +122 -0
  788. package/dist/web/_app/immutable/nodes/3.B4pqXsqq.js +6 -0
  789. package/dist/web/_app/immutable/nodes/4.CKXEgIAP.js +11 -0
  790. package/dist/web/_app/immutable/nodes/5.A2TASl4Q.js +1 -0
  791. package/dist/web/_app/immutable/nodes/6.DaIRO3SI.js +6 -0
  792. package/dist/web/_app/immutable/nodes/7.ga2UvFm-.js +1 -0
  793. package/dist/web/_app/immutable/nodes/8.BTEWT81u.js +1 -0
  794. package/dist/web/_app/immutable/nodes/9.BVxo0E0c.js +1 -0
  795. package/dist/web/_app/version.json +1 -1
  796. package/dist/web/favicon-16.png +0 -0
  797. package/dist/web/favicon-180.png +0 -0
  798. package/dist/web/favicon-32.png +0 -0
  799. package/dist/web/favicon-48.png +0 -0
  800. package/dist/web/favicon-512.png +0 -0
  801. package/dist/web/favicon.ico +0 -0
  802. package/dist/web/favicon.svg +8 -0
  803. package/dist/web/index.html +56 -21
  804. package/dist/web/studiograph-logomark.svg +29 -0
  805. package/package.json +35 -14
  806. package/dist/agent/skills/enrich-entities.md +0 -136
  807. package/dist/agent/skills/sync-configuration.md +0 -119
  808. package/dist/agent/skills/sync-setup.md +0 -82
  809. package/dist/agent/tools/message-tools.d.ts +0 -42
  810. package/dist/agent/tools/message-tools.js +0 -106
  811. package/dist/agent/tools/message-tools.js.map +0 -1
  812. package/dist/cli/commands/app.d.ts +0 -7
  813. package/dist/cli/commands/app.js +0 -268
  814. package/dist/cli/commands/app.js.map +0 -1
  815. package/dist/cli/commands/clear.d.ts +0 -5
  816. package/dist/cli/commands/clear.js +0 -36
  817. package/dist/cli/commands/clear.js.map +0 -1
  818. package/dist/cli/commands/collection.d.ts +0 -10
  819. package/dist/cli/commands/collection.js.map +0 -1
  820. package/dist/cli/commands/join.d.ts +0 -9
  821. package/dist/cli/commands/join.js +0 -255
  822. package/dist/cli/commands/join.js.map +0 -1
  823. package/dist/cli/commands/start.d.ts +0 -7
  824. package/dist/cli/commands/start.js +0 -584
  825. package/dist/cli/commands/start.js.map +0 -1
  826. package/dist/cli/commands/update.d.ts +0 -8
  827. package/dist/cli/commands/update.js +0 -155
  828. package/dist/cli/commands/update.js.map +0 -1
  829. package/dist/cli/commands/user.d.ts +0 -7
  830. package/dist/cli/commands/user.js +0 -175
  831. package/dist/cli/commands/user.js.map +0 -1
  832. package/dist/cli/scaffolding.d.ts +0 -12
  833. package/dist/cli/scaffolding.js +0 -397
  834. package/dist/cli/scaffolding.js.map +0 -1
  835. package/dist/integrations/registry.d.ts +0 -8
  836. package/dist/integrations/registry.js +0 -7
  837. package/dist/integrations/registry.js.map +0 -1
  838. package/dist/integrations/types.d.ts +0 -43
  839. package/dist/integrations/types.js +0 -5
  840. package/dist/integrations/types.js.map +0 -1
  841. package/dist/lib/lib/utils.d.ts +0 -2
  842. package/dist/lib/lib/utils.js +0 -6
  843. package/dist/lib/lib/utils.js.map +0 -1
  844. package/dist/server/chrome/chrome.css +0 -691
  845. package/dist/server/chrome/chrome.js +0 -374
  846. package/dist/server/commit-scheduler.d.ts +0 -39
  847. package/dist/server/commit-scheduler.js +0 -113
  848. package/dist/server/commit-scheduler.js.map +0 -1
  849. package/dist/server/plugin-loader.d.ts +0 -53
  850. package/dist/server/plugin-loader.js +0 -155
  851. package/dist/server/plugin-loader.js.map +0 -1
  852. package/dist/server/routes/meeting-ingest-api.d.ts +0 -14
  853. package/dist/server/routes/meeting-ingest-api.js.map +0 -1
  854. package/dist/server/routes/messages-api.d.ts +0 -15
  855. package/dist/server/routes/messages-api.js +0 -385
  856. package/dist/server/routes/messages-api.js.map +0 -1
  857. package/dist/services/batch-processor.d.ts +0 -49
  858. package/dist/services/batch-processor.js +0 -166
  859. package/dist/services/batch-processor.js.map +0 -1
  860. package/dist/services/briefing.d.ts +0 -26
  861. package/dist/services/briefing.js +0 -230
  862. package/dist/services/briefing.js.map +0 -1
  863. package/dist/services/heartbeat.d.ts +0 -28
  864. package/dist/services/heartbeat.js +0 -183
  865. package/dist/services/heartbeat.js.map +0 -1
  866. package/dist/services/image-import-service.d.ts +0 -24
  867. package/dist/services/image-import-service.js +0 -108
  868. package/dist/services/image-import-service.js.map +0 -1
  869. package/dist/services/insights.d.ts +0 -38
  870. package/dist/services/insights.js +0 -269
  871. package/dist/services/insights.js.map +0 -1
  872. package/dist/services/meeting-processor.js.map +0 -1
  873. package/dist/services/message-service.d.ts +0 -68
  874. package/dist/services/message-service.js +0 -220
  875. package/dist/services/message-service.js.map +0 -1
  876. package/dist/utils/workspace-config.d.ts +0 -8
  877. package/dist/utils/workspace-config.js +0 -22
  878. package/dist/utils/workspace-config.js.map +0 -1
  879. package/dist/web/_app/immutable/assets/0.uPSqtsC5.css +0 -1
  880. package/dist/web/_app/immutable/assets/11.2jzI3cZY.css +0 -1
  881. package/dist/web/_app/immutable/assets/12.CT4xL4K3.css +0 -1
  882. package/dist/web/_app/immutable/assets/13.BUrHkYry.css +0 -1
  883. package/dist/web/_app/immutable/assets/2.DpQWr6q6.css +0 -1
  884. package/dist/web/_app/immutable/assets/3.BxdqT7zi.css +0 -1
  885. package/dist/web/_app/immutable/assets/4.DdNqjd0T.css +0 -1
  886. package/dist/web/_app/immutable/assets/5.DFeGxLYf.css +0 -1
  887. package/dist/web/_app/immutable/assets/6.DXZr_rI_.css +0 -1
  888. package/dist/web/_app/immutable/assets/GraphView.D9ePpZez.css +0 -1
  889. package/dist/web/_app/immutable/assets/Toaster.rN8r_Hzv.css +0 -1
  890. package/dist/web/_app/immutable/assets/ViewToolbar.BWE03S64.css +0 -1
  891. package/dist/web/_app/immutable/assets/WorkspaceView.CgGDi_Zb.css +0 -1
  892. package/dist/web/_app/immutable/assets/wikilinks.CzMBkUMB.css +0 -1
  893. package/dist/web/_app/immutable/chunks/-jG4Obyh.js +0 -4
  894. package/dist/web/_app/immutable/chunks/2nrc8f_O.js +0 -1
  895. package/dist/web/_app/immutable/chunks/7S2LGqoP.js +0 -2
  896. package/dist/web/_app/immutable/chunks/8Q3ZJIYu.js +0 -1
  897. package/dist/web/_app/immutable/chunks/B8ydT5xX.js +0 -2
  898. package/dist/web/_app/immutable/chunks/BB_5th5W.js +0 -3383
  899. package/dist/web/_app/immutable/chunks/BEn6N5c2.js +0 -1
  900. package/dist/web/_app/immutable/chunks/BFNNbCAM.js +0 -1
  901. package/dist/web/_app/immutable/chunks/BFZZEUd5.js +0 -1
  902. package/dist/web/_app/immutable/chunks/BGk8kfOE.js +0 -2
  903. package/dist/web/_app/immutable/chunks/BIBCr278.js +0 -1
  904. package/dist/web/_app/immutable/chunks/BPCkkwqe.js +0 -1
  905. package/dist/web/_app/immutable/chunks/BhlvzGQx.js +0 -1
  906. package/dist/web/_app/immutable/chunks/BlMknQeF.js +0 -1
  907. package/dist/web/_app/immutable/chunks/BpjqrlCg.js +0 -1
  908. package/dist/web/_app/immutable/chunks/BrebtJNG.js +0 -18
  909. package/dist/web/_app/immutable/chunks/ByBZ7JIT.js +0 -1
  910. package/dist/web/_app/immutable/chunks/Bz0ZjVQE.js +0 -2
  911. package/dist/web/_app/immutable/chunks/BzJTZOK2.js +0 -1
  912. package/dist/web/_app/immutable/chunks/C0LVZhvn.js +0 -1
  913. package/dist/web/_app/immutable/chunks/C3uBrOS5.js +0 -1
  914. package/dist/web/_app/immutable/chunks/C8R1sDpW.js +0 -1
  915. package/dist/web/_app/immutable/chunks/CDRHrs0g.js +0 -1
  916. package/dist/web/_app/immutable/chunks/CN5_py1I.js +0 -1
  917. package/dist/web/_app/immutable/chunks/CPne482a.js +0 -1
  918. package/dist/web/_app/immutable/chunks/CSVbGg_b.js +0 -5
  919. package/dist/web/_app/immutable/chunks/CWyU-seV.js +0 -1
  920. package/dist/web/_app/immutable/chunks/CX_61fY8.js +0 -5
  921. package/dist/web/_app/immutable/chunks/CYrVHOHA.js +0 -1
  922. package/dist/web/_app/immutable/chunks/ChsiIm-E.js +0 -1
  923. package/dist/web/_app/immutable/chunks/CojKppbh.js +0 -1
  924. package/dist/web/_app/immutable/chunks/CqkleIqs.js +0 -1
  925. package/dist/web/_app/immutable/chunks/CtYLtD7K.js +0 -1
  926. package/dist/web/_app/immutable/chunks/D-HDy5qS.js +0 -1
  927. package/dist/web/_app/immutable/chunks/D5aIYSkd.js +0 -1
  928. package/dist/web/_app/immutable/chunks/DBBXARVf.js +0 -1
  929. package/dist/web/_app/immutable/chunks/DFYOFE3o.js +0 -1
  930. package/dist/web/_app/immutable/chunks/DKaafS50.js +0 -1
  931. package/dist/web/_app/immutable/chunks/DUMOo1Pn.js +0 -1
  932. package/dist/web/_app/immutable/chunks/DWX9f6AF.js +0 -1
  933. package/dist/web/_app/immutable/chunks/DY8-EONP.js +0 -1
  934. package/dist/web/_app/immutable/chunks/DkoHPElM.js +0 -1
  935. package/dist/web/_app/immutable/chunks/DnL9f0lc.js +0 -7
  936. package/dist/web/_app/immutable/chunks/DsnmJJEf.js +0 -1
  937. package/dist/web/_app/immutable/chunks/DujahU3W.js +0 -1
  938. package/dist/web/_app/immutable/chunks/FBn-ES5k.js +0 -1
  939. package/dist/web/_app/immutable/chunks/HQpwSOb3.js +0 -1
  940. package/dist/web/_app/immutable/chunks/KyjzlWRN.js +0 -2
  941. package/dist/web/_app/immutable/chunks/PPVm8Dsz.js +0 -1
  942. package/dist/web/_app/immutable/chunks/UwYfmrPx.js +0 -2
  943. package/dist/web/_app/immutable/chunks/W25ZVI8L.js +0 -2
  944. package/dist/web/_app/immutable/chunks/WSUKABI_.js +0 -1
  945. package/dist/web/_app/immutable/chunks/XjsuEnNE.js +0 -5
  946. package/dist/web/_app/immutable/chunks/ZD6ARAtb.js +0 -1
  947. package/dist/web/_app/immutable/chunks/pBTf4stg.js +0 -2
  948. package/dist/web/_app/immutable/chunks/rGIarcnp.js +0 -1
  949. package/dist/web/_app/immutable/chunks/vxEtkL8z.js +0 -1
  950. package/dist/web/_app/immutable/chunks/wDIGUaoU.js +0 -3
  951. package/dist/web/_app/immutable/entry/app.ZHiTM8WS.js +0 -2
  952. package/dist/web/_app/immutable/entry/start.C9-FfAVc.js +0 -1
  953. package/dist/web/_app/immutable/nodes/0.D92Orz8k.js +0 -2
  954. package/dist/web/_app/immutable/nodes/1.BMdyglM_.js +0 -1
  955. package/dist/web/_app/immutable/nodes/10.BilVDHSL.js +0 -1
  956. package/dist/web/_app/immutable/nodes/11.CS9E0Hic.js +0 -1
  957. package/dist/web/_app/immutable/nodes/12.D3BOxYzG.js +0 -1
  958. package/dist/web/_app/immutable/nodes/13.CClMKYtN.js +0 -1
  959. package/dist/web/_app/immutable/nodes/2.C8KKElPL.js +0 -267
  960. package/dist/web/_app/immutable/nodes/3.C5n5uiWU.js +0 -5
  961. package/dist/web/_app/immutable/nodes/4.DcGVvgiL.js +0 -11
  962. package/dist/web/_app/immutable/nodes/5.DxIjnFQO.js +0 -4
  963. package/dist/web/_app/immutable/nodes/6.lkh6kKLj.js +0 -1
  964. package/dist/web/_app/immutable/nodes/7.Bg7SbfhA.js +0 -1
  965. package/dist/web/_app/immutable/nodes/8.DcXWCNBE.js +0 -1
  966. package/dist/web/_app/immutable/nodes/9.Cefi8Jfm.js +0 -1
@@ -4,118 +4,327 @@
4
4
  * Hosts:
5
5
  * - Graph API (read-only REST over WorkspaceManager)
6
6
  * - Agent chat via POST /api/chat and SSE GET /api/chat/stream
7
- * - App plugins mounted via PluginLoader
7
+ * - Static web UI (SvelteKit build) with SPA fallback
8
8
  */
9
9
  import Fastify from 'fastify';
10
10
  import cookie from '@fastify/cookie';
11
11
  import cors from '@fastify/cors';
12
+ import rateLimit from '@fastify/rate-limit';
12
13
  import websocket from '@fastify/websocket';
14
+ import { sanitize } from './middleware/sanitize.js';
13
15
  import { existsSync, readFileSync, createReadStream, statSync } from 'fs';
14
- import { join, extname } from 'path';
16
+ import { join, extname, resolve, sep } from 'path';
15
17
  import { Workspace } from '../core/workspace.js';
16
18
  import { AgentOrchestrator } from '../agent/orchestrator.js';
17
- import { PluginLoader, MIME_TYPES } from './plugin-loader.js';
18
19
  import { registerGraphApiRoutes } from './routes/graph-api.js';
19
20
  import { registerChatRoutes } from './routes/chat.js';
20
21
  import { registerWorkspaceApiRoutes } from './routes/workspace-api.js';
22
+ import { registerHealthApiRoutes } from './routes/health.js';
21
23
  import { registerPermissionsApiRoutes } from './routes/permissions-api.js';
22
24
  import { registerAuthApiRoutes } from './routes/auth-api.js';
23
25
  import { registerGitHttpRoutes } from './routes/git-http.js';
24
26
  import { registerViewsApiRoutes } from './routes/views-api.js';
25
- import { registerBriefingApiRoutes } from './routes/insights-api.js';
27
+ import { registerInsightsApiRoutes } from './routes/insights-api.js';
26
28
  import { registerMcpRoutes } from './routes/mcp.js';
27
29
  import { registerConnectorsApiRoutes } from './routes/connectors-api.js';
30
+ import { registerOAuthApiRoutes, resolveCanonicalBaseUrl } from './routes/oauth-api.js';
28
31
  import { registerAssetApiRoutes } from './routes/asset-api.js';
32
+ import { verifyArtifactAssetToken } from '../services/artifact-asset-token.js';
33
+ import { registerR2ApiRoutes } from './routes/r2-api.js';
29
34
  import multipart from '@fastify/multipart';
30
- import { registerMeetingIngestRoutes } from './routes/meeting-ingest-api.js';
35
+ import { registerEventIngestRoutes } from './routes/event-ingest-api.js';
31
36
  import { registerCaptureRoutes } from './routes/capture-api.js';
32
37
  import { registerUrlRoutes } from './routes/url-api.js';
33
- import { registerMessagesApiRoutes } from './routes/messages-api.js';
34
38
  import { registerConfigApiRoutes } from './routes/config-api.js';
35
- import { AuthService, isAdminOrOwner } from '../services/auth-service.js';
36
- import { MessageService } from '../services/message-service.js';
39
+ import { registerSettingsApiRoutes } from './routes/settings-api.js';
40
+ import { registerSecretsApiRoutes } from './routes/secrets-api.js';
41
+ import { registerAuditApiRoutes } from './routes/audit-api.js';
42
+ import { registerCommentsApiRoutes } from './routes/comments-api.js';
43
+ import { registerChatSessionsApiRoutes } from './routes/chat-sessions-api.js';
44
+ import { registerShareApiRoutes } from './routes/share-api.js';
45
+ import { registerArtifactExportApiRoutes } from './routes/artifact-export-api.js';
46
+ import { AuthService, isWorkspaceOwner } from '../services/auth-service.js';
37
47
  import { MemoryService } from '../services/memory-service.js';
48
+ import { CommentService } from '../services/comment-service.js';
49
+ import { ChatSessionService } from '../services/chat-session-service.js';
50
+ import { ShareLinkService } from '../services/share-link-service.js';
51
+ import { NotificationService } from '../services/notification-service.js';
38
52
  import { WsHub } from './ws-hub.js';
39
53
  import { registerWsRoute } from './routes/ws.js';
40
54
  import { SessionManager } from './session-manager.js';
55
+ import { AssetIndexQueue } from './asset-index-queue.js';
41
56
  import { YjsManager } from './yjs-manager.js';
42
- /** Ensure every user has a private "My Entries" collection (idempotent). */
43
- async function ensureAllPrivateCollections(authService, workspacePath, workspaceManager) {
57
+ import { YjsPersistence, acquireYjsStateLock } from './yjs-persistence.js';
58
+ import { BodyWriteCoordinator } from './body-write-coordinator.js';
59
+ import { ensurePersonalFolder } from '../services/personal-folder.js';
60
+ import { loadUserConfig, resolveProvider, resolveModelId, resolveApiKey } from '../core/user-config.js';
61
+ /** Ensure every user has a personal "My Folder" folder + admin access on it (idempotent). */
62
+ async function ensureAllPersonalFolders(authService, workspacePath, workspaceManager) {
44
63
  const users = authService.listUsers();
45
64
  const workspace = new Workspace(workspacePath);
46
65
  for (const user of users) {
47
- if (workspaceManager.getUserPrivateRepo(user.id))
48
- continue;
49
66
  try {
50
- await workspace.registerRepo({
51
- name: `entries-${user.id}`,
52
- display_name: 'My Entries',
53
- description: `Private entries for ${user.displayName}`,
54
- private: true,
55
- owner_id: user.id,
56
- });
57
- workspaceManager.reloadConfig();
67
+ await ensurePersonalFolder(workspace, authService, user);
58
68
  }
59
- catch { /* already exists or other issue skip */ }
69
+ catch { /* non-fatal skip and let the user re-authenticate */ }
70
+ }
71
+ workspaceManager.reloadConfig();
72
+ }
73
+ /** MIME types for static file serving (web UI assets). */
74
+ const MIME_TYPES = {
75
+ '.html': 'text/html',
76
+ '.css': 'text/css',
77
+ '.js': 'application/javascript',
78
+ '.mjs': 'application/javascript',
79
+ '.json': 'application/json',
80
+ '.png': 'image/png',
81
+ '.jpg': 'image/jpeg',
82
+ '.jpeg': 'image/jpeg',
83
+ '.gif': 'image/gif',
84
+ '.svg': 'image/svg+xml',
85
+ '.ico': 'image/x-icon',
86
+ '.woff': 'font/woff',
87
+ '.woff2': 'font/woff2',
88
+ '.ttf': 'font/ttf',
89
+ '.otf': 'font/otf',
90
+ '.webp': 'image/webp',
91
+ '.avif': 'image/avif',
92
+ '.mp4': 'video/mp4',
93
+ '.webm': 'video/webm',
94
+ };
95
+ const PUBLIC_IMMUTABLE_FONT_ASSET_RE = /^\/api\/assets\/[^/]+\/[^/]+\.(?:woff2?|ttf|otf)$/i;
96
+ // EXACT two-segment immutable asset shape — the only path the artifact-asset
97
+ // capability token (`?at=`) may unlock. Excludes the legacy 3-segment route
98
+ // (which carries known repo-identity hazards) by matching to end-of-path.
99
+ const ARTIFACT_ASSET_TOKEN_PATH_RE = /^\/api\/assets\/[^/]+\/[^/]+$/;
100
+ export function resolveStaticFilePath(webDir, urlPath) {
101
+ let decodedPath;
102
+ try {
103
+ decodedPath = decodeURIComponent(urlPath);
104
+ }
105
+ catch {
106
+ return null;
107
+ }
108
+ const filePath = decodedPath === '/' ? '/index.html' : decodedPath;
109
+ const webRoot = resolve(webDir);
110
+ const fullPath = resolve(webRoot, `.${filePath}`);
111
+ if (fullPath !== webRoot && !fullPath.startsWith(webRoot + sep)) {
112
+ return null;
113
+ }
114
+ return fullPath;
115
+ }
116
+ function normalizeOrigin(value) {
117
+ if (!value)
118
+ return null;
119
+ try {
120
+ return new URL(value).origin;
121
+ }
122
+ catch {
123
+ return null;
60
124
  }
61
125
  }
126
+ export function buildAllowedCorsOrigins(workspaceConfig, port) {
127
+ const rawOrigins = [];
128
+ const envOrigins = process.env.STUDIOGRAPH_CORS_ORIGINS;
129
+ if (envOrigins)
130
+ rawOrigins.push(...envOrigins.split(',').map(o => o.trim()).filter(Boolean));
131
+ const configured = workspaceConfig.cors_origins;
132
+ if (Array.isArray(configured)) {
133
+ rawOrigins.push(...configured.filter((o) => typeof o === 'string'));
134
+ }
135
+ else if (typeof configured === 'string') {
136
+ rawOrigins.push(...configured.split(',').map(o => o.trim()).filter(Boolean));
137
+ }
138
+ const serverUrl = workspaceConfig.server_url;
139
+ if (typeof serverUrl === 'string')
140
+ rawOrigins.push(serverUrl);
141
+ rawOrigins.push(`http://localhost:${port}`, `http://127.0.0.1:${port}`, `http://[::1]:${port}`);
142
+ return new Set(rawOrigins.map(normalizeOrigin).filter((o) => Boolean(o)));
143
+ }
144
+ export function isCorsOriginAllowed(origin, allowedOrigins) {
145
+ if (!origin)
146
+ return true;
147
+ const normalized = normalizeOrigin(origin);
148
+ return Boolean(normalized && allowedOrigins.has(normalized));
149
+ }
150
+ function isTruthyEnv(value) {
151
+ return Boolean(value && ['1', 'true', 'yes', 'on'].includes(value.trim().toLowerCase()));
152
+ }
62
153
  /**
63
154
  * Auth middleware — validates JWT cookie or Authorization: Bearer <key> header.
64
155
  * When users exist in the auth database, all routes require authentication
65
156
  * (except auth endpoints and static assets). When no users exist, everything
66
157
  * is open (backward compatible).
67
158
  */
68
- function createAuthHook(apiKeys, authService) {
159
+ export function createAuthHook(authService, workspaceConfig) {
69
160
  return async (req, reply) => {
70
161
  // Always allow auth endpoints
71
162
  if (req.url.startsWith('/api/auth/'))
72
163
  return;
164
+ // Managed Cloud login handoff verifies its own short-lived token.
165
+ if (req.url.startsWith('/api/cloud/auth/'))
166
+ return;
73
167
  // OAuth callbacks and claims identify the user via state/claim code, not cookies
74
168
  if (req.url.startsWith('/api/oauth/callback/'))
75
169
  return;
76
170
  if (req.url.startsWith('/api/oauth/claim/'))
77
171
  return;
172
+ // OAuth authorization-server discovery + token endpoints are
173
+ // unauthenticated by spec (the client has no user yet). They are
174
+ // rate-limited + validated in oauth-api.ts. NOT /oauth/authorize — that
175
+ // one must run as the logged-in user (handled explicitly below, above the
176
+ // HTML-GET bypass which would otherwise skip cookie resolution).
177
+ if (req.url.startsWith('/.well-known/oauth-'))
178
+ return;
179
+ if (req.url.startsWith('/oauth/token') ||
180
+ req.url.startsWith('/oauth/register') ||
181
+ req.url.startsWith('/oauth/revoke'))
182
+ return;
183
+ if (req.url.startsWith('/oauth/authorize')) {
184
+ const cookieTok = req.cookies?.['__sg_token'];
185
+ // Resolve the session cookie, but REFUSE an OAuth access token here — a
186
+ // cookie is a browser-session surface and must never carry an
187
+ // authorization-server-issued token (see the cookie branch below).
188
+ const c = cookieTok ? authService.verifyTokenWithClaims(cookieTok) : null;
189
+ if (c && !c.oauth) {
190
+ req.user = c.user;
191
+ return;
192
+ }
193
+ // Not logged in: bounce a browser GET to login (carrying the authorize
194
+ // URL as returnTo); refuse a POST.
195
+ if (req.method === 'GET') {
196
+ return reply.redirect(`/login?returnTo=${encodeURIComponent(req.url)}`);
197
+ }
198
+ return reply.status(401).send({ error: 'login_required' });
199
+ }
78
200
  // Git HTTP routes have their own Basic Auth
79
201
  if (req.url.startsWith('/git/'))
80
202
  return;
81
203
  // WebSocket route handles its own auth
82
204
  if (req.url.startsWith('/ws'))
83
205
  return;
84
- // Allow static assets (JS, CSS, fonts, images) — needed for login page
85
- if (req.url.startsWith('/_app/') || req.url.startsWith('/api/chrome/'))
206
+ // Allow static assets (JS, CSS, fonts, images) — needed for login page.
207
+ // Covers SvelteKit-bundled output under /_app/ and root-level public
208
+ // files (favicon.ico, studiograph-logomark.svg, …) identified by a file
209
+ // extension on a non-API GET.
210
+ if (req.url.startsWith('/_app/'))
211
+ return;
212
+ // Public share surface: the two read-only routes are authorized by the
213
+ // opaque token (verified in share-api.ts), not a session. Scope the bypass
214
+ // to EXACTLY the public read + asset shapes so a future /api/share/* route
215
+ // isn't accidentally public. Allow HEAD as well as GET — the asset route is
216
+ // registered for both (image preflights / link unfurlers issue HEAD). The
217
+ // mint route is POST /api/repos/... and stays fully authed.
218
+ if ((req.method === 'GET' || req.method === 'HEAD') &&
219
+ /^\/api\/share\/[^/]+(\/asset\/[^/]+\/[^/]+)?$/.test(req.url.split('?')[0]))
220
+ return;
221
+ // Sandboxed artifact iframes have an opaque origin, so @font-face loads may
222
+ // arrive without the web session cookie. Let immutable font asset requests
223
+ // reach the asset route; the route still only serves public workspace-wide
224
+ // font assets and keeps all other asset URLs behind normal authz.
225
+ if ((req.method === 'GET' || req.method === 'HEAD') &&
226
+ PUBLIC_IMMUTABLE_FONT_ASSET_RE.test(req.url.split('?')[0]))
227
+ return;
228
+ // Sandboxed artifact iframes (opaque origin, no cookie) carry an
229
+ // artifact-asset capability token in `?at=`. Only the EXACT 2-segment
230
+ // immutable shape qualifies (never the legacy 3-seg route). Verify the
231
+ // signature and stash the payload as a CANDIDATE — the asset route does the
232
+ // authoritative authz against the resolved row (repo + allowlist + per-serve
233
+ // user re-check). We do NOT set req.user. An invalid token falls through to
234
+ // normal auth (→ 401/404), so a bad `?at=` can't downgrade security.
235
+ if ((req.method === 'GET' || req.method === 'HEAD') &&
236
+ ARTIFACT_ASSET_TOKEN_PATH_RE.test(req.url.split('?')[0])) {
237
+ const at = req.query?.at;
238
+ if (typeof at === 'string' && at.length > 0) {
239
+ try {
240
+ req.artifactAssetToken = verifyArtifactAssetToken(at, authService.getJwtSecret());
241
+ // Only short-circuit for a cookieless caller (the sandboxed iframe).
242
+ // If a session cookie is present, fall through to normal auth so
243
+ // req.user is set and the route serves on the SESSION's access — the
244
+ // token is a fallback for the no-cookie case, never an override.
245
+ if (!req.cookies?.['__sg_token'])
246
+ return;
247
+ }
248
+ catch { /* invalid/expired → fall through to normal auth */ }
249
+ }
250
+ }
251
+ if (req.method === 'GET' &&
252
+ !req.url.startsWith('/api/') &&
253
+ extname(req.url.split('?')[0]).length > 1)
86
254
  return;
87
255
  // Allow HTML page requests (SPA routing) — the app handles auth client-side
88
256
  if (req.method === 'GET' && req.headers.accept?.includes('text/html'))
89
257
  return;
90
- // If no users configured, allow everything (open mode)
91
- if (!authService.hasUsers()) {
92
- // Still check API keys for programmatic routes if configured
93
- if (apiKeys.length > 0) {
94
- const authHeader = req.headers['authorization'];
95
- const bearer = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : undefined;
96
- if (bearer && apiKeys.includes(bearer))
97
- return;
98
- // For non-API routes (i.e. browser requests), allow without auth
99
- if (!req.url.startsWith('/api/') || req.url.startsWith('/api/auth/'))
100
- return;
101
- // For API routes without a bearer token, allow if no users exist
102
- }
258
+ // If no users configured, allow everything in local open mode. Managed
259
+ // Cloud workspaces still require auth before the first user handoff lands.
260
+ if (!authService.hasUsers() && !isTruthyEnv(process.env.STUDIOGRAPH_MANAGED_AUTH))
103
261
  return;
104
- }
105
- // Check JWT cookie (web UI sessions)
262
+ // Check JWT cookie (web UI sessions).
263
+ //
264
+ // Security: resolve with verifyTokenWithClaims and REJECT OAuth access
265
+ // tokens (`oauth:true`). OAuth tokens are JWTs signed with the same
266
+ // jwtSecret, so the legacy verifyToken() would accept one supplied as a
267
+ // cookie and return a fully-privileged session — bypassing the MCP-only
268
+ // audience scoping enforced on the Bearer path below. A session cookie is
269
+ // a web-UI surface and must never carry an authorization-server token.
106
270
  const jwtToken = req.cookies?.['__sg_token'];
107
271
  if (jwtToken) {
108
- const user = authService.verifyToken(jwtToken);
109
- if (user) {
110
- req.user = user;
272
+ const claims = authService.verifyTokenWithClaims(jwtToken);
273
+ if (claims && !claims.oauth) {
274
+ req.user = claims.user;
111
275
  return;
112
276
  }
113
277
  }
114
- // Check Bearer token (API clients, MCP)
278
+ // Check Bearer token (API clients, MCP, CLI): JWT → per-user API token.
279
+ // Both yield a user identity. There is no shared workspace key — every
280
+ // caller must be identifiable so access control can enforce per-folder rules.
115
281
  const authHeader = req.headers['authorization'];
116
282
  const bearer = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : undefined;
117
- if (bearer && apiKeys.includes(bearer))
118
- return;
283
+ if (bearer) {
284
+ const claims = authService.verifyTokenWithClaims(bearer);
285
+ if (claims) {
286
+ if (claims.oauth) {
287
+ // OAuth access tokens are confined to the MCP surface. Reject them on
288
+ // the rest of the REST API so a leaked connector token can't drive the
289
+ // whole product (least privilege — sg_ tokens stay full).
290
+ const path = req.url.split('?')[0];
291
+ const mcpScoped = path === '/mcp' || path.startsWith('/mcp/') || path.startsWith('/oauth/');
292
+ if (!mcpScoped) {
293
+ return reply.status(403).send({ error: 'insufficient_scope' });
294
+ }
295
+ // Enforce the token audience: it must be bound to THIS server's /mcp
296
+ // resource. Without this, any JWT signed with the workspace secret +
297
+ // `oauth:true` would be accepted regardless of the `aud` it was minted
298
+ // for (stale/wrong-resource/host-spoofed tokens).
299
+ let expectedAud = null;
300
+ try {
301
+ expectedAud = `${resolveCanonicalBaseUrl(workspaceConfig, req)}/mcp`;
302
+ }
303
+ catch {
304
+ expectedAud = null;
305
+ }
306
+ if (!expectedAud || claims.aud !== expectedAud) {
307
+ return reply.status(403).send({ error: 'insufficient_scope', error_description: 'token audience mismatch' });
308
+ }
309
+ // Label the integration for agent-activity attribution (parity with
310
+ // the api-token name path below). cid is the stable client_id.
311
+ if (claims.cid) {
312
+ const client = authService.oauthServer.getClient(claims.cid);
313
+ req.apiTokenName = client?.client_name ?? 'OAuth client';
314
+ }
315
+ }
316
+ req.user = claims.user;
317
+ return;
318
+ }
319
+ const tokenMeta = authService.verifyApiTokenWithMeta(bearer);
320
+ if (tokenMeta) {
321
+ req.user = tokenMeta.user;
322
+ // Stash the token's user-set name so downstream surfaces (MCP, agent
323
+ // activity) can label the integration. Absent for cookie/JWT auth.
324
+ req.apiTokenName = tokenMeta.tokenName;
325
+ return;
326
+ }
327
+ }
119
328
  // Check Basic Auth (CLI join command)
120
329
  if (authHeader?.startsWith('Basic ')) {
121
330
  try {
@@ -131,27 +340,158 @@ function createAuthHook(apiKeys, authService) {
131
340
  }
132
341
  catch { /* fall through to 401 */ }
133
342
  }
343
+ // MCP OAuth discovery: an unauthenticated /mcp request gets a
344
+ // WWW-Authenticate header pointing at the protected-resource metadata, so
345
+ // an MCP client (Claude Chat/Cowork) knows to begin the OAuth flow.
346
+ if (req.url.startsWith('/mcp')) {
347
+ // Best-effort: if no canonical issuer is pinned (fails closed in prod),
348
+ // skip the discovery hint rather than 500 the 401.
349
+ try {
350
+ const base = resolveCanonicalBaseUrl(workspaceConfig, req);
351
+ reply.header('WWW-Authenticate', `Bearer resource_metadata="${base}/.well-known/oauth-protected-resource"`);
352
+ }
353
+ catch { /* issuer unconfigured — omit discovery header */ }
354
+ }
134
355
  return reply.status(401).send({ error: 'Unauthorized' });
135
356
  };
136
357
  }
358
+ /**
359
+ * Phase 11 — preSerialization payload sanitizer.
360
+ *
361
+ * The API has many routes that respond with `{ error: err.message }` from a
362
+ * catch block. Those messages can carry upstream provider errors, git
363
+ * stderr (with embedded credentials), or other free-form text that we
364
+ * don't want surfacing in HTTP responses. Walk the response payload,
365
+ * sanitize any string value at a "message" / "error" / "detail" key,
366
+ * and leave structured fields (status codes, IDs, classification flags)
367
+ * alone.
368
+ *
369
+ * Conservatively, we ONLY redact at keys that conventionally carry
370
+ * free-form text. Arbitrary string fields (entity names, IDs, etc.) are
371
+ * left untouched so we don't garble legitimate content. The Fastify
372
+ * logger sanitization (above) handles log-line redaction independently;
373
+ * this hook focuses on the wire response.
374
+ */
375
+ const FREEFORM_ERROR_KEYS = new Set(['error', 'message', 'detail', 'stack', 'description', 'error_description']);
376
+ function sanitizeResponsePayload(value) {
377
+ if (typeof value === 'string')
378
+ return sanitize(value);
379
+ if (Array.isArray(value))
380
+ return value.map(sanitizeResponsePayload);
381
+ if (value && typeof value === 'object') {
382
+ const out = {};
383
+ for (const [k, v] of Object.entries(value)) {
384
+ if (FREEFORM_ERROR_KEYS.has(k) && typeof v === 'string') {
385
+ out[k] = sanitize(v);
386
+ }
387
+ else if (v && typeof v === 'object') {
388
+ out[k] = sanitizeResponsePayload(v);
389
+ }
390
+ else {
391
+ out[k] = v;
392
+ }
393
+ }
394
+ return out;
395
+ }
396
+ return value;
397
+ }
137
398
  export async function createServer(config) {
399
+ // Phase 11: every Fastify log line flows through sanitize() before
400
+ // reaching stdout. A thin Writable wrapper passes each chunk through
401
+ // the redactor; pino accepts any `stream` with a `write()` method.
402
+ // STUDIOGRAPH_LOG_RAW=1 disables redaction for local debugging.
403
+ const shouldRedactFastifyLogs = process.env.STUDIOGRAPH_LOG_RAW !== '1' &&
404
+ (process.env.NODE_ENV === 'production' || !!process.env.RAILWAY_ENVIRONMENT);
405
+ const redactingStream = {
406
+ write(chunk) {
407
+ const out = shouldRedactFastifyLogs ? sanitize(chunk) : chunk;
408
+ process.stdout.write(out);
409
+ },
410
+ };
138
411
  const fastify = Fastify({
139
412
  logger: process.env.STUDIOGRAPH_LOG === '1'
140
- ? true
141
- : { level: 'warn' },
413
+ ? { stream: redactingStream }
414
+ : { level: 'warn', stream: redactingStream },
142
415
  bodyLimit: 10 * 1024 * 1024, // 10 MB — entities can be large (e.g. meeting transcripts)
143
416
  });
417
+ // Phase 11: sanitize uncaught errors before serialization so neither
418
+ // err.message nor err.stack echoes upstream secrets. Stable error
419
+ // codes (err.code) pass through; we only redact freeform text.
420
+ fastify.setErrorHandler((error, _req, reply) => {
421
+ const sanitizedMsg = shouldRedactFastifyLogs ? sanitize(error.message) : error.message;
422
+ const status = error.statusCode ?? 500;
423
+ fastify.log.error({
424
+ err: { message: sanitizedMsg, code: error.code },
425
+ });
426
+ reply.status(status).send({
427
+ statusCode: status,
428
+ code: error.code,
429
+ error: status >= 500 ? 'Internal Server Error' : (error.name || 'Error'),
430
+ message: status >= 500 ? 'An internal error occurred.' : sanitizedMsg,
431
+ });
432
+ });
433
+ // Phase 11: pre-serialization hook scans every response body for
434
+ // freeform error/message strings and runs them through sanitize().
435
+ // This catches the many `reply.status(4xx).send({ error: err.message })`
436
+ // call sites scattered across the API routes — without this, an
437
+ // upstream provider error or git remote error would echo through to
438
+ // the API consumer with embedded credentials. Stable error codes pass
439
+ // through unchanged. Only kicks in when redaction is enabled.
440
+ if (shouldRedactFastifyLogs) {
441
+ fastify.addHook('preSerialization', async (_req, _reply, payload) => {
442
+ if (!payload || typeof payload !== 'object')
443
+ return payload;
444
+ return sanitizeResponsePayload(payload);
445
+ });
446
+ }
144
447
  // CORS
448
+ const allowedCorsOrigins = buildAllowedCorsOrigins(config.workspaceConfig, config.port);
145
449
  await fastify.register(cors, {
146
- origin: true,
450
+ origin: (origin, cb) => {
451
+ cb(null, isCorsOriginAllowed(origin, allowedCorsOrigins));
452
+ },
147
453
  credentials: true,
148
454
  });
149
455
  // Cookie support for web UI session auth
150
456
  await fastify.register(cookie);
151
- // WebSocket support for real-time notifications
152
- await fastify.register(websocket);
457
+ // WebSocket support for real-time notifications.
458
+ //
459
+ // perMessageDeflate is enabled here on purpose. Our origin would otherwise
460
+ // leave compression off (ws lib default), but Railway's edge proxy
461
+ // negotiates `permessage-deflate` with the browser anyway and does its own
462
+ // inflate/deflate layer in the middle — which Safari/WebKit mis-decodes,
463
+ // producing truncated buffers and `RangeError: Length out of range of buffer`
464
+ // inside Yjs's varint reader, followed by WS disconnect loops.
465
+ //
466
+ // Negotiating compression at the origin forces end-to-end deflate (Node's
467
+ // zlib ↔ browser) and prevents the proxy from inserting its own layer.
468
+ // `noContextTakeover` on both sides keeps each frame independently
469
+ // decompressible, which avoids cross-frame state bugs that have historically
470
+ // hit Safari.
471
+ await fastify.register(websocket, {
472
+ options: {
473
+ perMessageDeflate: {
474
+ threshold: 1024,
475
+ serverNoContextTakeover: true,
476
+ clientNoContextTakeover: true,
477
+ },
478
+ },
479
+ });
153
480
  // Multipart support for file uploads (images, assets)
154
481
  await fastify.register(multipart, { limits: { fileSize: 10 * 1024 * 1024 } });
482
+ // Global rate-limit registration with a permissive default; individual
483
+ // routes opt into tighter limits via `config: { rateLimit: ... }`. Today
484
+ // only comment mutations apply tight limits — keeps the runtime in
485
+ // place without changing the rest of the API's request-rate behaviour.
486
+ await fastify.register(rateLimit, {
487
+ global: false,
488
+ max: 1000,
489
+ timeWindow: '1 minute',
490
+ keyGenerator: (req) => {
491
+ const u = req.user;
492
+ return u?.id ? `u:${u.id}` : (req.ip ?? 'unknown');
493
+ },
494
+ });
155
495
  fastify.addContentTypeParser('application/json', { parseAs: 'buffer' }, (req, body, done) => {
156
496
  try {
157
497
  req.rawBody = body;
@@ -161,6 +501,21 @@ export async function createServer(config) {
161
501
  done(err, undefined);
162
502
  }
163
503
  });
504
+ // application/x-www-form-urlencoded — the OAuth token + consent POSTs use
505
+ // form encoding (RFC 6749). Parsed inline to avoid pulling in a dependency
506
+ // just for two endpoints; mirrors the hand-rolled JSON parser above.
507
+ fastify.addContentTypeParser('application/x-www-form-urlencoded', { parseAs: 'string' }, (_req, body, done) => {
508
+ try {
509
+ const params = new URLSearchParams(body);
510
+ const obj = {};
511
+ for (const [k, v] of params)
512
+ obj[k] = v;
513
+ done(null, obj);
514
+ }
515
+ catch (err) {
516
+ done(err, undefined);
517
+ }
518
+ });
164
519
  // Git Smart HTTP binary content types — pass raw stream through (no parsing)
165
520
  for (const gitType of [
166
521
  'application/x-git-upload-pack-request',
@@ -170,159 +525,187 @@ export async function createServer(config) {
170
525
  done(null); // Don't consume the body — the route handler pipes req.raw directly
171
526
  });
172
527
  }
173
- // Initialize auth service, WebSocket hub, session manager, and collab server
174
- const authService = new AuthService(config.workspacePath);
175
- // Ensure API key is committed to auth-seed.json so `studiograph pull` can use it
176
- authService.syncApiKeyToSeed();
177
- // Ensure all users have their private "My Entries" collection
178
- await ensureAllPrivateCollections(authService, config.workspacePath, config.workspaceManager);
179
- const wsHub = new WsHub();
180
- const messageService = new MessageService(config.workspacePath);
528
+ // Initialize auth service, WebSocket hub, session manager, and collab server.
529
+ // Phase 8: when the boot path provided master-key options, hand them through
530
+ // so OAuth tokens + per-user secret prefs use the encrypted column path.
531
+ const authService = new AuthService(config.workspacePath, config.authCryptoOptions);
532
+ // Ensure all users have their personal "My Folder" folder
533
+ await ensureAllPersonalFolders(authService, config.workspacePath, config.workspaceManager);
534
+ const { getCurrentVersion } = await import('../utils/version-checker.js');
535
+ const wsHub = new WsHub(getCurrentVersion());
536
+ const commentService = new CommentService(config.workspacePath);
537
+ const chatSessionService = new ChatSessionService(config.workspacePath);
538
+ const shareLinkService = new ShareLinkService(config.workspacePath);
539
+ const notificationService = new NotificationService(config.workspacePath);
181
540
  const sessionManager = new SessionManager(config.workspaceManager, wsHub);
182
- const yjsManager = new YjsManager(config.workspaceManager);
541
+ // Recover any dirty repos before accepting requests. Replays persisted
542
+ // pending commits from a previous process AND sweeps every working tree
543
+ // for uncommitted state. Catches anything dropped by a crash, OOM, or
544
+ // hard restart. Logs and continues on failure — recovery is best-effort
545
+ // and must never block server startup.
546
+ try {
547
+ await sessionManager.recoverOnBoot();
548
+ }
549
+ catch (err) {
550
+ console.warn('[server] sessionManager.recoverOnBoot failed:', err.message);
551
+ }
552
+ // Live asset-index drain: captions new image uploads and re-extracts document
553
+ // sidecars while the server runs, instead of only at boot (a tenant can run
554
+ // for weeks between deploys). Config (model/key/gate/repos) is resolved fresh
555
+ // each drain so a Settings change takes effect without a restart. Web upload
556
+ // + MCP finalize call `enqueue()` after the asset entity exists.
557
+ const assetIndexQueue = new AssetIndexQueue({
558
+ workspaceManager: config.workspaceManager,
559
+ resolveConfig: () => {
560
+ const userConfig = loadUserConfig();
561
+ // Read the LIVE workspace config from the WorkspaceManager, NOT the boot
562
+ // snapshot (config.workspaceConfig). reloadConfig() refreshes both the
563
+ // repo list (so a folder auto-created at runtime — e.g. the shared
564
+ // "Assets" store on first inline/presign upload — is visible to the
565
+ // sweep) and the captioning gate (so toggling it in workspace.json takes
566
+ // effect without a restart). Model/key already resolve store-first.
567
+ const wsConfig = config.workspaceManager.getConfig();
568
+ const apiKey = resolveApiKey(userConfig, wsConfig);
569
+ return {
570
+ repos: config.workspaceManager.getAllRepoConfigs(),
571
+ captionConfig: apiKey
572
+ ? { provider: resolveProvider(userConfig, wsConfig), modelId: resolveModelId(userConfig, wsConfig), apiKey }
573
+ : null,
574
+ captioningEnabled: wsConfig.captioning?.enabled ?? true,
575
+ };
576
+ },
577
+ log: (msg) => console.warn(`[asset-index] ${msg}`),
578
+ });
579
+ assetIndexQueue.start();
580
+ // Single chokepoint: VectorService fires onAssetPending exactly when a fresh
581
+ // indexable IMAGE assets.db row is seeded `pending` (every image-upload path
582
+ // funnels through ensureInitialAssetIndexState — import, manager/editor
583
+ // upload, presign finalize). Registering here replaces per-call-site enqueue
584
+ // wiring and keys the queue on the immutable assetId.
585
+ config.workspaceManager.vectorService?.setOnAssetPending((assetId) => assetIndexQueue.enqueue(assetId));
586
+ // Boot recovery: drain anything a prior process left queued AND re-derive the
587
+ // full unfinished set in LanceDB (legacy assets, a mid-flight crash). Fire-and-
588
+ // forget — it can take minutes on a large gallery and must not block listen().
589
+ void assetIndexQueue.recoverOnBoot().catch((err) => {
590
+ console.warn('[asset-index] recoverOnBoot failed (non-fatal):', err instanceof Error ? err.message : err);
591
+ });
592
+ // Persistence layer: durable Y.Doc snapshot store across server restarts.
593
+ // Lives at `<workspace>/.studiograph/yjs-state/` — gitignored binary state
594
+ // separate from the .md files (which remain canonical for git, MCP, agents).
595
+ // See yjs-persistence.ts for the file format and concurrency model.
596
+ const yjsStateDir = join(config.workspacePath, '.studiograph', 'yjs-state');
597
+ // Single-instance lock. Throws if another studiograph server is already
598
+ // running against this workspace. We don't support horizontal scaling today
599
+ // (no shared Yjs pubsub), so the safe default is to fail fast on misuse
600
+ // rather than silently corrupt snapshots via concurrent writers.
601
+ const releaseYjsLock = acquireYjsStateLock(yjsStateDir);
602
+ const yjsPersistence = new YjsPersistence(yjsStateDir);
603
+ const yjsManager = new YjsManager(config.workspaceManager, yjsPersistence);
604
+ // Yjs configuration startup log. Surfacing the flags at boot lets operators
605
+ // verify the configuration without reading source. The fail-closed guards
606
+ // (`assertWritable`, 412 convergence gate) prevent silent data corruption
607
+ // regardless of how the flags are set, but `broadcast=off` while clients
608
+ // are connected degrades UX, so we warn explicitly.
609
+ const broadcastEnabled = process.env.STUDIOGRAPH_YJS_BROADCAST !== '0';
610
+ const allowDegradedWrites = process.env.STUDIOGRAPH_ALLOW_DEGRADED_WRITES === '1';
611
+ console.log(`[yjs] config: broadcast=${broadcastEnabled ? 'on' : 'OFF'} ` +
612
+ `allow-degraded-writes=${allowDegradedWrites ? 'on' : 'off'}`);
613
+ if (!broadcastEnabled && !allowDegradedWrites) {
614
+ console.warn('[yjs] STUDIOGRAPH_YJS_BROADCAST=0 without STUDIOGRAPH_ALLOW_DEGRADED_WRITES=1: ' +
615
+ 'server-side body writes will REFUSE while clients are connected. ' +
616
+ 'This is the fail-closed kill-switch path — intentional during incident response, ' +
617
+ 'misconfiguration otherwise.');
618
+ }
619
+ // Wire the Yjs persistence layer to follow structural .md mutations.
620
+ // delete / rename / changeType all change a doc's identity (path on disk
621
+ // and its docName), so the snapshot file has to move with it. Without
622
+ // these hooks, snapshots leak (orphaned files for deleted entities) or
623
+ // serve stale state at the new identity (ghost content after a rename).
624
+ config.workspaceManager.setPersistenceCallbacks({
625
+ onDelete: (repo, entityType, entityId) => yjsPersistence.delete(`${repo}/${entityType}/${entityId}`),
626
+ onRename: (repo, entityType, oldId, newId) => yjsPersistence.rename(`${repo}/${entityType}/${oldId}`, `${repo}/${entityType}/${newId}`),
627
+ onChangeType: (repo, entityId, fromType, toType) => yjsPersistence.rename(`${repo}/${fromType}/${entityId}`, `${repo}/${toType}/${entityId}`),
628
+ });
629
+ // Body-write coordinator owns the single critical section for every body
630
+ // write (autosave PUT + MCP / agent / event-processor server-writes). Holds
631
+ // the per-doc lock, base-hash precondition, atomic disk write, commit +
632
+ // dirty recovery, post-commit applyAgentContent broadcast, and
633
+ // mdRevisionHash bookkeeping. Replaces the previous nine-callback shape on
634
+ // graph.update.
635
+ const bodyWriteCoordinator = new BodyWriteCoordinator({
636
+ // Narrow commit hook — keeps the coordinator from reaching back
637
+ // through WorkspaceManager. No-op when the repo has no GitService.
638
+ commit: async (coords, actor, message) => {
639
+ const gitService = config.workspaceManager.getGraph(coords.repo).getGitService();
640
+ if (!gitService)
641
+ return;
642
+ const tx = gitService.createTransaction();
643
+ tx.add({
644
+ operation: 'update',
645
+ entityType: coords.entityType,
646
+ entityId: coords.entityId,
647
+ filePath: coords.entityPath,
648
+ });
649
+ await tx.commit(actor, message);
650
+ },
651
+ }, yjsManager, sessionManager);
652
+ config.workspaceManager.setBodyWriteCoordinator(bodyWriteCoordinator);
183
653
  // Register auth API routes (before the auth hook so they are always accessible)
184
654
  await registerAuthApiRoutes(fastify, authService, config.workspacePath, config.workspaceManager, config.workspaceConfig.team_name);
185
655
  // Auth hook — applied to all routes
186
- const authHook = createAuthHook(config.apiKeys, authService);
656
+ const authHook = createAuthHook(authService, config.workspaceConfig);
187
657
  fastify.addHook('preHandler', authHook);
188
658
  // Register WebSocket route (real-time change notifications + cursor broadcasting)
189
- await registerWsRoute(fastify, wsHub, authService, yjsManager);
190
- // Register graph API routes (with per-collection access filtering)
659
+ await registerWsRoute(fastify, wsHub, authService, config.workspaceManager, yjsManager);
660
+ // Register graph API routes (with per-folder access filtering)
191
661
  await registerGraphApiRoutes(fastify, config.workspaceManager, authService, wsHub, sessionManager);
192
662
  // Register workspace API routes (git operations)
193
- await registerWorkspaceApiRoutes(fastify, config.workspaceManager, config.gitUser);
663
+ await registerWorkspaceApiRoutes(fastify, config.workspaceManager, config.gitUser, authService);
664
+ // Register health endpoints (per-repo dirty status)
665
+ await registerHealthApiRoutes(fastify, config.workspaceManager, sessionManager);
194
666
  // Register config + workspace data API routes (for `studiograph push/pull --remote`)
195
667
  await registerConfigApiRoutes(fastify, config.workspaceManager, config.workspacePath, authService);
196
- // Register permissions API routes (self-hosted collection access)
668
+ // Register Phase 7 settings/secrets/audit API routes. Owner-only via
669
+ // isWorkspaceOwner() inside each handler.
670
+ await registerSettingsApiRoutes(fastify, authService);
671
+ await registerSecretsApiRoutes(fastify, authService);
672
+ await registerAuditApiRoutes(fastify, authService);
673
+ // Register comments API + notifications feed (Comments-First Phase 1).
674
+ // The getAgent closure resolves the orchestrator lazily — it's
675
+ // initialised below at line ~613, AFTER routes register, so route
676
+ // handlers can't capture it eagerly. The closure binding to the outer
677
+ // `agent` variable picks up the value once the orchestrator is alive.
678
+ await registerCommentsApiRoutes(fastify, commentService, notificationService, authService, wsHub, config.workspaceManager, () => agent);
679
+ // Register chat-session persistence routes (cross-device agent history).
680
+ // `() => agent` defers the orchestrator handle (assigned below) so the scratch
681
+ // cleanup can also evict the agent-pool session JSON on chat deletion.
682
+ await registerChatSessionsApiRoutes(fastify, chatSessionService, config.workspaceManager, authService, wsHub, () => agent);
683
+ // Register permissions API routes (self-hosted folder access)
197
684
  await registerPermissionsApiRoutes(fastify, authService, config.workspaceManager, wsHub);
198
685
  // Messages API routes registered after agent init (below) so @agent invocation works
199
686
  // Register connectors API routes (MCP connector management + OAuth)
200
687
  await registerConnectorsApiRoutes(fastify, authService, config.workspacePath);
688
+ // Register OAuth 2.0 authorization-server routes (Claude Chat/Cowork connect)
689
+ await registerOAuthApiRoutes(fastify, authService, config.workspaceConfig);
201
690
  // Register asset upload routes (image upload to R2/local)
202
691
  await registerAssetApiRoutes(fastify, config.workspaceManager, authService);
692
+ // Register public share-link routes (mint authed; read + asset public)
693
+ await registerShareApiRoutes(fastify, config.workspaceManager, authService, shareLinkService);
694
+ // Register authenticated artifact export routes.
695
+ await registerArtifactExportApiRoutes(fastify, config.workspaceManager, authService);
696
+ // Register R2 management routes (bucket creation, local-asset sync, status)
697
+ await registerR2ApiRoutes(fastify, config.workspaceManager, authService, config.gitUser);
203
698
  // Register git Smart HTTP routes (clone/fetch/push with Basic Auth)
204
699
  await registerGitHttpRoutes(fastify, config.workspaceManager, authService, config.workspacePath, wsHub);
205
700
  // Register views API routes (per-user saved views)
206
701
  await registerViewsApiRoutes(fastify, authService, config.workspaceManager);
207
702
  // Register insights + preferences API routes
208
- // Build core tool metadata once from actual tool factories
209
- const coreToolMeta = [];
210
- try {
211
- const { createGraphTools } = await import('../agent/tools/graph-tools.js');
212
- const { createOpsTools } = await import('../agent/tools/ops-tools.js');
213
- const dummyUser = { id: '0', name: 'system', email: 'system' };
214
- const allTools = [
215
- ...createGraphTools(config.workspaceManager, dummyUser, config.workspacePath, undefined, undefined, () => null, authService),
216
- ...createOpsTools(config.workspacePath, config.workspaceConfig),
217
- ];
218
- for (const t of allTools) {
219
- if (t.name && t.description) {
220
- coreToolMeta.push({ name: t.name, description: t.description });
221
- }
222
- }
223
- }
224
- catch (err) {
225
- console.warn('[insights] Failed to build tool metadata:', err);
226
- }
227
- const getToolsForUser = (userId) => {
228
- const tools = [...coreToolMeta];
229
- // Add connector tools based on user's actually connected services
230
- try {
231
- const connectedConnectors = authService.getUserConnectedConnectors(userId);
232
- for (const c of connectedConnectors) {
233
- tools.push({ name: `${c.connector_name}_connector`, description: `Connected ${c.connector_name} integration — can sync and access ${c.connector_name} data` });
234
- }
235
- }
236
- catch { /* no connector tools */ }
237
- return tools;
238
- };
239
- await registerBriefingApiRoutes(fastify, config.workspaceManager, authService, config.workspacePath, wsHub, getToolsForUser);
240
- // Background briefing refresh — check staleness for online users every heartbeat tick
241
- {
242
- const BRIEFING_MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours
243
- const loadWsConfig = () => {
244
- const cfgPath = join(config.workspacePath, '.studiograph', 'workspace.json');
245
- if (!existsSync(cfgPath))
246
- return undefined;
247
- try {
248
- return JSON.parse(readFileSync(cfgPath, 'utf-8'));
249
- }
250
- catch {
251
- return undefined;
252
- }
253
- };
254
- const getReposForUser = (userId) => {
255
- const allRepos = config.workspaceManager.getAllRepoConfigs();
256
- const user = authService.findUserById(userId);
257
- if (!user)
258
- return allRepos.filter(r => !r.private).map(r => r.name);
259
- if (isAdminOrOwner(user.role))
260
- return allRepos.filter(r => !r.private || r.owner_id === user.id).map(r => r.name);
261
- const allowed = new Set(authService.getUserCollections(user.id));
262
- return allRepos.filter(r => (r.private && r.owner_id === user.id) || allowed.has(r.name)).map(r => r.name);
263
- };
264
- wsHub.setBriefingRefresh((userId) => {
265
- const brief = authService.getHomeBrief(userId);
266
- if (!brief)
267
- return false;
268
- // Always regenerate if no briefing text exists yet (migration from old insights system)
269
- const cached = authService.getCachedBriefing(userId);
270
- if (!cached.briefingText)
271
- return true;
272
- const age = authService.getCachedBriefingAge(userId);
273
- return age === null || age > BRIEFING_MAX_AGE_MS;
274
- }, async (userId) => {
275
- const brief = authService.getHomeBrief(userId);
276
- const repoNames = getReposForUser(userId);
277
- const tools = getToolsForUser(userId);
278
- try {
279
- const { generateBriefing } = await import('../services/briefing.js');
280
- await generateBriefing(config.workspaceManager, repoNames, brief, authService, userId, loadWsConfig(), tools);
281
- wsHub.broadcastToUser(String(userId), { type: 'briefing_updated', timestamp: new Date().toISOString() });
282
- console.log(`[briefing] Background refresh complete for user ${userId}`);
283
- }
284
- catch (err) {
285
- console.error(`[briefing] Background refresh failed for user ${userId}:`, err.message);
286
- }
287
- });
288
- }
289
- // Register meeting ingest API (Chrome extension + other capture tools)
290
- await registerMeetingIngestRoutes(fastify, config.workspaceManager, authService, wsHub);
703
+ await registerInsightsApiRoutes(fastify, config.workspaceManager, authService, config.workspacePath, wsHub, chatSessionService);
704
+ // Register event ingest API (meeting capture from desktop and external sources)
705
+ await registerEventIngestRoutes(fastify, config.workspaceManager, authService, wsHub, yjsManager);
291
706
  // Register MCP Streamable HTTP endpoint
292
- await registerMcpRoutes(fastify, config.workspaceManager, authService);
293
- // Serve chrome assets (shared header/footer for app editors)
294
- const chromeDir = join(import.meta.dirname, 'chrome');
295
- if (existsSync(chromeDir)) {
296
- const CHROME_MIME = { '.js': 'application/javascript', '.css': 'text/css' };
297
- fastify.get('/api/chrome/:file', async (req, reply) => {
298
- const file = req.params.file;
299
- if (file.includes('..') || file.includes('/')) {
300
- return reply.status(400).send({ error: 'Invalid path' });
301
- }
302
- const filePath = join(chromeDir, file);
303
- if (!existsSync(filePath) || !statSync(filePath).isFile()) {
304
- return reply.status(404).send({ error: 'Not found' });
305
- }
306
- const ext = extname(filePath).toLowerCase();
307
- reply.type(CHROME_MIME[ext] || 'application/octet-stream');
308
- reply.header('Cache-Control', 'public, max-age=300');
309
- return reply.send(createReadStream(filePath));
310
- });
311
- }
312
- // Discover installed apps and collect their skill/tool dirs
313
- const loader = new PluginLoader();
314
- const manifests = loader.loadApps(config.appsDir);
315
- const appSkillsDirs = [];
316
- const appToolsDirs = [];
317
- for (const manifest of manifests) {
318
- const skillsDir = loader.getSkillsDir(config.appsDir, manifest);
319
- if (skillsDir)
320
- appSkillsDirs.push(skillsDir);
321
- const toolsDir = loader.getToolsDir(config.appsDir, manifest);
322
- if (toolsDir)
323
- appToolsDirs.push(toolsDir);
324
- }
325
- // Initialize memory service and agent — non-fatal so the server can still serve graph API and apps
707
+ await registerMcpRoutes(fastify, config.workspaceManager, authService, wsHub, commentService);
708
+ // Initialize memory service and agent non-fatal so the server can still serve graph API
326
709
  const memoryService = new MemoryService();
327
710
  let agent = null;
328
711
  try {
@@ -331,13 +714,23 @@ export async function createServer(config) {
331
714
  workspacePath: config.workspacePath,
332
715
  workspaceConfig: config.workspaceConfig,
333
716
  gitUser: config.gitUser,
334
- skillsDirs: appSkillsDirs,
335
- toolsDirs: appToolsDirs,
336
717
  recentMemory,
337
718
  workspaceManager: config.workspaceManager,
338
- messageService,
719
+ // Share the route-level AuthService. Without this the orchestrator
720
+ // constructs its own crypto-less copy and (a) errors when reading
721
+ // encrypted oauth_tokens rows, (b) subscribes to a different
722
+ // pubsub than the one the OAuth callback fires on — so per-user
723
+ // OAuth state changes never invalidate the agent pool.
724
+ authService,
725
+ // Lets the agent's generic read tools resolve the virtual `comment`
726
+ // entity type, and lets the chat route fold an entry's comments into
727
+ // context (see registerChatRoutes below).
728
+ commentService,
729
+ // Lets the agent's `save_file_to_assets` tool promote a conversation
730
+ // scratch image into the curated asset library.
731
+ chatSessionService,
339
732
  });
340
- await registerChatRoutes(fastify, agent, memoryService);
733
+ await registerChatRoutes(fastify, agent, wsHub, authService, memoryService, commentService, chatSessionService);
341
734
  await registerCaptureRoutes(fastify, agent);
342
735
  await registerUrlRoutes(fastify);
343
736
  }
@@ -357,67 +750,34 @@ export async function createServer(config) {
357
750
  return reply.status(503).send({ error: 'The AI agent is not configured yet. Please set the API key for your model provider in the server environment variables and restart.' });
358
751
  });
359
752
  }
360
- // Initialize heartbeat service for conversation analysis
361
- let heartbeatService = null;
362
- try {
363
- const { HeartbeatService } = await import('../services/heartbeat.js');
364
- heartbeatService = new HeartbeatService(messageService, config.workspaceManager, wsHub, config.workspaceConfig);
365
- heartbeatService.start();
366
- }
367
- catch (err) {
368
- fastify.log.warn(`Heartbeat not available: ${err.message}`);
369
- }
370
- // Register messages API routes (collection-scoped messaging with @agent support)
371
- await registerMessagesApiRoutes(fastify, messageService, authService, wsHub, config.workspaceManager, agent, heartbeatService);
372
- // GET /api/apps — list installed app manifests with resolved entity type names
373
- const resolvedApps = manifests.map(m => {
374
- let entityTypes = [];
375
- try {
376
- const etPath = join(config.appsDir, m.name, m.entityTypes);
377
- if (existsSync(etPath)) {
378
- const raw = JSON.parse(readFileSync(etPath, 'utf-8'));
379
- if (Array.isArray(raw)) {
380
- entityTypes = raw.map((item) => item.name).filter(Boolean);
381
- }
382
- }
383
- }
384
- catch {
385
- // gracefully degrade — return empty array
386
- }
387
- return { name: m.name, version: m.version, description: m.description, mountPath: m.mountPath, entityTypes };
388
- });
389
- fastify.get('/api/apps', async (_req, reply) => {
390
- return reply.send(resolvedApps);
391
- });
392
753
  // GET /api/workspace — workspace metadata
393
754
  fastify.get('/api/workspace', async (_req, reply) => {
394
755
  const workspace = new Workspace(config.workspacePath);
395
756
  const about = workspace.loadAbout();
396
- return reply.send({ name: config.workspaceConfig.team_name, about, accent_color: config.workspaceConfig.accent_color ?? null });
757
+ return reply.send({ name: config.workspaceConfig.team_name, about });
397
758
  });
398
- // PUT /api/workspaceupdate workspace identity (admin only)
759
+ // GET /api/server-infoidentity probe for the stdio MCP proxy.
760
+ // Authed by the global middleware (not in the exempt list), so a 401/403 here
761
+ // tells the proxy "server present, token rejected" → fail loud, never write
762
+ // locally. The serverId + workspacePath let the proxy confirm it is talking
763
+ // to the server that owns this exact workspace before forwarding tool calls.
764
+ fastify.get('/api/server-info', async (_req, reply) => {
765
+ return reply.send({
766
+ serverId: config.serverId ?? null,
767
+ workspacePath: config.workspacePath,
768
+ name: config.workspaceConfig.team_name,
769
+ });
770
+ });
771
+ // PUT /api/workspace — update workspace identity (owner only)
399
772
  fastify.put('/api/workspace', async (req, reply) => {
400
773
  const user = req.user;
401
- if (user && !isAdminOrOwner(user.role)) {
402
- return reply.status(403).send({ error: 'Admin access required' });
774
+ if (user && !isWorkspaceOwner(user.role)) {
775
+ return reply.status(403).send({ error: 'Workspace owner access required' });
403
776
  }
404
- const { name, about, accent_color } = req.body ?? {};
777
+ const { name, about } = req.body ?? {};
405
778
  const workspace = new Workspace(config.workspacePath);
406
- let configChanged = false;
407
779
  if (typeof name === 'string' && name.trim()) {
408
780
  config.workspaceConfig.team_name = name.trim();
409
- configChanged = true;
410
- }
411
- if (accent_color !== undefined) {
412
- if (accent_color === null) {
413
- delete config.workspaceConfig.accent_color;
414
- }
415
- else {
416
- config.workspaceConfig.accent_color = accent_color;
417
- }
418
- configChanged = true;
419
- }
420
- if (configChanged) {
421
781
  workspace.writeConfig(config.workspaceConfig);
422
782
  }
423
783
  if (typeof about === 'string') {
@@ -425,24 +785,38 @@ export async function createServer(config) {
425
785
  }
426
786
  return reply.send({ success: true });
427
787
  });
428
- // GET /api/workspace/mcp-config — MCP client configuration snippet (all authenticated users)
788
+ // GET /api/workspace/mcp-config — MCP client configuration snippet.
789
+ // Returns a template with `<your-token-here>` in the Authorization header;
790
+ // the frontend fills it in after the user mints a personal token via
791
+ // POST /api/auth/tokens. We never embed a shared key here because doing so
792
+ // would leak a workspace-wide credential and re-introduce the bug the
793
+ // per-user-token model exists to solve.
429
794
  fastify.get('/api/workspace/mcp-config', async (req, reply) => {
430
- const proto = req.headers['x-forwarded-proto'] === 'https' ? 'https' : (req.protocol ?? 'http');
431
- const host = req.headers['x-forwarded-host'] || req.headers.host || 'localhost';
432
- const baseUrl = `${proto}://${host}`;
433
- const apiKey = authService.getApiKey();
795
+ // Prefer the pinned canonical URL so the connect URL shown in Settings
796
+ // matches the OAuth issuer/resource. Fall back to the request origin when
797
+ // none is configured (local dev / unpinned), rather than failing closed —
798
+ // this endpoint is informational, not a security boundary.
799
+ let baseUrl;
800
+ try {
801
+ baseUrl = resolveCanonicalBaseUrl(config.workspaceConfig, req);
802
+ }
803
+ catch {
804
+ const proto = req.headers['x-forwarded-proto'] === 'https' ? 'https' : (req.protocol ?? 'http');
805
+ const host = req.headers['x-forwarded-host'] || req.headers.host || 'localhost';
806
+ baseUrl = `${proto}://${host}`;
807
+ }
434
808
  const mcpConfig = {
435
809
  mcpServers: {
436
810
  studiograph: {
437
811
  type: 'streamable-http',
438
812
  url: `${baseUrl}/mcp`,
439
813
  headers: {
440
- Authorization: `Bearer ${apiKey}`,
814
+ Authorization: 'Bearer <your-token-here>',
441
815
  },
442
816
  },
443
817
  },
444
818
  };
445
- return reply.send({ config: mcpConfig });
819
+ return reply.send({ config: mcpConfig, url: `${baseUrl}/mcp` });
446
820
  });
447
821
  // DELETE /api/memory — clear all agent memory files
448
822
  fastify.delete('/api/memory', async (_req, reply) => {
@@ -454,19 +828,6 @@ export async function createServer(config) {
454
828
  const count = agent?.clearSessions() ?? 0;
455
829
  return reply.send({ cleared: count });
456
830
  });
457
- // Mount app plugins
458
- for (const manifest of manifests) {
459
- try {
460
- await loader.mountPlugin(fastify, manifest, config.appsDir, {
461
- workspaceManager: config.workspaceManager,
462
- gitUser: config.gitUser,
463
- });
464
- fastify.log.info(`Mounted app "${manifest.name}" at ${manifest.mountPath}`);
465
- }
466
- catch (err) {
467
- fastify.log.error(err, `Failed to mount app "${manifest.name}"`);
468
- }
469
- }
470
831
  // Serve web UI static files from dist/web/ with SPA fallback
471
832
  // When running via tsx from source, import.meta.dirname is src/server/ so ../web doesn't exist.
472
833
  // Fall back to dist/web/ relative to project root.
@@ -474,12 +835,34 @@ export async function createServer(config) {
474
835
  const webDir = existsSync(webDirCandidate) ? webDirCandidate : join(import.meta.dirname, '..', '..', 'dist', 'web');
475
836
  if (existsSync(webDir)) {
476
837
  const indexPath = join(webDir, 'index.html');
477
- // SPA fallback for app routes: if an app plugin returns a JSON error for a
478
- // browser GET request, serve the SPA instead. This happens when the browser
479
- // refreshes a URL like /documents/document/doc-123 — the app's greedy route
480
- // catches it but fails because the params don't match its expectations.
838
+ // SPA fallback: if any API route returns a JSON error for a browser GET,
839
+ // serve the SPA instead so client-side routing can handle the URL.
481
840
  // Read fresh from disk each time so rebuilds take effect without server restart.
482
841
  const readIndexHtml = () => readFileSync(indexPath);
842
+ /**
843
+ * Cache policy for static assets:
844
+ * - `/_app/immutable/*` are content-hashed by SvelteKit, so the URL
845
+ * changes every time the file does — safe to cache forever.
846
+ * - `index.html` (and the SPA fallback) MUST revalidate every load,
847
+ * otherwise stale HTML keeps pointing at expired immutable hashes.
848
+ * - Other root assets (favicons, og images, etc.) get a modest TTL.
849
+ *
850
+ * Without these headers the upstream CDN (Fastly in front of Railway)
851
+ * forwards almost every request to origin, producing multi-second
852
+ * TTFB on cold edges and a 3-5s "black screen" while the SPA bundle
853
+ * loads.
854
+ */
855
+ function setStaticCacheHeader(reply, urlPath) {
856
+ if (urlPath.startsWith('/_app/immutable/')) {
857
+ reply.header('Cache-Control', 'public, max-age=31536000, immutable');
858
+ }
859
+ else if (urlPath === '/' || urlPath === '/index.html' || !extname(urlPath)) {
860
+ reply.header('Cache-Control', 'no-cache');
861
+ }
862
+ else {
863
+ reply.header('Cache-Control', 'public, max-age=3600');
864
+ }
865
+ }
483
866
  fastify.addHook('onSend', async (request, reply, payload) => {
484
867
  if (reply.sent)
485
868
  return payload;
@@ -490,23 +873,24 @@ export async function createServer(config) {
490
873
  typeof ct === 'string' && ct.includes('application/json')) {
491
874
  reply.removeHeader('content-length');
492
875
  reply.code(200).type('text/html');
876
+ reply.header('Cache-Control', 'no-cache');
493
877
  return readIndexHtml();
494
878
  }
495
879
  return payload;
496
880
  });
497
- // Use setNotFoundHandler so app plugins and API routes take priority
881
+ // Use setNotFoundHandler so API routes take priority over SPA fallback
498
882
  fastify.setNotFoundHandler(async (req, reply) => {
499
883
  const urlPath = req.url.split('?')[0];
500
- const filePath = urlPath === '/' ? '/index.html' : urlPath;
501
- const fullPath = join(webDir, filePath);
884
+ const fullPath = resolveStaticFilePath(webDir, urlPath);
885
+ if (!fullPath) {
886
+ return reply.status(400).send({ error: 'Invalid path' });
887
+ }
502
888
  // Serve the file if it exists (and is a file, not directory)
503
889
  if (existsSync(fullPath) && statSync(fullPath).isFile()) {
504
- if (fullPath.includes('..')) {
505
- return reply.status(400).send({ error: 'Invalid path' });
506
- }
507
890
  const ext = extname(fullPath).toLowerCase();
508
891
  const contentType = MIME_TYPES[ext] || 'application/octet-stream';
509
892
  reply.type(contentType);
893
+ setStaticCacheHeader(reply, urlPath);
510
894
  return reply.send(createReadStream(fullPath));
511
895
  }
512
896
  // SPA fallback — serve index.html for client-side routing.
@@ -516,18 +900,22 @@ export async function createServer(config) {
516
900
  const hasFileExt = extname(urlPath).length > 1;
517
901
  if (req.method === 'GET' && !hasFileExt && existsSync(indexPath)) {
518
902
  reply.type('text/html');
903
+ reply.header('Cache-Control', 'no-cache');
519
904
  return reply.send(createReadStream(indexPath));
520
905
  }
521
906
  return reply.status(404).send({ error: 'Not found' });
522
907
  });
523
908
  fastify.log.info('Web UI enabled — serving from dist/web/');
524
909
  }
525
- // Graceful shutdown
910
+ // Graceful shutdown — sessionManager.shutdown() flushes pending commits
911
+ // BEFORE tearing down the timer (the old destroy()-only path could lose
912
+ // dirty sessions if called without an explicit flush).
526
913
  fastify.addHook('onClose', async () => {
527
- await sessionManager.flushAll();
528
- sessionManager.destroy();
529
- yjsManager.destroy();
914
+ await sessionManager.shutdown();
915
+ await assetIndexQueue.shutdown();
916
+ await yjsManager.destroy();
530
917
  wsHub.destroy();
918
+ releaseYjsLock();
531
919
  });
532
920
  return fastify;
533
921
  }