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,8 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
- import hmac
4
3
  import os
5
- import uuid
4
+ from collections.abc import AsyncIterator
5
+ from contextlib import asynccontextmanager
6
6
  from typing import Annotated, cast
7
7
 
8
8
  from fastapi import FastAPI, Header, Query, Request
@@ -10,30 +10,36 @@ from fastapi.exceptions import RequestValidationError
10
10
  from fastapi.responses import JSONResponse, StreamingResponse
11
11
  from pydantic import ValidationError
12
12
 
13
- from .adk_adapter import AdkEngineAdapter
14
- from .config import EnvironmentSecretResolver, OpenAiCompatibleConfig
13
+ from .artifact_input import ArtifactLoader
15
14
  from .contracts import (
16
- AgentError,
17
15
  CancelRunRequest,
18
16
  CancelRunResponse,
17
+ EvaluationResultRequest,
18
+ EvaluationResultResponse,
19
+ JsonValue,
19
20
  RunResultResponse,
21
+ RunStatusResponse,
20
22
  StartRunRequest,
21
23
  StartRunResponse,
22
24
  )
23
- from .engine import AgentEngineAdapter
24
- from .openai_provider import OpenAiCompatibleProvider
25
- from .provider_audit import ProviderAuditStore
26
- from .provider_governance import ProviderRequestGovernance
25
+ from .engine import AgentEngineAdapter, SafeEngineError
26
+ from .http_helpers import (
27
+ authorized,
28
+ parse_cursor,
29
+ provider_governance_snapshot,
30
+ safe_error,
31
+ )
32
+ from .runtime_factory import default_engine, default_store
27
33
  from .service import (
28
34
  AgentRunService,
29
35
  IdempotencyConflictError,
30
36
  OperationNotFoundError,
31
37
  RunNotFoundError,
32
38
  )
33
- from .sqlite_store import RunStoreCapacityError, SqliteRunStore
39
+ from .sqlite_store import RunStoreCapacityError
34
40
  from .sse import event_stream
35
- from .store import InMemoryRunStore, RunStore
36
- from .tool_host import ToolHostClient
41
+ from .store import ReleaseNotFoundError, RunStore
42
+ from .telemetry import configure_telemetry
37
43
 
38
44
 
