@k2b/cloud 0.6.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (540) hide show
  1. package/README.md +98 -0
  2. package/package.json +128 -0
  3. package/public/logo.svg +1 -0
  4. package/scripts/app-favicon.test.ts +49 -0
  5. package/scripts/app-favicon.ts +51 -0
  6. package/scripts/build.ts +205 -0
  7. package/scripts/preload.ts +164 -0
  8. package/scripts/sync-recovery-smoke.ts +200 -0
  9. package/src/_internal/app-favicon.ts +2 -0
  10. package/src/_internal/app-presentation.ts +61 -0
  11. package/src/_internal/app-workspace-state.ts +24 -0
  12. package/src/_internal/bounded-json.ts +47 -0
  13. package/src/_internal/build-metadata.ts +9 -0
  14. package/src/_internal/capabilities.ts +1118 -0
  15. package/src/_internal/define-app.ts +801 -0
  16. package/src/_internal/heartbeat.ts +167 -0
  17. package/src/_internal/help-catalog.ts +241 -0
  18. package/src/_internal/help.ts +95 -0
  19. package/src/_internal/nats-connection.ts +34 -0
  20. package/src/_internal/page-responses.ts +40 -0
  21. package/src/_internal/process-sync.ts +117 -0
  22. package/src/_internal/readiness.ts +3 -0
  23. package/src/_internal/registry-validation.ts +136 -0
  24. package/src/_internal/registry-watch.ts +52 -0
  25. package/src/_internal/registry.ts +338 -0
  26. package/src/_internal/runtime-compatibility.ts +64 -0
  27. package/src/_internal/runtime-context.ts +84 -0
  28. package/src/_internal/runtime-watcher.ts +78 -0
  29. package/src/_internal/static-assets.ts +86 -0
  30. package/src/_internal/status-preserving-ssr.ts +49 -0
  31. package/src/access/PermissionEditor.tsx +486 -0
  32. package/src/access/ResourceApiKeys.tsx +271 -0
  33. package/src/access/messages.ts +121 -0
  34. package/src/access/ui.ts +16 -0
  35. package/src/account/Avatar.tsx +28 -0
  36. package/src/account/EntitySearch.tsx +363 -0
  37. package/src/account/avatar-upload-dialog.tsx +197 -0
  38. package/src/account/avatar-upload.ts +149 -0
  39. package/src/account/ui.ts +3 -0
  40. package/src/ai/admin.ts +47 -0
  41. package/src/ai/approval-routes.ts +130 -0
  42. package/src/ai/approvals.ts +139 -0
  43. package/src/ai/assistant-models.ts +61 -0
  44. package/src/ai/attachments.ts +40 -0
  45. package/src/ai/browser.ts +92 -0
  46. package/src/ai/capabilities.ts +714 -0
  47. package/src/ai/capability-execution.ts +151 -0
  48. package/src/ai/chat/blocks.render.test.tsx +1108 -0
  49. package/src/ai/chat/blocks.tsx +730 -0
  50. package/src/ai/chat/builtin-tools.tsx +428 -0
  51. package/src/ai/chat/capability-table.tsx +87 -0
  52. package/src/ai/chat/composer-adapter.ts +169 -0
  53. package/src/ai/chat/file-tools.tsx +81 -0
  54. package/src/ai/chat/message-actions.tsx +370 -0
  55. package/src/ai/chat/message-utils.ts +479 -0
  56. package/src/ai/chat/messages.ts +43 -0
  57. package/src/ai/chat/presentation.tsx +395 -0
  58. package/src/ai/chat/primitives.tsx +3 -0
  59. package/src/ai/chat/tool-disclosure.tsx +41 -0
  60. package/src/ai/chat/turn-navigator-utils.ts +28 -0
  61. package/src/ai/chat/turn-navigator.tsx +164 -0
  62. package/src/ai/chat/user-message.tsx +149 -0
  63. package/src/ai/chat/visual-tools.tsx +435 -0
  64. package/src/ai/chat/web-tools.tsx +202 -0
  65. package/src/ai/chat-task-contracts.ts +113 -0
  66. package/src/ai/chat-tasks.ts +952 -0
  67. package/src/ai/client/controller.ts +1211 -0
  68. package/src/ai/client/file-source.ts +95 -0
  69. package/src/ai/client/live-connection.ts +140 -0
  70. package/src/ai/client/projection.ts +178 -0
  71. package/src/ai/client/transport.ts +111 -0
  72. package/src/ai/compaction.ts +182 -0
  73. package/src/ai/credentials.ts +124 -0
  74. package/src/ai/default-tool-contracts.ts +108 -0
  75. package/src/ai/default-tools.ts +127 -0
  76. package/src/ai/enrich.ts +280 -0
  77. package/src/ai/executor.ts +1624 -0
  78. package/src/ai/fetch-file-tool.ts +313 -0
  79. package/src/ai/file-context.ts +92 -0
  80. package/src/ai/file-media-type.ts +28 -0
  81. package/src/ai/file-mount.ts +16 -0
  82. package/src/ai/file-tools.ts +566 -0
  83. package/src/ai/files-store.ts +390 -0
  84. package/src/ai/firecrawl-tools.ts +305 -0
  85. package/src/ai/grids-skill.ts +126 -0
  86. package/src/ai/http.ts +265 -0
  87. package/src/ai/index.ts +337 -0
  88. package/src/ai/kit-skill.ts +22 -0
  89. package/src/ai/limits.ts +5 -0
  90. package/src/ai/live-events.ts +289 -0
  91. package/src/ai/live-messages.ts +45 -0
  92. package/src/ai/live-outbox.ts +102 -0
  93. package/src/ai/live-routes.ts +412 -0
  94. package/src/ai/live.ts +2 -0
  95. package/src/ai/maintenance.ts +302 -0
  96. package/src/ai/markdown-pdf-tool.ts +95 -0
  97. package/src/ai/memories.ts +605 -0
  98. package/src/ai/memory-learning-runs.ts +222 -0
  99. package/src/ai/memory-learning.ts +619 -0
  100. package/src/ai/memory-tool.ts +120 -0
  101. package/src/ai/memory-workflow-evidence.ts +158 -0
  102. package/src/ai/migrate.ts +2082 -0
  103. package/src/ai/model-access-migrate.ts +30 -0
  104. package/src/ai/model-access.ts +191 -0
  105. package/src/ai/personal-agent.ts +6 -0
  106. package/src/ai/prefs.ts +72 -0
  107. package/src/ai/project-tool.ts +94 -0
  108. package/src/ai/projects-routes.ts +277 -0
  109. package/src/ai/projects.ts +902 -0
  110. package/src/ai/protocol.ts +279 -0
  111. package/src/ai/provider.ts +73 -0
  112. package/src/ai/public-projection.ts +25 -0
  113. package/src/ai/resource-markers.ts +31 -0
  114. package/src/ai/resource-refs.ts +70 -0
  115. package/src/ai/routes.ts +1131 -0
  116. package/src/ai/runtime-tools.ts +39 -0
  117. package/src/ai/runtime.ts +544 -0
  118. package/src/ai/settings.ts +529 -0
  119. package/src/ai/short-id.ts +45 -0
  120. package/src/ai/skill-catalog.ts +73 -0
  121. package/src/ai/skill-format.ts +145 -0
  122. package/src/ai/skill-seeds.ts +430 -0
  123. package/src/ai/skill-tool.ts +81 -0
  124. package/src/ai/skills-routes.ts +143 -0
  125. package/src/ai/skills.ts +934 -0
  126. package/src/ai/solid.ts +27 -0
  127. package/src/ai/store.ts +3113 -0
  128. package/src/ai/stream.ts +308 -0
  129. package/src/ai/structured-runs.ts +66 -0
  130. package/src/ai/structured.ts +186 -0
  131. package/src/ai/system-prompt.ts +170 -0
  132. package/src/ai/task-prompt.ts +22 -0
  133. package/src/ai/timeline.ts +148 -0
  134. package/src/ai/tool-audit.ts +177 -0
  135. package/src/ai/tool-result-budget.ts +16 -0
  136. package/src/ai/tools.ts +155 -0
  137. package/src/ai/types.ts +967 -0
  138. package/src/ai/ui.tsx +39 -0
  139. package/src/ai/usage-migrate.ts +81 -0
  140. package/src/ai/usage.ts +315 -0
  141. package/src/ai/validate.ts +54 -0
  142. package/src/ai/vision-tool.ts +79 -0
  143. package/src/api/accounts-entities.ts +174 -0
  144. package/src/api/admin-ai-projects.ts +74 -0
  145. package/src/api/admin-ai-skills.ts +76 -0
  146. package/src/api/admin-ai-usage.ts +42 -0
  147. package/src/api/admin-core-settings.ts +508 -0
  148. package/src/api/admin-identity.ts +148 -0
  149. package/src/api/admin-lifecycle.ts +206 -0
  150. package/src/api/admin-linux-identities.ts +66 -0
  151. package/src/api/announcements.ts +131 -0
  152. package/src/api/app-approval.ts +129 -0
  153. package/src/api/apps.ts +51 -0
  154. package/src/api/auth/schemas.ts +54 -0
  155. package/src/api/auth.ts +370 -0
  156. package/src/api/capabilities.ts +658 -0
  157. package/src/api/help-messages.ts +19 -0
  158. package/src/api/help.ts +78 -0
  159. package/src/api/index.ts +108 -0
  160. package/src/api/mcp.ts +744 -0
  161. package/src/api/me-rail.ts +45 -0
  162. package/src/api/me.ts +753 -0
  163. package/src/api/search/schemas.ts +79 -0
  164. package/src/api/search.bench.ts +185 -0
  165. package/src/api/search.ts +407 -0
  166. package/src/api/sync-ops.ts +79 -0
  167. package/src/api/widgets.ts +143 -0
  168. package/src/browser/CloudResourceSearch.behavior.test.tsx +74 -0
  169. package/src/browser/CloudResourceSearch.tsx +791 -0
  170. package/src/browser/app-approval-vault.ts +305 -0
  171. package/src/browser/app-approval.ts +234 -0
  172. package/src/browser/live-websocket.ts +268 -0
  173. package/src/browser/locale-preference.ts +25 -0
  174. package/src/browser/notification-target.ts +27 -0
  175. package/src/browser/notification-timeout.ts +14 -0
  176. package/src/browser/notifications.ts +181 -0
  177. package/src/browser/resource-clipboard.ts +70 -0
  178. package/src/browser/resource-picker.tsx +39 -0
  179. package/src/browser/resource-search-messages.ts +55 -0
  180. package/src/browser/resource-search.ts +27 -0
  181. package/src/capabilities/client.ts +149 -0
  182. package/src/capabilities/response.ts +39 -0
  183. package/src/capabilities/server.ts +215 -0
  184. package/src/capabilities/signals.ts +4 -0
  185. package/src/capabilities/testing.ts +12 -0
  186. package/src/capabilities/types.ts +32 -0
  187. package/src/cli/access.ts +499 -0
  188. package/src/cli/account.ts +382 -0
  189. package/src/cli/admin/account-administration.ts +45 -0
  190. package/src/cli/admin/account-categories.ts +49 -0
  191. package/src/cli/admin/ai-usage.ts +118 -0
  192. package/src/cli/admin/app-sign-in.ts +49 -0
  193. package/src/cli/admin/data.ts +343 -0
  194. package/src/cli/admin/documentation.ts +34 -0
  195. package/src/cli/admin/gateway.ts +155 -0
  196. package/src/cli/admin/index.ts +61 -0
  197. package/src/cli/admin/instance.ts +250 -0
  198. package/src/cli/admin/jobs.ts +223 -0
  199. package/src/cli/admin/legal.ts +122 -0
  200. package/src/cli/admin/linux.ts +53 -0
  201. package/src/cli/admin/logs.ts +324 -0
  202. package/src/cli/admin/metrics.ts +142 -0
  203. package/src/cli/admin/nats.ts +167 -0
  204. package/src/cli/admin/notifications.ts +516 -0
  205. package/src/cli/admin/shared.ts +113 -0
  206. package/src/cli/admin/sync.ts +188 -0
  207. package/src/cli/admin/telemetry.ts +314 -0
  208. package/src/cli/admin/webhooks.ts +222 -0
  209. package/src/cli/admin/workflows.ts +367 -0
  210. package/src/cli/apps.ts +40 -0
  211. package/src/cli/capabilities.ts +239 -0
  212. package/src/cli/commands.ts +604 -0
  213. package/src/cli/index.ts +61 -0
  214. package/src/cli/locale.ts +31 -0
  215. package/src/cli/output.ts +58 -0
  216. package/src/clients/core.ts +20 -0
  217. package/src/config/env.ts +54 -0
  218. package/src/config/index.ts +6 -0
  219. package/src/contracts/account-categories.ts +62 -0
  220. package/src/contracts/announcements.ts +121 -0
  221. package/src/contracts/app-approval.ts +197 -0
  222. package/src/contracts/app.ts +162 -0
  223. package/src/contracts/capabilities.ts +484 -0
  224. package/src/contracts/index.ts +14 -0
  225. package/src/contracts/notification-live.ts +66 -0
  226. package/src/contracts/notification-types.ts +235 -0
  227. package/src/contracts/posix.ts +69 -0
  228. package/src/contracts/profile.ts +85 -0
  229. package/src/contracts/rail-preferences.ts +53 -0
  230. package/src/contracts/registry.ts +110 -0
  231. package/src/contracts/resource-clipboard.ts +33 -0
  232. package/src/contracts/settings-types.ts +98 -0
  233. package/src/contracts/shared.ts +408 -0
  234. package/src/contracts/user-notifications.ts +101 -0
  235. package/src/contracts/widgets.ts +183 -0
  236. package/src/desktop/index.ts +704 -0
  237. package/src/desktop/solid.tsx +938 -0
  238. package/src/index.ts +58 -0
  239. package/src/server/actor.ts +41 -0
  240. package/src/server/api/index.ts +2 -0
  241. package/src/server/api/respond.ts +96 -0
  242. package/src/server/api-client.ts +54 -0
  243. package/src/server/app-context.ts +39 -0
  244. package/src/server/help.ts +187 -0
  245. package/src/server/index.ts +89 -0
  246. package/src/server/locale.ts +55 -0
  247. package/src/server/middleware/auth.ts +386 -0
  248. package/src/server/middleware/index.ts +28 -0
  249. package/src/server/middleware/invocation.ts +55 -0
  250. package/src/server/middleware/middleware.ts +65 -0
  251. package/src/server/middleware/openapi.ts +126 -0
  252. package/src/server/middleware/rate-limit.ts +125 -0
  253. package/src/server/middleware/request-logger.ts +41 -0
  254. package/src/server/middleware/route-template.ts +46 -0
  255. package/src/server/middleware/runtime.ts +22 -0
  256. package/src/server/middleware/settings.ts +55 -0
  257. package/src/server/middleware/validator.ts +49 -0
  258. package/src/server/middleware/workload.ts +22 -0
  259. package/src/server/ratelimit.ts +151 -0
  260. package/src/server/request-cookies.ts +18 -0
  261. package/src/server/services/access.ts +644 -0
  262. package/src/server/services/freeipa/client.ts +137 -0
  263. package/src/server/services/freeipa/index.ts +9 -0
  264. package/src/server/services/freeipa/session.ts +123 -0
  265. package/src/server/services/freeipa/test-certificates.ts +44 -0
  266. package/src/server/services/freeipa/tls.ts +44 -0
  267. package/src/server/services/freeipa/transport.ts +116 -0
  268. package/src/server/services/freeipa/util.ts +59 -0
  269. package/src/server/services/geo.ts +198 -0
  270. package/src/server/services/index.ts +48 -0
  271. package/src/server/services/pagination.ts +22 -0
  272. package/src/server/services/services.ts +13 -0
  273. package/src/server/time.ts +33 -0
  274. package/src/services/account-category-policy.ts +51 -0
  275. package/src/services/account-lifecycle/audit.ts +41 -0
  276. package/src/services/account-lifecycle/index.ts +940 -0
  277. package/src/services/account-lifecycle/ipa-backfill.ts +152 -0
  278. package/src/services/account-lifecycle/notification-sender.ts +13 -0
  279. package/src/services/account-lifecycle/scheduler.ts +506 -0
  280. package/src/services/account-model.ts +22 -0
  281. package/src/services/accounts/app.ts +1671 -0
  282. package/src/services/accounts/authz.ts +44 -0
  283. package/src/services/accounts/avatar.ts +120 -0
  284. package/src/services/accounts/base-group.ts +11 -0
  285. package/src/services/accounts/base-user.ts +46 -0
  286. package/src/services/accounts/duplicate-emails.ts +82 -0
  287. package/src/services/accounts/entities.ts +622 -0
  288. package/src/services/accounts/group-sql.ts +106 -0
  289. package/src/services/accounts/groups.ts +272 -0
  290. package/src/services/accounts/index.ts +14 -0
  291. package/src/services/accounts/ipa-data.ts +66 -0
  292. package/src/services/accounts/lifecycle.ts +2 -0
  293. package/src/services/accounts/local-groups.ts +499 -0
  294. package/src/services/accounts/model.ts +129 -0
  295. package/src/services/accounts/notification-sender.ts +31 -0
  296. package/src/services/accounts/posix.ts +386 -0
  297. package/src/services/accounts/request-policy.ts +11 -0
  298. package/src/services/accounts/switching.ts +126 -0
  299. package/src/services/accounts/users.ts +745 -0
  300. package/src/services/announcements/index.ts +224 -0
  301. package/src/services/app-approval.ts +498 -0
  302. package/src/services/audit/index.ts +438 -0
  303. package/src/services/auth-flows/index.ts +15 -0
  304. package/src/services/auth-flows/ipa.ts +197 -0
  305. package/src/services/auth-flows/magic-link.ts +185 -0
  306. package/src/services/auth-flows/notification-sender.ts +10 -0
  307. package/src/services/auth-flows/password-reset.ts +268 -0
  308. package/src/services/auth-flows/proxy-return.ts +49 -0
  309. package/src/services/document-extraction.ts +148 -0
  310. package/src/services/freeipa-config.ts +179 -0
  311. package/src/services/gateway.ts +186 -0
  312. package/src/services/identity/constants.ts +18 -0
  313. package/src/services/identity/index.ts +52 -0
  314. package/src/services/identity/invocation-actor.ts +120 -0
  315. package/src/services/identity/invocation-authority.ts +53 -0
  316. package/src/services/identity/invocation-operations.ts +15 -0
  317. package/src/services/identity/invocation-token.ts +276 -0
  318. package/src/services/identity/key-config.ts +41 -0
  319. package/src/services/identity/key-ring.ts +530 -0
  320. package/src/services/identity/metrics.ts +29 -0
  321. package/src/services/identity/routes.ts +18 -0
  322. package/src/services/identity/runtime-config.ts +88 -0
  323. package/src/services/identity/session-token.ts +152 -0
  324. package/src/services/identity/signer-refresh.ts +44 -0
  325. package/src/services/identity/workload-auth.ts +64 -0
  326. package/src/services/index.ts +187 -0
  327. package/src/services/ipa/auth.ts +216 -0
  328. package/src/services/ipa/connection.ts +57 -0
  329. package/src/services/ipa/effective-groups.ts +70 -0
  330. package/src/services/ipa/groups.ts +722 -0
  331. package/src/services/ipa/guard.ts +17 -0
  332. package/src/services/ipa/index.ts +17 -0
  333. package/src/services/ipa/posix.ts +19 -0
  334. package/src/services/ipa/profile.ts +132 -0
  335. package/src/services/ipa/runtime.ts +28 -0
  336. package/src/services/ipa/search.ts +154 -0
  337. package/src/services/ipa/service-account.ts +15 -0
  338. package/src/services/ipa/sync-planning.ts +100 -0
  339. package/src/services/ipa/sync.ts +862 -0
  340. package/src/services/ipa/users.ts +806 -0
  341. package/src/services/legal-consent.ts +76 -0
  342. package/src/services/logging/index.ts +477 -0
  343. package/src/services/logging/redaction.ts +20 -0
  344. package/src/services/logging/trace.ts +1231 -0
  345. package/src/services/mandates/index.ts +855 -0
  346. package/src/services/mandates/policy.ts +85 -0
  347. package/src/services/network-security.ts +31 -0
  348. package/src/services/notifications/batches.ts +722 -0
  349. package/src/services/notifications/browser.ts +252 -0
  350. package/src/services/notifications/catalog.ts +113 -0
  351. package/src/services/notifications/channels.ts +104 -0
  352. package/src/services/notifications/dispatcher.ts +139 -0
  353. package/src/services/notifications/email.ts +118 -0
  354. package/src/services/notifications/index.ts +633 -0
  355. package/src/services/notifications/live.ts +50 -0
  356. package/src/services/notifications/observability.ts +407 -0
  357. package/src/services/notifications/platform.ts +421 -0
  358. package/src/services/notifications/runtime.ts +120 -0
  359. package/src/services/notifications/user.ts +248 -0
  360. package/src/services/notifications/web-push-transport.ts +135 -0
  361. package/src/services/oauth-tokens.ts +209 -0
  362. package/src/services/outbox.ts +187 -0
  363. package/src/services/pdf/gotenberg.ts +290 -0
  364. package/src/services/pdf/index.ts +43 -0
  365. package/src/services/pdf/markdown.ts +209 -0
  366. package/src/services/pdf/template-preview.ts +122 -0
  367. package/src/services/postgres.ts +76 -0
  368. package/src/services/providers/index.ts +27 -0
  369. package/src/services/providers/local/auth.ts +51 -0
  370. package/src/services/providers/local/index.ts +4 -0
  371. package/src/services/providers/local/users.ts +263 -0
  372. package/src/services/rail-preferences.ts +27 -0
  373. package/src/services/runtime-lifecycle.ts +191 -0
  374. package/src/services/secrets.ts +10 -0
  375. package/src/services/service-account-credentials.ts +891 -0
  376. package/src/services/service-accounts.ts +184 -0
  377. package/src/services/session/index.ts +236 -0
  378. package/src/services/session/test-fixture.ts +18 -0
  379. package/src/services/session/user.ts +144 -0
  380. package/src/services/settings/api.ts +61 -0
  381. package/src/services/settings/app.ts +86 -0
  382. package/src/services/settings/core-settings.ts +796 -0
  383. package/src/services/settings/crypto.ts +69 -0
  384. package/src/services/settings/defaults.ts +374 -0
  385. package/src/services/settings/index.ts +215 -0
  386. package/src/services/settings/namespace.ts +9 -0
  387. package/src/services/settings/snapshot.ts +49 -0
  388. package/src/services/settings/store.ts +241 -0
  389. package/src/services/settings/templates.ts +4 -0
  390. package/src/services/sync-ops.ts +403 -0
  391. package/src/services/topic-cursor.ts +22 -0
  392. package/src/services/weather/forecast.ts +323 -0
  393. package/src/services/weather/geo.ts +119 -0
  394. package/src/services/weather/index.ts +67 -0
  395. package/src/services/weather/location.ts +24 -0
  396. package/src/services/weather/locations.ts +133 -0
  397. package/src/services/weather/migrate.ts +45 -0
  398. package/src/services/weather/settings.ts +66 -0
  399. package/src/services/weather/short-id.ts +57 -0
  400. package/src/services/weather/types.ts +61 -0
  401. package/src/services/weather/ui.ts +50 -0
  402. package/src/services/webauthn.ts +385 -0
  403. package/src/shared/account-action-notice.ts +70 -0
  404. package/src/shared/account-display.ts +15 -0
  405. package/src/shared/account-session.ts +15 -0
  406. package/src/shared/ai-platform-prompt.ts +134 -0
  407. package/src/shared/ai-usage.ts +49 -0
  408. package/src/shared/app-presentation.ts +48 -0
  409. package/src/shared/app-url.ts +12 -0
  410. package/src/shared/branding.ts +14 -0
  411. package/src/shared/capability-messages.ts +70 -0
  412. package/src/shared/email-html.ts +54 -0
  413. package/src/shared/format.ts +91 -0
  414. package/src/shared/help.ts +68 -0
  415. package/src/shared/icons.ts +400 -0
  416. package/src/shared/index.ts +54 -0
  417. package/src/shared/locale.ts +48 -0
  418. package/src/shared/login-method.ts +44 -0
  419. package/src/shared/markdown/client.ts +130 -0
  420. package/src/shared/markdown/extensions/code.ts +105 -0
  421. package/src/shared/markdown/extensions/guided-help.ts +100 -0
  422. package/src/shared/markdown/extensions/images.ts +79 -0
  423. package/src/shared/markdown/extensions/info-blocks.ts +97 -0
  424. package/src/shared/markdown/extensions/katex.ts +120 -0
  425. package/src/shared/markdown/extensions/links.ts +43 -0
  426. package/src/shared/markdown/extensions/mark.ts +48 -0
  427. package/src/shared/markdown/extensions/sub-sup.ts +60 -0
  428. package/src/shared/markdown/extensions/tables.ts +109 -0
  429. package/src/shared/markdown/extensions/task-list.ts +53 -0
  430. package/src/shared/markdown/formula.ts +1187 -0
  431. package/src/shared/markdown/index.ts +193 -0
  432. package/src/shared/markdown/shared.ts +36 -0
  433. package/src/shared/mock-cover.ts +130 -0
  434. package/src/shared/network-address.ts +106 -0
  435. package/src/shared/redirect.ts +56 -0
  436. package/src/shared/template-rendering.ts +140 -0
  437. package/src/shared/theme.ts +68 -0
  438. package/src/shared/time.ts +13 -0
  439. package/src/ssr/AdminLayout.tsx +53 -0
  440. package/src/ssr/AdminSidebar.tsx +50 -0
  441. package/src/ssr/AppLaunchpad.island.tsx +236 -0
  442. package/src/ssr/AppWorkspaceController.island.tsx +21 -0
  443. package/src/ssr/BrowserNotifications.island.tsx +135 -0
  444. package/src/ssr/GlobalAnnouncements.island.tsx +142 -0
  445. package/src/ssr/GlobalSearchDialog.behavior.test.tsx +96 -0
  446. package/src/ssr/GlobalSearchDialog.tsx +35 -0
  447. package/src/ssr/GlobalSearchHelpDialog.tsx +104 -0
  448. package/src/ssr/GlobalSearchTrigger.island.tsx +55 -0
  449. package/src/ssr/HotkeysHelpRail.island.tsx +55 -0
  450. package/src/ssr/Layout.render.test.tsx +148 -0
  451. package/src/ssr/Layout.tsx +292 -0
  452. package/src/ssr/LayoutBreadcrumbs.island.tsx +44 -0
  453. package/src/ssr/LayoutFooter.tsx +22 -0
  454. package/src/ssr/LayoutHeader.tsx +81 -0
  455. package/src/ssr/LayoutHelp.tsx +869 -0
  456. package/src/ssr/LayoutPreferences.island.tsx +36 -0
  457. package/src/ssr/LayoutRail.tsx +58 -0
  458. package/src/ssr/MinimalLayout.tsx +46 -0
  459. package/src/ssr/PageError.tsx +29 -0
  460. package/src/ssr/ProfilePreferences.island.tsx +92 -0
  461. package/src/ssr/RailApps.island.tsx +61 -0
  462. package/src/ssr/RailEditor.tsx +293 -0
  463. package/src/ssr/RegisteredHelpDocuments.island.tsx +3 -0
  464. package/src/ssr/TimezoneCookie.island.tsx +23 -0
  465. package/src/ssr/admin-active-link.ts +47 -0
  466. package/src/ssr/admin-navigation.ts +78 -0
  467. package/src/ssr/app-appearance.ts +23 -0
  468. package/src/ssr/app-navigation.ts +41 -0
  469. package/src/ssr/help-messages.ts +104 -0
  470. package/src/ssr/index.ts +14 -0
  471. package/src/ssr/islands/SearchBar.island.tsx +80 -0
  472. package/src/ssr/islands/index.ts +16 -0
  473. package/src/ssr/layout-context.ts +17 -0
  474. package/src/ssr/layout-help-markdown.ts +9 -0
  475. package/src/ssr/layout-help-navigation.ts +24 -0
  476. package/src/ssr/layout-help-url.ts +5 -0
  477. package/src/ssr/layout-runtime.ts +22 -0
  478. package/src/ssr/page-error-messages.ts +25 -0
  479. package/src/ssr/platform-messages.ts +135 -0
  480. package/src/ssr/preference-controller.ts +44 -0
  481. package/src/ssr/profile-preferences-messages.ts +29 -0
  482. package/src/ssr/rail-context.ts +21 -0
  483. package/src/ssr/rail-messages.ts +66 -0
  484. package/src/ssr/rail-navigation.ts +60 -0
  485. package/src/ssr/runtime.ts +31 -0
  486. package/src/ssr/url-filter.ts +127 -0
  487. package/src/styles/base-popover.css +36 -0
  488. package/src/styles/effects.css +851 -0
  489. package/src/styles/global.css +268 -0
  490. package/src/styles/input.css +58 -0
  491. package/src/styles/tokens.css +230 -0
  492. package/src/styles/utilities-buttons.css +405 -0
  493. package/src/styles/utilities-data.css +33 -0
  494. package/src/styles/utilities-feedback.css +232 -0
  495. package/src/styles/utilities-layout.css +603 -0
  496. package/src/styles/utilities-markdown-editor.css +525 -0
  497. package/src/styles/utilities-markdown-table.css +234 -0
  498. package/src/styles/utilities-navigation.css +1054 -0
  499. package/src/types/ambient.d.ts +24 -0
  500. package/src/workflows/ai/index.ts +17 -0
  501. package/src/workflows/ai/runtime.ts +286 -0
  502. package/src/workflows/ai/store.ts +314 -0
  503. package/src/workflows/ai/types.ts +111 -0
  504. package/src/workflows/ai-actions.ts +269 -0
  505. package/src/workflows/authoring.ts +88 -0
  506. package/src/workflows/builtins.ts +193 -0
  507. package/src/workflows/contracts.ts +280 -0
  508. package/src/workflows/definition.ts +334 -0
  509. package/src/workflows/editor.ts +113 -0
  510. package/src/workflows/index.ts +71 -0
  511. package/src/workflows/language/binder.ts +45 -0
  512. package/src/workflows/language/canonical.ts +29 -0
  513. package/src/workflows/language/compiler.ts +396 -0
  514. package/src/workflows/language/expressions.ts +34 -0
  515. package/src/workflows/language/index.ts +20 -0
  516. package/src/workflows/language/references.ts +55 -0
  517. package/src/workflows/language/schema.ts +134 -0
  518. package/src/workflows/language/strict-yaml.ts +131 -0
  519. package/src/workflows/manifest.ts +24 -0
  520. package/src/workflows/module.ts +12 -0
  521. package/src/workflows/runtime/coordinator.ts +115 -0
  522. package/src/workflows/runtime/executor.ts +1003 -0
  523. package/src/workflows/runtime/index.ts +60 -0
  524. package/src/workflows/runtime/instant.ts +8 -0
  525. package/src/workflows/runtime/ports.ts +213 -0
  526. package/src/workflows/runtime/schedule.ts +203 -0
  527. package/src/workflows/runtime/trigger.ts +37 -0
  528. package/src/workflows/runtime/wake.ts +13 -0
  529. package/src/workflows/store/actions.ts +438 -0
  530. package/src/workflows/store/budget.ts +145 -0
  531. package/src/workflows/store/definitions.ts +345 -0
  532. package/src/workflows/store/events.ts +383 -0
  533. package/src/workflows/store/index.ts +107 -0
  534. package/src/workflows/store/observability.ts +594 -0
  535. package/src/workflows/store/runs.ts +975 -0
  536. package/src/workflows/store/transaction.ts +23 -0
  537. package/src/workflows/store/worker.ts +439 -0
  538. package/src/workflows/testing/conformance.ts +72 -0
  539. package/src/workflows/testing/index.ts +11 -0
  540. package/src/workflows/testing/process-fixtures.ts +275 -0
