@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,23 @@
1
+ /**
2
+ * Composing kernel writes into an app's own transaction.
3
+ *
4
+ * Every store function takes an optional `db`, and the whole point of that
5
+ * option is that an app can run a kernel write inside a transaction it already
6
+ * owns — creating a workflow and its app-side profile together, say, so a crash
7
+ * cannot leave one without the other.
8
+ *
9
+ * Bun rejects `begin` inside a transaction; a nested unit is a savepoint. A
10
+ * handle only has `savepoint` when it *is* a transaction, which is what makes
11
+ * this reliable rather than a convention callers have to remember.
12
+ */
13
+ import { type SQL, sql } from "bun";
14
+
15
+ type Savepointed = SQL & { savepoint: <T>(fn: (tx: SQL) => Promise<T>) => Promise<T> };
16
+
17
+ const isTransaction = (db: SQL): db is Savepointed => typeof (db as Partial<Savepointed>).savepoint === "function";
18
+
19
+ /** Runs `fn` atomically, nesting as a savepoint when the caller already has a transaction. */
20
+ export const withTransaction = <T>(db: SQL | undefined, fn: (tx: SQL) => Promise<T>): Promise<T> => {
21
+ const handle = db ?? sql;
22
+ return isTransaction(handle) ? handle.savepoint(fn) : handle.begin(fn);
23
+ };
@@ -0,0 +1,439 @@
1
+ /**
2
+ * The loop that turns queued runs into finished ones.
3
+ *
4
+ * Grids and Mail each wrote this: claim a run, load its plan, drive the
5
+ * executor, record the outcome, renew the lease while it works. The bodies
6
+ * differed enough that a fix in one never reached the other — Grids leased the
7
+ * run, Mail leased a target, and only one of them woke parked runs on a
8
+ * deadline.
9
+ *
10
+ * An app now brings its ID, current module, and runtime ports. Everything
11
+ * about *when* something runs, *which* plan it runs, and what happens if the
12
+ * process dies belongs here.
13
+ */
14
+ import { type SQL, sql } from "bun";
15
+ import type { WorkflowActor, WorkflowInvocationMode, WorkflowJsonValue } from "../contracts";
16
+ import { hashWorkflowJson } from "../language/canonical";
17
+ import type { DefinedWorkflowModule } from "../module";
18
+ import { coordinateWorkflowExecution } from "../runtime/coordinator";
19
+ import { dryRunWorkflowPlan, executeWorkflowPlan } from "../runtime/executor";
20
+ import type {
21
+ WorkflowDryRunActionPort,
22
+ WorkflowDryRunResult,
23
+ WorkflowExecuteActionPort,
24
+ WorkflowRuntimeRunIdentity,
25
+ WorkflowTracePort,
26
+ WorkflowValueResolverPort,
27
+ } from "../runtime/ports";
28
+ import { dispatchPendingWorkflowEvents } from "./events";
29
+ import {
30
+ claimWorkflowRun,
31
+ createWorkflowCoordinatorPort,
32
+ createWorkflowRuntimeRepository,
33
+ finishExpiredWorkflowRunCancels,
34
+ finishWorkflowRunCancel,
35
+ WORKFLOW_RUN_LEASE_MS,
36
+ WORKFLOW_RUN_MAX_CONSECUTIVE_FAILURES,
37
+ type WorkflowRunClaim,
38
+ type WorkflowRunResult,
39
+ wakeExpiredWorkflowRuns,
40
+ } from "./runs";
41
+
42
+ /** Renew well inside the lease, so one slow query does not cost the claim. */
43
+ const HEARTBEAT_MS = Math.floor(WORKFLOW_RUN_LEASE_MS / 3);
44
+
45
+ export type WorkflowWorkerOptions = {
46
+ /** Identifies this process in `lease_owner`. Only ever read by humans. */
47
+ worker: string;
48
+ /** The app's action implementations. */
49
+ actions: WorkflowExecuteActionPort;
50
+ /** The app whose runs this worker may claim. */
51
+ appId: string;
52
+ /** The current language declaration. Older alpha manifests are not executed. */
53
+ module: DefinedWorkflowModule;
54
+ trace?: WorkflowTracePort;
55
+ /**
56
+ * Builds the value resolver for one claimed run.
57
+ *
58
+ * A function of the claim rather than a port, because a worker serves every
59
+ * run in the app while a resolver answers under one run's scope and actor.
60
+ * One shared instance would resolve a reference through whichever run
61
+ * happened to warm its caches.
62
+ */
63
+ values?: (claim: WorkflowRunClaim) => WorkflowValueResolverPort;
64
+ maxLoopItems?: number;
65
+ db?: SQL;
66
+ };
67
+
68
+ export type WorkflowWorkerOutcome =
69
+ | { state: "idle" }
70
+ | { state: "finished"; runId: string; result: WorkflowRunResult }
71
+ | { state: "released"; runId: string; error: unknown }
72
+ | { state: "lost"; runId: string };
73
+
74
+ /**
75
+ * The actor a run executes as.
76
+ *
77
+ * Read from the authorization snapshot frozen when the run was accepted, so an
78
+ * edit to a group membership cannot silently widen what an already-queued run
79
+ * is allowed to do.
80
+ *
81
+ * An app's snapshot holds more than the actor — what it was accepted under,
82
+ * which credential — so the actor is a named key inside it rather than the
83
+ * snapshot itself. Nobody named means nobody: an unrecognised snapshot yields
84
+ * an empty actor, and every permission check an action makes then refuses.
85
+ * Inventing a system identity here is how a run acts with more authority than
86
+ * whoever asked for it had.
87
+ */
88
+ const actorFromSnapshot = (snapshot: WorkflowJsonValue): WorkflowActor => {
89
+ if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot)) return {};
90
+ const actor = snapshot.actor;
91
+ return actor && typeof actor === "object" && !Array.isArray(actor) ? (actor as unknown as WorkflowActor) : {};
92
+ };
93
+
94
+ /**
95
+ * Maps what the executor reported onto how the run settles.
96
+ *
97
+ * `waiting` is not an outcome — the step parked itself and the run with it, so
98
+ * this only tells the coordinator the run is where it belongs.
99
+ */
100
+ const settle = (result: Awaited<ReturnType<typeof executeWorkflowPlan>>): WorkflowRunResult => {
101
+ switch (result.state) {
102
+ case "succeeded":
103
+ return { state: "succeeded", result: result.output ?? null, ...(result.message ? { message: result.message } : {}) };
104
+ case "waiting":
105
+ return { state: "waiting" };
106
+ case "failed":
107
+ return { state: "failed", error: result.error as unknown as WorkflowJsonValue };
108
+ case "needs_attention":
109
+ return { state: "needs_attention", error: result.error as unknown as WorkflowJsonValue };
110
+ case "canceled":
111
+ return { state: "canceled", ...(result.message ? { message: result.message } : {}) };
112
+ }
113
+ };
114
+
115
+ /** The identity the trace port names a run by, built from what the claim returned. */
116
+ const runIdentity = (claim: WorkflowRunClaim, mode: WorkflowInvocationMode): WorkflowRuntimeRunIdentity => ({
117
+ runId: claim.runId,
118
+ executionGeneration: claim.executionGeneration,
119
+ mode,
120
+ workflowId: claim.workflowId,
121
+ sourceHash: claim.sourceHash,
122
+ idempotencyKey: claim.idempotencyKey,
123
+ });
124
+
125
+ /**
126
+ * Announces a run's own transitions, around the step-level ones the executor emits.
127
+ *
128
+ * Best-effort on purpose: an observer that throws — a browser stream, a metric
129
+ * sink — must not be able to fail a run that has otherwise done its work.
130
+ */
131
+ const traceRun = async (trace: WorkflowTracePort | undefined, event: Parameters<WorkflowTracePort["emit"]>[0]): Promise<void> => {
132
+ if (!trace) return;
133
+ try {
134
+ await trace.emit(event);
135
+ } catch {
136
+ // Observing is not part of the run's contract.
137
+ }
138
+ };
139
+
140
+ /** Refuses a plan that was bound against any language other than the app's current module. */
141
+ const validateCurrentModule = async (
142
+ claim: WorkflowRunClaim,
143
+ module: DefinedWorkflowModule,
144
+ ): Promise<Extract<WorkflowRunResult, { state: "needs_attention" }> | null> => {
145
+ const manifestHash = await hashWorkflowJson(module.manifest);
146
+ if (
147
+ claim.plan.languageId === module.manifest.id &&
148
+ claim.plan.languageVersion === module.manifest.version &&
149
+ claim.plan.manifestHash === manifestHash
150
+ ) {
151
+ return null;
152
+ }
153
+ return {
154
+ state: "needs_attention",
155
+ error: {
156
+ code: "WORKFLOW_MODULE_MISMATCH",
157
+ message: "workflow version was bound against a different app module; publish it again with the current module",
158
+ retryable: false,
159
+ },
160
+ };
161
+ };
162
+
163
+ /**
164
+ * Claims one run and carries it as far as it goes.
165
+ *
166
+ * Returns `idle` when there was nothing to claim, which is what a polling loop
167
+ * uses to back off. A crash anywhere inside leaves the run claimable again once
168
+ * its lease expires, and the journal makes the resumed attempt skip whatever
169
+ * already completed.
170
+ */
171
+ export const runOneWorkflow = async (options: WorkflowWorkerOptions & { runId?: string }): Promise<WorkflowWorkerOutcome> => {
172
+ const db = options.db ?? sql;
173
+ const repository = createWorkflowRuntimeRepository({ db });
174
+ // Execute only. A dry run is a question and belongs to the dry-run path; the
175
+ // claim filter is what keeps this loop from answering it by doing the work.
176
+ const port = createWorkflowCoordinatorPort({ worker: options.worker, runId: options.runId, appId: options.appId, mode: "execute" });
177
+
178
+ const outcome = await coordinateWorkflowExecution<void, WorkflowRunClaim, WorkflowRunResult>({
179
+ input: undefined,
180
+ heartbeatMs: HEARTBEAT_MS,
181
+ port,
182
+ execute: async ({ claim, heartbeat }) => {
183
+ await traceRun(options.trace, { type: "run.started", run: runIdentity(claim, "execute") });
184
+ const incompatible = await validateCurrentModule(claim, options.module);
185
+ if (incompatible) return incompatible;
186
+ if (claim.consecutiveFailures >= WORKFLOW_RUN_MAX_CONSECUTIVE_FAILURES) {
187
+ const error = {
188
+ code: "WORKFLOW_RETRY_EXHAUSTED",
189
+ message: `workflow failed ${claim.consecutiveFailures} consecutive attempts`,
190
+ retryable: false,
191
+ };
192
+ await db`
193
+ UPDATE workflows.step_outcome AS s
194
+ SET state = 'failed',
195
+ outcome = ${{ mode: "execute", outcome: { state: "failed", error } }},
196
+ execution_generation = ${claim.executionGeneration},
197
+ finished_at = now(),
198
+ updated_at = now()
199
+ FROM workflows.run AS r
200
+ WHERE s.run_id = r.id
201
+ AND r.id = ${claim.runId}::uuid
202
+ AND r.execution_generation = ${claim.executionGeneration}
203
+ AND r.state = 'running'
204
+ AND s.state = 'running'
205
+ `;
206
+ return {
207
+ state: "failed",
208
+ error,
209
+ };
210
+ }
211
+ const result = await executeWorkflowPlan({
212
+ runId: claim.runId,
213
+ executionGeneration: claim.executionGeneration,
214
+ plan: claim.plan,
215
+ invocation: {
216
+ workflowId: claim.workflowId,
217
+ mode: "execute",
218
+ channel: "event",
219
+ actor: actorFromSnapshot(claim.authorization),
220
+ inputs: claim.inputs,
221
+ idempotencyKey: claim.idempotencyKey,
222
+ // Everything the plan reads under context.*, carried from the event.
223
+ context: claim.context,
224
+ // The occurrence's time, not the worker's: a replay after a crash has
225
+ // to see the same "now" the first attempt did, or a step that reads
226
+ // the date stops being a function of its inputs.
227
+ occurredAt: claim.occurredAt.toISOString(),
228
+ },
229
+ repository,
230
+ clock: { now: () => claim.occurredAt.toISOString() },
231
+ actions: options.actions,
232
+ ...(options.trace ? { trace: options.trace } : {}),
233
+ ...(options.values ? { values: options.values(claim) } : {}),
234
+ ...(options.maxLoopItems === undefined ? {} : { maxLoopItems: options.maxLoopItems }),
235
+ });
236
+ const runResult = settle(result);
237
+ // Parking deliberately releases the lease. Heartbeating after that
238
+ // misdiagnoses a successful park as a lost claim.
239
+ if (runResult.state !== "waiting") await heartbeat();
240
+ return runResult;
241
+ },
242
+ });
243
+
244
+ switch (outcome.state) {
245
+ case "idle":
246
+ return { state: "idle" };
247
+ case "finished":
248
+ // After the coordinator persisted it: an observer that re-reads the run
249
+ // has to find the state this announces, not the one before it.
250
+ await traceRun(options.trace, {
251
+ type: "run.finished",
252
+ run: runIdentity(outcome.claim, "execute"),
253
+ state: outcome.result.state,
254
+ });
255
+ return { state: "finished", runId: outcome.claim.runId, result: outcome.result };
256
+ case "released":
257
+ case "retry":
258
+ await traceRun(options.trace, { type: "run.finished", run: runIdentity(outcome.claim, "execute"), state: "released" });
259
+ return { state: "released", runId: outcome.claim.runId, error: outcome.error };
260
+ case "stale":
261
+ return { state: "lost", runId: outcome.claim.runId };
262
+ case "canceled": {
263
+ const finished = await finishWorkflowRunCancel(outcome.claim, { db });
264
+ if (finished.state === "stale") return { state: "lost", runId: outcome.claim.runId };
265
+ await traceRun(options.trace, {
266
+ type: "run.finished",
267
+ run: runIdentity(outcome.claim, "execute"),
268
+ state: finished.result.state,
269
+ });
270
+ return { state: "finished", runId: outcome.claim.runId, result: finished.result };
271
+ }
272
+ }
273
+ };
274
+
275
+ export type WorkflowDryRunWorkerOptions = Omit<WorkflowWorkerOptions, "actions"> & {
276
+ /** The same declarations, planning instead of acting. */
277
+ actions: WorkflowDryRunActionPort;
278
+ };
279
+
280
+ export type WorkflowDryRunOutcome =
281
+ | { state: "idle" }
282
+ | { state: "finished"; runId: string; result: WorkflowDryRunResult }
283
+ | { state: "released"; runId: string; error: unknown }
284
+ | { state: "lost"; runId: string };
285
+
286
+ /**
287
+ * How a dry run reports: a plan is not a run that happened.
288
+ *
289
+ * `planned` and a terminal `succeeded` both mean the plan holds; anything that
290
+ * could not be determined is a failure of the *question*, not of the work, and
291
+ * shows up as the run's error so the caller sees which step could not be
292
+ * planned rather than an empty answer.
293
+ */
294
+ const settleDryRun = (result: WorkflowDryRunResult): WorkflowRunResult => {
295
+ if (result.state === "planned") return { state: "succeeded", result: { effects: result.effects, output: result.output ?? null } };
296
+ if (result.state === "canceled") return { state: "canceled", ...(result.message ? { message: result.message } : {}) };
297
+ if (result.state === "terminal" && result.status === "succeeded") {
298
+ return { state: "succeeded", result: { effects: result.effects }, ...(result.message ? { message: result.message } : {}) };
299
+ }
300
+ const reason = result.state === "terminal" ? (result.message ?? "the plan would fail") : result.reason;
301
+ return {
302
+ state: "failed",
303
+ error: { code: `WORKFLOW_DRY_RUN_${result.state === "terminal" ? "TERMINAL" : result.state.toUpperCase()}`, message: reason },
304
+ };
305
+ };
306
+
307
+ /**
308
+ * The same loop for the question rather than the work.
309
+ *
310
+ * A dry run is a real run with real bookkeeping — it is leased, journaled and
311
+ * recoverable — because a preflight over ten thousand records takes as long as
312
+ * the work would and must survive the same crashes. Only the claim's mode and
313
+ * the port it drives differ, which is exactly the distinction that keeps a dry
314
+ * run from performing what it was meant to describe.
315
+ */
316
+ export const dryRunOneWorkflow = async (options: WorkflowDryRunWorkerOptions & { runId?: string }): Promise<WorkflowDryRunOutcome> => {
317
+ const db = options.db ?? sql;
318
+ const repository = createWorkflowRuntimeRepository({ db });
319
+ const port = createWorkflowCoordinatorPort({ worker: options.worker, runId: options.runId, appId: options.appId, mode: "dryRun" });
320
+
321
+ let planned: WorkflowDryRunResult | null = null;
322
+ const outcome = await coordinateWorkflowExecution<void, WorkflowRunClaim, WorkflowRunResult>({
323
+ input: undefined,
324
+ heartbeatMs: HEARTBEAT_MS,
325
+ port,
326
+ execute: async ({ claim, heartbeat }) => {
327
+ await traceRun(options.trace, { type: "run.started", run: runIdentity(claim, "dryRun") });
328
+ const incompatible = await validateCurrentModule(claim, options.module);
329
+ if (incompatible) {
330
+ planned = {
331
+ state: "terminal",
332
+ status: "failed",
333
+ message: String((incompatible.error as { message?: unknown }).message ?? "workflow module mismatch"),
334
+ effects: [],
335
+ };
336
+ return incompatible;
337
+ }
338
+ planned = await dryRunWorkflowPlan({
339
+ runId: claim.runId,
340
+ executionGeneration: claim.executionGeneration,
341
+ plan: claim.plan,
342
+ invocation: {
343
+ workflowId: claim.workflowId,
344
+ mode: "dryRun",
345
+ channel: "event",
346
+ actor: actorFromSnapshot(claim.authorization),
347
+ inputs: claim.inputs,
348
+ idempotencyKey: claim.idempotencyKey,
349
+ context: claim.context,
350
+ occurredAt: claim.occurredAt.toISOString(),
351
+ },
352
+ repository,
353
+ clock: { now: () => claim.occurredAt.toISOString() },
354
+ actions: options.actions,
355
+ ...(options.trace ? { trace: options.trace } : {}),
356
+ ...(options.values ? { values: options.values(claim) } : {}),
357
+ ...(options.maxLoopItems === undefined ? {} : { maxLoopItems: options.maxLoopItems }),
358
+ });
359
+ await heartbeat();
360
+ return settleDryRun(planned);
361
+ },
362
+ });
363
+
364
+ switch (outcome.state) {
365
+ case "idle":
366
+ return { state: "idle" };
367
+ case "finished":
368
+ await traceRun(options.trace, {
369
+ type: "run.finished",
370
+ run: runIdentity(outcome.claim, "dryRun"),
371
+ state: outcome.result.state,
372
+ });
373
+ return { state: "finished", runId: outcome.claim.runId, result: planned as unknown as WorkflowDryRunResult };
374
+ case "released":
375
+ case "retry":
376
+ await traceRun(options.trace, { type: "run.finished", run: runIdentity(outcome.claim, "dryRun"), state: "released" });
377
+ return { state: "released", runId: outcome.claim.runId, error: outcome.error };
378
+ case "stale":
379
+ return { state: "lost", runId: outcome.claim.runId };
380
+ case "canceled": {
381
+ const finished = await finishWorkflowRunCancel(outcome.claim, { db });
382
+ if (finished.state === "stale") return { state: "lost", runId: outcome.claim.runId };
383
+ return { state: "finished", runId: outcome.claim.runId, result: planned as unknown as WorkflowDryRunResult };
384
+ }
385
+ }
386
+ };
387
+
388
+ export type WorkflowTickResult = {
389
+ /** Events turned into runs this tick. */
390
+ dispatched: number;
391
+ /** Parked runs whose deadline passed. */
392
+ woken: number;
393
+ /** Runs carried to an outcome. */
394
+ executed: number;
395
+ failed: number;
396
+ };
397
+
398
+ /**
399
+ * One pass of everything the kernel owes a workflow: dispatch what happened,
400
+ * wake what was waiting, then run what is ready.
401
+ *
402
+ * In that order deliberately. Dispatching first means an event that arrived a
403
+ * moment ago is executed in the same tick rather than the next one, which is
404
+ * the difference between a workflow that feels immediate and one that always
405
+ * lags a poll interval behind.
406
+ */
407
+ export const tickWorkflows = async (options: WorkflowWorkerOptions & { maxRuns?: number }): Promise<WorkflowTickResult> => {
408
+ const db = options.db ?? sql;
409
+ const dispatch = await dispatchPendingWorkflowEvents(100, { appId: options.appId, db });
410
+ await finishExpiredWorkflowRunCancels(100, { appId: options.appId, db });
411
+ const woken = await wakeExpiredWorkflowRuns(100, { appId: options.appId, db });
412
+
413
+ let executed = 0;
414
+ let failed = 0;
415
+ const maxRuns = options.maxRuns ?? 25;
416
+ for (let index = 0; index < maxRuns; index += 1) {
417
+ const outcome = await runOneWorkflow(options);
418
+ if (outcome.state === "idle") break;
419
+ executed += 1;
420
+ if (outcome.state === "released") failed += 1;
421
+ }
422
+
423
+ return { dispatched: dispatch.dispatched, woken: woken.length, executed, failed };
424
+ };
425
+
426
+ /** Whether there is anything for a worker to do, without claiming it. */
427
+ export const hasClaimableWorkflowRun = async (options: { appId?: string; db?: SQL } = {}): Promise<boolean> => {
428
+ const db = options.db ?? sql;
429
+ const [row] = await db<{ exists: boolean }[]>`
430
+ SELECT EXISTS (
431
+ SELECT 1 FROM workflows.run
432
+ WHERE state IN ('queued', 'running') AND claimable_at < now() AND cancel_requested_at IS NULL
433
+ AND (${options.appId ?? null}::text IS NULL OR app_id = ${options.appId ?? null})
434
+ ) AS exists
435
+ `;
436
+ return Boolean(row?.exists);
437
+ };
438
+
439
+ export { claimWorkflowRun };
@@ -0,0 +1,72 @@
1
+ import type { WorkflowBoundPlan, WorkflowDependency, WorkflowIr } from "../contracts";
2
+ import { bindWorkflow, compileWorkflow } from "../language";
3
+ import {
4
+ executeWorkflowPlan,
5
+ type WorkflowExecutionResult,
6
+ type WorkflowRuntimeRepositoryPort,
7
+ type WorkflowRuntimeRunIdentity,
8
+ type WorkflowRuntimeStepIdentity,
9
+ type WorkflowRuntimeStepResult,
10
+ } from "../runtime";
11
+ import { type WorkflowProcessFixture, workflowProcessWorkflows } from "./process-fixtures";
12
+
13
+ class FixtureRepository implements WorkflowRuntimeRepositoryPort {
14
+ readonly finished: Array<{ step: WorkflowRuntimeStepIdentity; result: WorkflowRuntimeStepResult }> = [];
15
+
16
+ async heartbeat(_run: WorkflowRuntimeRunIdentity) {
17
+ return { state: "active" } as const;
18
+ }
19
+
20
+ async restoreStepOutcome(_step: WorkflowRuntimeStepIdentity) {
21
+ return null;
22
+ }
23
+
24
+ async startStep(_step: WorkflowRuntimeStepIdentity) {}
25
+
26
+ async finishStep(step: WorkflowRuntimeStepIdentity, result: WorkflowRuntimeStepResult) {
27
+ this.finished.push({ step, result });
28
+ }
29
+
30
+ async parkStep(step: WorkflowRuntimeStepIdentity, dependency: WorkflowDependency) {
31
+ this.finished.push({ step, result: { mode: "execute", outcome: { state: "waiting", dependency } } });
32
+ }
33
+ }
34
+
35
+ export type WorkflowProcessFixtureResult = {
36
+ ir: WorkflowIr;
37
+ plan: WorkflowBoundPlan;
38
+ execution: WorkflowExecutionResult;
39
+ finishedSteps: Array<{ step: WorkflowRuntimeStepIdentity; result: WorkflowRuntimeStepResult }>;
40
+ };
41
+
42
+ export const runWorkflowProcessFixture = async (fixture: WorkflowProcessFixture): Promise<WorkflowProcessFixtureResult> => {
43
+ const compiled = await compileWorkflow(fixture.source, workflowProcessWorkflows);
44
+ if (!compiled.ok) {
45
+ throw new Error(compiled.diagnostics.map((diagnostic) => `${diagnostic.path.join(".")}: ${diagnostic.message}`).join("\n"));
46
+ }
47
+ const plan = await bindWorkflow(compiled.ir, workflowProcessWorkflows, () => ({
48
+ catalog: fixture.catalog,
49
+ bindings: fixture.bindings,
50
+ }));
51
+ const repository = new FixtureRepository();
52
+ const execution = await executeWorkflowPlan({
53
+ runId: `fixture:${fixture.id}`,
54
+ executionGeneration: 1,
55
+ clock: { now: () => fixture.invocation.occurredAt },
56
+ plan,
57
+ invocation: fixture.invocation,
58
+ repository,
59
+ actions: {
60
+ get: (action) =>
61
+ action === "capture"
62
+ ? {
63
+ execute: async (context, step) => ({
64
+ state: "completed",
65
+ output: await context.evaluate(step.config.value ?? null),
66
+ }),
67
+ }
68
+ : undefined,
69
+ },
70
+ });
71
+ return { ir: compiled.ir, plan, execution, finishedSteps: repository.finished };
72
+ };
@@ -0,0 +1,11 @@
1
+ export { runWorkflowProcessFixture, type WorkflowProcessFixtureResult } from "./conformance";
2
+ export {
3
+ bulkLauncherProcessFixture,
4
+ directOnlyProcessFixture,
5
+ recordEventProcessFixture,
6
+ scannerLauncherProcessFixture,
7
+ scheduleProcessFixture,
8
+ type WorkflowProcessFixture,
9
+ workflowProcessFixtures,
10
+ workflowProcessManifest,
11
+ } from "./process-fixtures";