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
@@ -0,0 +1,149 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import hashlib
5
+ import json
6
+ import uuid
7
+
8
+ from .adapter import DocumentAdapter, SafeDocumentError
9
+ from .contracts import ErrorResponse, StartDocumentRunRequest
10
+ from .store import DocumentRunStore, RunRecord
11
+
12
+
13
+ class DocumentRunService:
14
+ def __init__(self, adapter: DocumentAdapter, store: DocumentRunStore) -> None:
15
+ self.adapter = adapter
16
+ self.store = store
17
+ self.tasks: dict[str, asyncio.Task[None]] = {}
18
+
19
+ async def start(
20
+ self, request: StartDocumentRunRequest, idempotency_key: str
21
+ ) -> RunRecord:
22
+ validate_request_budget(request)
23
+ fingerprint = hashlib.sha256(
24
+ json.dumps(
25
+ {
26
+ "operationKey": request.operation_key,
27
+ "sourceSha256": [source.sha256 for source in request.sources],
28
+ "options": request.options,
29
+ "capabilities": sorted(request.required_capabilities),
30
+ },
31
+ ensure_ascii=False,
32
+ separators=(",", ":"),
33
+ sort_keys=True,
34
+ ).encode()
35
+ ).hexdigest()
36
+ record, created = self.store.create(
37
+ f"doc_{uuid.uuid4().hex}",
38
+ request.operation_key,
39
+ idempotency_key,
40
+ fingerprint,
41
+ )
42
+ if created:
43
+ task = asyncio.create_task(self._execute(record.run_id, request))
44
+ self.tasks[record.run_id] = task
45
+ task.add_done_callback(lambda _task: self.tasks.pop(record.run_id, None))
46
+ return record
47
+
48
+ async def cancel(self, run_id: str) -> RunRecord:
49
+ record = self.require(run_id)
50
+ if not record.terminal:
51
+ self.store.append(run_id, "run.cancelled")
52
+ task = self.tasks.get(run_id)
53
+ if task:
54
+ task.cancel()
55
+ return self.require(run_id)
56
+
57
+ def require(self, run_id: str) -> RunRecord:
58
+ record = self.store.get(run_id)
59
+ if not record:
60
+ raise KeyError(run_id)
61
+ return record
62
+
63
+ async def _execute(self, run_id: str, request: StartDocumentRunRequest) -> None:
64
+ self.store.append(run_id, "run.started")
65
+ try:
66
+ async with asyncio.timeout(request.limits.max_duration_seconds):
67
+ output = await self.adapter.execute(request)
68
+ if self.require(run_id).status == "cancelled":
69
+ return
70
+ pages = output.get("pages") if isinstance(output, dict) else None
71
+ if isinstance(pages, list):
72
+ total = len(pages)
73
+ for index in range(total):
74
+ self.store.append(
75
+ run_id,
76
+ "document.page.completed",
77
+ {
78
+ "pageNumber": index + 1,
79
+ "completedPages": index + 1,
80
+ "totalPages": total,
81
+ },
82
+ )
83
+ self.store.complete(run_id, output)
84
+ except TimeoutError:
85
+ self._fail(run_id, "RUN_TIMEOUT", "Document 任务超时", True)
86
+ except SafeDocumentError as error:
87
+ self._fail(
88
+ run_id,
89
+ error.code,
90
+ error.safe_message,
91
+ error.retryable,
92
+ )
93
+ except Exception:
94
+ self._fail(
95
+ run_id,
96
+ "RUNTIME_UNAVAILABLE",
97
+ "Document Runtime 执行失败",
98
+ True,
99
+ )
100
+
101
+ def _fail(self, run_id: str, code: str, message: str, retryable: bool) -> None:
102
+ self.store.append(
103
+ run_id,
104
+ "run.failed",
105
+ {
106
+ "error": ErrorResponse(
107
+ code=code,
108
+ message=message,
109
+ request_id=f"document_{uuid.uuid4().hex}",
110
+ retryable=retryable,
111
+ ).model_dump(by_alias=True)
112
+ },
113
+ )
114
+
115
+
116
+ def validate_request_budget(request: StartDocumentRunRequest) -> None:
117
+ if len(request.sources) > request.limits.max_artifacts:
118
+ raise SafeDocumentError(
119
+ "BUDGET_EXCEEDED",
120
+ "Document Artifact 数量超过输入上限",
121
+ False,
122
+ )
123
+ total_bytes = sum(source.byte_size for source in request.sources)
124
+ if total_bytes > request.limits.max_input_bytes:
125
+ raise SafeDocumentError(
126
+ "BUDGET_EXCEEDED",
127
+ "Document Artifact 超过输入上限",
128
+ False,
129
+ )
130
+ options = request.options
131
+ if not isinstance(options, dict):
132
+ return
133
+ page_range = options.get("pageRange")
134
+ if page_range is None:
135
+ return
136
+ if not isinstance(page_range, dict):
137
+ raise SafeDocumentError("INVALID_INPUT", "Document 页码范围无效", False)
138
+ start = page_range.get("from")
139
+ end = page_range.get("to")
140
+ if (
141
+ not isinstance(start, int)
142
+ or isinstance(start, bool)
143
+ or not isinstance(end, int)
144
+ or isinstance(end, bool)
145
+ or start < 1
146
+ or end < start
147
+ or end - start + 1 > request.limits.max_units
148
+ ):
149
+ raise SafeDocumentError("INVALID_INPUT", "Document 页码范围无效", False)
@@ -0,0 +1,185 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import sqlite3
5
+ import time
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+ from typing import cast
9
+
10
+ from .contracts import JsonValue, RunStatus
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class RunRecord:
15
+ run_id: str
16
+ operation_key: str
17
+ idempotency_key: str
18
+ fingerprint: str
19
+ status: RunStatus
20
+ output: JsonValue = None
21
+
22
+ @property
23
+ def terminal(self) -> bool:
24
+ return self.status in {"completed", "failed", "cancelled"}
25
+
26
+
27
+ class RunConflictError(Exception):
28
+ pass
29
+
30
+
31
+ class DocumentRunStore:
32
+ def __init__(self, path: str) -> None:
33
+ Path(path).parent.mkdir(parents=True, exist_ok=True)
34
+ self.database = sqlite3.connect(path, check_same_thread=False)
35
+ self.database.row_factory = sqlite3.Row
36
+ self._initialize()
37
+ self._recover()
38
+
39
+ def create(
40
+ self,
41
+ run_id: str,
42
+ operation_key: str,
43
+ idempotency_key: str,
44
+ fingerprint: str,
45
+ ) -> tuple[RunRecord, bool]:
46
+ existing = self.database.execute(
47
+ "select run_id from document_runs where idempotency_key = ?",
48
+ (idempotency_key,),
49
+ ).fetchone()
50
+ if existing:
51
+ record = self.get(str(existing["run_id"]))
52
+ if not record or record.fingerprint != fingerprint:
53
+ raise RunConflictError
54
+ return record, False
55
+ now = time.time()
56
+ self.database.execute(
57
+ """
58
+ insert into document_runs values (?, ?, ?, ?, 'accepted', 'null', ?, ?)
59
+ """,
60
+ (run_id, operation_key, idempotency_key, fingerprint, now, now),
61
+ )
62
+ self.database.commit()
63
+ return self.get(run_id) or _missing(), True
64
+
65
+ def get(self, run_id: str) -> RunRecord | None:
66
+ row = self.database.execute(
67
+ "select * from document_runs where run_id = ?", (run_id,)
68
+ ).fetchone()
69
+ if not row:
70
+ return None
71
+ return RunRecord(
72
+ run_id=str(row["run_id"]),
73
+ operation_key=str(row["operation_key"]),
74
+ idempotency_key=str(row["idempotency_key"]),
75
+ fingerprint=str(row["fingerprint"]),
76
+ status=cast(RunStatus, str(row["status"])),
77
+ output=json.loads(str(row["output_json"])),
78
+ )
79
+
80
+ def append(
81
+ self, run_id: str, event_type: str, fields: dict[str, object] | None = None
82
+ ) -> None:
83
+ record = self.get(run_id)
84
+ if not record or record.terminal:
85
+ return
86
+ sequence = (
87
+ int(
88
+ self.database.execute(
89
+ "select count(*) from document_events where run_id = ?", (run_id,)
90
+ ).fetchone()[0]
91
+ )
92
+ + 1
93
+ )
94
+ event = {
95
+ "type": event_type,
96
+ "runId": run_id,
97
+ "sequence": sequence,
98
+ **(fields or {}),
99
+ }
100
+ status = {
101
+ "run.started": "running",
102
+ "run.completed": "completed",
103
+ "run.failed": "failed",
104
+ "run.cancelled": "cancelled",
105
+ }.get(event_type, record.status)
106
+ self.database.execute(
107
+ "insert into document_events values (?, ?, ?)",
108
+ (run_id, sequence, encode(event)),
109
+ )
110
+ self.database.execute(
111
+ "update document_runs set status = ?, updated_at = ? where run_id = ?",
112
+ (status, time.time(), run_id),
113
+ )
114
+ self.database.commit()
115
+
116
+ def complete(self, run_id: str, output: JsonValue) -> None:
117
+ self.database.execute(
118
+ "update document_runs set output_json = ? where run_id = ?",
119
+ (encode(output), run_id),
120
+ )
121
+ self.database.commit()
122
+ self.append(run_id, "run.completed")
123
+
124
+ def events_after(self, run_id: str, after: int) -> list[dict[str, object]]:
125
+ return [
126
+ json.loads(str(row["event_json"]))
127
+ for row in self.database.execute(
128
+ """
129
+ select event_json from document_events
130
+ where run_id = ? and sequence > ? order by sequence
131
+ """,
132
+ (run_id, after),
133
+ )
134
+ ]
135
+
136
+ def _initialize(self) -> None:
137
+ self.database.executescript(
138
+ """
139
+ create table if not exists document_runs (
140
+ run_id text primary key,
141
+ operation_key text not null,
142
+ idempotency_key text not null unique,
143
+ fingerprint text not null,
144
+ status text not null,
145
+ output_json text not null,
146
+ created_at real not null,
147
+ updated_at real not null
148
+ );
149
+ create table if not exists document_events (
150
+ run_id text not null references document_runs(run_id) on delete cascade,
151
+ sequence integer not null,
152
+ event_json text not null,
153
+ primary key (run_id, sequence)
154
+ );
155
+ """
156
+ )
157
+ self.database.commit()
158
+
159
+ def _recover(self) -> None:
160
+ rows = self.database.execute(
161
+ "select run_id from document_runs where status in ('accepted', 'running')"
162
+ ).fetchall()
163
+ for row in rows:
164
+ self.append(
165
+ str(row["run_id"]),
166
+ "run.failed",
167
+ {
168
+ "error": {
169
+ "code": "RUNTIME_UNAVAILABLE",
170
+ "message": (
171
+ "Document Runtime 重启中断了任务,请使用新幂等键重试"
172
+ ),
173
+ "requestId": "document-recovery",
174
+ "retryable": True,
175
+ }
176
+ },
177
+ )
178
+
179
+
180
+ def encode(value: object) -> str:
181
+ return json.dumps(value, ensure_ascii=False, separators=(",", ":"))
182
+
183
+
184
+ def _missing() -> RunRecord:
185
+ raise RuntimeError("Document Run 创建后不可读")
@@ -0,0 +1 @@
1
+ """Document Runtime tests."""
@@ -0,0 +1,42 @@
1
+ from __future__ import annotations
2
+
3
+ import pytest
4
+
5
+ from southwind_document_server.config import DocumentRuntimeConfig
6
+
7
+
8
+ def test_adapter_must_be_local_or_intranet() -> None:
9
+ with pytest.raises(ValueError, match="禁止使用公网地址"):
10
+ DocumentRuntimeConfig.from_environment(
11
+ {
12
+ "DOCUMENT_SERVER_INTERNAL_TOKEN": "internal-token",
13
+ "DOCUMENT_ADAPTER_URL": "https://8.8.8.8",
14
+ }
15
+ )
16
+
17
+
18
+ def test_artifact_endpoint_must_be_local_or_intranet() -> None:
19
+ with pytest.raises(ValueError, match="禁止使用公网地址"):
20
+ DocumentRuntimeConfig.from_environment(
21
+ {
22
+ "DOCUMENT_SERVER_INTERNAL_TOKEN": "internal-token",
23
+ "DOCUMENT_ARTIFACT_URL": "https://8.8.8.8/artifacts",
24
+ }
25
+ )
26
+
27
+
28
+ def test_adapter_rejects_public_dns_names() -> None:
29
+ with pytest.raises(ValueError, match="内部服务名"):
30
+ DocumentRuntimeConfig.from_environment(
31
+ {
32
+ "DOCUMENT_SERVER_INTERNAL_TOKEN": "internal-token",
33
+ "DOCUMENT_ADAPTER_URL": "https://public.example.com",
34
+ }
35
+ )
36
+
37
+
38
+ def test_unconfigured_adapter_keeps_runtime_bootable() -> None:
39
+ config = DocumentRuntimeConfig.from_environment(
40
+ {"DOCUMENT_SERVER_INTERNAL_TOKEN": "internal-token"}
41
+ )
42
+ assert config.adapter_url is None
@@ -0,0 +1,140 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+
5
+ from southwind_document_server.adapter import (
6
+ DocumentAdapter,
7
+ SafeDocumentError,
8
+ validate_output,
9
+ )
10
+ from southwind_document_server.config import DocumentRuntimeConfig
11
+ from southwind_document_server.contracts import (
12
+ ArtifactInput,
13
+ JsonValue,
14
+ ProviderEgress,
15
+ RuntimeLimits,
16
+ StartDocumentRunRequest,
17
+ )
18
+ from southwind_document_server.service import DocumentRunService
19
+ from southwind_document_server.store import DocumentRunStore, RunConflictError
20
+
21
+
22
+ class FakeAdapter(DocumentAdapter):
23
+ def __init__(self) -> None:
24
+ super().__init__(
25
+ DocumentRuntimeConfig(
26
+ internal_token="internal",
27
+ store_path=":memory:",
28
+ artifact_url="http://server.internal/api/internal/ai/artifacts",
29
+ adapter_url="http://127.0.0.1:9000",
30
+ adapter_token=None,
31
+ deployment_key=None,
32
+ capabilities=("document.ocr",),
33
+ )
34
+ )
35
+
36
+ async def execute(self, request: StartDocumentRunRequest) -> JsonValue:
37
+ return {
38
+ "pages": [
39
+ {
40
+ "pageNumber": 1,
41
+ "text": "第一页",
42
+ "blocks": [],
43
+ }
44
+ ],
45
+ "tables": [],
46
+ "artifacts": [],
47
+ "metadata": {"engine": "fake"},
48
+ }
49
+
50
+
51
+ async def test_idempotent_run_emits_page_progress_and_terminal_result() -> None:
52
+ store = DocumentRunStore(":memory:")
53
+ service = DocumentRunService(FakeAdapter(), store)
54
+ request = document_request()
55
+
56
+ created = await service.start(request, "document-idempotent")
57
+ replayed = await service.start(request, "document-idempotent")
58
+ assert replayed.run_id == created.run_id
59
+ await asyncio.gather(*service.tasks.values())
60
+
61
+ completed = service.require(created.run_id)
62
+ assert completed.status == "completed"
63
+ assert completed.output == {
64
+ "pages": [{"pageNumber": 1, "text": "第一页", "blocks": []}],
65
+ "tables": [],
66
+ "artifacts": [],
67
+ "metadata": {"engine": "fake"},
68
+ }
69
+ assert [event["type"] for event in store.events_after(created.run_id, 0)] == [
70
+ "run.started",
71
+ "document.page.completed",
72
+ "run.completed",
73
+ ]
74
+
75
+
76
+ async def test_same_key_with_changed_source_is_rejected() -> None:
77
+ service = DocumentRunService(FakeAdapter(), DocumentRunStore(":memory:"))
78
+ request = document_request()
79
+ await service.start(request, "document-conflict")
80
+ changed = request.model_copy(
81
+ update={"sources": [request.sources[0].model_copy(update={"sha256": "b" * 64})]}
82
+ )
83
+ try:
84
+ await service.start(changed, "document-conflict")
85
+ except RunConflictError:
86
+ pass
87
+ else:
88
+ raise AssertionError("相同幂等键的语义漂移必须拒绝")
89
+
90
+
91
+ def test_output_rejects_page_overrun_and_provider_artifact() -> None:
92
+ output: JsonValue = {
93
+ "pages": [
94
+ {"pageNumber": 1, "text": "第一页", "blocks": []},
95
+ {"pageNumber": 2, "text": "第二页", "blocks": []},
96
+ ]
97
+ }
98
+ try:
99
+ validate_output(output, 1)
100
+ except SafeDocumentError as error:
101
+ assert error.code == "OUTPUT_VALIDATION_FAILED"
102
+ else:
103
+ raise AssertionError("超过页数上限的输出必须拒绝")
104
+
105
+ output = {
106
+ "pages": [{"pageNumber": 1, "text": "第一页", "blocks": []}],
107
+ "artifacts": [{"url": "https://provider.example/output.pdf"}],
108
+ }
109
+ try:
110
+ validate_output(output, 1)
111
+ except SafeDocumentError as error:
112
+ assert error.code == "OUTPUT_VALIDATION_FAILED"
113
+ else:
114
+ raise AssertionError("Adapter 不能直接签发 Artifact")
115
+
116
+
117
+ def document_request() -> StartDocumentRunRequest:
118
+ return StartDocumentRunRequest(
119
+ kind="document",
120
+ operation_key="analysis.transcript.ocr",
121
+ options={"outputs": ["pages"]},
122
+ sources=[
123
+ ArtifactInput(
124
+ value="opaque-artifact-ref",
125
+ media_type="application/pdf",
126
+ byte_size=128,
127
+ sha256="a" * 64,
128
+ )
129
+ ],
130
+ required_capabilities=["document.ocr"],
131
+ data_classification="sensitive",
132
+ limits=RuntimeLimits(
133
+ max_duration_seconds=60,
134
+ max_input_bytes=1_024,
135
+ max_output_bytes=1_024,
136
+ max_units=20,
137
+ max_artifacts=1,
138
+ ),
139
+ provider_egress=ProviderEgress(mode="forbidden"),
140
+ )