@@ -0,0 +1,149 @@
1
+ import { createCroppedImageCanvas, type ImageCropState } from "@k2b/ui";
2
+ import { MAX_AVATAR_BYTES, MAX_AVATAR_DATA_URL_LENGTH } from "../contracts";
3
+
4
+ const ACCEPTED_AVATAR_TYPES = new Set(["image/png", "image/jpeg", "image/webp"]);
5
+ const MAX_SOURCE_AVATAR_BYTES = 32 * 1024 * 1024;
6
+ const AVATAR_CANVAS_SIZES = [256, 192, 160, 128, 96, 64] as const;
7
+ const AVATAR_OUTPUT_ATTEMPTS = [
8
+ { type: "image/webp", qualities: [0.86, 0.76, 0.66, 0.56, 0.46, 0.36] },
9
+ { type: "image/jpeg", qualities: [0.84, 0.74, 0.64, 0.54, 0.44, 0.34] },
10
+ { type: "image/png", qualities: [undefined] },
11
+ ] as const;
12
+
13
+ export class AvatarUploadError extends Error {
14
+ constructor(readonly code: "type_invalid" | "too_large" | "empty" | "unsupported" | "compression_failed") {
15
+ super(code);
16
+ this.name = "AvatarUploadError";
17
+ }
18
+ }
19
+
20
+ const blobToDataUrl = (blob: Blob): Promise<string> =>
21
+ new Promise((resolve, reject) => {
22
+ const reader = new FileReader();
23
+ reader.onload = () => resolve(String(reader.result));
24
+ reader.onerror = () => reject(reader.error ?? new Error("Failed to read avatar image."));
25
+ reader.readAsDataURL(blob);
26
+ });
27
+
28
+ const canvasToBlob = (canvas: HTMLCanvasElement, type: string, quality?: number): Promise<Blob | null> =>
29
+ new Promise((resolve) => canvas.toBlob(resolve, type, quality));
30
+
31
+ const fitsAvatarLimits = async (blob: Blob): Promise<string | null> => {
32
+ if (blob.size > MAX_AVATAR_BYTES) return null;
33
+ const dataUrl = await blobToDataUrl(blob);
34
+ return dataUrl.length <= MAX_AVATAR_DATA_URL_LENGTH ? dataUrl : null;
35
+ };
36
+
37
+ const loadImage = async (file: File): Promise<HTMLImageElement> => {
38
+ const url = URL.createObjectURL(file);
39
+ const image = new Image();
40
+ image.src = url;
41
+ try {
42
+ await image.decode();
43
+ return image;
44
+ } catch {
45
+ await new Promise<void>((resolve, reject) => {
46
+ image.onload = () => resolve();
47
+ image.onerror = () => reject(new Error("Failed to decode avatar image."));
48
+ });
49
+ return image;
50
+ } finally {
51
+ URL.revokeObjectURL(url);
52
+ }
53
+ };
54
+
55
+ export const validateAvatarSourceFile = (file: File): void => {
56
+ if (!ACCEPTED_AVATAR_TYPES.has(file.type)) {
57
+ throw new AvatarUploadError("type_invalid");
58
+ }
59
+ if (file.size > MAX_SOURCE_AVATAR_BYTES) {
60
+ throw new AvatarUploadError("too_large");
61
+ }
62
+ };
63
+
64
+ export const createAvatarDataUrlFromFile = async (file: File, cropState?: ImageCropState): Promise<string> => {
65
+ validateAvatarSourceFile(file);
66
+
67
+ if (cropState) {
68
+ for (const size of AVATAR_CANVAS_SIZES) {
69
+ const canvas = await createCroppedImageCanvas(file, cropState, { width: size, height: size });
70
+
71
+ for (const attempt of AVATAR_OUTPUT_ATTEMPTS) {
72
+ for (const quality of attempt.qualities) {
73
+ const blob = await canvasToBlob(canvas, attempt.type, quality);
74
+ if (!blob || blob.type !== attempt.type) continue;
75
+ const dataUrl = await fitsAvatarLimits(blob);
76
+ if (dataUrl) return dataUrl;
77
+ }
78
+ }
79
+ }
80
+
81
+ throw new AvatarUploadError("compression_failed");
82
+ }
83
+
84
+ const image = await loadImage(file);
85
+ const sourceSize = Math.min(image.naturalWidth, image.naturalHeight);
86
+ if (!sourceSize) throw new AvatarUploadError("empty");
87
+
88
+ const sourceX = Math.floor((image.naturalWidth - sourceSize) / 2);
89
+ const sourceY = Math.floor((image.naturalHeight - sourceSize) / 2);
90
+
91
+ for (const size of AVATAR_CANVAS_SIZES) {
92
+ const canvas = document.createElement("canvas");
93
+ canvas.width = size;
94
+ canvas.height = size;
95
+ const context = canvas.getContext("2d");
96
+ if (!context) throw new AvatarUploadError("unsupported");
97
+ context.drawImage(image, sourceX, sourceY, sourceSize, sourceSize, 0, 0, size, size);
98
+
99
+ for (const attempt of AVATAR_OUTPUT_ATTEMPTS) {
100
+ for (const quality of attempt.qualities) {
101
+ const blob = await canvasToBlob(canvas, attempt.type, quality);
102
+ if (!blob || blob.type !== attempt.type) continue;
103
+ const dataUrl = await fitsAvatarLimits(blob);
104
+ if (dataUrl) return dataUrl;
105
+ }
106
+ }
107
+ }
108
+
109
+ throw new AvatarUploadError("compression_failed");
110
+ };
111
+
112
+ export const pickAvatarDataUrl = (): Promise<string | null> =>
113
+ new Promise((resolve, reject) => {
114
+ const input = document.createElement("input");
115
+ let settled = false;
116
+ input.type = "file";
117
+ input.accept = "image/png,image/jpeg,image/webp";
118
+
119
+ const settle = (value: string | null, error?: unknown) => {
120
+ if (settled) return;
121
+ settled = true;
122
+ window.removeEventListener("focus", handleFocus);
123
+ input.remove();
124
+ if (error) reject(error);
125
+ else resolve(value);
126
+ };
127
+
128
+ const handleFocus = () => {
129
+ setTimeout(() => {
130
+ if (!input.files?.length) settle(null);
131
+ }, 250);
132
+ };
133
+
134
+ input.onchange = async () => {
135
+ const file = input.files?.[0];
136
+ if (!file) {
137
+ settle(null);
138
+ return;
139
+ }
140
+ try {
141
+ settle(await createAvatarDataUrlFromFile(file));
142
+ } catch (error) {
143
+ settle(null, error);
144
+ }
145
+ };
146
+
147
+ window.addEventListener("focus", handleFocus);
148
+ input.click();
149
+ });
@@ -0,0 +1,3 @@
1
+ export { CloudAvatar, type CloudAvatarProps } from "./Avatar";
2
+ export { type AvatarUploadDialogOptions, openAvatarUploadDialog } from "./avatar-upload-dialog";
3
+ export { default as EntitySearch, type EntitySearchPrincipal } from "./EntitySearch";
@@ -0,0 +1,47 @@
1
+ /** Server-only AI administration and usage accounting. */
2
+ export {
3
+ type AiUsageRun,
4
+ type AiUsageStats,
5
+ type AiUsageGroup,
6
+ type AiUsagePage,
7
+ AI_USAGE_RANGES,
8
+ type AiUsageBackgroundTask,
9
+ type AiUsageCapability,
10
+ type AiUsageFeedback,
11
+ type AiUsageLaunch,
12
+ type AiUsageModel,
13
+ type AiUsageOverview,
14
+ type AiUsagePoint,
15
+ type AiUsageRange,
16
+ type AiUsagePagination,
17
+ type AiUsageReportOptions,
18
+ type AiUsageReport,
19
+ type AiUsageUser,
20
+ aiUsage,
21
+ } from "./usage";
22
+
23
+ export { type AiModelAccessDraft, type AiModelAccessMap, type AiModelAccessState, aiModelAccess } from "./model-access";
24
+
25
+ import { ENRICH_SYSTEM_PROMPT, ENRICH_OUTPUT_CONTRACT } from "./enrich";
26
+ import {
27
+ MEMORY_LEARNING_PROMPT,
28
+ MEMORY_LEARNING_OUTPUT_CONTRACT,
29
+ WORKFLOW_PATTERN_PROMPT,
30
+ WORKFLOW_PATTERN_OUTPUT_CONTRACT,
31
+ } from "./memory-learning";
32
+ import { DEFAULT_COMPACTION_PROMPT, COMPACTION_OUTPUT_CONTRACT } from "./compaction";
33
+ import { buildAiTaskPrompt } from "./task-prompt";
34
+
35
+ /** Built-in task instructions for the admin viewer; excludes organization guidance and runtime input. */
36
+ export const AI_BACKGROUND_TASK_PROMPTS = {
37
+ "ai.chat_enrichment_instructions": [
38
+ buildAiTaskPrompt({ baseInstructions: ENRICH_SYSTEM_PROMPT, outputContract: ENRICH_OUTPUT_CONTRACT }),
39
+ ],
40
+ "ai.memory_learning_instructions": [
41
+ buildAiTaskPrompt({ baseInstructions: MEMORY_LEARNING_PROMPT, outputContract: MEMORY_LEARNING_OUTPUT_CONTRACT }),
42
+ buildAiTaskPrompt({ baseInstructions: WORKFLOW_PATTERN_PROMPT, outputContract: WORKFLOW_PATTERN_OUTPUT_CONTRACT }),
43
+ ],
44
+ "ai.compaction_instructions": [
45
+ buildAiTaskPrompt({ baseInstructions: DEFAULT_COMPACTION_PROMPT, outputContract: COMPACTION_OUTPUT_CONTRACT }),
46
+ ],
47
+ };
@@ -0,0 +1,130 @@
1
+ import { Hono, type MiddlewareHandler } from "hono";
2
+ import { describeRoute } from "hono-openapi";
3
+ import { z } from "zod";
4
+ import { listCapabilities } from "../_internal/registry";
5
+ import { ErrorResponseSchema } from "../contracts";
6
+ import type { CapabilityRegistryEntry } from "../contracts/registry";
7
+ import { type AuthContext, auth, expectUserBackedActor, jsonResponse, rateLimit, requiresAuth, v } from "../server";
8
+ import { type AiToolApprovalPreference, listAiToolApprovalPreferences, revokeAiToolApprovalPreference } from "./approvals";
9
+ import { buildAiCapabilityCatalog } from "./capabilities";
10
+
11
+ export type AiApprovalPreferenceView = AiToolApprovalPreference & {
12
+ title: string;
13
+ app: {
14
+ id: string;
15
+ name: string;
16
+ icon: string;
17
+ accent?: string;
18
+ } | null;
19
+ };
20
+
21
+ const AiApprovalPreferenceSchema = z.object({
22
+ id: z.uuid(),
23
+ toolName: z.string(),
24
+ approvalScope: z.string(),
25
+ createdAt: z.iso.datetime(),
26
+ lastUsedAt: z.iso.datetime().nullable(),
27
+ expiresAt: z.iso.datetime().nullable(),
28
+ title: z.string(),
29
+ app: z
30
+ .object({
31
+ id: z.string(),
32
+ name: z.string(),
33
+ icon: z.string(),
34
+ accent: z.string().optional(),
35
+ })
36
+ .nullable(),
37
+ });
38
+
39
+ const AiApprovalPreferenceListSchema = z.object({ approvals: z.array(AiApprovalPreferenceSchema) });
40
+ const AiApprovalPreferenceParamSchema = z.object({ preferenceId: z.uuid() });
41
+ const AiApprovalPreferenceDeleteSchema = z.object({ deleted: z.literal(true) });
42
+
43
+ type AiApprovalRouteDependencies = {
44
+ limit?: MiddlewareHandler<AuthContext>;
45
+ authenticate?: MiddlewareHandler<AuthContext>;
46
+ listPreferences?: (actorUserId: string) => Promise<AiToolApprovalPreference[]>;
47
+ revokePreference?: (actorUserId: string, preferenceId: string) => Promise<boolean>;
48
+ listCapabilities?: () => Promise<CapabilityRegistryEntry[]>;
49
+ };
50
+
51
+ const fallbackTitle = (toolName: string): string => {
52
+ const label = toolName
53
+ .replaceAll("__", " ")
54
+ .replace(/[-_.]+/g, " ")
55
+ .replace(/\s+/g, " ")
56
+ .trim();
57
+ return label ? `${label[0]!.toUpperCase()}${label.slice(1)}` : toolName;
58
+ };
59
+
60
+ export const createAiApprovalPreferenceRoutes = (dependencies: AiApprovalRouteDependencies = {}) => {
61
+ const listPreferences = dependencies.listPreferences ?? listAiToolApprovalPreferences;
62
+ const revokePreference = dependencies.revokePreference ?? revokeAiToolApprovalPreference;
63
+ const registry = dependencies.listCapabilities ?? listCapabilities;
64
+
65
+ return new Hono<AuthContext>()
66
+ .use(dependencies.limit ?? rateLimit())
67
+ .use("*", dependencies.authenticate ?? auth.requireRole("authenticated"))
68
+ .get(
69
+ "/",
70
+ describeRoute({
71
+ tags: ["AI"],
72
+ summary: "List remembered AI approvals",
73
+ description: "Lists AI tool approvals remembered for the current user.",
74
+ ...requiresAuth,
75
+ responses: {
76
+ 200: jsonResponse(AiApprovalPreferenceListSchema, "Remembered approvals"),
77
+ 401: jsonResponse(ErrorResponseSchema, "Authentication required"),
78
+ 403: jsonResponse(ErrorResponseSchema, "User-backed actor required"),
79
+ },
80
+ }),
81
+ async (c) => {
82
+ const user = expectUserBackedActor(c);
83
+ const [preferences, capabilities] = await Promise.all([
84
+ listPreferences(user.id),
85
+ registry().catch(() => [] as CapabilityRegistryEntry[]),
86
+ ]);
87
+ const catalogByName = new Map(buildAiCapabilityCatalog(capabilities).map((entry) => [entry.name, entry]));
88
+ const approvals: AiApprovalPreferenceView[] = preferences.map((preference) => {
89
+ const capability = catalogByName.get(preference.toolName);
90
+ return {
91
+ ...preference,
92
+ title: capability?.title ?? fallbackTitle(preference.toolName),
93
+ app: capability
94
+ ? {
95
+ id: capability.appId,
96
+ name: capability.appName,
97
+ icon: capability.app.appIcon,
98
+ ...(capability.app.appAccent ? { accent: capability.app.appAccent } : {}),
99
+ }
100
+ : null,
101
+ };
102
+ });
103
+ return c.json({ approvals });
104
+ },
105
+ )
106
+ .delete(
107
+ "/:preferenceId",
108
+ describeRoute({
109
+ tags: ["AI"],
110
+ summary: "Revoke a remembered AI approval",
111
+ description: "Revokes one remembered AI tool approval owned by the current user.",
112
+ ...requiresAuth,
113
+ responses: {
114
+ 200: jsonResponse(AiApprovalPreferenceDeleteSchema, "Approval revoked"),
115
+ 400: jsonResponse(ErrorResponseSchema, "Invalid approval id"),
116
+ 401: jsonResponse(ErrorResponseSchema, "Authentication required"),
117
+ 403: jsonResponse(ErrorResponseSchema, "User-backed actor required"),
118
+ 404: jsonResponse(ErrorResponseSchema, "Approval not found"),
119
+ },
120
+ }),
121
+ v("param", AiApprovalPreferenceParamSchema),
122
+ async (c) => {
123
+ const user = expectUserBackedActor(c);
124
+ const deleted = await revokePreference(user.id, c.req.valid("param").preferenceId);
125
+ return deleted ? c.json({ deleted: true as const }) : c.json({ message: "Approval not found", code: "NOT_FOUND" }, 404);
126
+ },
127
+ );
128
+ };
129
+
130
+ export type AiApprovalPreferenceRoutes = ReturnType<typeof createAiApprovalPreferenceRoutes>;
@@ -0,0 +1,139 @@
1
+ import { sql } from "bun";
2
+ import type { AiToolApprovalPolicy, AiTurnRunConfig } from "./types";
3
+
4
+ /** Background authority never inherits a user's interactive Always Allow preference. */
5
+ export const aiTurnAllowsRememberedApprovals = (config: AiTurnRunConfig | null | undefined): boolean =>
6
+ config == null || config.kind === "compact" || config.mandate === undefined;
7
+
8
+ export type AiToolApprovalContext = {
9
+ actorUserId: string;
10
+ };
11
+
12
+ export type AiToolApprovalPreference = {
13
+ id: string;
14
+ toolName: string;
15
+ approvalScope: string;
16
+ createdAt: string;
17
+ lastUsedAt: string | null;
18
+ expiresAt: string | null;
19
+ };
20
+
21
+ type AiToolApprovalPreferenceRow = {
22
+ id: string;
23
+ tool_name: string;
24
+ approval_scope: string;
25
+ created_at: Date | string;
26
+ last_used_at: Date | string | null;
27
+ expires_at: Date | string | null;
28
+ };
29
+
30
+ export const aiToolApprovalScope = (toolName: string, policy: AiToolApprovalPolicy | undefined): string => {
31
+ if (policy && typeof policy === "object") return policy.scope ?? toolName;
32
+ return toolName;
33
+ };
34
+
35
+ export const aiToolNeedsApproval = (policy: AiToolApprovalPolicy | undefined): boolean => policy !== "never";
36
+
37
+ export const aiToolAllowsAlways = (policy: AiToolApprovalPolicy | undefined): boolean =>
38
+ policy === "always" || (typeof policy === "object" && policy.kind === "user-configurable");
39
+
40
+ export const hasRememberedAiToolApproval = async (
41
+ context: AiToolApprovalContext,
42
+ input: { toolName: string; approvalScope: string },
43
+ ): Promise<boolean> => {
44
+ const rows = await sql<{ id: string }[]>`
45
+ SELECT id
46
+ FROM ai.tool_approval_preferences
47
+ WHERE actor_user_id = ${context.actorUserId}
48
+ AND tool_name = ${input.toolName}
49
+ AND approval_scope = ${input.approvalScope}
50
+ AND (expires_at IS NULL OR expires_at > now())
51
+ LIMIT 1
52
+ `;
53
+
54
+ const id = rows[0]?.id;
55
+ if (!id) return false;
56
+ await sql`UPDATE ai.tool_approval_preferences SET last_used_at = now() WHERE id = ${id}`;
57
+ return true;
58
+ };
59
+
60
+ export const rememberAiToolApproval = async (
61
+ context: AiToolApprovalContext,
62
+ input: { toolName: string; approvalScope: string; expiresAt?: Date | null },
63
+ ): Promise<void> => {
64
+ await sql.begin(async () => {
65
+ await sql`
66
+ DELETE FROM ai.tool_approval_preferences
67
+ WHERE actor_user_id = ${context.actorUserId}
68
+ AND tool_name = ${input.toolName}
69
+ AND approval_scope = ${input.approvalScope}
70
+ `;
71
+
72
+ await sql`
73
+ INSERT INTO ai.tool_approval_preferences (
74
+ actor_user_id,
75
+ tool_name,
76
+ approval_scope,
77
+ last_used_at,
78
+ expires_at
79
+ )
80
+ VALUES (
81
+ ${context.actorUserId},
82
+ ${input.toolName},
83
+ ${input.approvalScope},
84
+ now(),
85
+ ${input.expiresAt ?? null}
86
+ )
87
+ `;
88
+ });
89
+ };
90
+
91
+ export const forgetAiToolApproval = async (
92
+ context: AiToolApprovalContext,
93
+ input: { toolName: string; approvalScope: string },
94
+ ): Promise<void> => {
95
+ await sql`
96
+ DELETE FROM ai.tool_approval_preferences
97
+ WHERE actor_user_id = ${context.actorUserId}
98
+ AND tool_name = ${input.toolName}
99
+ AND approval_scope = ${input.approvalScope}
100
+ `;
101
+ };
102
+
103
+ const toIsoString = (value: Date | string): string => (value instanceof Date ? value : new Date(value)).toISOString();
104
+
105
+ const approvalPreferenceFromRow = (row: AiToolApprovalPreferenceRow): AiToolApprovalPreference => ({
106
+ id: row.id,
107
+ toolName: row.tool_name,
108
+ approvalScope: row.approval_scope,
109
+ createdAt: toIsoString(row.created_at),
110
+ lastUsedAt: row.last_used_at ? toIsoString(row.last_used_at) : null,
111
+ expiresAt: row.expires_at ? toIsoString(row.expires_at) : null,
112
+ });
113
+
114
+ export const listAiToolApprovalPreferences = async (actorUserId: string): Promise<AiToolApprovalPreference[]> => {
115
+ const rows = await sql<AiToolApprovalPreferenceRow[]>`
116
+ SELECT
117
+ id,
118
+ tool_name,
119
+ approval_scope,
120
+ created_at,
121
+ last_used_at,
122
+ expires_at
123
+ FROM ai.tool_approval_preferences
124
+ WHERE actor_user_id = ${actorUserId}
125
+ AND (expires_at IS NULL OR expires_at > now())
126
+ ORDER BY last_used_at DESC NULLS LAST, created_at DESC, id
127
+ `;
128
+ return rows.map(approvalPreferenceFromRow);
129
+ };
130
+
131
+ export const revokeAiToolApprovalPreference = async (actorUserId: string, preferenceId: string): Promise<boolean> => {
132
+ const rows = await sql<{ id: string }[]>`
133
+ DELETE FROM ai.tool_approval_preferences
134
+ WHERE id = ${preferenceId}
135
+ AND actor_user_id = ${actorUserId}
136
+ RETURNING id
137
+ `;
138
+ return rows.length > 0;
139
+ };
@@ -0,0 +1,61 @@
1
+ import type { AccessSubject, RequestActor } from "../server";
2
+ import { aiModelAccess } from "./model-access";
3
+ import { personalAiModelPolicy } from "./personal-agent";
4
+ import { listAiModels, toPublicAiSettingsState } from "./settings";
5
+ import type { AiChatTurnRunConfig, AiModelPolicy, AiSettingsError } from "./types";
6
+
7
+ export const aiChatAccessSubject = (actor: RequestActor | undefined): AccessSubject | null => {
8
+ if (!actor) return null;
9
+ if (actor.kind === "user") return { type: "user", userId: actor.user.id };
10
+ if (actor.delegatedUser) {
11
+ return { type: "user", userId: actor.delegatedUser.id, delegatedByServiceAccountId: actor.serviceAccount.id };
12
+ }
13
+ return { type: "service_account", serviceAccountId: actor.serviceAccount.id };
14
+ };
15
+
16
+ /** Background chat deliveries also use default tools; only the explicit HTTP marker identifies interactive turns. */
17
+ export const isAssistantChatTurn = (config: AiChatTurnRunConfig): boolean => config.assistantChat === true && !config.mandate;
18
+
19
+ export const listAssistantAiModels = async (subject: AccessSubject | null, policy: AiModelPolicy = personalAiModelPolicy) =>
20
+ aiModelAccess.filterModels(await listAiModels(policy), subject);
21
+
22
+ export const assistantAiSettingsState = async (subject: AccessSubject | null) => {
23
+ const [status, models] = await Promise.all([
24
+ toPublicAiSettingsState(personalAiModelPolicy.allowedDataBoundaries),
25
+ listAssistantAiModels(subject),
26
+ ]);
27
+ return {
28
+ ...status,
29
+ models,
30
+ defaultModelId: models.some((model) => model.id === status.defaultModelId) ? status.defaultModelId : (models[0]?.id ?? ""),
31
+ };
32
+ };
33
+
34
+ /** Explicit and project selections never silently switch models. */
35
+ export const selectAssistantAiModelId = async (
36
+ subject: AccessSubject | null,
37
+ requestedModelId?: string,
38
+ policy: AiModelPolicy = personalAiModelPolicy,
39
+ ): Promise<string> => {
40
+ if (policy.kind === "locked") {
41
+ await aiModelAccess.assertAllowed(policy.modelId, subject);
42
+ return policy.modelId;
43
+ }
44
+ if (policy.kind === "platform-default") {
45
+ const status = await toPublicAiSettingsState();
46
+ await aiModelAccess.assertAllowed(status.defaultModelId, subject);
47
+ return status.defaultModelId;
48
+ }
49
+ if (requestedModelId) {
50
+ await aiModelAccess.assertAllowed(requestedModelId, subject);
51
+ return requestedModelId;
52
+ }
53
+ const [status, models] = await Promise.all([toPublicAiSettingsState(), listAssistantAiModels(subject, policy)]);
54
+ const preferred = policy.defaultModelId;
55
+ const model = models.find((candidate) => candidate.id === (preferred ?? status.defaultModelId)) ?? models[0];
56
+ if (!model) {
57
+ const message = "No AI model is available for your Assistant access. Contact an administrator.";
58
+ throw Object.assign(new Error(message), { aiError: { code: "model_access_denied", message } satisfies AiSettingsError });
59
+ }
60
+ return model.id;
61
+ };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Attachments live in the conversation file store, not inline in the
3
+ * model context: the composer uploads the file, then sends an attachment
4
+ * input part which becomes a marker text part in the user message. The model
5
+ * reads the path with the conversation file tools; the chat UI parses
6
+ * the same marker back into a chip. Browser-safe module.
7
+ */
8
+
9
+ export type AiAttachmentRef = { path: string; mediaType: string; size: number };
10
+
11
+ const MARKER_RE = /<attachment path="([^"]+)" media-type="([^"]*)" size="(\d+)" \/>/g;
12
+
13
+ export const aiAttachmentMarker = (ref: AiAttachmentRef): string =>
14
+ `<attachment path="${ref.path}" media-type="${ref.mediaType}" size="${ref.size}" />`;
15
+
16
+ /** Replace client-supplied marker metadata with canonical file-store values. */
17
+ export const canonicalizeAiAttachmentMarkers = (text: string, refs: ReadonlyMap<string, AiAttachmentRef>): string =>
18
+ text.replace(MARKER_RE, (marker, path: string) => {
19
+ const ref = refs.get(path);
20
+ return ref ? aiAttachmentMarker(ref) : marker;
21
+ });
22
+
23
+ /** Split a text part into plain text and attachment chips for rendering. */
24
+ export const parseAiAttachmentMarkers = (text: string): { text: string; attachments: AiAttachmentRef[] } => {
25
+ const attachments: AiAttachmentRef[] = [];
26
+ const stripped = text
27
+ .replace(MARKER_RE, (_all, path: string, mediaType: string, size: string) => {
28
+ attachments.push({ path, mediaType, size: Number(size) });
29
+ return "";
30
+ })
31
+ .trim();
32
+ return { text: stripped, attachments };
33
+ };
34
+
35
+ export const formatAiFileSize = (bytes: number): string => {
36
+ if (bytes < 1024) return `${bytes} B`;
37
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
38
+ if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
39
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
40
+ };
@@ -0,0 +1,92 @@
1
+ import { AI_TURN_ATTACHMENT_MAX_ITEMS } from "./limits";
2
+ import type { AiConversation, AiDraftContentPart } from "./types";
3
+
4
+ export { parseAiSse } from "./client/transport";
5
+ export {
6
+ CLOUD_AI_TEXT_EDITOR_MAX_CHARS,
7
+ CloudAiCardInputSchema,
8
+ CloudAiLocalBashInputSchema,
9
+ CloudAiSurveyInputSchema,
10
+ CloudAiTextEditorInputSchema,
11
+ } from "./default-tool-contracts";
12
+ export { guessAiMediaType } from "./file-media-type";
13
+ export { AI_IMAGE_INPUT_MAX_BYTES, AI_TURN_ATTACHMENT_MAX_ITEMS, AI_TURN_IMAGE_MAX_TOTAL_BYTES } from "./limits";
14
+ export {
15
+ AI_SKILL_REFERENCE_MAX_ITEMS,
16
+ AI_SKILL_REFERENCES_MAX_CHARS,
17
+ type AiSkillDocument,
18
+ type AiSkillReferenceInput,
19
+ parseAiSkillMarkdown,
20
+ serializeAiSkillMarkdown,
21
+ validateAiSkillReferences,
22
+ } from "./skill-format";
23
+ export { isAiImageMediaType } from "./types";
24
+
25
+ export type LaunchAssistantInput = {
26
+ title?: string;
27
+ projectId?: string;
28
+ /** Stable application id when another Cloud application explicitly launches this chat. */
29
+ launchedByAppId?: string;
30
+ draft?: { content: Array<Extract<AiDraftContentPart, { type: "text" | "resource" }>> };
31
+ files?: File[];
32
+ preloadTools?: Array<{ name: string } | { appId: string; kind: "query" | "action"; id: string }>;
33
+ /** Optional immutable ceiling of built-in names and qualified capability IDs. */
34
+ allowedTools?: string[];
35
+ /** Exact enabled Skill names, resolved into ordinary removable resource attachments. */
36
+ skills?: string[];
37
+ };
38
+
39
+ export type AssistantLaunch = {
40
+ conversation: AiConversation;
41
+ href: string;
42
+ };
43
+
44
+ export const launchAssistant = async (input: LaunchAssistantInput): Promise<AssistantLaunch> => {
45
+ const { files = [], ...createInput } = input;
46
+ if (files.length > AI_TURN_ATTACHMENT_MAX_ITEMS) {
47
+ throw new Error(`Assistant drafts can attach at most ${AI_TURN_ATTACHMENT_MAX_ITEMS} files`);
48
+ }
49
+ if ((input.draft?.content.length ?? 0) + files.length > 20) {
50
+ throw new Error("Assistant drafts can contain at most 20 parts");
51
+ }
52
+ const response = await fetch("/api/ai/conversations", {
53
+ method: "POST",
54
+ headers: { "Content-Type": "application/json" },
55
+ body: JSON.stringify(createInput),
56
+ });
57
+ if (!response.ok) {
58
+ const body = (await response.json().catch(() => null)) as { message?: string } | null;
59
+ throw new Error(body?.message ?? "Assistant chat could not be created");
60
+ }
61
+ let conversation = (await response.json()) as AiConversation;
62
+ try {
63
+ if (files.length > 0) {
64
+ const uploaded = await Promise.all(
65
+ files.map(async (file) => {
66
+ const form = new FormData();
67
+ form.set("file", file);
68
+ const upload = await fetch(`/api/ai/conversations/${encodeURIComponent(conversation.id)}/files`, { method: "POST", body: form });
69
+ if (!upload.ok) throw new Error(`Could not attach ${file.name}`);
70
+ return (await upload.json()) as { file: Omit<Extract<AiDraftContentPart, { type: "file" }>, "type"> };
71
+ }),
72
+ );
73
+ const draftResponse = await fetch(`/api/ai/conversations/${encodeURIComponent(conversation.id)}/draft`, {
74
+ method: "PUT",
75
+ headers: { "Content-Type": "application/json" },
76
+ body: JSON.stringify({
77
+ expectedRevision: conversation.draft.revision,
78
+ content: [...conversation.draft.content, ...uploaded.map((item) => ({ type: "file" as const, ...item.file }))],
79
+ }),
80
+ });
81
+ if (!draftResponse.ok) throw new Error("Assistant draft attachments could not be saved");
82
+ conversation = { ...conversation, draft: (await draftResponse.json()) as AiConversation["draft"] };
83
+ }
84
+ } catch (error) {
85
+ await fetch(`/api/ai/conversations/${encodeURIComponent(conversation.id)}/archive`, { method: "POST" }).catch(() => undefined);
86
+ throw error;
87
+ }
88
+ return {
89
+ conversation,
90
+ href: `/app/assistant?conversation=${encodeURIComponent(conversation.id)}`,
91
+ };
92
+ };