create-windy 0.3.2 → 0.3.5

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 (343) hide show
  1. package/README.md +27 -9
  2. package/dist/cli.js +825 -117
  3. package/package.json +1 -1
  4. package/template/.agents/skills/windy-business-module/SKILL.md +16 -3
  5. package/template/.windy-template.json +2 -2
  6. package/template/AGENTS.md +4 -0
  7. package/template/README.md +18 -11
  8. package/template/_gitignore +1 -0
  9. package/template/apps/agent-server/README.md +2 -0
  10. package/template/apps/agent-server/pyproject.toml +8 -0
  11. package/template/apps/agent-server/src/southwind_agent_server/adk_adapter.py +87 -10
  12. package/template/apps/agent-server/src/southwind_agent_server/adk_model.py +18 -3
  13. package/template/apps/agent-server/src/southwind_agent_server/adk_tools.py +78 -11
  14. package/template/apps/agent-server/src/southwind_agent_server/app.py +120 -115
  15. package/template/apps/agent-server/src/southwind_agent_server/artifact_input.py +153 -0
  16. package/template/apps/agent-server/src/southwind_agent_server/config.py +29 -0
  17. package/template/apps/agent-server/src/southwind_agent_server/contracts.py +81 -1
  18. package/template/apps/agent-server/src/southwind_agent_server/deterministic.py +8 -0
  19. package/template/apps/agent-server/src/southwind_agent_server/engine.py +28 -0
  20. package/template/apps/agent-server/src/southwind_agent_server/fingerprint.py +41 -0
  21. package/template/apps/agent-server/src/southwind_agent_server/http_helpers.py +62 -0
  22. package/template/apps/agent-server/src/southwind_agent_server/lease.py +15 -0
  23. package/template/apps/agent-server/src/southwind_agent_server/multimodal.py +76 -0
  24. package/template/apps/agent-server/src/southwind_agent_server/openai_engine.py +112 -20
  25. package/template/apps/agent-server/src/southwind_agent_server/openai_provider.py +14 -1
  26. package/template/apps/agent-server/src/southwind_agent_server/postgres_store.py +329 -0
  27. package/template/apps/agent-server/src/southwind_agent_server/postgres_store_backend.py +332 -0
  28. package/template/apps/agent-server/src/southwind_agent_server/provider_governance.py +64 -3
  29. package/template/apps/agent-server/src/southwind_agent_server/runtime_factory.py +68 -0
  30. package/template/apps/agent-server/src/southwind_agent_server/service.py +218 -56
  31. package/template/apps/agent-server/src/southwind_agent_server/signal_events.py +36 -0
  32. package/template/apps/agent-server/src/southwind_agent_server/sqlite_catalog.py +107 -0
  33. package/template/apps/agent-server/src/southwind_agent_server/sqlite_schema.py +65 -0
  34. package/template/apps/agent-server/src/southwind_agent_server/sqlite_serialization.py +16 -0
  35. package/template/apps/agent-server/src/southwind_agent_server/sqlite_store.py +89 -57
  36. package/template/apps/agent-server/src/southwind_agent_server/store.py +105 -2
  37. package/template/apps/agent-server/src/southwind_agent_server/store_cipher.py +40 -0
  38. package/template/apps/agent-server/src/southwind_agent_server/telemetry.py +89 -0
  39. package/template/apps/agent-server/src/southwind_agent_server/tool_host.py +72 -0
  40. package/template/apps/agent-server/tests/test_adk_adapter.py +6 -2
  41. package/template/apps/agent-server/tests/test_api.py +24 -1
  42. package/template/apps/agent-server/tests/test_artifact_input.py +110 -0
  43. package/template/apps/agent-server/tests/test_config.py +4 -0
  44. package/template/apps/agent-server/tests/test_fingerprint.py +10 -0
  45. package/template/apps/agent-server/tests/test_multimodal.py +51 -0
  46. package/template/apps/agent-server/tests/test_openai_provider.py +6 -2
  47. package/template/apps/agent-server/tests/test_postgres_store.py +131 -0
  48. package/template/apps/agent-server/tests/test_provider_governance.py +61 -0
  49. package/template/apps/agent-server/tests/test_sqlite_store.py +95 -10
  50. package/template/apps/agent-server/tests/test_telemetry.py +25 -0
  51. package/template/apps/agent-server/tests/test_tool_approval.py +28 -0
  52. package/template/apps/agent-server/uv.lock +165 -0
  53. package/template/apps/document-server/Dockerfile +19 -0
  54. package/template/apps/document-server/pyproject.toml +43 -0
  55. package/template/apps/document-server/src/southwind_document_server/__init__.py +1 -0
  56. package/template/apps/document-server/src/southwind_document_server/adapter.py +337 -0
  57. package/template/apps/document-server/src/southwind_document_server/app.py +151 -0
  58. package/template/apps/document-server/src/southwind_document_server/config.py +94 -0
  59. package/template/apps/document-server/src/southwind_document_server/contracts.py +103 -0
  60. package/template/apps/document-server/src/southwind_document_server/service.py +149 -0
  61. package/template/apps/document-server/src/southwind_document_server/store.py +185 -0
  62. package/template/apps/document-server/tests/__init__.py +1 -0
  63. package/template/apps/document-server/tests/test_config.py +42 -0
  64. package/template/apps/document-server/tests/test_service.py +140 -0
  65. package/template/apps/document-server/uv.lock +430 -0
  66. package/template/apps/embedding-server/Dockerfile +17 -0
  67. package/template/apps/embedding-server/pyproject.toml +44 -0
  68. package/template/apps/embedding-server/src/southwind_embedding_server/__init__.py +1 -0
  69. package/template/apps/embedding-server/src/southwind_embedding_server/adapter.py +254 -0
  70. package/template/apps/embedding-server/src/southwind_embedding_server/app.py +119 -0
  71. package/template/apps/embedding-server/src/southwind_embedding_server/config.py +120 -0
  72. package/template/apps/embedding-server/src/southwind_embedding_server/contracts.py +106 -0
  73. package/template/apps/embedding-server/src/southwind_embedding_server/error.py +6 -0
  74. package/template/apps/embedding-server/src/southwind_embedding_server/image_input.py +87 -0
  75. package/template/apps/embedding-server/tests/__init__.py +1 -0
  76. package/template/apps/embedding-server/tests/test_adapter.py +92 -0
  77. package/template/apps/embedding-server/tests/test_config.py +38 -0
  78. package/template/apps/embedding-server/uv.lock +449 -0
  79. package/template/apps/server/src/agent/background-operation-port.ts +37 -0
  80. package/template/apps/server/src/agent/background-operation.test.ts +231 -0
  81. package/template/apps/server/src/agent/background-operation.ts +252 -0
  82. package/template/apps/server/src/agent/drizzle-run-binding-repository.ts +114 -0
  83. package/template/apps/server/src/agent/execution-grants.ts +7 -1
  84. package/template/apps/server/src/agent/fake-runtime.test-support.ts +43 -0
  85. package/template/apps/server/src/agent/idempotency-key.test.ts +28 -0
  86. package/template/apps/server/src/agent/idempotency-key.ts +11 -0
  87. package/template/apps/server/src/agent/remote-runtime.test.ts +115 -1
  88. package/template/apps/server/src/agent/remote-runtime.ts +173 -5
  89. package/template/apps/server/src/agent/run-authorization.test-support.ts +9 -0
  90. package/template/apps/server/src/agent/run-authorization.test.ts +126 -0
  91. package/template/apps/server/src/agent/run-authorization.ts +180 -0
  92. package/template/apps/server/src/agent/run-binding-repository.ts +93 -0
  93. package/template/apps/server/src/agent/run-contracts.ts +88 -1
  94. package/template/apps/server/src/agent/run-definition-support.ts +58 -0
  95. package/template/apps/server/src/agent/run-errors.ts +25 -0
  96. package/template/apps/server/src/agent/run-facade.test.ts +57 -46
  97. package/template/apps/server/src/agent/run-facade.ts +185 -65
  98. package/template/apps/server/src/agent/run-release.ts +78 -0
  99. package/template/apps/server/src/agent/run-route-input.ts +169 -0
  100. package/template/apps/server/src/agent/run-routes.ts +63 -76
  101. package/template/apps/server/src/agent/runtime-bootstrap.ts +169 -13
  102. package/template/apps/server/src/agent/sandbox-client.ts +200 -0
  103. package/template/apps/server/src/agent/sandbox-protocol.ts +76 -0
  104. package/template/apps/server/src/agent/sandbox-registry.ts +205 -0
  105. package/template/apps/server/src/agent/sandbox-runtime.test.ts +137 -0
  106. package/template/apps/server/src/agent/sandbox-worker-server.ts +195 -0
  107. package/template/apps/server/src/agent/sandbox-worker.ts +12 -0
  108. package/template/apps/server/src/agent/tool-approval.test.ts +75 -0
  109. package/template/apps/server/src/agent/tool-approval.ts +115 -0
  110. package/template/apps/server/src/agent/tool-audit.ts +62 -0
  111. package/template/apps/server/src/agent/tool-host.test.ts +39 -11
  112. package/template/apps/server/src/agent/tool-host.ts +184 -17
  113. package/template/apps/server/src/agent/tool-registry.ts +52 -47
  114. package/template/apps/server/src/agent/vision-facade.test.ts +256 -0
  115. package/template/apps/server/src/agent/vision-input.ts +120 -0
  116. package/template/apps/server/src/ai-artifact/codec.test.ts +44 -0
  117. package/template/apps/server/src/ai-artifact/codec.ts +137 -0
  118. package/template/apps/server/src/ai-artifact/routes.ts +37 -0
  119. package/template/apps/server/src/ai-artifact/runtime.ts +205 -0
  120. package/template/apps/server/src/application-services.ts +62 -2
  121. package/template/apps/server/src/auth/reauthentication.test.ts +40 -0
  122. package/template/apps/server/src/auth/reauthentication.ts +19 -0
  123. package/template/apps/server/src/auth/runtime.ts +9 -1
  124. package/template/apps/server/src/data-access/context.ts +6 -4
  125. package/template/apps/server/src/data-access/scope-snapshot.ts +27 -0
  126. package/template/apps/server/src/data-governance/export/approval-port.ts +6 -3
  127. package/template/apps/server/src/data-governance/export/authorization-port.test.ts +23 -4
  128. package/template/apps/server/src/data-governance/export/authorization-port.ts +89 -25
  129. package/template/apps/server/src/data-governance/export/drizzle-repository.integration.test.ts +2 -0
  130. package/template/apps/server/src/data-governance/export/drizzle-repository.ts +1 -0
  131. package/template/apps/server/src/data-governance/export/job-runtime.test.ts +15 -4
  132. package/template/apps/server/src/data-governance/export/job-runtime.ts +68 -15
  133. package/template/apps/server/src/data-governance/export/ndjson-stream.ts +25 -6
  134. package/template/apps/server/src/data-governance/export/postgres-recovery.integration.test.ts +4 -2
  135. package/template/apps/server/src/data-governance/export/renderer-job-runtime.test.ts +235 -0
  136. package/template/apps/server/src/data-governance/export/renderer-runtime.ts +293 -0
  137. package/template/apps/server/src/data-governance/export/renderer-selection.test.ts +67 -0
  138. package/template/apps/server/src/data-governance/export/repository.test.ts +2 -0
  139. package/template/apps/server/src/data-governance/export/request-fingerprint.test.ts +13 -0
  140. package/template/apps/server/src/data-governance/export/request-fingerprint.ts +2 -0
  141. package/template/apps/server/src/data-governance/export/routes.ts +11 -1
  142. package/template/apps/server/src/data-governance/export/scope-snapshot.ts +6 -18
  143. package/template/apps/server/src/data-governance/export/service-support.ts +188 -0
  144. package/template/apps/server/src/data-governance/export/service.ts +90 -113
  145. package/template/apps/server/src/data-governance/export/source.ts +101 -0
  146. package/template/apps/server/src/data-governance/export/types.ts +27 -0
  147. package/template/apps/server/src/document/contracts.ts +76 -0
  148. package/template/apps/server/src/document/facade.ts +339 -0
  149. package/template/apps/server/src/document/remote-runtime.test.ts +96 -0
  150. package/template/apps/server/src/document/remote-runtime.ts +223 -0
  151. package/template/apps/server/src/document/routes.ts +193 -0
  152. package/template/apps/server/src/embedding/contracts.ts +83 -0
  153. package/template/apps/server/src/embedding/error.ts +10 -0
  154. package/template/apps/server/src/embedding/facade.ts +217 -0
  155. package/template/apps/server/src/embedding/input.ts +172 -0
  156. package/template/apps/server/src/embedding/remote-runtime.test.ts +66 -0
  157. package/template/apps/server/src/embedding/remote-runtime.ts +218 -0
  158. package/template/apps/server/src/embedding/routes.ts +152 -0
  159. package/template/apps/server/src/index.ts +40 -43
  160. package/template/apps/server/src/jobs/artifact-service.test.ts +14 -2
  161. package/template/apps/server/src/jobs/artifact-service.ts +10 -0
  162. package/template/apps/server/src/jobs/drizzle-execution-repository.ts +17 -18
  163. package/template/apps/server/src/jobs/drizzle-execution.integration.test.ts +19 -1
  164. package/template/apps/server/src/jobs/drizzle-mappers.ts +4 -1
  165. package/template/apps/server/src/jobs/drizzle-recovery-repository.ts +49 -2
  166. package/template/apps/server/src/jobs/drizzle-repository.ts +1 -0
  167. package/template/apps/server/src/jobs/execution-repository.ts +17 -0
  168. package/template/apps/server/src/jobs/execution.test.ts +34 -0
  169. package/template/apps/server/src/jobs/in-memory-control.ts +135 -0
  170. package/template/apps/server/src/jobs/in-memory-execution.ts +34 -65
  171. package/template/apps/server/src/jobs/legacy-service-runtime.ts +5 -2
  172. package/template/apps/server/src/jobs/repository.ts +3 -0
  173. package/template/apps/server/src/jobs/worker.ts +80 -41
  174. package/template/apps/server/src/module-host/ai-operation-port.ts +170 -0
  175. package/template/apps/server/src/module-host/durable-context.test.ts +312 -0
  176. package/template/apps/server/src/module-host/durable-context.ts +124 -0
  177. package/template/apps/server/src/module-host/durable-handler-registration.ts +72 -0
  178. package/template/apps/server/src/module-host/governed-export-route-support.ts +171 -0
  179. package/template/apps/server/src/module-host/governed-export-routes.test.ts +327 -0
  180. package/template/apps/server/src/module-host/governed-export-routes.ts +238 -0
  181. package/template/apps/server/src/module-host/governed-renderer-registration.test.ts +247 -0
  182. package/template/apps/server/src/module-host/governed-resource-routes.ts +331 -0
  183. package/template/apps/server/src/module-host/governed-resources.test.ts +344 -0
  184. package/template/apps/server/src/module-host/governed-resources.ts +203 -0
  185. package/template/apps/server/src/module-host/http-runtime.ts +95 -0
  186. package/template/apps/server/src/module-host/initialize-contracts.ts +14 -0
  187. package/template/apps/server/src/module-host/initialize.test.ts +49 -1
  188. package/template/apps/server/src/module-host/initialize.ts +39 -16
  189. package/template/apps/server/src/module-host/job-port.test.ts +262 -0
  190. package/template/apps/server/src/module-host/job-port.ts +334 -0
  191. package/template/apps/server/src/module-host/request-context.test.ts +88 -2
  192. package/template/apps/server/src/module-host/request-context.ts +56 -22
  193. package/template/apps/server/src/module-host/route-installer.test.ts +10 -0
  194. package/template/apps/server/src/module-host/route-installer.ts +36 -1
  195. package/template/apps/server/src/module-host/sandbox-tool-registration.ts +42 -0
  196. package/template/apps/server/src/module-host/tool-handlers.ts +67 -31
  197. package/template/apps/server/src/module-host.ts +1 -0
  198. package/template/apps/server/src/module-runtime-validation.ts +7 -3
  199. package/template/apps/server/src/speech/contracts.ts +76 -0
  200. package/template/apps/server/src/speech/facade.ts +316 -0
  201. package/template/apps/server/src/speech/remote-runtime.test.ts +88 -0
  202. package/template/apps/server/src/speech/remote-runtime.ts +227 -0
  203. package/template/apps/server/src/speech/routes.ts +180 -0
  204. package/template/apps/server/src/system/actor.ts +39 -0
  205. package/template/apps/server/src/system/route-helpers.ts +1 -0
  206. package/template/apps/server/src/workflow/service.ts +2 -1
  207. package/template/apps/speech-server/Dockerfile +22 -0
  208. package/template/apps/speech-server/pyproject.toml +43 -0
  209. package/template/apps/speech-server/src/southwind_speech_server/__init__.py +1 -0
  210. package/template/apps/speech-server/src/southwind_speech_server/adapter.py +249 -0
  211. package/template/apps/speech-server/src/southwind_speech_server/app.py +183 -0
  212. package/template/apps/speech-server/src/southwind_speech_server/config.py +100 -0
  213. package/template/apps/speech-server/src/southwind_speech_server/contracts.py +103 -0
  214. package/template/apps/speech-server/src/southwind_speech_server/errors.py +6 -0
  215. package/template/apps/speech-server/src/southwind_speech_server/normalizer.py +108 -0
  216. package/template/apps/speech-server/src/southwind_speech_server/output_validation.py +128 -0
  217. package/template/apps/speech-server/src/southwind_speech_server/service.py +162 -0
  218. package/template/apps/speech-server/src/southwind_speech_server/store.py +209 -0
  219. package/template/apps/speech-server/tests/__init__.py +1 -0
  220. package/template/apps/speech-server/tests/test_config.py +33 -0
  221. package/template/apps/speech-server/tests/test_service.py +154 -0
  222. package/template/apps/speech-server/uv.lock +430 -0
  223. package/template/apps/web/src/app/business-routes.ts +22 -0
  224. package/template/apps/web/src/components/common/AccessSnapshotBoundary.vue +112 -0
  225. package/template/apps/web/src/components/common/AccessSnapshotBoundary.webtest.ts +130 -0
  226. package/template/apps/web/src/components/common/AccessSnapshotNotice.vue +117 -0
  227. package/template/apps/web/src/components/common/FormDialog.vue +6 -0
  228. package/template/apps/web/src/components/common/ModalLayout.webtest.ts +13 -0
  229. package/template/apps/web/src/composables/access-snapshot-change.ts +131 -0
  230. package/template/apps/web/src/composables/access-snapshot-change.webtest.ts +103 -0
  231. package/template/apps/web/src/composables/useAccessSnapshot.ts +48 -2
  232. package/template/apps/web/src/composables/useAccessSnapshot.webtest.ts +54 -0
  233. package/template/apps/web/src/composables/useAccessSnapshotMonitor.ts +42 -0
  234. package/template/apps/web/src/layout/AppShell.vue +28 -25
  235. package/template/apps/web/src/layout/AppShell.webtest.ts +1 -0
  236. package/template/apps/web/src/router/business-routes.ts +68 -0
  237. package/template/apps/web/src/router/business-routes.webtest.ts +85 -0
  238. package/template/apps/web/src/router/index.ts +7 -0
  239. package/template/apps/web/src/router/shared-scaffold-neutrality.webtest.ts +12 -0
  240. package/template/apps/web/src/services/access-api.ts +5 -1
  241. package/template/apps/web/src/services/access-api.webtest.ts +35 -0
  242. package/template/docker-compose.yml +127 -0
  243. package/template/docs/architecture/ai-egress.md +12 -9
  244. package/template/docs/architecture/ai-runtime.md +23 -11
  245. package/template/docs/architecture/durable-jobs.md +25 -3
  246. package/template/docs/architecture/foundation-infrastructure.md +163 -0
  247. package/template/docs/architecture/module-host.md +115 -0
  248. package/template/docs/architecture/server-module-interface.md +39 -0
  249. package/template/docs/architecture/web-access-snapshot-resilience.md +60 -0
  250. package/template/docs/development/business-web-routes.md +66 -0
  251. package/template/docs/development/custom-login-page.md +3 -3
  252. package/template/docs/development/module-lifecycle.md +71 -0
  253. package/template/docs/operations/observability.md +5 -1
  254. package/template/docs/platform/agent-runtime.md +21 -11
  255. package/template/docs/platform/agent-tool-sandbox.md +58 -0
  256. package/template/docs/platform/agent-tools.md +5 -0
  257. package/template/docs/platform/document-ai-runtime.md +204 -0
  258. package/template/docs/platform/governed-export-renderers.md +132 -0
  259. package/template/docs/platform/navigation-registry.md +5 -0
  260. package/template/docs/platform/organization-membership.md +2 -1
  261. package/template/docs/platform/speech-ai-runtime.md +156 -0
  262. package/template/docs/platform/system-crud-api.md +7 -1
  263. package/template/docs/platform/vision-ai-runtime.md +97 -0
  264. package/template/docs/platform/web-system-crud.md +6 -0
  265. package/template/package.json +16 -2
  266. package/template/packages/ai-client/index.ts +4 -0
  267. package/template/packages/ai-client/package.json +5 -2
  268. package/template/packages/ai-client/src/agent/client/artifact.ts +26 -0
  269. package/template/packages/ai-client/src/agent/client/attach.test.ts +124 -0
  270. package/template/packages/ai-client/src/agent/client/client.ts +119 -7
  271. package/template/packages/ai-client/src/agent/client/run.ts +14 -0
  272. package/template/packages/ai-client/src/agent/client/types.ts +18 -0
  273. package/template/packages/ai-client/src/agent/client/vision.test.ts +53 -0
  274. package/template/packages/ai-client/src/agent/contracts/contracts.test.ts +1 -0
  275. package/template/packages/ai-client/src/agent/contracts/events.ts +33 -0
  276. package/template/packages/ai-client/src/agent/contracts/runs.ts +71 -0
  277. package/template/packages/ai-client/src/agent/index.ts +1 -0
  278. package/template/packages/ai-client/src/artifact-ref.ts +8 -0
  279. package/template/packages/ai-client/src/document/client.test.ts +114 -0
  280. package/template/packages/ai-client/src/document/client.ts +134 -0
  281. package/template/packages/ai-client/src/document/contracts.ts +152 -0
  282. package/template/packages/ai-client/src/document/error.ts +43 -0
  283. package/template/packages/ai-client/src/document/index.ts +3 -0
  284. package/template/packages/ai-client/src/document/run.ts +104 -0
  285. package/template/packages/ai-client/src/document/validation.ts +115 -0
  286. package/template/packages/ai-client/src/embedding/client.test.ts +77 -0
  287. package/template/packages/ai-client/src/embedding/client.ts +96 -0
  288. package/template/packages/ai-client/src/embedding/contracts.ts +46 -0
  289. package/template/packages/ai-client/src/embedding/error.ts +61 -0
  290. package/template/packages/ai-client/src/embedding/index.ts +3 -0
  291. package/template/packages/ai-client/src/embedding/validation.ts +117 -0
  292. package/template/packages/ai-client/src/speech/client.test.ts +135 -0
  293. package/template/packages/ai-client/src/speech/client.ts +104 -0
  294. package/template/packages/ai-client/src/speech/contracts.ts +129 -0
  295. package/template/packages/ai-client/src/speech/error.ts +43 -0
  296. package/template/packages/ai-client/src/speech/index.ts +3 -0
  297. package/template/packages/ai-client/src/speech/run.ts +133 -0
  298. package/template/packages/ai-client/src/speech/validation.ts +132 -0
  299. package/template/packages/config/package.json +2 -2
  300. package/template/packages/database/drizzle/0039_early_ezekiel_stane.sql +1 -0
  301. package/template/packages/database/drizzle/0040_colossal_baron_strucker.sql +63 -0
  302. package/template/packages/database/drizzle/0041_yummy_prowler.sql +21 -0
  303. package/template/packages/database/drizzle/0042_puzzling_rumiko_fujikawa.sql +27 -0
  304. package/template/packages/database/drizzle/meta/0039_snapshot.json +7755 -0
  305. package/template/packages/database/drizzle/meta/0040_snapshot.json +8150 -0
  306. package/template/packages/database/drizzle/meta/0041_snapshot.json +8323 -0
  307. package/template/packages/database/drizzle/meta/0042_snapshot.json +8541 -0
  308. package/template/packages/database/drizzle/meta/_journal.json +28 -0
  309. package/template/packages/database/package.json +2 -2
  310. package/template/packages/database/src/agent-runtime-schema.test.ts +54 -0
  311. package/template/packages/database/src/schema/agent-runtime.ts +149 -0
  312. package/template/packages/database/src/schema/common.ts +2 -14
  313. package/template/packages/database/src/schema/durable-jobs.ts +5 -0
  314. package/template/packages/database/src/schema/governed-exports.ts +17 -5
  315. package/template/packages/database/src/schema/index.ts +3 -0
  316. package/template/packages/database/src/schema-durable-recovery.test.ts +31 -0
  317. package/template/packages/database/src/schema-governed-exports.test.ts +21 -1
  318. package/template/packages/database/src/schema.test.ts +6 -23
  319. package/template/packages/jobs/package.json +3 -2
  320. package/template/packages/jobs/src/handlers.test.ts +13 -0
  321. package/template/packages/jobs/src/handlers.ts +7 -1
  322. package/template/packages/jobs/src/types.ts +4 -0
  323. package/template/packages/modules/package.json +2 -2
  324. package/template/packages/modules/src/ai-operation-composition.test-support.ts +165 -0
  325. package/template/packages/modules/src/ai-operation-composition.test.ts +91 -127
  326. package/template/packages/modules/src/ai-operation-composition.ts +59 -1
  327. package/template/packages/modules/src/manifest.ts +25 -1
  328. package/template/packages/modules/src/tool-composition.ts +18 -0
  329. package/template/packages/server-sdk/index.ts +50 -0
  330. package/template/packages/server-sdk/package.json +10 -4
  331. package/template/packages/server-sdk/src/ai-artifact-port.ts +34 -0
  332. package/template/packages/server-sdk/src/ai-operation-port.ts +65 -0
  333. package/template/packages/server-sdk/src/ai-operation-registration.ts +42 -1
  334. package/template/packages/server-sdk/src/background-ai-port.ts +43 -0
  335. package/template/packages/server-sdk/src/data-governance-registration.ts +116 -0
  336. package/template/packages/server-sdk/src/durable-handler.ts +29 -0
  337. package/template/packages/server-sdk/src/jobs-port.ts +69 -0
  338. package/template/packages/server-sdk/src/module-host.ts +16 -2
  339. package/template/packages/server-sdk/src/request-context.ts +6 -2
  340. package/template/packages/server-sdk/src/sandbox-tool.ts +21 -0
  341. package/template/packages/server-sdk/src/tool-registration.ts +2 -0
  342. package/template/packages/shared/package.json +1 -1
  343. package/template/packages/shared/src/rbac.ts +30 -0
