@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,730 @@
1
+ import { dates } from "@k2b/stdlib";
2
+ import { mutation } from "@k2b/stdlib/solid";
3
+ import { Button, ButtonLink, Chat, isStructuredDataValue, SplitButton, StructuredDataPreview, useLocale } from "@k2b/ui";
4
+ import { createSignal, For, type JSX, Match, Show, Switch } from "solid-js";
5
+ import type { CapabilityActionReview } from "../../contracts/capabilities";
6
+ import { markdown } from "../../shared";
7
+ import type { AiTurnBlock } from "../protocol";
8
+ import { isRenderableTurnBlock } from "../protocol";
9
+ import { hasSpecializedBuiltinToolView, SpecializedBuiltinToolBlock } from "./builtin-tools";
10
+ import { CapabilityTablePreview } from "./capability-table";
11
+ import { PresentToolBlock } from "./file-tools";
12
+ import { useAiChatActions } from "./message-actions";
13
+ import {
14
+ aiToolIcon,
15
+ capabilityErrorDescription,
16
+ displayToolName,
17
+ fetchFileErrorPresentation,
18
+ formatToolDetailText,
19
+ isCardToolName,
20
+ isRecord,
21
+ isSurveyToolName,
22
+ isTextEditorToolName,
23
+ jsonPreview,
24
+ memoryToolPresentation,
25
+ } from "./message-utils";
26
+ import { AssistantMarkdownBlock } from "./primitives";
27
+ import { AiToolActivity, AiToolDisclosureProvider, type AiToolDisclosureState, createAiToolDisclosureState } from "./tool-disclosure";
28
+ import { CloudCardBlock, CloudSurveyBlock, CloudSurveyResultBlock, CloudTextEditorBlock, CloudTextEditorResultBlock } from "./visual-tools";
29
+ import { FetchFileToolBlock, WebExtractToolBlock, WebSearchToolBlock } from "./web-tools";
30
+
31
+ type ToolBlock = Extract<AiTurnBlock, { kind: "tool" }>;
32
+ type ReviewDetail = NonNullable<CapabilityActionReview["details"]>[number];
33
+
34
+ const reviewDetailText = (detail: ReviewDetail, locale: string): string => {
35
+ if (detail.format === "date")
36
+ return /^\d{4}-\d{2}-\d{2}$/.test(detail.value)
37
+ ? dates.formatDate(`${detail.value}T12:00:00.000Z`, { locale, timeZone: "UTC" })
38
+ : dates.formatDate(detail.value, { locale });
39
+ if (detail.format === "date-time") return dates.formatDateTime(detail.value, { locale });
40
+ return detail.value;
41
+ };
42
+
43
+ function ReviewDetailValue(props: { detail: ReviewDetail }) {
44
+ const locale = useLocale();
45
+ const value = () => reviewDetailText(props.detail, locale());
46
+ return (
47
+ <Show when={props.detail.format} fallback={value()}>
48
+ <time dateTime={props.detail.value}>{value()}</time>
49
+ </Show>
50
+ );
51
+ }
52
+
53
+ // All state branches below live in reactive JSX (Show/Switch), never in the
54
+ // component body: blocks are born empty/running and mutate in place while the
55
+ // turn streams, so every branch must re-evaluate when the store updates.
56
+
57
+ function ThinkingBlockView(props: { text: string; streaming?: boolean }) {
58
+ return (
59
+ <Show
60
+ when={props.text.trim()}
61
+ fallback={
62
+ <Show when={props.streaming}>
63
+ <Chat.Activity label="Thinking" icon="ti ti-sparkles" tone="ai" busy />
64
+ </Show>
65
+ }
66
+ >
67
+ <Chat.Activity label="Show reasoning" icon="ti ti-sparkles" tone="ai" bodyInset={false}>
68
+ <pre class="max-h-52 w-full min-w-0 overflow-auto whitespace-pre-wrap rounded-md bg-zinc-100/70 p-2 text-[11px] leading-5 text-secondary [box-shadow:var(--ui-control-recess)] dark:bg-zinc-950/70">
69
+ {props.text}
70
+ </pre>
71
+ </Chat.Activity>
72
+ </Show>
73
+ );
74
+ }
75
+
76
+ function CompactionBlockView(props: { block: Extract<AiTurnBlock, { kind: "compaction" }> }) {
77
+ const locale = useLocale();
78
+ const status = () => props.block.status;
79
+ const description = () => {
80
+ if (status() === "completed") return "Context compacted";
81
+ if (status() === "skipped") return "No-op";
82
+ if (status() === "failed") return "Compaction failed";
83
+ return "Compacting context";
84
+ };
85
+
86
+ return (
87
+ <Show when={status() !== "running"} fallback={<Chat.Activity label="Compacting context" icon="ti ti-brain" tone="ai" busy />}>
88
+ <Chat.Activity
89
+ label="Show compaction"
90
+ description={description()}
91
+ icon="ti ti-brain"
92
+ tone={status() === "failed" ? "danger" : "ai"}
93
+ bodyInset={false}
94
+ >
95
+ <div class="w-full min-w-0 rounded-md bg-zinc-100/70 p-2 text-[11px] leading-5 text-secondary [box-shadow:var(--ui-control-recess)] dark:bg-zinc-950/70">
96
+ <Show when={props.block.result} fallback={<p>Older chat context was summarized into compact conversation memory.</p>}>
97
+ {(compactResult) => (
98
+ <dl class="grid grid-cols-2 gap-2">
99
+ <div class="rounded-md bg-white/65 px-2 py-1 dark:bg-white/5">
100
+ <dt class="uppercase tracking-wide text-dimmed">Before</dt>
101
+ <dd class="font-medium text-primary">{compactResult().entriesBefore.toLocaleString(locale())}</dd>
102
+ </div>
103
+ <div class="rounded-md bg-white/65 px-2 py-1 dark:bg-white/5">
104
+ <dt class="uppercase tracking-wide text-dimmed">After</dt>
105
+ <dd class="font-medium text-primary">{compactResult().entriesAfter.toLocaleString(locale())}</dd>
106
+ </div>
107
+ </dl>
108
+ )}
109
+ </Show>
110
+ </div>
111
+ </Chat.Activity>
112
+ </Show>
113
+ );
114
+ }
115
+
116
+ function ToolTextDetail(props: { children: JSX.Element }) {
117
+ return (
118
+ <pre class="max-h-52 w-full min-w-0 overflow-auto whitespace-pre-wrap rounded-md bg-zinc-100 p-2 text-[11px] leading-4 text-primary [box-shadow:var(--ui-control-recess)] dark:bg-zinc-950/70">
119
+ {props.children}
120
+ </pre>
121
+ );
122
+ }
123
+
124
+ function ToolDetail(props: { title: string; toolName: string; value: unknown }) {
125
+ const structured = () =>
126
+ props.toolName !== "web_search" &&
127
+ props.toolName !== "web_extract" &&
128
+ typeof props.value !== "string" &&
129
+ isStructuredDataValue(props.value)
130
+ ? { data: props.value }
131
+ : null;
132
+
133
+ return (
134
+ <div class="min-w-0">
135
+ <p class="mb-1 text-[10px] font-medium uppercase tracking-wide text-dimmed">{props.title}</p>
136
+ <Show when={structured()} fallback={<ToolTextDetail>{formatToolDetailText(props.toolName, props.value)}</ToolTextDetail>}>
137
+ {(value) => <StructuredDataPreview data={value().data} maxRows={8} class="w-full" />}
138
+ </Show>
139
+ </div>
140
+ );
141
+ }
142
+
143
+ function ToolResultDisclosure(props: {
144
+ blockId: string;
145
+ name: string;
146
+ toolName: string;
147
+ args?: unknown;
148
+ result: unknown;
149
+ isError: boolean;
150
+ icon?: string;
151
+ accent?: string;
152
+ labelOnError?: string;
153
+ errorLabel?: string;
154
+ errorDescription?: string;
155
+ }) {
156
+ return (
157
+ <AiToolActivity
158
+ blockId={props.blockId}
159
+ icon={props.icon ?? (props.isError ? "ti ti-alert-circle" : aiToolIcon(props.toolName))}
160
+ label={props.isError ? (props.errorLabel ?? `${props.labelOnError ?? props.name} failed`) : props.name}
161
+ description={props.isError ? props.errorDescription : undefined}
162
+ tone={props.isError ? "danger" : "neutral"}
163
+ accent={props.accent}
164
+ defaultOpen={props.isError}
165
+ bodyInset={false}
166
+ >
167
+ <div class="flex w-full min-w-0 flex-col gap-2">
168
+ <Show when={props.args !== undefined}>
169
+ <ToolDetail title="Input" toolName={props.toolName} value={props.args} />
170
+ </Show>
171
+ <ToolDetail title="Response" toolName={props.toolName} value={props.result} />
172
+ </div>
173
+ </AiToolActivity>
174
+ );
175
+ }
176
+
177
+ function ApprovalBlockView(props: { turnId: string; block: ToolBlock }) {
178
+ const actions = useAiChatActions();
179
+ const request = () => ({ turnId: props.turnId, callId: props.block.callId, name: props.block.name });
180
+ const pending = () => props.block.status === "awaiting_approval";
181
+ const actionDisabled = () => actions.actionDisabled?.() ?? false;
182
+ const [submitted, setSubmitted] = createSignal(false);
183
+ const [detailsOpen, setDetailsOpen] = createSignal(false);
184
+ const approval = mutation.create<void, { approved: boolean; remember?: "always" }>({
185
+ mutation: async (input) => {
186
+ if (!actions.onApproval) throw new Error("Approval is unavailable.");
187
+ await actions.onApproval(request(), input);
188
+ },
189
+ onSuccess: () => setSubmitted(true),
190
+ });
191
+ const submit = (input: { approved: boolean; remember?: "always" }) => {
192
+ if (!actionDisabled() && !approval.loading()) void approval.mutate(input);
193
+ };
194
+ const title = () => props.block.presentation?.title ?? displayToolName(props.block.name);
195
+ const ownerName = () => props.block.presentation?.appName ?? "Assistant";
196
+ const description = () => {
197
+ const reviewMessage = props.block.approval?.review?.message.trim();
198
+ if (reviewMessage) return reviewMessage;
199
+ const message = props.block.approval?.message?.trim();
200
+ if (!message) return null;
201
+ const duplicateTitle = props.block.presentation ? `${props.block.presentation.appName}: ${props.block.presentation.title}\n` : "";
202
+ const withoutDuplicateTitle = message.startsWith(duplicateTitle) ? message.slice(duplicateTitle.length).trim() : message;
203
+ if (/^Review the validated arguments below before running this action\.$/i.test(withoutDuplicateTitle)) return null;
204
+ return withoutDuplicateTitle || null;
205
+ };
206
+ const reviewLines = () =>
207
+ (props.block.approval?.review ? "" : (description() ?? ""))
208
+ .split("\n")
209
+ .map((line) => line.trim())
210
+ .filter(Boolean)
211
+ .map((line) => {
212
+ const match = /^([^:]{1,40}):\s+(.+)$/.exec(line);
213
+ return match ? { label: match[1], value: match[2] } : { value: line };
214
+ });
215
+ const inlineReviewDetails = () =>
216
+ (props.block.approval?.review?.details ?? []).filter((detail) => (detail.display ?? "inline") === "inline");
217
+ const blockReviewDetails = () => (props.block.approval?.review?.details ?? []).filter((detail) => detail.display === "block");
218
+ const reviewLinks = () => props.block.approval?.review?.links ?? [];
219
+ const reviewLinkTitle = (rel: "open" | "edit" | "status" | "preview" | "download") =>
220
+ ({
221
+ open: `Open in ${ownerName()}`,
222
+ edit: `Edit in ${ownerName()}`,
223
+ status: `View status in ${ownerName()}`,
224
+ preview: "Preview",
225
+ download: "Download",
226
+ })[rel];
227
+ const detailData = () => (isStructuredDataValue(props.block.args) ? props.block.args : undefined);
228
+ const appAccent = () => props.block.presentation?.appAccent;
229
+ const detailsId = `approval-details-${props.block.callId}`;
230
+ return (
231
+ <div class="w-full">
232
+ <section
233
+ class={`w-full overflow-hidden rounded-xl border border-[var(--k2b-border)] bg-[var(--k2b-surface)] text-sm text-primary ${appAccent() ? "app-accent-scope" : ""}`}
234
+ style={{ "--app-accent": appAccent() }}
235
+ aria-label={`Approval required: ${title()}`}
236
+ >
237
+ <div class="p-4">
238
+ <div class="flex min-w-0 items-center gap-3">
239
+ <span
240
+ class="inline-flex size-8 shrink-0 items-center justify-center rounded-lg text-base leading-none"
241
+ style={{
242
+ color: appAccent() ? "var(--ui-app-accent-text)" : "var(--k2b-ai-accent)",
243
+ background: appAccent()
244
+ ? "color-mix(in srgb, var(--app-accent) 10%, var(--k2b-surface))"
245
+ : "color-mix(in srgb, var(--k2b-ai-accent) 10%, var(--k2b-surface))",
246
+ }}
247
+ aria-hidden="true"
248
+ >
249
+ <i class={`${aiToolIcon(props.block.name, props.block.presentation?.appIcon)} leading-none`} />
250
+ </span>
251
+ <div class="min-w-0">
252
+ <h3 class="truncate text-sm font-semibold leading-5 text-primary">
253
+ {ownerName()} · {title()}
254
+ </h3>
255
+ <p class="text-xs text-dimmed">Action</p>
256
+ </div>
257
+ </div>
258
+ <Show when={reviewLines().length > 0}>
259
+ <div class="mt-4 flex flex-col gap-1 text-xs leading-5 text-secondary">
260
+ <For each={reviewLines()}>
261
+ {(line) => (
262
+ <p class="whitespace-pre-wrap">
263
+ <Show when={line.label}>{(label) => <strong class="font-semibold text-primary">{label()}: </strong>}</Show>
264
+ {line.value}
265
+ </p>
266
+ )}
267
+ </For>
268
+ </div>
269
+ </Show>
270
+ <Show when={props.block.approval?.review}>
271
+ <div class="mt-4 flex flex-col gap-4 text-xs leading-5 text-secondary">
272
+ <p class="whitespace-pre-wrap">{description()}</p>
273
+ <Show when={inlineReviewDetails().length > 0}>
274
+ <dl class="grid gap-x-5 gap-y-1.5 border-t border-[var(--k2b-border)] pt-3 sm:grid-cols-[max-content_minmax(0,1fr)]">
275
+ <For each={inlineReviewDetails()}>
276
+ {(detail) => (
277
+ <>
278
+ <dt class="font-semibold text-primary">{detail.label}</dt>
279
+ <dd class="min-w-0 whitespace-pre-wrap break-words">
280
+ <ReviewDetailValue detail={detail} />
281
+ </dd>
282
+ </>
283
+ )}
284
+ </For>
285
+ </dl>
286
+ </Show>
287
+ <For each={blockReviewDetails()}>
288
+ {(detail) => (
289
+ <section class="min-w-0" aria-label={detail.label}>
290
+ <h4 class="mb-1.5 font-semibold text-primary">{detail.label}</h4>
291
+ <pre
292
+ class="max-h-72 overflow-auto whitespace-pre-wrap break-words pr-2 font-sans text-xs leading-5 text-secondary"
293
+ role="region"
294
+ tabIndex={0}
295
+ aria-label={`${detail.label} content`}
296
+ >
297
+ <ReviewDetailValue detail={detail} />
298
+ </pre>
299
+ </section>
300
+ )}
301
+ </For>
302
+ </div>
303
+ </Show>
304
+ </div>
305
+ <footer
306
+ class="flex min-h-12 flex-wrap items-center gap-2 border-t border-[var(--k2b-border)] bg-[var(--k2b-surface-subtle)] px-4 py-2.5"
307
+ data-ai-approval-footer
308
+ >
309
+ <Show when={reviewLinks().length > 0}>
310
+ <nav class="flex flex-wrap gap-1" aria-label={`${title()} links`}>
311
+ <For each={reviewLinks()}>
312
+ {(link) => (
313
+ <ButtonLink href={link.href} target="_blank" rel="noopener noreferrer" size="xs" variant="ghost">
314
+ {link.title ?? reviewLinkTitle(link.rel)}
315
+ </ButtonLink>
316
+ )}
317
+ </For>
318
+ </nav>
319
+ </Show>
320
+ <Show when={approval.error()}>
321
+ <p class="text-xs text-red-700 dark:text-red-300">Could not submit. Try again.</p>
322
+ </Show>
323
+ <div class="ml-auto shrink-0">
324
+ <Show
325
+ when={pending()}
326
+ fallback={
327
+ <span class="text-xs font-medium text-secondary">
328
+ {title()} · {props.block.status === "rejected" ? "Rejected" : "Approved"}
329
+ </span>
330
+ }
331
+ >
332
+ <Show when={actions.onApproval} fallback={<span class="text-xs font-medium text-secondary">Approval unavailable</span>}>
333
+ <Show
334
+ when={!actionDisabled()}
335
+ fallback={
336
+ <span class="inline-flex items-center gap-1 text-xs font-medium text-secondary">
337
+ <i class="ti ti-player-stop" aria-hidden="true" />
338
+ Stopping response
339
+ </span>
340
+ }
341
+ >
342
+ <Show
343
+ when={!approval.loading() && !submitted()}
344
+ fallback={
345
+ <span class="inline-flex items-center gap-1 text-xs font-medium text-secondary">
346
+ <i class={`ti ${submitted() ? "ti-check" : "ti-loader-2 animate-spin"}`} aria-hidden="true" />
347
+ {submitted() ? "Submitted" : "Submitting"}
348
+ </span>
349
+ }
350
+ >
351
+ <div class="flex flex-wrap justify-end gap-1">
352
+ <Button size="xs" variant="ghost" onClick={() => submit({ approved: false })}>
353
+ Reject
354
+ </Button>
355
+ <SplitButton
356
+ size="xs"
357
+ variant="ai"
358
+ onClick={() => submit({ approved: true })}
359
+ menuLabel={`More options for ${title()}`}
360
+ menuPosition="bottom-right"
361
+ items={[
362
+ {
363
+ label: detailsOpen() ? "Hide details" : "Details",
364
+ icon: detailsOpen() ? "ti ti-eye-off" : "ti ti-eye",
365
+ action: () => setDetailsOpen((open) => !open),
366
+ },
367
+ ...(props.block.approval?.allowAlways
368
+ ? [
369
+ {
370
+ label: "Always approve",
371
+ icon: "ti ti-shield-check",
372
+ action: () => submit({ approved: true, remember: "always" }),
373
+ },
374
+ ]
375
+ : []),
376
+ ]}
377
+ >
378
+ {title()}
379
+ </SplitButton>
380
+ </div>
381
+ </Show>
382
+ </Show>
383
+ </Show>
384
+ </Show>
385
+ </div>
386
+ </footer>
387
+ </section>
388
+ <Show when={detailsOpen()}>
389
+ <div id={detailsId} class="mt-2 w-full" role="region" aria-label={`${title()} details`}>
390
+ <Show
391
+ when={detailData()}
392
+ fallback={
393
+ <pre class="max-h-40 overflow-auto rounded-md bg-white/55 p-2 text-[11px] text-primary dark:bg-black/20">
394
+ {jsonPreview(props.block.args)}
395
+ </pre>
396
+ }
397
+ >
398
+ {(data) => <StructuredDataPreview data={data()} class="w-full" />}
399
+ </Show>
400
+ </div>
401
+ </Show>
402
+ </div>
403
+ );
404
+ }
405
+
406
+ function CapabilityToolView(props: { block: ToolBlock }) {
407
+ const presentation = () => props.block.presentation!;
408
+ const label = () => presentation().title;
409
+ const result = () => (isRecord(props.block.result) ? props.block.result : null);
410
+ const summary = () => {
411
+ const value = result()?.summary;
412
+ if (typeof value !== "string") return "";
413
+ const trimmed = value.trim();
414
+ return trimmed.length <= 500 ? trimmed : "";
415
+ };
416
+ const links = () => {
417
+ const value = result()?.links;
418
+ return Array.isArray(value)
419
+ ? value.filter((link) => isRecord(link) && typeof link.href === "string" && /^\/(?![\\/])[^\\\u0000-\u001f\u007f]*$/.test(link.href))
420
+ : [];
421
+ };
422
+ const hasReadableResult = () => !props.block.isError && Boolean(summary());
423
+ return (
424
+ <>
425
+ <Show
426
+ when={props.block.status !== "running"}
427
+ fallback={
428
+ <Chat.Activity
429
+ icon={aiToolIcon(props.block.name, presentation().appIcon)}
430
+ label={label()}
431
+ tone="ai"
432
+ accent={presentation().appAccent}
433
+ busy
434
+ />
435
+ }
436
+ >
437
+ <Show
438
+ when={!props.block.isError}
439
+ fallback={
440
+ <Chat.Activity
441
+ icon={aiToolIcon(props.block.name, presentation().appIcon)}
442
+ label={`${label()} failed`}
443
+ description={capabilityErrorDescription(props.block.result)}
444
+ tone="danger"
445
+ accent={presentation().appAccent}
446
+ />
447
+ }
448
+ >
449
+ <Show
450
+ when={hasReadableResult()}
451
+ fallback={
452
+ <ToolResultDisclosure
453
+ blockId={props.block.id}
454
+ name={label()}
455
+ labelOnError={label()}
456
+ icon={aiToolIcon(props.block.name, presentation().appIcon)}
457
+ accent={presentation().appAccent}
458
+ toolName={props.block.name}
459
+ args={props.block.args}
460
+ result={props.block.result}
461
+ isError={Boolean(props.block.isError)}
462
+ />
463
+ }
464
+ >
465
+ <Chat.Activity
466
+ icon={aiToolIcon(props.block.name, presentation().appIcon)}
467
+ label={summary()}
468
+ accent={presentation().appAccent}
469
+ trailing={
470
+ links().length > 0 ? (
471
+ <span class="flex min-w-0 flex-wrap items-center justify-end gap-1">
472
+ <For each={links()}>
473
+ {(link) => (
474
+ <ButtonLink
475
+ class="ai-chat-result-link"
476
+ href={String(link.href)}
477
+ target="_blank"
478
+ rel="noopener noreferrer"
479
+ size="xs"
480
+ variant="ghost"
481
+ >
482
+ {typeof link.title === "string"
483
+ ? link.title
484
+ : link.rel === "edit"
485
+ ? "Edit"
486
+ : link.rel === "download"
487
+ ? "Download"
488
+ : "Open"}
489
+ </ButtonLink>
490
+ )}
491
+ </For>
492
+ </span>
493
+ ) : undefined
494
+ }
495
+ />
496
+ </Show>
497
+ </Show>
498
+ </Show>
499
+ <Show when={props.block.status !== "running" && !props.block.isError}>
500
+ <CapabilityTablePreview result={props.block.result} label={label()} />
501
+ </Show>
502
+ </>
503
+ );
504
+ }
505
+
506
+ function RejectedToolView(props: { block: ToolBlock }) {
507
+ const presentation = () => props.block.presentation;
508
+ const title = () => presentation()?.title ?? displayToolName(props.block.name);
509
+ return (
510
+ <Chat.Activity
511
+ icon={aiToolIcon(props.block.name, presentation()?.appIcon)}
512
+ label={`${title()} was rejected`}
513
+ accent={presentation()?.appAccent}
514
+ />
515
+ );
516
+ }
517
+
518
+ function SurveyToolView(props: { turnId: string; block: ToolBlock; active?: boolean }) {
519
+ const actions = useAiChatActions();
520
+ const request = () => ({ turnId: props.turnId, callId: props.block.callId, name: props.block.name });
521
+ const submit = actions.onFrontendToolResult;
522
+ const submittedResult = () =>
523
+ props.block.status === "completed" && isRecord(props.block.result) && props.block.result.submitted === true ? props.block.result : null;
524
+ return (
525
+ <Switch fallback={<CloudSurveyBlock args={props.block.args} disabled />}>
526
+ <Match when={props.block.status === "awaiting_client"}>
527
+ <CloudSurveyBlock
528
+ args={props.block.args}
529
+ disabled={!submit || actions.actionDisabled?.()}
530
+ disabledLabel={actions.actionDisabled?.() ? "Stopping response." : undefined}
531
+ onSubmit={submit ? (result) => submit(request(), result) : undefined}
532
+ />
533
+ </Match>
534
+ <Match when={submittedResult()}>
535
+ {(result) => (
536
+ // biome-ignore lint/a11y/useValidAriaRole: Chat.Message uses a conversation role, not an ARIA role.
537
+ <Chat.Message role="user">
538
+ <CloudSurveyResultBlock args={props.block.args} result={result()} />
539
+ </Chat.Message>
540
+ )}
541
+ </Match>
542
+ </Switch>
543
+ );
544
+ }
545
+
546
+ function TextEditorToolView(props: { turnId: string; block: ToolBlock; active?: boolean }) {
547
+ const actions = useAiChatActions();
548
+ const request = () => ({ turnId: props.turnId, callId: props.block.callId, name: props.block.name });
549
+ const submit = actions.onFrontendToolResult;
550
+ const completedResult = () =>
551
+ props.block.status === "completed" &&
552
+ isRecord(props.block.result) &&
553
+ ((props.block.result.submitted === true && typeof props.block.result.content === "string") ||
554
+ (props.block.result.submitted === false && typeof props.block.result.feedback === "string"))
555
+ ? props.block.result
556
+ : null;
557
+ return (
558
+ <Switch
559
+ fallback={
560
+ <ToolResultDisclosure
561
+ blockId={props.block.id}
562
+ name="text editor"
563
+ toolName={props.block.name}
564
+ args={props.block.args}
565
+ result={props.block.result}
566
+ isError={Boolean(props.block.isError)}
567
+ />
568
+ }
569
+ >
570
+ <Match when={props.block.status === "running"}>
571
+ <Chat.Activity label="Preparing text editor" icon="ti ti-edit" tone="ai" busy />
572
+ </Match>
573
+ <Match when={props.block.status === "awaiting_client"}>
574
+ <CloudTextEditorBlock
575
+ args={props.block.args}
576
+ disabled={!submit || actions.actionDisabled?.()}
577
+ disabledLabel={actions.actionDisabled?.() ? "Stopping response." : undefined}
578
+ onSubmit={submit ? (result) => submit(request(), result) : undefined}
579
+ />
580
+ </Match>
581
+ <Match when={completedResult()}>
582
+ {(result) => (
583
+ <CloudTextEditorResultBlock blockId={props.block.id} args={props.block.args} result={result()} continuing={props.active} />
584
+ )}
585
+ </Match>
586
+ </Switch>
587
+ );
588
+ }
589
+
590
+ function MemoryToolView(props: { block: ToolBlock }) {
591
+ const presentation = () => memoryToolPresentation(props.block.args, props.block.result);
592
+ return (
593
+ <Show when={props.block.status !== "running"} fallback={<Chat.Activity label="Using memory" icon="ti ti-brain" tone="ai" busy />}>
594
+ <Show
595
+ when={presentation()}
596
+ fallback={
597
+ <ToolResultDisclosure
598
+ blockId={props.block.id}
599
+ name="Memory"
600
+ toolName={props.block.name}
601
+ args={props.block.args}
602
+ result={props.block.result}
603
+ isError={Boolean(props.block.isError)}
604
+ />
605
+ }
606
+ >
607
+ {(item) => (
608
+ <Chat.Activity
609
+ icon={`ti ${item().failed ? "ti-alert-circle" : "ti-brain"}`}
610
+ label={item().label}
611
+ description={item().description}
612
+ tone={item().failed ? "danger" : "ai"}
613
+ />
614
+ )}
615
+ </Show>
616
+ </Show>
617
+ );
618
+ }
619
+
620
+ function ToolBlockView(props: { turnId: string; block: ToolBlock; active?: boolean }) {
621
+ const status = () => props.block.status;
622
+ const fetchError = () =>
623
+ props.block.name === "fetch_file" && props.block.isError ? fetchFileErrorPresentation(props.block.result) : undefined;
624
+ return (
625
+ <Switch
626
+ fallback={
627
+ <ToolResultDisclosure
628
+ blockId={props.block.id}
629
+ name={displayToolName(props.block.name)}
630
+ toolName={props.block.name}
631
+ args={props.block.args}
632
+ result={props.block.result}
633
+ isError={Boolean(props.block.isError)}
634
+ errorLabel={fetchError()?.label}
635
+ errorDescription={fetchError()?.description}
636
+ />
637
+ }
638
+ >
639
+ <Match when={status() === "awaiting_approval"}>
640
+ <ApprovalBlockView turnId={props.turnId} block={props.block} />
641
+ </Match>
642
+ <Match when={status() === "rejected"}>
643
+ <RejectedToolView block={props.block} />
644
+ </Match>
645
+ <Match when={props.block.presentation?.kind === "capability"}>
646
+ <CapabilityToolView block={props.block} />
647
+ </Match>
648
+ <Match when={props.block.name === "present" && !props.block.isError}>
649
+ <PresentToolBlock block={props.block} />
650
+ </Match>
651
+ <Match when={props.block.name === "web_search" && !props.block.isError}>
652
+ <WebSearchToolBlock block={props.block} />
653
+ </Match>
654
+ <Match when={props.block.name === "web_extract" && !props.block.isError}>
655
+ <WebExtractToolBlock block={props.block} />
656
+ </Match>
657
+ <Match when={props.block.name === "fetch_file" && !props.block.isError}>
658
+ <FetchFileToolBlock block={props.block} />
659
+ </Match>
660
+ <Match when={props.block.name === "memory"}>
661
+ <MemoryToolView block={props.block} />
662
+ </Match>
663
+ <Match when={hasSpecializedBuiltinToolView(props.block.name, props.block.result) && status() === "completed" && !props.block.isError}>
664
+ <SpecializedBuiltinToolBlock block={props.block} />
665
+ </Match>
666
+ <Match when={isCardToolName(props.block.name) && !props.block.isError}>
667
+ <CloudCardBlock args={props.block.args} />
668
+ </Match>
669
+ <Match when={isSurveyToolName(props.block.name) && !props.block.isError}>
670
+ <SurveyToolView turnId={props.turnId} block={props.block} active={props.active} />
671
+ </Match>
672
+ <Match when={isTextEditorToolName(props.block.name)}>
673
+ <TextEditorToolView turnId={props.turnId} block={props.block} active={props.active} />
674
+ </Match>
675
+ <Match when={status() === "running" || status() === "awaiting_client"}>
676
+ <Chat.Activity label={displayToolName(props.block.name)} icon={aiToolIcon(props.block.name)} busy />
677
+ </Match>
678
+ </Switch>
679
+ );
680
+ }
681
+
682
+ /** Render one unified turn block. Shared by persisted assistant groups and the live turn. */
683
+ export function AiTurnBlockView(props: { block: AiTurnBlock; turnId: string; streaming?: boolean; active?: boolean }) {
684
+ // block.kind is immutable for a given block id, so this switch may run once.
685
+ const block = props.block;
686
+ switch (block.kind) {
687
+ case "text":
688
+ return <AssistantMarkdownBlock html={markdown.renderSync(block.text)} />;
689
+ case "thinking":
690
+ return <ThinkingBlockView text={block.text} streaming={props.streaming} />;
691
+ case "steer_message":
692
+ return null;
693
+ case "steer_applied":
694
+ return <Chat.Activity label="Conversation steered" icon="ti ti-route" tone="ai" />;
695
+ case "tool":
696
+ return <ToolBlockView turnId={props.turnId} block={block} active={props.active} />;
697
+ case "compaction":
698
+ return <CompactionBlockView block={block} />;
699
+ }
700
+ }
701
+
702
+ export function AiTurnBlockList(props: {
703
+ blocks: AiTurnBlock[];
704
+ turnId: string;
705
+ streaming?: boolean;
706
+ compact?: boolean;
707
+ active?: boolean;
708
+ disclosureState?: AiToolDisclosureState;
709
+ }) {
710
+ const visible = () => props.blocks.filter(isRenderableTurnBlock);
711
+ const disclosureState = props.disclosureState ?? createAiToolDisclosureState();
712
+ return (
713
+ <Show when={visible().length > 0}>
714
+ <AiToolDisclosureProvider state={disclosureState}>
715
+ <div class={`flex flex-col ${props.compact ? "gap-1" : "gap-2"}`}>
716
+ <For each={visible()}>
717
+ {(block, index) => (
718
+ <AiTurnBlockView
719
+ block={block}
720
+ turnId={props.turnId}
721
+ streaming={props.streaming && index() === visible().length - 1}
722
+ active={props.active}
723
+ />
724
+ )}
725
+ </For>
726
+ </div>
727
+ </AiToolDisclosureProvider>
728
+ </Show>
729
+ );
730
+ }