39
45
  def create_app(
@@ -43,16 +49,44 @@ def create_app(
43
49
  internal_token: str | None = None,
44
50
  store: RunStore | None = None,
45
51
  ) -> FastAPI:
52
+ configure_telemetry()
46
53
  runtime_engine = engine or default_engine()
47
- runtime = AgentRunService(runtime_engine, store or default_store())
48
- application = FastAPI(title="Southwind AI Agent Server", version="0.1.0")
54
+ runtime = AgentRunService(
55
+ runtime_engine,
56
+ store or default_store(),
57
+ (
58
+ ArtifactLoader(
59
+ os.environ.get(
60
+ "AI_ARTIFACT_URL",
61
+ "http://server:9747/api/internal/ai/artifacts",
62
+ ),
63
+ internal_token,
64
+ )
65
+ if internal_token
66
+ else None
67
+ ),
68
+ )
69
+
70
+ @asynccontextmanager
71
+ async def lifespan(_application: FastAPI) -> AsyncIterator[None]:
72
+ await runtime.recover()
73
+ try:
74
+ yield
75
+ finally:
76
+ await runtime.close()
77
+
78
+ application = FastAPI(
79
+ title="Southwind AI Agent Server",
80
+ version="0.1.0",
81
+ lifespan=lifespan,
82
+ )
49
83
 
50
84
  @application.get("/health/live")
51
85
  async def live() -> dict[str, str]:
52
86
  return {"status": "live"}
53
87
 
54
88
  @application.get("/health/ready", response_model=None)
55
- async def ready() -> dict[str, str] | JSONResponse:
89
+ async def ready() -> dict[str, object] | JSONResponse:
56
90
  health = getattr(runtime_engine, "health", None)
57
91
  if not health:
58
92
  return safe_error(
@@ -62,8 +96,13 @@ def create_app(
62
96
  True,
63
97
  )
64
98
  try:
99
+ await runtime.probe()
65
100
  await health()
66
- return {"status": "ready"}
101
+ return {
102
+ "status": "ready",
103
+ "capabilities": list(runtime_engine.capabilities),
104
+ "inputModalities": list(runtime_engine.input_modalities),
105
+ }
67
106
  except Exception:
68
107
  return safe_error(
69
108
  503,
@@ -111,6 +150,13 @@ def create_app(
111
150
  ) -> JSONResponse:
112
151
  return safe_error(409, "RUN_CONFLICT", "幂等键已用于其他请求", False)
113
152
 
153
+ @application.exception_handler(ReleaseNotFoundError)
154
+ async def release_not_found(
155
+ _request: Request,
156
+ _error: ReleaseNotFoundError,
157
+ ) -> JSONResponse:
158
+ return safe_error(404, "RUN_CONFLICT", "Agent Release 不存在", False)
159
+
114
160
  @application.exception_handler(RunStoreCapacityError)
115
161
  async def run_store_capacity(
116
162
  _request: Request,
@@ -123,6 +169,21 @@ def create_app(
123
169
  True,
124
170
  )
125
171
 
172
+ @application.exception_handler(SafeEngineError)
173
+ async def safe_engine_error(
174
+ _request: Request,
175
+ error: SafeEngineError,
176
+ ) -> JSONResponse:
177
+ status = 403 if error.code == "FORBIDDEN" else 503
178
+ if error.code in {"INVALID_INPUT", "BUDGET_EXCEEDED"}:
179
+ status = 400
180
+ return safe_error(
181
+ status,
182
+ error.code,
183
+ error.safe_message,
184
+ error.retryable,
185
+ )
186
+
126
187
  @application.exception_handler(ValidationError)
127
188
  async def validation_error(
128
189
  _request: Request,
@@ -157,6 +218,28 @@ def create_app(
157
218
  events_url=f"/api/agent/runs/{record.run_id}/events",
158
219
  )
159
220
 
221
+ @application.get(
222
+ "/api/agent/runs/{run_id}",
223
+ response_model=RunStatusResponse,
224
+ )
225
+ async def get_run_status(
226
+ run_id: str,
227
+ authorization: Annotated[str | None, Header()] = None,
228
+ ) -> RunStatusResponse | JSONResponse:
229
+ if not authorized(authorization, internal_token):
230
+ return safe_error(403, "FORBIDDEN", "内部调用认证失败", False)
231
+ record = await runtime.get(run_id)
232
+ terminal_event = cast(
233
+ dict[str, JsonValue] | None,
234
+ record.events[-1] if record.terminal and record.events else None,
235
+ )
236
+ return RunStatusResponse(
237
+ run_id=record.run_id,
238
+ status=record.status,
239
+ last_sequence=len(record.events),
240
+ terminal_event=terminal_event,
241
+ )
242
+
160
243
  @application.get("/api/agent/runs/{run_id}/events", response_model=None)
161
244
  async def get_events(
162
245
  run_id: str,
@@ -217,107 +300,29 @@ def create_app(
217
300
  record = await runtime.cancel(run_id)
218
301
  return CancelRunResponse(run_id=record.run_id, status=record.status)
219
302
 
220
- application.state.run_service = runtime
221
- return application
222
-
223
-
224
- def default_engine() -> AgentEngineAdapter:
225
- config = OpenAiCompatibleConfig.from_environment()
226
- if config is None:
227
- return AdkEngineAdapter()
228
- internal_token = os.environ.get("AGENT_SERVER_INTERNAL_TOKEN", "").strip()
229
- if not internal_token:
230
- raise ValueError("Agent Server Provider 已配置但内部 Token 缺失")
231
- return AdkEngineAdapter(
232
- OpenAiCompatibleProvider(
233
- config,
234
- EnvironmentSecretResolver(os.environ),
235
- ),
236
- ToolHostClient(internal_token),
237
- ProviderRequestGovernance(
238
- ProviderAuditStore(
239
- required_environment("AI_PROVIDER_AUDIT_STORE_PATH"),
240
- retention_days=provider_audit_retention_days(),
241
- )
242
- ),
243
- )
244
-
245
-
246
- def default_store() -> RunStore:
247
- path = os.environ.get("AGENT_RUN_STORE_PATH", "").strip()
248
- return SqliteRunStore(path) if path else InMemoryRunStore()
249
-
250
-
251
- def required_environment(key: str) -> str:
252
- value = os.environ.get(key, "").strip()
253
- if not value:
254
- raise ValueError(f"Provider 已配置但缺少 {key}")
255
- return value
256
-
257
-
258
- def provider_audit_retention_days() -> int:
259
- raw = os.environ.get("AI_PROVIDER_AUDIT_RETENTION_DAYS", "90")
260
- try:
261
- value = int(raw)
262
- except ValueError as error:
263
- raise ValueError("AI_PROVIDER_AUDIT_RETENTION_DAYS 必须是整数") from error
264
- if not 1 <= value <= 3650:
265
- raise ValueError("AI_PROVIDER_AUDIT_RETENTION_DAYS 超出允许范围")
266
- return value
267
-
268
-
269
- def provider_governance_snapshot(
270
- value: dict[str, int | float | str | None],
271
- ) -> dict[str, int | float | str | None]:
272
- allowed = (
273
- "status",
274
- "retentionDays",
275
- "retainedRecords",
276
- "rejectedAttempts",
277
- "redactedAttempts",
278
- "lastAttemptAt",
279
- "lastPurgeAt",
280
- "lastDeletedRecords",
281
- "totalDeletedRecords",
303
+ @application.post(
304
+ "/api/agent/evaluations",
305
+ response_model=EvaluationResultResponse,
282
306
  )
283
- return {key: value.get(key) for key in allowed}
284
-
285
-
286
- def authorized(provided: str | None, expected: str | None) -> bool:
287
- if expected is None:
288
- return True
289
- if not expected or not provided or not provided.startswith("Bearer "):
290
- return False
291
- return hmac.compare_digest(provided.removeprefix("Bearer "), expected)
292
-
293
-
294
- def parse_cursor(after: int | None, last_event_id: str | None) -> int:
295
- header_cursor = 0
296
- if last_event_id is not None:
297
- if not last_event_id.isdigit():
298
- raise ValueError
299
- header_cursor = int(last_event_id)
300
- return max(after or 0, header_cursor)
301
-
307
+ async def record_evaluation(
308
+ request: EvaluationResultRequest,
309
+ authorization: Annotated[str | None, Header()] = None,
310
+ ) -> EvaluationResultResponse | JSONResponse:
311
+ if not authorized(authorization, internal_token):
312
+ return safe_error(403, "FORBIDDEN", "内部调用认证失败", False)
313
+ evaluation_id = await runtime.record_evaluation(
314
+ request.release_id,
315
+ request.evaluation_set_key,
316
+ request.evaluation_set_version,
317
+ request.metrics,
318
+ )
319
+ return EvaluationResultResponse(
320
+ evaluation_id=evaluation_id,
321
+ release_id=request.release_id,
322
+ )
302
323
 
303
- def safe_error(
304
- status_code: int,
305
- code: str,
306
- message: str,
307
- retryable: bool,
308
- ) -> JSONResponse:
309
- error = AgentError.model_validate(
310
- {
311
- "code": code,
312
- "message": message,
313
- "requestId": f"request_{uuid.uuid4().hex}",
314
- "retryable": retryable,
315
- }
316
- )
317
- return JSONResponse(
318
- status_code=status_code,
319
- content=error.model_dump(by_alias=True),
320
- )
324
+ application.state.run_service = runtime
325
+ return application
321
326
 
322
327
 
323
328
  app = create_app(internal_token=os.environ.get("AGENT_SERVER_INTERNAL_TOKEN"))
@@ -0,0 +1,153 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import base64
5
+ import hashlib
6
+ from collections.abc import Sequence
7
+ from io import BytesIO
8
+ from urllib.parse import urlsplit
9
+
10
+ import httpx
11
+ from PIL import Image, UnidentifiedImageError
12
+
13
+ from .config import private_hostname
14
+ from .contracts import RuntimeArtifact, RuntimeLimits
15
+ from .engine import PreparedArtifact, SafeEngineError
16
+
17
+ _IMAGE_TYPES = frozenset({"image/jpeg", "image/png", "image/webp"})
18
+
19
+
20
+ class ArtifactLoader:
21
+ def __init__(
22
+ self,
23
+ artifact_url: str,
24
+ internal_token: str,
25
+ *,
26
+ transport: httpx.AsyncBaseTransport | None = None,
27
+ ) -> None:
28
+ self.artifact_url = validate_artifact_url(artifact_url)
29
+ self.internal_token = internal_token
30
+ self.transport = transport
31
+
32
+ async def prepare(
33
+ self,
34
+ artifacts: list[RuntimeArtifact],
35
+ modalities: Sequence[str],
36
+ limits: RuntimeLimits,
37
+ ) -> tuple[PreparedArtifact, ...]:
38
+ if not artifacts:
39
+ return ()
40
+ if (
41
+ "image" not in modalities
42
+ or limits.max_artifacts is None
43
+ or limits.max_pixels_per_artifact is None
44
+ ):
45
+ raise invalid("Agent Operation 未授权图片输入")
46
+ if len(artifacts) > limits.max_artifacts:
47
+ raise budget("图片数量超过 Operation 上限")
48
+ declared = sum(item.byte_size for item in artifacts)
49
+ if declared > limits.max_input_bytes:
50
+ raise budget("图片总大小超过 Operation 上限")
51
+ prepared: list[PreparedArtifact] = []
52
+ total = 0
53
+ for artifact in artifacts:
54
+ if artifact.media_type.lower() not in _IMAGE_TYPES:
55
+ raise invalid("Agent Vision 仅接受 JPEG、PNG 或 WebP")
56
+ raw = await self._download(artifact, limits.max_input_bytes - total)
57
+ item = await asyncio.to_thread(
58
+ sanitize_image,
59
+ raw,
60
+ limits.max_pixels_per_artifact,
61
+ )
62
+ total += item.byte_size
63
+ if total > limits.max_input_bytes:
64
+ raise budget("归一化图片总大小超过 Operation 上限")
65
+ prepared.append(item)
66
+ return tuple(prepared)
67
+
68
+ async def _download(self, artifact: RuntimeArtifact, remaining: int) -> bytes:
69
+ body = bytearray()
70
+ digest = hashlib.sha256()
71
+ try:
72
+ async with httpx.AsyncClient(
73
+ timeout=httpx.Timeout(60, connect=5),
74
+ transport=self.transport,
75
+ ) as client:
76
+ async with client.stream(
77
+ "GET",
78
+ self.artifact_url,
79
+ headers={
80
+ "authorization": f"Bearer {self.internal_token}",
81
+ "x-ai-artifact-ref": artifact.value,
82
+ },
83
+ ) as response:
84
+ response.raise_for_status()
85
+ if response.headers.get("content-type", "").split(";")[0] != (
86
+ artifact.media_type
87
+ ):
88
+ raise invalid("Artifact MIME 不一致")
89
+ async for chunk in response.aiter_bytes():
90
+ body.extend(chunk)
91
+ digest.update(chunk)
92
+ if len(body) > min(artifact.byte_size, remaining):
93
+ raise budget("Artifact 内容超过声明上限")
94
+ except SafeEngineError:
95
+ raise
96
+ except Exception as error:
97
+ raise SafeEngineError(
98
+ "RUNTIME_UNAVAILABLE",
99
+ "Agent Runtime 无法读取图片 Artifact",
100
+ retryable=True,
101
+ ) from error
102
+ if len(body) != artifact.byte_size or digest.hexdigest() != artifact.sha256:
103
+ raise invalid("Artifact 完整性校验失败")
104
+ return bytes(body)
105
+
106
+
107
+ def sanitize_image(raw: bytes, max_pixels: int) -> PreparedArtifact:
108
+ try:
109
+ with Image.open(BytesIO(raw)) as source:
110
+ width, height = source.size
111
+ if width <= 0 or height <= 0 or width * height > max_pixels:
112
+ raise budget("图片像素超过 Operation 上限")
113
+ source.load()
114
+ normalized = source.convert(
115
+ "RGBA" if source.has_transparency_data else "RGB"
116
+ )
117
+ target = BytesIO()
118
+ normalized.save(target, format="PNG", optimize=False)
119
+ except SafeEngineError:
120
+ raise
121
+ except (Image.DecompressionBombError, UnidentifiedImageError, OSError):
122
+ raise invalid("图片内容损坏或格式不受支持") from None
123
+ body = target.getvalue()
124
+ digest = hashlib.sha256(body).hexdigest()
125
+ return PreparedArtifact(
126
+ media_type="image/png",
127
+ data_url=f"data:image/png;base64,{base64.b64encode(body).decode()}",
128
+ byte_size=len(body),
129
+ sha256=digest,
130
+ )
131
+
132
+
133
+ def validate_artifact_url(raw: str) -> str:
134
+ parsed = urlsplit(raw.strip())
135
+ if (
136
+ parsed.scheme not in {"http", "https"}
137
+ or not parsed.hostname
138
+ or parsed.username
139
+ or parsed.password
140
+ or parsed.query
141
+ or parsed.fragment
142
+ or not private_hostname(parsed.hostname.lower())
143
+ ):
144
+ raise ValueError("Agent Artifact URL 必须是固定本地或内网地址")
145
+ return raw.rstrip("/")
146
+
147
+
148
+ def invalid(message: str) -> SafeEngineError:
149
+ return SafeEngineError("INVALID_INPUT", message, retryable=False)
150
+
151
+
152
+ def budget(message: str) -> SafeEngineError:
153
+ return SafeEngineError("BUDGET_EXCEEDED", message, retryable=False)
@@ -38,6 +38,8 @@ class OpenAiCompatibleConfig:
38
38
  max_tool_rounds: int = 8
39
39
  https_host_allowlist: tuple[str, ...] = ()
40
40
  deployment_key: str = "ai.openai-compatible"
41
+ capabilities: tuple[str, ...] = ("text-generation",)
42
+ input_modalities: tuple[str, ...] = ("text",)
41
43
 
42
44
  def __post_init__(self) -> None:
43
45
  normalized = normalize_base_url(self.base_url)
@@ -89,6 +91,16 @@ class OpenAiCompatibleConfig:
89
91
  1_024,
90
92
  16 * 1024 * 1024,
91
93
  ),
94
+ capabilities=read_csv(
95
+ environment,
96
+ "AI_PROVIDER_CAPABILITIES",
97
+ ("text-generation",),
98
+ ),
99
+ input_modalities=read_csv(
100
+ environment,
101
+ "AI_PROVIDER_INPUT_MODALITIES",
102
+ ("text",),
103
+ ),
92
104
  )
93
105
 
94
106
 
@@ -187,3 +199,20 @@ def read_integer(
187
199
  if not minimum <= value <= maximum:
188
200
  raise ValueError(f"{key} 超出允许范围")
189
201
  return value
202
+
203
+
204
+ def read_csv(
205
+ environment: Mapping[str, str],
206
+ key: str,
207
+ default: tuple[str, ...],
208
+ ) -> tuple[str, ...]:
209
+ values = tuple(
210
+ dict.fromkeys(
211
+ item.strip()
212
+ for item in environment.get(key, ",".join(default)).split(",")
213
+ if item.strip()
214
+ )
215
+ )
216
+ if not values or len(values) > 32:
217
+ raise ValueError(f"{key} 无效")
218
+ return values
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from math import isfinite
3
4
  from typing import Literal, Self
4
5
 
5
- from pydantic import BaseModel, ConfigDict, Field, model_validator
6
+ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
6
7
 
7
8
  type JsonValue = (
8
9
  str | int | float | bool | None | list[JsonValue] | dict[str, JsonValue]
@@ -15,6 +16,7 @@ type RunStatus = Literal[
15
16
  "failed",
16
17
  "cancelled",
17
18
  ]
19
+ type InputModality = Literal["text", "image", "audio", "video", "document"]
18
20
  type AgentErrorCode = Literal[
19
21
  "OPERATION_NOT_FOUND",
20
22
  "OPERATION_DISABLED",
@@ -53,11 +55,17 @@ class RuntimeToolDefinition(CanonicalModel):
53
55
  key: str = Field(min_length=1)
54
56
  label: str = Field(min_length=1)
55
57
  description: str = Field(min_length=1)
58
+ approval: Literal["none", "required"] = "none"
59
+ idempotency: Literal["none", "supported", "required"] = "supported"
60
+ approval_timeout_seconds: int | None = Field(default=None, ge=1, le=604_800)
56
61
 
57
62
 
58
63
  class RuntimeLimits(CanonicalModel):
59
64
  max_duration_seconds: int = Field(default=60, ge=1, le=300)
65
+ max_input_bytes: int = Field(default=1_048_576, ge=1_024, le=67_108_864)
60
66
  max_output_bytes: int = Field(default=1_048_576, ge=1_024, le=16_777_216)
67
+ max_artifacts: int | None = Field(default=None, ge=1, le=16)
68
+ max_pixels_per_artifact: int | None = Field(default=None, ge=1, le=100_000_000)
61
69
 
62
70
 
63
71
  class ProviderEgress(CanonicalModel):
@@ -78,6 +86,8 @@ type ProviderDataField = Literal[
78
86
  "instructions",
79
87
  "user-prompt",
80
88
  "tool-definitions",
89
+ "artifact-content",
90
+ "output-schema",
81
91
  "provider-output",
82
92
  "tool-results",
83
93
  ]
@@ -112,11 +122,49 @@ class ToolNetworkPolicy(CanonicalModel):
112
122
  mode: Literal["forbidden"] = "forbidden"
113
123
 
114
124
 
125
+ class RuntimeArtifact(CanonicalModel):
126
+ value: str = Field(min_length=1)
127
+ media_type: str = Field(min_length=1)
128
+ byte_size: int = Field(ge=1)
129
+ sha256: str = Field(pattern=r"^[a-f0-9]{64}$")
130
+
131
+
132
+ class RuntimeRelease(CanonicalModel):
133
+ release_id: str = Field(min_length=1, max_length=80)
134
+ operation_version: str = Field(min_length=1, max_length=80)
135
+ workflow_version: str = Field(min_length=1, max_length=80)
136
+ prompt_version: str = Field(min_length=1, max_length=80)
137
+ route_version: str = Field(min_length=1, max_length=80)
138
+ tool_schema_version: str = Field(min_length=1, max_length=80)
139
+ manifest_digest: str = Field(pattern=r"^[a-f0-9]{64}$")
140
+
141
+
142
+ def legacy_release() -> RuntimeRelease:
143
+ return RuntimeRelease(
144
+ release_id="legacy",
145
+ operation_version="legacy",
146
+ workflow_version="legacy",
147
+ prompt_version="legacy",
148
+ route_version="legacy",
149
+ tool_schema_version="legacy",
150
+ manifest_digest="0" * 64,
151
+ )
152
+
153
+
115
154
  class StartRunRequest(CanonicalModel):
155
+ kind: Literal["agent"] = "agent"
116
156
  operation_key: str = Field(min_length=1)
117
157
  input: JsonValue
158
+ input_modalities: list[InputModality] = Field(
159
+ default_factory=lambda: default_modalities(),
160
+ min_length=1,
161
+ )
162
+ required_capabilities: list[str] = Field(default_factory=list, max_length=32)
163
+ artifacts: list[RuntimeArtifact] = Field(default_factory=list, max_length=16)
118
164
  instructions: str = ""
119
165
  user_prompt: str = ""
166
+ output_schema_key: str = ""
167
+ output_schema: dict[str, JsonValue] | None = None
120
168
  allowed_tools: list[RuntimeToolDefinition] = Field(default_factory=list)
121
169
  limits: RuntimeLimits = Field(default_factory=RuntimeLimits)
122
170
  provider_egress: ProviderEgress = Field(default_factory=ProviderEgress)
@@ -124,6 +172,8 @@ class StartRunRequest(CanonicalModel):
124
172
  tool_network_policy: ToolNetworkPolicy = Field(default_factory=ToolNetworkPolicy)
125
173
  execution_grant: str = ""
126
174
  tool_host_url: str = ""
175
+ release: RuntimeRelease = Field(default_factory=legacy_release)
176
+ trace_id: str = Field(default="", max_length=160)
127
177
 
128
178
 
129
179
  class StartRunResponse(CanonicalModel):
@@ -132,6 +182,13 @@ class StartRunResponse(CanonicalModel):
132
182
  events_url: str
133
183
 
134
184
 
185
+ class RunStatusResponse(CanonicalModel):
186
+ run_id: str
187
+ status: RunStatus
188
+ last_sequence: int = Field(ge=0)
189
+ terminal_event: dict[str, JsonValue] | None = None
190
+
191
+
135
192
  class RunResultResponse(CanonicalModel):
136
193
  run_id: str
137
194
  status: Literal["completed"]
@@ -152,3 +209,26 @@ class AgentError(CanonicalModel):
152
209
  message: str
153
210
  request_id: str
154
211
  retryable: bool
212
+
213
+
214
+ class EvaluationResultRequest(CanonicalModel):
215
+ release_id: str = Field(min_length=1, max_length=80)
216
+ evaluation_set_key: str = Field(min_length=1, max_length=160)
217
+ evaluation_set_version: str = Field(min_length=1, max_length=80)
218
+ metrics: dict[str, float] = Field(min_length=1, max_length=100)
219
+
220
+ @field_validator("metrics")
221
+ @classmethod
222
+ def validate_metrics(cls, metrics: dict[str, float]) -> dict[str, float]:
223
+ if not all(isfinite(value) for value in metrics.values()):
224
+ raise ValueError("评估指标必须是有限数值")
225
+ return metrics
226
+
227
+
228
+ class EvaluationResultResponse(CanonicalModel):
229
+ evaluation_id: str
230
+ release_id: str
231
+
232
+
233
+ def default_modalities() -> list[InputModality]:
234
+ return ["text"]
@@ -20,6 +20,14 @@ class DeterministicEngineAdapter:
20
20
  def __init__(self) -> None:
21
21
  self._cancellations: dict[str, asyncio.Event] = {}
22
22
 
23
+ @property
24
+ def capabilities(self) -> tuple[str, ...]:
25
+ return ("text-generation",)
26
+
27
+ @property
28
+ def input_modalities(self) -> tuple[str, ...]:
29
+ return ("text",)
30
+
23
31
  def supports(self, operation_key: str) -> bool:
24
32
  return operation_key in self.operations
25
33