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
@@ -8,6 +8,7 @@ import {
8
8
  resolveToolNetworkPolicy,
9
9
  } from "@southwind-ai/modules";
10
10
  import type {
11
+ ModuleAiArtifactRef,
11
12
  ModuleAiJsonValue,
12
13
  ModuleAiOperationHandlerRegistration,
13
14
  } from "@southwind-ai/server-sdk";
@@ -17,15 +18,35 @@ import type { InitializedModuleHosts } from "../module-host.js";
17
18
  import { toModuleActor } from "../module-host.js";
18
19
  import type { createServerRuntime } from "../runtime.js";
19
20
  import { findFeature } from "../route-guards.js";
20
- import { ExecutionGrantStore } from "./execution-grants.js";
21
+ import type { AiArtifactRuntime } from "../ai-artifact/runtime.js";
22
+ import {
23
+ AgentRunAuthorizationError,
24
+ type AgentExecutionAuthorization,
25
+ type AgentRunAuthorizationStore,
26
+ } from "./run-authorization.js";
21
27
  import type {
22
28
  AgentRuntimePort,
29
+ AgentRuntimeOperation,
23
30
  AgentRuntimeStartResponse,
31
+ AgentRuntimeStatusResponse,
24
32
  } from "./run-contracts.js";
33
+ import { AgentRuntimeError } from "./run-contracts.js";
34
+ import { buildAgentRuntimeRelease } from "./run-release.js";
25
35
  import {
26
36
  governProviderInput,
27
37
  ProviderInputGovernanceError,
28
38
  } from "./provider-input-governance.js";
39
+ import { rotateVisionArtifacts, validateOutputSchema } from "./vision-input.js";
40
+ import {
41
+ assertInputSize,
42
+ digest,
43
+ publicRunPath,
44
+ runtimeErrorStatus,
45
+ toolDefinitions,
46
+ } from "./run-definition-support.js";
47
+ import { AgentRunFacadeError, inaccessibleRun } from "./run-errors.js";
48
+ import { scopedRuntimeIdempotencyKey } from "./idempotency-key.js";
49
+ export { AgentRunFacadeError } from "./run-errors.js";
29
50
 
30
51
  type ServerRuntime = ReturnType<typeof createServerRuntime>;
31
52
 