@@ -1,11 +1,18 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
  import { buildActorScopeContexts } from "../../data-access/context.js";
3
- import { createFoundationSnapshot } from "../../foundation.js";
3
+ import {
4
+ createFoundationSnapshot,
5
+ loadSystemModules,
6
+ } from "../../foundation.js";
4
7
  import type { RequestActor } from "../../guards.js";
8
+ import { createServerRuntime } from "../../runtime.js";
5
9
  import { createSystemRepositories } from "../../system/seed.js";
6
10
  import { SystemGovernedExportAuthorization } from "./authorization-port.js";
7
11
  import { captureGovernedExportScope } from "./scope-snapshot.js";
8
- import type { GovernedExportRequest } from "./types.js";
12
+ import {
13
+ legacyGovernedExportRendererSnapshot,
14
+ type GovernedExportRequest,
15
+ } from "./types.js";
9
16
 
10
17
  const actor: RequestActor = {
11
18
  id: "test-bootstrap",
@@ -17,7 +24,8 @@ const actor: RequestActor = {
17
24
 
18
25
  describe("governed export current authorization", () => {
19
26
  test("按当前角色权限与 Data Scope 重建执行授权", async () => {
20
- const repositories = createSystemRepositories(createFoundationSnapshot());
27
+ const foundation = createFoundationSnapshot();
28
+ const repositories = createSystemRepositories(foundation);
21
29
  await repositories.roles.create(
22
30
  {
23
31
  id: "role-export-auditor",
@@ -52,7 +60,17 @@ describe("governed export current authorization", () => {
52
60
  ]).scopeContext,
53
61
  );
54
62
  const request = exportRequest(scope.snapshot, scope.hash);
55
- const authorization = new SystemGovernedExportAuthorization(repositories);
63
+ const runtime = createServerRuntime(
64
+ {},
65
+ { featureCatalog: foundation.features },
66
+ );
67
+ const authorization = new SystemGovernedExportAuthorization(repositories, {
68
+ policies: loadSystemModules().flatMap(
69
+ (manifest) => manifest.dataPolicies ?? [],
70
+ ),
71
+ features: foundation.features,
72
+ runtime,
73
+ });
56
74
 
57
75
  expect(await authorization.evaluate(request)).toEqual({ allowed: true });
58
76
  await repositories.roles.update(
@@ -94,6 +112,7 @@ function exportRequest(
94
112
  scopeSnapshot,
95
113
  scopeHash,
96
114
  querySnapshot: { includeDeleted: false },
115
+ renderer: legacyGovernedExportRendererSnapshot,
97
116
  status: "queued",
98
117
  approvalRequired: false,
99
118
  expiresAt: "2027-01-01T00:00:00.000Z",
@@ -1,5 +1,12 @@
1
- import { buildActorScopeContexts } from "../../data-access/context.js";
1
+ import type {
2
+ DataGovernancePolicyDefinition,
3
+ FeatureFlagDefinition,
4
+ } from "@southwind-ai/shared";
5
+ import { featureKey } from "@southwind-ai/shared";
6
+ import { evaluateRouteGuards, findFeature } from "../../route-guards.js";
7
+ import type { createServerRuntime } from "../../runtime.js";
2
8
  import type { SystemRepositories } from "../../system/seed.js";
9
+ import { resolveCurrentSystemActor } from "../../system/actor.js";
3
10
  import { captureGovernedExportScope } from "./scope-snapshot.js";
4
11
  import type { GovernedExportRequest } from "./types.js";
5
12
 
@@ -7,7 +14,11 @@ export type GovernedExportAuthorizationDecision =
7
14
  | { allowed: true }
8
15
  | {
9
16
  allowed: false;
10
- reason: "user-inactive" | "permission-revoked" | "scope-changed";
17
+ reason:
18
+ | "user-inactive"
19
+ | "policy-changed"
20
+ | "permission-revoked"
21
+ | "scope-changed";
11
22
  currentScopeHash?: string;
12
23
  };
13
24
 
@@ -20,38 +31,34 @@ export interface GovernedExportAuthorizationPort {
20
31
  export class SystemGovernedExportAuthorization implements GovernedExportAuthorizationPort {
21
32
  constructor(
22
33
  private readonly repositories: Pick<SystemRepositories, "users" | "roles">,
34
+ private readonly options?: {
35
+ readonly policies: readonly DataGovernancePolicyDefinition[];
36
+ readonly features: readonly FeatureFlagDefinition[];
37
+ readonly runtime: Pick<
38
+ ReturnType<typeof createServerRuntime>,
39
+ "createGuardContextForActor" | "recordDecisions" | "resolveFeature"
40
+ >;
41
+ },
23
42
  ) {}
24
43
 
25
44
  async evaluate(
26
45
  request: GovernedExportRequest,
27
46
  ): Promise<GovernedExportAuthorizationDecision> {
28
- const user = await this.repositories.users.get(request.requesterId);
29
- if (!user || user.status !== "active") {
47
+ const actor = await resolveCurrentSystemActor(
48
+ this.repositories,
49
+ request.requesterId,
50
+ );
51
+ if (!actor) {
30
52
  return { allowed: false, reason: "user-inactive" };
31
53
  }
32
- const roles = (
33
- await Promise.all(
34
- user.roleIds.map((id) => this.repositories.roles.get(id)),
35
- )
36
- ).filter(
37
- (role): role is NonNullable<typeof role> => role?.status === "active",
38
- );
39
- const permissions = new Set(roles.flatMap((role) => role.permissionKeys));
40
- if (!permissions.has("system.user.export")) {
54
+ const policy = this.resolvePolicy(request);
55
+ if (!policy) {
56
+ return { allowed: false, reason: "policy-changed" };
57
+ }
58
+ if (!(await this.accessAllowed(actor, policy))) {
41
59
  return { allowed: false, reason: "permission-revoked" };
42
60
  }
43
- const current = captureGovernedExportScope(
44
- buildActorScopeContexts(
45
- user.id,
46
- user.departmentId,
47
- roles.map((role) => ({
48
- id: role.id,
49
- code: role.code,
50
- dataScope: role.dataScope,
51
- departmentIds: role.departmentIds || [],
52
- })),
53
- ).scopeContext,
54
- );
61
+ const current = captureGovernedExportScope(actor.scopeContext);
55
62
  return current.hash === request.scopeHash
56
63
  ? { allowed: true }
57
64
  : {
@@ -60,6 +67,63 @@ export class SystemGovernedExportAuthorization implements GovernedExportAuthoriz
60
67
  currentScopeHash: current.hash,
61
68
  };
62
69
  }
70
+
71
+ private resolvePolicy(request: GovernedExportRequest) {
72
+ if (!this.options) {
73
+ return request.resourceType === "system.user" &&
74
+ request.policyKey === "system.user.fields" &&
75
+ request.policyVersion === "1"
76
+ ? {
77
+ key: request.policyKey,
78
+ resourceType: request.resourceType,
79
+ version: request.policyVersion,
80
+ featureKey: featureKey("system.rbac"),
81
+ exportPermissionKey: "system.user.export",
82
+ }
83
+ : undefined;
84
+ }
85
+ return this.options.policies.find(
86
+ (policy) =>
87
+ policy.key === request.policyKey &&
88
+ policy.resourceType === request.resourceType &&
89
+ policy.version === request.policyVersion,
90
+ );
91
+ }
92
+
93
+ private async accessAllowed(
94
+ actor: NonNullable<Awaited<ReturnType<typeof resolveCurrentSystemActor>>>,
95
+ policy: Pick<
96
+ DataGovernancePolicyDefinition,
97
+ "featureKey" | "exportPermissionKey"
98
+ >,
99
+ ) {
100
+ if (!this.options) {
101
+ return actor.permissionKeys.includes(policy.exportPermissionKey);
102
+ }
103
+ const feature = findFeature([...this.options.features], policy.featureKey);
104
+ if (!feature) return false;
105
+ const context = await this.options.runtime.createGuardContextForActor(
106
+ new Request("http://windy.internal/governed-export/authorize"),
107
+ actor,
108
+ );
109
+ return (
110
+ await evaluateRouteGuards(
111
+ this.options.runtime,
112
+ context,
113
+ {
114
+ permission: {
115
+ method: "POST",
116
+ path: "/internal/governed-export/authorize",
117
+ permissionKey: policy.exportPermissionKey,
118
+ featureKey: policy.featureKey,
119
+ },
120
+ feature,
121
+ restrictedModeAccess: "backup-export",
122
+ },
123
+ { record: false },
124
+ )
125
+ ).allowed;
126
+ }
63
127
  }
64
128
 
65
129
  export const allowGovernedExportExecution: GovernedExportAuthorizationPort = {
@@ -4,6 +4,7 @@ import { drizzle } from "drizzle-orm/node-postgres";
4
4
  import { migrate } from "drizzle-orm/node-postgres/migrator";
5
5
  import { Pool } from "pg";
6
6
  import { DrizzleGovernedExportRepository } from "./drizzle-repository.js";
7
+ import { legacyGovernedExportRendererSnapshot } from "./types.js";
7
8
 
8
9
  const databaseUrl = process.env.GOVERNED_EXPORT_TEST_DATABASE_URL;
9
10
  const describePostgres = databaseUrl ? describe : describe.skip;
@@ -49,6 +50,7 @@ describePostgres("PostgreSQL governed export repository", () => {
49
50
  },
50
51
  scopeHash: "a".repeat(64),
51
52
  querySnapshot: { includeDeleted: false },
53
+ renderer: legacyGovernedExportRendererSnapshot,
52
54
  approvalRequired: false,
53
55
  expiresAt: "2026-07-17T02:00:00.000Z",
54
56
  };
@@ -86,6 +86,7 @@ function mapRequest(row: Row): GovernedExportRequest {
86
86
  fieldKeys: [...row.fieldKeys],
87
87
  scopeSnapshot: structuredClone(row.scopeSnapshot),
88
88
  querySnapshot: structuredClone(row.querySnapshot),
89
+ renderer: structuredClone(row.renderer),
89
90
  status: row.status as GovernedExportRequest["status"],
90
91
  approvalRequired: row.approvalRequired,
91
92
  approvalId: row.approvalId || undefined,
@@ -15,6 +15,7 @@ import type { GovernedExportAuthorizationPort } from "./authorization-port.js";
15
15
  import { InMemoryGovernedExportRepository } from "./repository.js";
16
16
  import { captureGovernedExportScope } from "./scope-snapshot.js";
17
17
  import type { GovernedExportSource } from "./source.js";
18
+ import { legacyGovernedExportRendererSnapshot } from "./types.js";
18
19
 
19
20
  const base = new Date("2026-07-16T03:00:00.000Z");
20
21
  const canary = "sensitive-canary-13800138000";
@@ -124,14 +125,18 @@ describe("governed export job runtime", () => {
124
125
  expect(await new Response(downloadable.body).text()).toContain(canary);
125
126
  });
126
127
 
127
- test("执行时权限或范围失效会在查询与 Blob 写入前终止申请", async () => {
128
+ test("分页间权限或范围失效会在读取下一页前终止申请", async () => {
129
+ let checks = 0;
128
130
  const fixture = await createFixture(
129
131
  undefined,
130
132
  new MemoryBlobStore(),
131
133
  () => base,
132
134
  {
133
135
  async evaluate() {
134
- return { allowed: false, reason: "scope-changed" };
136
+ checks += 1;
137
+ return checks < 3
138
+ ? { allowed: true }
139
+ : { allowed: false, reason: "scope-changed" };
135
140
  },
136
141
  },
137
142
  );
@@ -141,7 +146,7 @@ describe("governed export job runtime", () => {
141
146
  status: "failed",
142
147
  jobId: job.id,
143
148
  });
144
- expect(fixture.pageReads()).toBe(0);
149
+ expect(fixture.pageReads()).toBe(1);
145
150
  expect(fixture.blobs.size).toBe(0);
146
151
  expect(await fixture.requests.get(fixture.request.id)).toMatchObject({
147
152
  status: "failed",
@@ -151,6 +156,10 @@ describe("governed export job runtime", () => {
151
156
  .list()
152
157
  .filter(({ action }) => action === "data.governed-export.generate"),
153
158
  ).toEqual([
159
+ expect.objectContaining({
160
+ outcome: "attempted",
161
+ metadata: expect.objectContaining({ phase: "intent" }),
162
+ }),
154
163
  expect.objectContaining({
155
164
  outcome: "denied",
156
165
  metadata: expect.objectContaining({
@@ -263,8 +272,8 @@ describe("governed export job runtime", () => {
263
272
  }),
264
273
  ]);
265
274
  });
266
- });
267
275
 
276
+ });
268
277
  async function createFixture(
269
278
  auditWriter?: AuditEventWriter,
270
279
  blobStore: BlobStore = new MemoryBlobStore(),
@@ -283,6 +292,7 @@ async function createFixture(
283
292
  async *pages() {
284
293
  pageReads += 1;
285
294
  yield [{ id: "user-1", phone: canary }];
295
+ pageReads += 1;
286
296
  yield [{ id: "user-2", email: canary }];
287
297
  },
288
298
  };
@@ -311,6 +321,7 @@ async function createFixture(
311
321
  scopeSnapshot: scope.snapshot,
312
322
  scopeHash: scope.hash,
313
323
  querySnapshot: { includeDeleted: false },
324
+ renderer: legacyGovernedExportRendererSnapshot,
314
325
  approvalRequired: false,
315
326
  expiresAt: "2026-07-17T03:00:00.000Z",
316
327
  },
@@ -1,12 +1,18 @@
1
- import { DurableHandlerError, type DurableHandlerRegistry } from "@southwind-ai/jobs";
1
+ import {
2
+ DurableHandlerError,
3
+ type DurableHandlerRegistry,
4
+ } from "@southwind-ai/jobs";
2
5
  import type { BlobStore } from "@southwind-ai/storage";
3
6
  import type { createServerRuntime } from "../../runtime.js";
4
7
  import type { DurableJobService } from "../../jobs/service.js";
5
8
  import type { DurableJobWorker } from "../../jobs/worker.js";
6
9
  import type { GovernedExportRepository } from "./repository.js";
7
10
  import type { GovernedExportSource } from "./source.js";
8
- import { createWatermarkedNdjsonStream } from "./ndjson-stream.js";
9
11
  import type { GovernedExportRequest } from "./types.js";
12
+ import {
13
+ createGovernedExportRenderSession,
14
+ type GovernedExportRendererBinding,
15
+ } from "./renderer-runtime.js";
10
16
  import {
11
17
  allowGovernedExportExecution,
12
18
  type GovernedExportAuthorizationDecision,
@@ -30,6 +36,7 @@ export class GovernedExportJobRuntime {
30
36
  ) => Promise<boolean> = async (request) => !request.approvalRequired,
31
37
  private readonly authorization: GovernedExportAuthorizationPort = allowGovernedExportExecution,
32
38
  private readonly now: () => Date = () => new Date(),
39
+ private readonly renderers: readonly GovernedExportRendererBinding[] = [],
33
40
  ) {
34
41
  registry.register({
35
42
  key: handlerKey,
@@ -129,39 +136,64 @@ export class GovernedExportJobRuntime {
129
136
  } catch {
130
137
  throw safeFailure("EXPORT_AUDIT_UNAVAILABLE", true);
131
138
  }
132
- let stored;
139
+ let stored: Awaited<ReturnType<BlobStore["put"]>> | undefined;
140
+ let session;
133
141
  try {
134
- stored = await this.blobStore.put({
135
- body: createWatermarkedNdjsonStream({
142
+ session = await createGovernedExportRenderSession(
143
+ {
136
144
  request,
137
145
  source,
138
146
  signal: context.signal,
139
- watermark: {
140
- actorId: request.requesterId,
141
- generatedAt,
142
- scopeHash: request.scopeHash,
143
- jobId: context.jobId,
144
- requestId: request.id,
147
+ jobId: context.jobId,
148
+ generatedAt,
149
+ heartbeat: context.heartbeat,
150
+ beforePage: async () => {
151
+ const authorization = await this.authorization.evaluate(request);
152
+ if (!authorization.allowed) {
153
+ await this.denyAuthorization(request, authorization);
154
+ }
145
155
  },
146
156
  pageCompleted: async (completed) => {
147
157
  await context.heartbeat();
148
158
  await context.progress({ completedUnits: completed });
149
159
  },
150
- }),
160
+ },
161
+ this.renderers,
162
+ );
163
+ stored = await this.blobStore.put({
164
+ body: session.body,
151
165
  });
152
- } catch {
166
+ session.assertRecordsConsumed();
167
+ } catch (error) {
168
+ if (stored) await this.blobStore.delete(stored.ref);
169
+ if (error instanceof DurableHandlerError) throw error;
170
+ if (context.signal.aborted) throw error;
171
+ throw safeFailure("EXPORT_STREAM_FAILED", isRetryableRenderFailure(error));
172
+ } finally {
173
+ session?.dispose();
174
+ }
175
+ if (!stored || !session) {
153
176
  throw safeFailure("EXPORT_STREAM_FAILED", true);
154
177
  }
155
178
  try {
156
179
  await context.publishArtifact({
157
180
  kind: "governed-export",
158
181
  artifactRef: stored.ref,
159
- filename: "governed-users.ndjson",
160
- contentType: "application/x-ndjson",
182
+ filename: `${safeFilename(request.resourceType)}.${request.renderer.filenameExtension}`,
183
+ contentType: request.renderer.contentType,
161
184
  byteSize: stored.byteSize,
162
185
  sha256: stored.contentHash.value,
163
186
  ownerId: request.requesterId,
164
187
  expiresAt: request.expiresAt,
188
+ metadata: {
189
+ formatKey: request.renderer.formatKey,
190
+ templateKey: request.renderer.templateKey,
191
+ templateVersion: request.renderer.templateVersion,
192
+ inputSchemaKey: request.renderer.inputSchemaKey,
193
+ ...(session?.contentMetadata
194
+ ? { contentMetadata: session.contentMetadata }
195
+ : {}),
196
+ },
165
197
  });
166
198
  } catch (error) {
167
199
  await this.blobStore.delete(stored.ref);
@@ -183,6 +215,10 @@ function parsePayload(value: unknown): { requestId: string } {
183
215
  return { requestId: payload.requestId };
184
216
  }
185
217
 
218
+ function safeFilename(resourceType: string) {
219
+ return `governed-${resourceType.replaceAll(/[^a-zA-Z0-9_-]/g, "-")}`;
220
+ }
221
+
186
222
  function safeFailure(code: string, retryable: boolean) {
187
223
  return new DurableHandlerError(code, "治理导出生成失败", retryable);
188
224
  }
@@ -206,6 +242,11 @@ function generationIntentEvent(
206
242
  metadata: {
207
243
  policyKey: request.policyKey,
208
244
  policyVersion: request.policyVersion,
245
+ formatKey: request.renderer.formatKey,
246
+ templateKey: request.renderer.templateKey,
247
+ templateVersion: request.renderer.templateVersion,
248
+ inputSchemaKey: request.renderer.inputSchemaKey,
249
+ contentType: request.renderer.contentType,
209
250
  scopeHash: request.scopeHash,
210
251
  jobId,
211
252
  phase: "intent",
@@ -214,6 +255,18 @@ function generationIntentEvent(
214
255
  };
215
256
  }
216
257
 
258
+ function isRetryableRenderFailure(error: unknown) {
259
+ const code = error instanceof Error ? error.message : "";
260
+ return ![
261
+ "EXPORT_RENDERER_NOT_REGISTERED",
262
+ "EXPORT_RENDERER_VERSION_DRIFT",
263
+ "EXPORT_RECORD_LIMIT_EXCEEDED",
264
+ "EXPORT_OUTPUT_LIMIT_EXCEEDED",
265
+ "EXPORT_RECORDS_NOT_CONSUMED",
266
+ "EXPORT_CONTENT_METADATA_TOO_LARGE",
267
+ ].includes(code);
268
+ }
269
+
217
270
  function authorizationDeniedEvent(
218
271
  request: GovernedExportRequest,
219
272
  decision: Extract<GovernedExportAuthorizationDecision, { allowed: false }>,
@@ -14,6 +14,8 @@ export function createWatermarkedNdjsonStream(input: {
14
14
  source: GovernedExportSource;
15
15
  watermark: ExportWatermark;
16
16
  signal: AbortSignal;
17
+ maxRecords?: number;
18
+ beforePage(): Promise<void>;
17
19
  pageCompleted(completed: number): Promise<void>;
18
20
  }) {
19
21
  const iterator = lines(input)[Symbol.asyncIterator]();
@@ -42,6 +44,8 @@ async function* lines(input: {
42
44
  source: GovernedExportSource;
43
45
  watermark: ExportWatermark;
44
46
  signal: AbortSignal;
47
+ maxRecords?: number;
48
+ beforePage(): Promise<void>;
45
49
  pageCompleted(completed: number): Promise<void>;
46
50
  }) {
47
51
  yield {
@@ -51,12 +55,27 @@ async function* lines(input: {
51
55
  watermark: input.watermark,
52
56
  };
53
57
  let completed = 0;
54
- for await (const page of input.source.pages(input.request)) {
55
- if (input.signal.aborted) throw new Error("EXPORT_ABORTED");
56
- for (const record of page) {
57
- yield { type: "record", watermark: input.watermark, data: record };
58
- completed += 1;
58
+ const sourcePages = input.source.pages(input.request, input.signal);
59
+ const pages = sourcePages[Symbol.asyncIterator]();
60
+ try {
61
+ while (true) {
62
+ await input.beforePage();
63
+ const result = await pages.next();
64
+ if (result.done) return;
65
+ if (input.signal.aborted) throw new Error("EXPORT_ABORTED");
66
+ for (const record of result.value) {
67
+ if (
68
+ input.maxRecords !== undefined &&
69
+ completed >= input.maxRecords
70
+ ) {
71
+ throw new Error("EXPORT_RECORD_LIMIT_EXCEEDED");
72
+ }
73
+ yield { type: "record", watermark: input.watermark, data: record };
74
+ completed += 1;
75
+ }
76
+ await input.pageCompleted(completed);
59
77
  }
60
- await input.pageCompleted(completed);
78
+ } finally {
79
+ await pages.return?.();
61
80
  }
62
81
  }
@@ -21,6 +21,7 @@ import {
21
21
  createPostgresApiFixture,
22
22
  ownerCanaries,
23
23
  } from "./postgres-api-fixture.js";
24
+ import { legacyGovernedExportRendererSnapshot } from "./types.js";
24
25
  import { captureGovernedExportScope } from "./scope-snapshot.js";
25
26
  import type { GovernedExportSource } from "./source.js";
26
27
 
@@ -190,7 +191,7 @@ async function exerciseFailureRetry(
190
191
  const request = await fixture.requests.create(
191
192
  {
192
193
  id: Bun.randomUUIDv7(),
193
- resourceType: "canary.failure",
194
+ resourceType: "system.user",
194
195
  policyKey: "system.user.fields",
195
196
  policyVersion: "1",
196
197
  fieldKeys: ["phone"],
@@ -202,6 +203,7 @@ async function exerciseFailureRetry(
202
203
  scopeSnapshot: scope.snapshot,
203
204
  scopeHash: scope.hash,
204
205
  querySnapshot: { includeDeleted: false },
206
+ renderer: legacyGovernedExportRendererSnapshot,
205
207
  approvalRequired: false,
206
208
  expiresAt: new Date(
207
209
  fixture.clock.current.getTime() + 86_400_000,
@@ -210,7 +212,7 @@ async function exerciseFailureRetry(
210
212
  fixture.clock.current,
211
213
  );
212
214
  const failing: GovernedExportSource = {
213
- resourceType: "canary.failure",
215
+ resourceType: "system.user",
214
216
  async *pages() {
215
217
  yield [];
216
218
  throw new Error(`source failed ${ownerCanaries[1]}`);