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,16 +1,22 @@
1
1
  import type {
2
2
  AgentOperation,
3
3
  StartAgentRunRequest,
4
+ TerminalAgentEvent,
5
+ } from "../contracts/index.js";
6
+ import {
7
+ parseAttachAgentRunResponse,
8
+ parseStartAgentRunResponse,
4
9
  } from "../contracts/index.js";
5
- import { parseStartAgentRunResponse } from "../contracts/index.js";
6
10
  import { AgentClientError, requestError, safeHttpError } from "./error.js";
7
11
  import { safeJson } from "./http.js";
8
12
  import { HttpAgentRun } from "./run.js";
13
+ import { parseAgentArtifactRef } from "./artifact.js";
9
14
  import type {
10
15
  AgentCaller,
11
16
  AgentClientOptions,
12
17
  AgentFetch,
13
18
  AgentRun,
19
+ AttachAgentRunOptions,
14
20
  StartAgentRunOptions,
15
21
  } from "./types.js";
16
22
 
@@ -62,6 +68,9 @@ export class HttpAgentCaller implements AgentCaller {
62
68
  const request: StartAgentRunRequest<Input> = {
63
69
  operationKey: operation.key,
64
70
  input,
71
+ ...(options.artifacts?.length
72
+ ? { artifacts: options.artifacts.map(parseAgentArtifactRef) }
73
+ : {}),
65
74
  };
66
75
  let response: Response;
67
76
  try {
@@ -89,18 +98,94 @@ export class HttpAgentCaller implements AgentCaller {
89
98
  if (!response.ok) throw await safeHttpError(response, "创建 Agent Run");
90
99
 
91
100
  const created = parseCreatedRun(await safeJson(response, "创建 Agent Run"));
92
- const eventsUrl = this.endpoint(created.eventsUrl);
101
+ return this.createRun(operation, created.runId, created.eventsUrl, {
102
+ signal: options.signal,
103
+ });
104
+ }
105
+
106
+ async attach<Input, Output>(
107
+ operation: AgentOperation<Input, Output>,
108
+ runId: string,
109
+ options: AttachAgentRunOptions = {},
110
+ ): Promise<AgentRun<Output>> {
111
+ assertOperationAndRun(operation, runId);
112
+ const cursor = nonNegativeInteger(options.cursor, 0, "cursor");
113
+ let response: Response;
114
+ try {
115
+ response = await this.fetch(
116
+ this.endpoint(`${this.runsPath}/${encodeURIComponent(runId)}/attach`),
117
+ {
118
+ method: "POST",
119
+ headers: { "content-type": "application/json" },
120
+ body: JSON.stringify({ operationKey: operation.key, cursor }),
121
+ signal: options.signal,
122
+ },
123
+ );
124
+ } catch (error) {
125
+ if (options.signal?.aborted) {
126
+ throw new AgentClientError(
127
+ "CANCELLED",
128
+ "恢复 Agent Run 已取消",
129
+ false,
130
+ runId,
131
+ { cause: error },
132
+ );
133
+ }
134
+ throw requestError(error, "恢复 Agent Run", runId);
135
+ }
136
+ if (!response.ok) {
137
+ throw await safeHttpError(response, "恢复 Agent Run", runId);
138
+ }
139
+ const attached = parseAttachedRun(
140
+ await safeJson(response, "恢复 Agent Run", runId),
141
+ );
142
+ if (attached.runId !== runId) {
143
+ throw new AgentClientError(
144
+ "INVALID_RESPONSE",
145
+ "恢复 Agent Run 响应引用了错误的 Run",
146
+ false,
147
+ runId,
148
+ );
149
+ }
150
+ return this.createRun(operation, runId, attached.eventsUrl, {
151
+ signal: options.signal,
152
+ initialSequence: cursor,
153
+ initialTerminal: attached.terminalEvent,
154
+ });
155
+ }
156
+
157
+ resume<Input, Output>(
158
+ operation: AgentOperation<Input, Output>,
159
+ runId: string,
160
+ options?: AttachAgentRunOptions,
161
+ ): Promise<AgentRun<Output>> {
162
+ return this.attach(operation, runId, options);
163
+ }
164
+
165
+ private createRun<Input, Output>(
166
+ operation: AgentOperation<Input, Output>,
167
+ runId: string,
168
+ eventsPath: string,
169
+ state: {
170
+ signal?: AbortSignal;
171
+ initialSequence?: number;
172
+ initialTerminal?: TerminalAgentEvent;
173
+ },
174
+ ): AgentRun<Output> {
175
+ const operationQuery = `operationKey=${encodeURIComponent(operation.key)}`;
93
176
  return new HttpAgentRun<Output>({
94
- id: created.runId,
95
- eventsUrl,
177
+ id: runId,
178
+ eventsUrl: this.endpoint(eventsPath),
96
179
  resultUrl: this.endpoint(
97
- `${this.runsPath}/${encodeURIComponent(created.runId)}/result`,
180
+ `${this.runsPath}/${encodeURIComponent(runId)}/result?${operationQuery}`,
98
181
  ),
99
182
  cancelUrl: this.endpoint(
100
- `${this.runsPath}/${encodeURIComponent(created.runId)}/cancel`,
183
+ `${this.runsPath}/${encodeURIComponent(runId)}/cancel?${operationQuery}`,
101
184
  ),
102
185
  fetch: this.fetch,
103
- signal: options.signal,
186
+ signal: state.signal,
187
+ initialSequence: state.initialSequence,
188
+ initialTerminal: state.initialTerminal,
104
189
  maxReconnects: this.maxReconnects,
105
190
  reconnectDelayMs: this.reconnectDelayMs,
106
191
  wait: this.wait,
@@ -121,6 +206,19 @@ export class HttpAgentCaller implements AgentCaller {
121
206
  }
122
207
  }
123
208
 
209
+ function assertOperationAndRun<Input, Output>(
210
+ operation: AgentOperation<Input, Output>,
211
+ runId: string,
212
+ ): void {
213
+ if (!operation.key.trim() || !runId.trim() || runId.length > 160) {
214
+ throw new AgentClientError(
215
+ "INVALID_CONFIGURATION",
216
+ "Agent Operation 或 Run ID 无效",
217
+ false,
218
+ );
219
+ }
220
+ }
221
+
124
222
  function parseCreatedRun(value: unknown) {
125
223
  try {
126
224
  return parseStartAgentRunResponse(value);
@@ -135,6 +233,20 @@ function parseCreatedRun(value: unknown) {
135
233
  }
136
234
  }
137
235
 
236
+ function parseAttachedRun(value: unknown) {
237
+ try {
238
+ return parseAttachAgentRunResponse(value);
239
+ } catch (error) {
240
+ throw new AgentClientError(
241
+ "INVALID_RESPONSE",
242
+ "恢复 Agent Run 响应格式无效",
243
+ false,
244
+ undefined,
245
+ { cause: error },
246
+ );
247
+ }
248
+ }
249
+
138
250
  function validateBaseUrl(value: string | URL): URL {
139
251
  const url = new URL(value);
140
252
  if (url.protocol !== "http:" && url.protocol !== "https:") {
@@ -21,6 +21,8 @@ interface HttpAgentRunOptions<Output> {
21
21
  reconnectDelayMs: number;
22
22
  wait(milliseconds: number, signal?: AbortSignal): Promise<void>;
23
23
  parseOutput(value: unknown): Output;
24
+ initialSequence?: number;
25
+ initialTerminal?: TerminalAgentEvent;
24
26
  }
25
27
 
26
28
  type TerminalOutcome =
@@ -47,6 +49,18 @@ export class HttpAgentRun<Output> implements AgentRun<Output> {
47
49
  this.terminalOutcome = new Promise((resolve) => {
48
50
  this.resolveTerminal = resolve;
49
51
  });
52
+ this.lastSequence = options.initialSequence ?? 0;
53
+ if (
54
+ options.initialTerminal &&
55
+ options.initialTerminal.sequence <= this.lastSequence
56
+ ) {
57
+ if (options.initialTerminal.runId !== this.id) {
58
+ throw this.protocolViolation("恢复的 Agent Run 终态引用了错误的 Run");
59
+ }
60
+ this.terminal = options.initialTerminal;
61
+ this.queue.close();
62
+ this.resolveTerminal({ terminal: options.initialTerminal });
63
+ }
50
64
 
51
65
  if (options.signal?.aborted) {
52
66
  void this.cancel("调用方已取消").catch(() => undefined);
@@ -1,7 +1,15 @@
1
1
  import type { AgentEvent, AgentOperation } from "../contracts/index.js";
2
+ import type { ArtifactRef } from "../../artifact-ref.js";
2
3
 
3
4
  export interface StartAgentRunOptions {
4
5
  idempotencyKey: string;
6
+ /** 仅接受平台签发的 opaque ArtifactRef;客户端不上传 Base64 或任意 URL。 */
7
+ artifacts?: readonly ArtifactRef[];
8
+ signal?: AbortSignal;
9
+ }
10
+
11
+ export interface AttachAgentRunOptions {
12
+ cursor?: number;
5
13
  signal?: AbortSignal;
6
14
  }
7
15
 
@@ -18,6 +26,16 @@ export interface AgentCaller {
18
26
  input: Input,
19
27
  options: StartAgentRunOptions,
20
28
  ): Promise<AgentRun<Output>>;
29
+ attach<Input, Output>(
30
+ operation: AgentOperation<Input, Output>,
31
+ runId: string,
32
+ options?: AttachAgentRunOptions,
33
+ ): Promise<AgentRun<Output>>;
34
+ resume<Input, Output>(
35
+ operation: AgentOperation<Input, Output>,
36
+ runId: string,
37
+ options?: AttachAgentRunOptions,
38
+ ): Promise<AgentRun<Output>>;
21
39
  }
22
40
 
23
41
  export type AgentFetch = (
@@ -0,0 +1,53 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { createAgentCaller, defineAgentOperation } from "../index.js";
3
+ import { AgentClientError } from "./error.js";
4
+
5
+ const operation = defineAgentOperation<{ text: string }, unknown>(
6
+ "evidence.image.inspect",
7
+ (value) => value,
8
+ );
9
+
10
+ describe("Agent Vision Client", () => {
11
+ test("只提交经过校验的 opaque ArtifactRef", async () => {
12
+ let body: unknown;
13
+ const caller = createAgentCaller({
14
+ baseUrl: "https://agent.internal",
15
+ fetch: async (_input, init) => {
16
+ body = JSON.parse(String(init?.body));
17
+ return Response.json({
18
+ runId: "run-vision",
19
+ status: "accepted",
20
+ eventsUrl: "/api/agent/runs/run-vision/events",
21
+ });
22
+ },
23
+ });
24
+ const artifact = {
25
+ value: "opaque-artifact",
26
+ mediaType: "image/png",
27
+ byteSize: 128,
28
+ sha256: "a".repeat(64),
29
+ };
30
+
31
+ await caller.start(
32
+ operation,
33
+ { text: "分析图片" },
34
+ { idempotencyKey: "vision-1", artifacts: [artifact] },
35
+ );
36
+ expect(body).toEqual({
37
+ operationKey: "evidence.image.inspect",
38
+ input: { text: "分析图片" },
39
+ artifacts: [artifact],
40
+ });
41
+
42
+ await expect(
43
+ caller.start(
44
+ operation,
45
+ { text: "分析图片" },
46
+ {
47
+ idempotencyKey: "vision-invalid",
48
+ artifacts: [{ ...artifact, value: "https://evil.example/a.png" }],
49
+ },
50
+ ),
51
+ ).rejects.toBeInstanceOf(AgentClientError);
52
+ });
53
+ });
@@ -37,6 +37,7 @@ const events: AgentEvent[] = [
37
37
  runId: "run-1",
38
38
  sequence: 4,
39
39
  workItemId: "work-1",
40
+ toolCallId: "tool-call-1",
40
41
  },
41
42
  {
42
43
  type: "tool.completed",
@@ -16,6 +16,10 @@ export interface AgentRunStartedEvent extends AgentEventBase {
16
16
  type: "run.started";
17
17
  }
18
18
 
19
+ export interface AgentRunResumedEvent extends AgentEventBase {
20
+ type: "run.resumed";
21
+ }
22
+
19
23
  export interface AgentOutputDeltaEvent extends AgentEventBase {
20
24
  type: "output.delta";
21
25
  text: string;
@@ -30,6 +34,14 @@ export interface AgentToolRequestedEvent extends AgentEventBase {
30
34
  export interface AgentApprovalRequiredEvent extends AgentEventBase {
31
35
  type: "approval.required";
32
36
  workItemId: string;
37
+ toolCallId: string;
38
+ }
39
+
40
+ export interface AgentApprovalResolvedEvent extends AgentEventBase {
41
+ type: "approval.resolved";
42
+ workItemId: string;
43
+ toolCallId: string;
44
+ outcome: "approved" | "rejected" | "cancelled" | "timed-out";
33
45
  }
34
46
 
35
47
  export interface AgentToolCompletedEvent extends AgentEventBase {
@@ -57,9 +69,11 @@ export interface AgentRunCancelledEvent extends AgentEventBase {
57
69
 
58
70
  export type AgentEvent =
59
71
  | AgentRunStartedEvent
72
+ | AgentRunResumedEvent
60
73
  | AgentOutputDeltaEvent
61
74
  | AgentToolRequestedEvent
62
75
  | AgentApprovalRequiredEvent
76
+ | AgentApprovalResolvedEvent
63
77
  | AgentToolCompletedEvent
64
78
  | AgentUsageReportedEvent
65
79
  | AgentRunCompletedEvent
@@ -74,9 +88,11 @@ export type TerminalAgentEvent =
74
88
 
75
89
  const eventTypes: ReadonlySet<string> = new Set<AgentEventType>([
76
90
  "run.started",
91
+ "run.resumed",
77
92
  "output.delta",
78
93
  "tool.requested",
79
94
  "approval.required",
95
+ "approval.resolved",
80
96
  "tool.completed",
81
97
  "usage.reported",
82
98
  "run.completed",
@@ -98,6 +114,7 @@ export function parseAgentEvent(input: unknown): AgentEvent {
98
114
 
99
115
  switch (eventType) {
100
116
  case "run.started":
117
+ case "run.resumed":
101
118
  case "run.completed":
102
119
  case "run.cancelled":
103
120
  return { type: eventType, ...base };
@@ -119,7 +136,23 @@ export function parseAgentEvent(input: unknown): AgentEvent {
119
136
  type: eventType,
120
137
  ...base,
121
138
  workItemId: stringAt(record, "workItemId", "event"),
139
+ toolCallId: stringAt(record, "toolCallId", "event"),
140
+ };
141
+ case "approval.resolved": {
142
+ const outcome = stringAt(record, "outcome", "event");
143
+ if (
144
+ !["approved", "rejected", "cancelled", "timed-out"].includes(outcome)
145
+ ) {
146
+ throw new AgentContractParseError("event.outcome", "审批结果无效");
147
+ }
148
+ return {
149
+ type: eventType,
150
+ ...base,
151
+ workItemId: stringAt(record, "workItemId", "event"),
152
+ toolCallId: stringAt(record, "toolCallId", "event"),
153
+ outcome: outcome as AgentApprovalResolvedEvent["outcome"],
122
154
  };
155
+ }
123
156
  case "tool.completed":
124
157
  return {
125
158
  type: eventType,
@@ -1,4 +1,10 @@
1
1
  import { AgentContractParseError, recordAt, stringAt } from "./validation.js";
2
+ import type { ArtifactRef } from "../../artifact-ref.js";
3
+ import {
4
+ isTerminalAgentEvent,
5
+ parseAgentEvent,
6
+ type TerminalAgentEvent,
7
+ } from "./events.js";
2
8
 
3
9
  export type AgentRunStatus =
4
10
  | "accepted"
@@ -11,6 +17,7 @@ export type AgentRunStatus =
11
17
  export interface StartAgentRunRequest<Input> {
12
18
  operationKey: string;
13
19
  input: Input;
20
+ artifacts?: readonly ArtifactRef[];
14
21
  }
15
22
 
16
23
  export interface StartAgentRunResponse {
@@ -19,6 +26,14 @@ export interface StartAgentRunResponse {
19
26
  eventsUrl: string;
20
27
  }
21
28
 
29
+ export interface AttachAgentRunResponse {
30
+ runId: string;
31
+ status: AgentRunStatus;
32
+ lastSequence: number;
33
+ eventsUrl: string;
34
+ terminalEvent?: TerminalAgentEvent;
35
+ }
36
+
22
37
  const terminalStatuses: ReadonlySet<AgentRunStatus> = new Set([
23
38
  "completed",
24
39
  "failed",
@@ -51,6 +66,62 @@ export function parseStartAgentRunResponse(
51
66
  };
52
67
  }
53
68
 
69
+ export function parseAttachAgentRunResponse(
70
+ input: unknown,
71
+ ): AttachAgentRunResponse {
72
+ const record = recordAt(input, "attachAgentRunResponse");
73
+ const status = stringAt(record, "status", "attachAgentRunResponse");
74
+ if (!runStatuses.has(status)) {
75
+ throw new AgentContractParseError(
76
+ "attachAgentRunResponse.status",
77
+ "Run 状态无效",
78
+ );
79
+ }
80
+ if (
81
+ !Number.isSafeInteger(record.lastSequence) ||
82
+ Number(record.lastSequence) < 0
83
+ ) {
84
+ throw new AgentContractParseError(
85
+ "attachAgentRunResponse.lastSequence",
86
+ "事件序列无效",
87
+ );
88
+ }
89
+ const runId = stringAt(record, "runId", "attachAgentRunResponse");
90
+ const terminalEvent =
91
+ record.terminalEvent === undefined
92
+ ? undefined
93
+ : parseAgentEvent(record.terminalEvent);
94
+ if (
95
+ terminalEvent &&
96
+ (!isTerminalAgentEvent(terminalEvent) ||
97
+ terminalEvent.runId !== runId ||
98
+ terminalEvent.sequence !== Number(record.lastSequence))
99
+ ) {
100
+ throw new AgentContractParseError(
101
+ "attachAgentRunResponse.terminalEvent",
102
+ "终态事件无效",
103
+ );
104
+ }
105
+ if (
106
+ isTerminalAgentRunStatus(status as AgentRunStatus) !==
107
+ Boolean(terminalEvent)
108
+ ) {
109
+ throw new AgentContractParseError(
110
+ "attachAgentRunResponse.terminalEvent",
111
+ "终态与事件不一致",
112
+ );
113
+ }
114
+ return {
115
+ runId,
116
+ status: status as AgentRunStatus,
117
+ lastSequence: Number(record.lastSequence),
118
+ eventsUrl: stringAt(record, "eventsUrl", "attachAgentRunResponse"),
119
+ ...(terminalEvent && isTerminalAgentEvent(terminalEvent)
120
+ ? { terminalEvent }
121
+ : {}),
122
+ };
123
+ }
124
+
54
125
  export function isTerminalAgentRunStatus(
55
126
  status: AgentRunStatus,
56
127
  ): status is Extract<AgentRunStatus, "completed" | "failed" | "cancelled"> {
@@ -1,4 +1,5 @@
1
1
  export * from "./client/index.js";
2
+ export type { ArtifactRef } from "../artifact-ref.js";
2
3
  export {
3
4
  defineAgentOperation,
4
5
  isTerminalAgentEvent,
@@ -0,0 +1,8 @@
1
+ export interface ArtifactRef {
2
+ /** 平台签发的不透明短时引用;业务不得解析、拼接或持久化底层地址。 */
3
+ readonly value: string;
4
+ readonly mediaType: string;
5
+ readonly byteSize: number;
6
+ readonly sha256: string;
7
+ readonly expiresAt?: string;
8
+ }
@@ -0,0 +1,114 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { createDocumentAiClient } from "./client.js";
3
+ import type { DocumentResult } from "./contracts.js";
4
+ import { DocumentClientError } from "./error.js";
5
+
6
+ const artifact = {
7
+ value: "artifact_opaque",
8
+ mediaType: "application/pdf",
9
+ byteSize: 128,
10
+ sha256: "a".repeat(64),
11
+ };
12
+
13
+ describe("Document AI Client", () => {
14
+ test("提交、页级事件、结果与取消使用稳定 Document 协议", async () => {
15
+ const requests: Array<{ url: string; init?: RequestInit }> = [];
16
+ const client = createDocumentAiClient({
17
+ baseUrl: "https://windy.example",
18
+ fetch: async (input, init) => {
19
+ const url = String(input);
20
+ requests.push({ url, init });
21
+ if (url.endsWith("/api/document/runs")) {
22
+ return Response.json({
23
+ runId: "doc-run-1",
24
+ status: "accepted",
25
+ eventsUrl: "/api/document/runs/doc-run-1/events",
26
+ });
27
+ }
28
+ if (url.includes("/events")) {
29
+ return new Response(
30
+ [
31
+ 'data: {"type":"run.started","runId":"doc-run-1","sequence":1}',
32
+ "",
33
+ 'data: {"type":"document.page.completed","runId":"doc-run-1","sequence":2,"pageNumber":1,"completedPages":1,"totalPages":1}',
34
+ "",
35
+ 'data: {"type":"run.completed","runId":"doc-run-1","sequence":3}',
36
+ "",
37
+ "",
38
+ ].join("\n"),
39
+ );
40
+ }
41
+ if (url.endsWith("/result")) {
42
+ return Response.json({
43
+ output: {
44
+ pages: [],
45
+ tables: [],
46
+ artifacts: [],
47
+ metadata: { engine: "local" },
48
+ },
49
+ });
50
+ }
51
+ return Response.json({ runId: "doc-run-1", status: "cancelled" });
52
+ },
53
+ });
54
+ const run = await client.submit(
55
+ {
56
+ key: "document.ocr.fast",
57
+ parseOutput(value) {
58
+ return value as DocumentResult;
59
+ },
60
+ },
61
+ { source: artifact, options: { outputs: ["pages"] } },
62
+ { idempotencyKey: "document-1" },
63
+ );
64
+
65
+ const events = [];
66
+ for await (const event of run.events) events.push(event);
67
+ expect(events.map(({ type }) => type)).toEqual([
68
+ "run.started",
69
+ "document.page.completed",
70
+ "run.completed",
71
+ ]);
72
+ expect(await run.result()).toEqual({
73
+ pages: [],
74
+ tables: [],
75
+ artifacts: [],
76
+ metadata: { engine: "local" },
77
+ });
78
+ await run.cancel("不再需要");
79
+ expect(JSON.parse(String(requests[0]?.init?.body))).toEqual({
80
+ operationKey: "document.ocr.fast",
81
+ source: artifact,
82
+ options: { outputs: ["pages"] },
83
+ });
84
+ expect(requests[0]?.init?.headers).toMatchObject({
85
+ "idempotency-key": "document-1",
86
+ });
87
+ });
88
+
89
+ test("拒绝无效 ArtifactRef 和跨 Origin events URL", async () => {
90
+ const client = createDocumentAiClient({
91
+ baseUrl: "https://windy.example",
92
+ fetch: async () =>
93
+ Response.json({
94
+ runId: "doc-run-1",
95
+ status: "accepted",
96
+ eventsUrl: "https://evil.example/events",
97
+ }),
98
+ });
99
+ await expect(
100
+ client.submit(
101
+ { key: "document.ocr.fast", parseOutput: (value) => value },
102
+ { source: { ...artifact, sha256: "invalid" } },
103
+ { idempotencyKey: "document-invalid" },
104
+ ),
105
+ ).rejects.toBeInstanceOf(DocumentClientError);
106
+ await expect(
107
+ client.submit(
108
+ { key: "document.ocr.fast", parseOutput: (value) => value },
109
+ { source: artifact },
110
+ { idempotencyKey: "document-origin" },
111
+ ),
112
+ ).rejects.toBeInstanceOf(DocumentClientError);
113
+ });
114
+ });