@@ -37,29 +58,37 @@ export class AgentRunFacade {
37
58
  hosts: InitializedModuleHosts,
38
59
  private readonly runtime: ServerRuntime,
39
60
  private readonly remote: AgentRuntimePort,
40
- private readonly grants: ExecutionGrantStore,
61
+ private readonly authorizations: AgentRunAuthorizationStore,
62
+ private readonly artifacts?: AiArtifactRuntime,
41
63
  ) {
42
64
  const handlers = new Map(
43
- hosts.aiOperationHandlers.map(({ registration }) => [
65
+ hosts.aiOperationHandlers.map(({ registration, moduleName }) => [
44
66
  registration.operationKey,
45
- registration,
67
+ { registration, moduleName },
46
68
  ]),
47
69
  );
48
70
  const features = modules.flatMap(({ features }) => features);
49
71
  this.#operations = new Map(
50
72
  modules.flatMap((module) =>
51
- (module.aiOperations ?? []).map(
52
- (definition) =>
53
- [
54
- definition.key,
55
- {
73
+ (module.aiOperations ?? []).map((definition) => {
74
+ const handler = handlers.get(definition.key);
75
+ const tools = toolDefinitions(modules, definition.allowedToolKeys);
76
+ return [
77
+ definition.key,
78
+ {
79
+ definition,
80
+ ...handler,
81
+ feature: findFeature(features, definition.featureKey),
82
+ tools,
83
+ release: buildAgentRuntimeRelease(
84
+ modules,
85
+ module,
56
86
  definition,
57
- handler: handlers.get(definition.key),
58
- feature: findFeature(features, definition.featureKey),
59
- tools: toolDefinitions(modules, definition.allowedToolKeys),
60
- },
61
- ] as const,
62
- ),
87
+ handler?.registration,
88
+ ),
89
+ },
90
+ ] as const;
91
+ }),
63
92
  ),
64
93
  );
65
94
  }
@@ -69,9 +98,15 @@ export class AgentRunFacade {
69
98
  input: ModuleAiJsonValue,
70
99
  idempotencyKey: string,
71
100
  context: RequestGuardContext,
101
+ artifacts: readonly ModuleAiArtifactRef[] = [],
102
+ signal: AbortSignal = AbortSignal.timeout(10_000),
72
103
  ): Promise<AgentRuntimeStartResponse> {
73
104
  const operation = this.#operations.get(operationKey);
74
- if (!operation?.handler) {
105
+ if (
106
+ !operation?.registration?.prepare ||
107
+ !operation.moduleName ||
108
+ operation.definition.kind !== "agent"
109
+ ) {
75
110
  throw new AgentRunFacadeError(
76
111
  "OPERATION_NOT_FOUND",
77
112
  "Agent Operation 不存在",
@@ -118,7 +153,7 @@ export class AgentRunFacade {
118
153
  }
119
154
  throw error;
120
155
  }
121
- const plan = await operation.handler.prepare(governedInput, {
156
+ const plan = await operation.registration.prepare(governedInput, {
122
157
  actor: toModuleActor(context.actor),
123
158
  requestId: context.requestId,
124
159
  });
@@ -129,14 +164,48 @@ export class AgentRunFacade {
129
164
  400,
130
165
  );
131
166
  }
132
- const grant = this.grants.issue(operation.definition, context);
167
+ validateOutputSchema(plan.outputSchema, operation.definition);
168
+ const rotatedArtifacts = await rotateVisionArtifacts(
169
+ artifacts,
170
+ operation.definition,
171
+ operation.moduleName,
172
+ this.artifacts,
173
+ context,
174
+ signal,
175
+ );
176
+ const release = operation.release;
177
+ const traceId = digest({
178
+ requestId: context.requestId,
179
+ operationKey,
180
+ }).slice(0, 32);
181
+ let authorization: AgentExecutionAuthorization | undefined;
133
182
  try {
183
+ authorization = await this.authorizations.issue({
184
+ operationKey,
185
+ releaseId: release.releaseId,
186
+ idempotencyKey,
187
+ authorizationSnapshot: {
188
+ permissionKey: operation.definition.permissionKey,
189
+ featureKey: operation.definition.featureKey,
190
+ allowedToolKeys: [...operation.definition.allowedToolKeys],
191
+ toolNetworkMode: resolveToolNetworkPolicy(operation.definition).mode,
192
+ },
193
+ context,
194
+ });
134
195
  const created = await this.remote.start(
135
196
  {
197
+ kind: "agent",
136
198
  operationKey,
137
199
  input: governedInput,
200
+ inputModalities: operation.definition.inputModalities ?? ["text"],
201
+ requiredCapabilities: operation.definition.requiredCapabilities,
202
+ artifacts: rotatedArtifacts,
138
203
  instructions: plan.instructions,
139
204
  userPrompt: plan.userPrompt,
205
+ outputSchemaKey: operation.definition.outputSchemaKey,
206
+ ...(plan.outputSchema === undefined
207
+ ? {}
208
+ : { outputSchema: plan.outputSchema }),
140
209
  allowedTools: operation.tools,
141
210
  limits: operation.definition.limits,
142
211
  providerEgress: resolveProviderEgress(operation.definition),
@@ -145,11 +214,17 @@ export class AgentRunFacade {
145
214
  envelopeFields: [],
146
215
  },
147
216
  toolNetworkPolicy: resolveToolNetworkPolicy(operation.definition),
148
- executionGrant: grant.token,
217
+ executionGrant: authorization.token,
218
+ release,
219
+ traceId,
149
220
  },
150
- idempotencyKey,
221
+ scopedRuntimeIdempotencyKey(
222
+ context.actor.id,
223
+ operationKey,
224
+ idempotencyKey,
225
+ ),
151
226
  );
152
- this.grants.bindRun(grant.token, created.runId);
227
+ await this.authorizations.bind(authorization, created.runId);
153
228
  this.runtime.auditSink.recordEvent({
154
229
  id: randomUUIDv7(),
155
230
  action: operation.definition.auditAction,
@@ -161,65 +236,110 @@ export class AgentRunFacade {
161
236
  label: operationKey,
162
237
  },
163
238
  requestId: context.requestId,
239
+ metadata: {
240
+ artifactCount: rotatedArtifacts.length,
241
+ artifactBytes: rotatedArtifacts.reduce(
242
+ (total, artifact) => total + artifact.byteSize,
243
+ 0,
244
+ ),
245
+ artifactSha256: rotatedArtifacts.map(({ sha256 }) => sha256),
246
+ requiredCapabilities: operation.definition.requiredCapabilities,
247
+ inputModalities: operation.definition.inputModalities ?? ["text"],
248
+ outputSchemaKey: operation.definition.outputSchemaKey,
249
+ releaseId: release.releaseId,
250
+ operationVersion: release.operationVersion,
251
+ workflowVersion: release.workflowVersion,
252
+ promptVersion: release.promptVersion,
253
+ routeVersion: release.routeVersion,
254
+ toolSchemaVersion: release.toolSchemaVersion,
255
+ traceId,
256
+ },
164
257
  occurredAt: new Date().toISOString(),
165
258
  });
166
259
  return {
167
260
  ...created,
168
- eventsUrl: `/api/agent/runs/${created.runId}/events`,
261
+ eventsUrl: publicRunPath(created.runId, "events", operationKey),
169
262
  };
170
263
  } catch (error) {
171
- this.grants.discard(grant.token);
264
+ if (authorization) await this.authorizations.discard(authorization);
265
+ if (error instanceof AgentRuntimeError) {
266
+ throw new AgentRunFacadeError(
267
+ error.code,
268
+ error.message,
269
+ runtimeErrorStatus(error.code),
270
+ );
271
+ }
172
272
  throw error;
173
273
  }
174
274
  }
175
275
 
176
- authorizeRun(runId: string, context: RequestGuardContext): void {
177
- this.grants.requireRun(runId, context.actor.id);
178
- }
179
- }
180
-
181
- interface RegisteredOperation {
182
- definition: ModuleAiOperationDefinition;
183
- handler?: ModuleAiOperationHandlerRegistration;
184
- feature: ReturnType<typeof findFeature>;
185
- tools: Array<{ key: string; label: string; description: string }>;
186
- }
187
-
188
- export class AgentRunFacadeError extends Error {
189
- constructor(
190
- readonly code:
191
- | "OPERATION_NOT_FOUND"
192
- | "INVALID_INPUT"
193
- | "FORBIDDEN"
194
- | "RUNTIME_UNAVAILABLE",
195
- message: string,
196
- readonly status: number,
276
+ async authorizeRun(
277
+ operationKey: string,
278
+ runId: string,
279
+ context: RequestGuardContext,
197
280
  ) {
198
- super(message);
281
+ const operation = this.#operations.get(operationKey);
282
+ if (!operation || operation.definition.kind !== "agent") {
283
+ throw inaccessibleRun();
284
+ }
285
+ try {
286
+ await this.authorizations.authorizeBrowser(operationKey, runId, context);
287
+ } catch (error) {
288
+ if (error instanceof AgentRunAuthorizationError) throw inaccessibleRun();
289
+ throw error;
290
+ }
291
+ const decision = await evaluateAccessGuards(this.runtime, context, {
292
+ permission: {
293
+ permissionKey: operation.definition.permissionKey,
294
+ },
295
+ feature: operation.feature,
296
+ });
297
+ if (!decision.decision.allowed) throw inaccessibleRun();
298
+ return operation;
199
299
  }
200
- }
201
300
 
202
- function toolDefinitions(
203
- modules: readonly ModuleManifest[],
204
- allowedKeys: readonly string[],
205
- ) {
206
- const tools = new Map(
207
- modules.flatMap(({ tools }) => tools.map((tool) => [tool.key, tool])),
208
- );
209
- return allowedKeys.map((key) => {
210
- const tool = tools.get(key);
211
- if (!tool) throw new Error(`AI Operation 引用了未知 Tool:${key}`);
301
+ async attach(
302
+ operationKey: string,
303
+ runId: string,
304
+ cursor: number,
305
+ context: RequestGuardContext,
306
+ signal?: AbortSignal,
307
+ ) {
308
+ await this.authorizeRun(operationKey, runId, context);
309
+ let status: AgentRuntimeStatusResponse;
310
+ try {
311
+ status = await this.remote.status(runId, signal);
312
+ } catch (error) {
313
+ if (error instanceof AgentRuntimeError) {
314
+ if (error.code === "RUN_CONFLICT") throw inaccessibleRun();
315
+ throw new AgentRunFacadeError(
316
+ error.code,
317
+ error.message,
318
+ runtimeErrorStatus(error.code),
319
+ );
320
+ }
321
+ throw error;
322
+ }
323
+ if (status.runId !== runId) throw inaccessibleRun();
324
+ if (cursor > status.lastSequence) {
325
+ throw new AgentRunFacadeError(
326
+ "INVALID_INPUT",
327
+ "事件游标超出 Agent Run 当前序列",
328
+ 400,
329
+ );
330
+ }
212
331
  return {
213
- key: tool.key,
214
- label: tool.label,
215
- description: tool.description,
332
+ ...status,
333
+ eventsUrl: publicRunPath(runId, "events", operationKey),
216
334
  };
217
- });
335
+ }
218
336
  }
219
337
 
220
- function assertInputSize(value: ModuleAiJsonValue, maxBytes: number): void {
221
- const size = new TextEncoder().encode(JSON.stringify(value)).byteLength;
222
- if (size > maxBytes) {
223
- throw new AgentRunFacadeError("INVALID_INPUT", "Agent 输入超过上限", 400);
224
- }
338
+ interface RegisteredOperation {
339
+ definition: ModuleAiOperationDefinition;
340
+ registration?: ModuleAiOperationHandlerRegistration;
341
+ moduleName?: string;
342
+ feature: ReturnType<typeof findFeature>;
343
+ tools: AgentRuntimeOperation["allowedTools"];
344
+ release: AgentRuntimeOperation["release"];
225
345
  }
@@ -0,0 +1,78 @@
1
+ import { createHash } from "node:crypto";
2
+ import type {
3
+ ModuleAiOperationDefinition,
4
+ ModuleManifest,
5
+ } from "@southwind-ai/modules";
6
+ import type { ModuleAiOperationHandlerRegistration } from "@southwind-ai/server-sdk";
7
+ import type { AgentRuntimeRelease } from "./run-contracts.js";
8
+
9
+ export function buildAgentRuntimeRelease(
10
+ modules: readonly ModuleManifest[],
11
+ module: ModuleManifest,
12
+ definition: ModuleAiOperationDefinition,
13
+ registration: ModuleAiOperationHandlerRegistration | undefined,
14
+ ): AgentRuntimeRelease {
15
+ const versions = registration?.release ?? {
16
+ workflowVersion: module.version,
17
+ promptVersion: module.version,
18
+ routeVersion: module.version,
19
+ };
20
+ const coordinates = {
21
+ operationVersion: module.version,
22
+ ...versions,
23
+ toolSchemaVersion: operationSchemaDigest(modules, definition),
24
+ manifestDigest: digest(module),
25
+ };
26
+ return {
27
+ releaseId: `release_${digest(coordinates).slice(0, 40)}`,
28
+ ...coordinates,
29
+ };
30
+ }
31
+
32
+ function operationSchemaDigest(
33
+ modules: readonly ModuleManifest[],
34
+ definition: ModuleAiOperationDefinition,
35
+ ): string {
36
+ const schemas = new Map(
37
+ modules.flatMap(({ jsonSchemas }) =>
38
+ (jsonSchemas ?? []).map((schema) => [schema.key, schema.schema] as const),
39
+ ),
40
+ );
41
+ const tools = new Map(
42
+ modules.flatMap((item) =>
43
+ item.tools.map((tool) => [tool.key, tool] as const),
44
+ ),
45
+ );
46
+ return digest({
47
+ operationInput: schemas.get(definition.inputSchemaKey),
48
+ operationOutput: schemas.get(definition.outputSchemaKey),
49
+ tools: definition.allowedToolKeys.map((key) => {
50
+ const tool = tools.get(key);
51
+ return {
52
+ definition: tool,
53
+ inputSchema: tool ? schemas.get(tool.inputSchemaKey) : undefined,
54
+ outputSchema: tool ? schemas.get(tool.outputSchemaKey) : undefined,
55
+ };
56
+ }),
57
+ });
58
+ }
59
+
60
+ function digest(value: unknown): string {
61
+ return createHash("sha256").update(canonicalJson(value)).digest("hex");
62
+ }
63
+
64
+ function canonicalJson(value: unknown): string {
65
+ return JSON.stringify(canonicalValue(value));
66
+ }
67
+
68
+ function canonicalValue(value: unknown): unknown {
69
+ if (Array.isArray(value)) return value.map(canonicalValue);
70
+ if (value !== null && typeof value === "object") {
71
+ return Object.fromEntries(
72
+ Object.entries(value as Record<string, unknown>)
73
+ .sort(([left], [right]) => left.localeCompare(right))
74
+ .map(([key, item]) => [key, canonicalValue(item)]),
75
+ );
76
+ }
77
+ return value;
78
+ }
@@ -0,0 +1,169 @@
1
+ import type {
2
+ ModuleAiArtifactRef,
3
+ ModuleAiJsonValue,
4
+ } from "@southwind-ai/server-sdk";
5
+ import { AgentRunFacadeError } from "./run-errors.js";
6
+
7
+ export function parseAttachRequest(body: unknown): {
8
+ operationKey: string;
9
+ cursor: number;
10
+ } {
11
+ if (
12
+ !isRecord(body) ||
13
+ Object.keys(body).some((key) => !["operationKey", "cursor"].includes(key))
14
+ ) {
15
+ throw new AgentRunFacadeError("INVALID_INPUT", "请求格式无效", 400);
16
+ }
17
+ return {
18
+ operationKey: parseOperationKey(body.operationKey),
19
+ cursor: parseCursorValue(body.cursor),
20
+ };
21
+ }
22
+
23
+ export function parseOperationKey(value: unknown): string {
24
+ if (
25
+ typeof value !== "string" ||
26
+ !/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$/.test(value) ||
27
+ value.length > 200
28
+ ) {
29
+ throw new AgentRunFacadeError("INVALID_INPUT", "Operation Key 无效", 400);
30
+ }
31
+ return value;
32
+ }
33
+
34
+ export function parseStartRequest(body: unknown): {
35
+ operationKey: string;
36
+ input: ModuleAiJsonValue;
37
+ artifacts: readonly ModuleAiArtifactRef[];
38
+ } {
39
+ if (
40
+ !isRecord(body) ||
41
+ Object.keys(body).some(
42
+ (key) => !["operationKey", "input", "artifacts"].includes(key),
43
+ )
44
+ ) {
45
+ throw new AgentRunFacadeError("INVALID_INPUT", "请求格式无效", 400);
46
+ }
47
+ const operationKey = parseOperationKey(body.operationKey);
48
+ assertJson(body.input);
49
+ return {
50
+ operationKey,
51
+ input: body.input,
52
+ artifacts: parseArtifacts(body.artifacts),
53
+ };
54
+ }
55
+
56
+ export function parseToolRequest(body: unknown) {
57
+ const allowed = [
58
+ "executionGrant",
59
+ "runId",
60
+ "toolCallId",
61
+ "toolKey",
62
+ "arguments",
63
+ ];
64
+ if (
65
+ !isRecord(body) ||
66
+ Object.keys(body).some((key) => !allowed.includes(key))
67
+ ) {
68
+ throw new Error("Tool 请求格式无效");
69
+ }
70
+ if (
71
+ typeof body.executionGrant !== "string" ||
72
+ typeof body.runId !== "string" ||
73
+ typeof body.toolCallId !== "string" ||
74
+ typeof body.toolKey !== "string" ||
75
+ !isRecord(body.arguments)
76
+ ) {
77
+ throw new Error("Tool 请求字段无效");
78
+ }
79
+ if (
80
+ !body.runId.trim() ||
81
+ !body.toolCallId.trim() ||
82
+ body.runId.length > 160 ||
83
+ body.toolCallId.length > 200 ||
84
+ body.toolKey.length > 200
85
+ ) {
86
+ throw new Error("Tool 请求标识无效");
87
+ }
88
+ assertJson(body.arguments);
89
+ return {
90
+ executionGrant: body.executionGrant,
91
+ invocation: {
92
+ runId: body.runId,
93
+ toolCallId: body.toolCallId,
94
+ toolKey: body.toolKey,
95
+ arguments: body.arguments,
96
+ },
97
+ };
98
+ }
99
+
100
+ export function parseCursor(
101
+ after: string | undefined,
102
+ header: string | null,
103
+ ): number {
104
+ const values = [after, header].filter(
105
+ (value): value is string => value !== undefined && value !== null,
106
+ );
107
+ if (values.some((value) => !/^\d+$/.test(value))) {
108
+ throw new AgentRunFacadeError("INVALID_INPUT", "事件游标无效", 400);
109
+ }
110
+ return Math.max(0, ...values.map(Number));
111
+ }
112
+
113
+ function parseCursorValue(value: unknown): number {
114
+ if (value === undefined) return 0;
115
+ if (!Number.isSafeInteger(value) || Number(value) < 0) {
116
+ throw new AgentRunFacadeError("INVALID_INPUT", "事件游标无效", 400);
117
+ }
118
+ return Number(value);
119
+ }
120
+
121
+ function parseArtifacts(value: unknown): readonly ModuleAiArtifactRef[] {
122
+ if (value === undefined) return [];
123
+ if (!Array.isArray(value) || value.length > 32) {
124
+ throw new AgentRunFacadeError(
125
+ "INVALID_INPUT",
126
+ "Agent Artifact 列表无效",
127
+ 400,
128
+ );
129
+ }
130
+ return value.map((item) => {
131
+ if (
132
+ !isRecord(item) ||
133
+ typeof item.value !== "string" ||
134
+ !item.value ||
135
+ /^[a-z][a-z0-9+.-]*:\/\//i.test(item.value) ||
136
+ typeof item.mediaType !== "string" ||
137
+ !item.mediaType ||
138
+ !Number.isSafeInteger(item.byteSize) ||
139
+ Number(item.byteSize) < 0 ||
140
+ typeof item.sha256 !== "string" ||
141
+ !/^[a-f0-9]{64}$/.test(item.sha256)
142
+ ) {
143
+ throw new AgentRunFacadeError(
144
+ "INVALID_INPUT",
145
+ "Agent ArtifactRef 无效",
146
+ 400,
147
+ );
148
+ }
149
+ return {
150
+ value: item.value,
151
+ mediaType: item.mediaType,
152
+ byteSize: Number(item.byteSize),
153
+ sha256: item.sha256,
154
+ expiresAt: typeof item.expiresAt === "string" ? item.expiresAt : "",
155
+ };
156
+ });
157
+ }
158
+
159
+ function isRecord(value: unknown): value is Record<string, unknown> {
160
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
161
+ }
162
+
163
+ function assertJson(value: unknown): asserts value is ModuleAiJsonValue {
164
+ try {
165
+ if (JSON.stringify(value) === undefined) throw new Error();
166
+ } catch {
167
+ throw new AgentRunFacadeError("INVALID_INPUT", "请求必须是 JSON", 400);
168
+ }
169
+ }