create-windy 0.9.1 → 0.10.1

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 (253) hide show
  1. package/dist/cli.js +1691 -1151
  2. package/package.json +2 -2
  3. package/template/.agents/skills/windy-business-module/SKILL.md +33 -1
  4. package/template/.windy-template.json +6 -2
  5. package/template/AGENTS.md +4 -0
  6. package/template/README.md +2 -0
  7. package/template/apps/agent-server/Dockerfile +1 -1
  8. package/template/apps/agent-server/README.md +1 -1
  9. package/template/apps/agent-server/native/EXTENSIONS.md +2 -2
  10. package/template/apps/agent-server/native/parallel-harness.ts +2 -0
  11. package/template/apps/agent-server/native/storage-lifecycle.test.ts +2 -1
  12. package/template/apps/agent-server/package.json +42 -41
  13. package/template/apps/agent-server/plugins/downloads/README.md +1 -1
  14. package/template/apps/agent-server/plugins/drop-overlay/README.md +1 -1
  15. package/template/apps/agent-server/plugins/settings/README.md +1 -1
  16. package/template/apps/agent-server/plugins/skill-library/client-chunks.test.ts +4 -1
  17. package/template/apps/agent-server/plugins/theme/README.md +5 -1
  18. package/template/apps/agent-server/plugins/theme/source-tokens.ts +0 -1
  19. package/template/apps/agent-server/plugins/theme/tokens.json +0 -4
  20. package/template/apps/agent-server/plugins/tool-catalog/README.md +6 -3
  21. package/template/apps/agent-server/plugins/tool-catalog/discovery.mjs +13 -10
  22. package/template/apps/agent-server/plugins/tool-catalog/discovery.test.ts +68 -6
  23. package/template/apps/agent-server/runtime/compatibility.ts +7 -1
  24. package/template/apps/agent-server/runtime/dsh-engine.ts +2 -0
  25. package/template/apps/agent-server/runtime/questions-workflow.nodecheck.ts +2 -0
  26. package/template/apps/server/Dockerfile +2 -1
  27. package/template/apps/server/package.json +11 -11
  28. package/template/apps/server/scripts/validate-module-composition-once.ts +2 -0
  29. package/template/apps/server/scripts/validate-module-composition.ts +8 -4
  30. package/template/apps/server/src/admin/routes.test.ts +29 -1
  31. package/template/apps/server/src/admin/routes.ts +20 -2
  32. package/template/apps/server/src/agent/client-session/completion.ts +1 -1
  33. package/template/apps/server/src/agent/client-session/manifest-admission.ts +1 -1
  34. package/template/apps/server/src/agent/client-session/request.ts +1 -1
  35. package/template/apps/server/src/agent/context-version-routes.ts +37 -13
  36. package/template/apps/server/src/agent/run-facade.ts +1 -1
  37. package/template/apps/server/src/agent/run-input-verification.ts +1 -1
  38. package/template/apps/server/src/agent/run-routes.ts +1 -14
  39. package/template/apps/server/src/agent/run-start-preparation.ts +1 -1
  40. package/template/apps/server/src/agent/runtime-bootstrap-input.ts +2 -0
  41. package/template/apps/server/src/agent/runtime-bootstrap.ts +49 -157
  42. package/template/apps/server/src/agent/runtime-disabled-composition.ts +2 -1
  43. package/template/apps/server/src/agent/session-invalidation/authorization-revocation.ts +54 -0
  44. package/template/apps/server/src/agent/session-runtime-composition.ts +1 -51
  45. package/template/apps/server/src/agent/unavailable-routes.ts +13 -2
  46. package/template/apps/server/src/agent-workflow/runtime-composition.ts +3 -0
  47. package/template/apps/server/src/agent-workflow/service.ts +17 -1
  48. package/template/apps/server/src/ai-artifact/media-resource-route.test.ts +134 -0
  49. package/template/apps/server/src/ai-artifact/routes.ts +1 -1
  50. package/template/apps/server/src/ai-artifact/runtime.ts +12 -0
  51. package/template/apps/server/src/ai-runtime-readiness/operation-port.test-support.ts +1 -1
  52. package/template/apps/server/src/ai-runtime-readiness/operation-port.ts +8 -0
  53. package/template/apps/server/src/ai-runtime-security/internal-token.ts +14 -0
  54. package/template/apps/server/src/app/antivirus-composition.test.ts +63 -0
  55. package/template/apps/server/src/app/antivirus-composition.ts +29 -0
  56. package/template/apps/server/src/app/dsh-workbench-socket-audit.test.ts +2 -1
  57. package/template/apps/server/src/configuration/definition.ts +15 -11
  58. package/template/apps/server/src/document/facade-support.ts +2 -2
  59. package/template/apps/server/src/document/facade.ts +2 -2
  60. package/template/apps/server/src/document/output-artifact-composition.ts +9 -5
  61. package/template/apps/server/src/document/output-artifact.test-support.ts +1 -1
  62. package/template/apps/server/src/embedding/facade.ts +1 -1
  63. package/template/apps/server/src/embedding/input.ts +1 -1
  64. package/template/apps/server/src/embedding/routes.ts +1 -1
  65. package/template/apps/server/src/index.ts +60 -60
  66. package/template/apps/server/src/license/routes.ts +4 -1
  67. package/template/apps/server/src/license/runtime-business-access.test.ts +261 -0
  68. package/template/apps/server/src/media-runtime/composition.ts +157 -0
  69. package/template/apps/server/src/media-runtime/media-only-composition.ts +44 -0
  70. package/template/apps/server/src/media-runtime/module-ai-port-factory.test.ts +69 -0
  71. package/template/apps/server/src/media-runtime/module-ai-port-factory.ts +72 -0
  72. package/template/apps/server/src/{agent → media-runtime}/runtime-readiness-sources.ts +14 -6
  73. package/template/apps/server/src/media-runtime/shared-composition.ts +104 -0
  74. package/template/apps/server/src/module-composition.test.ts +22 -0
  75. package/template/apps/server/src/module-composition.ts +48 -0
  76. package/template/apps/server/src/module-host/ai-operation-port.ts +97 -30
  77. package/template/apps/server/src/module-host/ai-operation-speech-normalize.test.ts +1 -1
  78. package/template/apps/server/src/module-host/http-runtime.ts +9 -3
  79. package/template/apps/server/src/module-host/static-route-authorization.test.ts +6 -3
  80. package/template/apps/server/src/module-host/storage-authorization-binding.test.ts +4 -0
  81. package/template/apps/server/src/module-runtime-validation.ts +2 -0
  82. package/template/apps/server/src/observability/diagnostic-index.ts +26 -1
  83. package/template/apps/server/src/observability/diagnostic-repository.ts +40 -11
  84. package/template/apps/server/src/platform-http-runtime.ts +1 -1
  85. package/template/apps/server/src/runtime-feature.ts +5 -1
  86. package/template/apps/server/src/runtime.ts +3 -0
  87. package/template/apps/server/src/settings/drizzle-watermark-repository.ts +35 -4
  88. package/template/apps/server/src/settings/watermark-repository.ts +14 -0
  89. package/template/apps/server/src/settings/watermark-routes.test.ts +84 -2
  90. package/template/apps/server/src/settings/watermark-routes.ts +109 -0
  91. package/template/apps/server/src/settings/watermark-transfer.ts +79 -0
  92. package/template/apps/server/src/speech/facade-validation.ts +1 -1
  93. package/template/apps/server/src/speech/facade.ts +2 -2
  94. package/template/apps/server/src/startup-composition-validation.test.ts +32 -0
  95. package/template/apps/server/src/startup-finalization.ts +2 -0
  96. package/template/apps/server/src/startup-runtime.ts +77 -0
  97. package/template/apps/server/src/system/ai-runtime-build.ts +3 -0
  98. package/template/apps/server/src/system/ai-runtime-configuration.test.ts +36 -0
  99. package/template/apps/server/src/system/ai-runtime-configuration.ts +58 -0
  100. package/template/apps/server/src/system/diagnostic-routes.test.ts +45 -0
  101. package/template/apps/server/src/system/diagnostic-routes.ts +35 -0
  102. package/template/apps/server/src/system/environment-policy-reader.test.ts +74 -0
  103. package/template/apps/server/src/system/environment-policy-reader.ts +62 -0
  104. package/template/apps/server/src/system/feature-environment-assessment.test.ts +148 -0
  105. package/template/apps/server/src/system/feature-environment-assessment.ts +179 -0
  106. package/template/apps/server/src/system/host-diagnostics.test.ts +155 -0
  107. package/template/apps/server/src/system/host-diagnostics.ts +225 -0
  108. package/template/apps/server/src/system/menu-registry.test.ts +26 -0
  109. package/template/apps/server/src/system/menu-registry.ts +9 -7
  110. package/template/apps/server/src/system/operations.test.ts +5 -0
  111. package/template/apps/server/src/system/operations.ts +36 -12
  112. package/template/apps/server/src/system/role-authorization-postgres.integration.test.ts +181 -0
  113. package/template/apps/web/Dockerfile +7 -3
  114. package/template/apps/web/dev/dev-port.test.ts +6 -6
  115. package/template/apps/web/dev/dev-port.ts +1 -1
  116. package/template/apps/web/dev/start.ts +6 -1
  117. package/template/apps/web/index.html +1 -0
  118. package/template/apps/web/package.json +16 -16
  119. package/template/apps/web/runtime-server.test.ts +22 -1
  120. package/template/apps/web/runtime-server.ts +6 -0
  121. package/template/apps/web/src/App.vue +6 -3
  122. package/template/apps/web/src/assets/windy.svg +4 -0
  123. package/template/apps/web/src/components/auth/LoginForm.vue +2 -2
  124. package/template/apps/web/src/components/auth/PasswordChangeForm.vue +14 -3
  125. package/template/apps/web/src/components/common/FormDialog.vue +1 -1
  126. package/template/apps/web/src/components/license/LicenseFlow.webtest.ts +3 -0
  127. package/template/apps/web/src/components/license/LicenseMachineCodeQr.vue +99 -0
  128. package/template/apps/web/src/components/license/LicenseMachineCodeQr.webtest.ts +83 -0
  129. package/template/apps/web/src/components/license/LicensePairingCard.vue +1 -1
  130. package/template/apps/web/src/components/license/LicenseStatusCard.vue +14 -2
  131. package/template/apps/web/src/composables/useLoginFlow.ts +2 -2
  132. package/template/apps/web/src/composables/usePlatformSettings.ts +1 -1
  133. package/template/apps/web/src/composables/useWatermarkSettings.ts +1 -0
  134. package/template/apps/web/src/layout/AppSidebar.vue +5 -9
  135. package/template/apps/web/src/layout/AppSidebar.webtest.ts +4 -1
  136. package/template/apps/web/src/pages/auth/ChangePasswordPage.vue +67 -6
  137. package/template/apps/web/src/pages/auth/LoginPage.vue +3 -12
  138. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationEditor.vue +30 -0
  139. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationWorkbench.vue +25 -14
  140. package/template/apps/web/src/pages/configuration/components/TimeZoneSelect.vue +55 -0
  141. package/template/apps/web/src/pages/profile/PersonalSettingsPage.vue +64 -11
  142. package/template/apps/web/src/pages/profile/PersonalSettingsPage.webtest.ts +21 -1
  143. package/template/apps/web/src/pages/settings/BrandingSettingsSection.vue +7 -18
  144. package/template/apps/web/src/pages/settings/BrandingSettingsSection.webtest.ts +9 -4
  145. package/template/apps/web/src/pages/settings/GlobalSettingsPage.vue +51 -6
  146. package/template/apps/web/src/pages/settings/GlobalSettingsPage.webtest.ts +19 -3
  147. package/template/apps/web/src/pages/settings/WatermarkSettingsSection.vue +1 -0
  148. package/template/apps/web/src/pages/settings/WatermarkYamlTransfer.vue +211 -0
  149. package/template/apps/web/src/pages/system/DiagnosticLookupPage.vue +18 -3
  150. package/template/apps/web/src/pages/system/DiagnosticLookupPage.webtest.ts +4 -1
  151. package/template/apps/web/src/pages/system/SystemOperationsPage.vue +23 -0
  152. package/template/apps/web/src/pages/system/components/DiagnosticLogList.vue +120 -0
  153. package/template/apps/web/src/pages/system/components/OperationEnvironmentPanel.vue +237 -0
  154. package/template/apps/web/src/pages/system/components/OperationFeatureAssessmentPanel.vue +131 -0
  155. package/template/apps/web/src/pages/system/components/PagedSelectionList.vue +31 -24
  156. package/template/apps/web/src/pages/system/components/SystemResourceActions.vue +2 -1
  157. package/template/apps/web/src/pages/system/components/SystemResourceEditor.vue +11 -2
  158. package/template/apps/web/src/pages/system/components/SystemResourcePage.vue +1 -0
  159. package/template/apps/web/src/pages/system/components/SystemResourcePage.webtest.ts +6 -2
  160. package/template/apps/web/src/pages/system/components/agent-skills/AgentSkillImportDialog.vue +18 -9
  161. package/template/apps/web/src/pages/system/components/agent-workbench/SessionReplayTimeline.vue +18 -9
  162. package/template/apps/web/src/pages/system/components/agent-workbench/TestRunComposer.vue +66 -34
  163. package/template/apps/web/src/pages/system/components/agent-workbench/ToolCatalogPanel.vue +48 -33
  164. package/template/apps/web/src/pages/system/components/agent-workbench/ToolCatalogPanel.webtest.ts +9 -2
  165. package/template/apps/web/src/pages/system/components/agent-workbench/ToolRuleDetail.vue +23 -16
  166. package/template/apps/web/src/pages/system/components/dictionary-bulk/DictionaryBulkJobDetail.vue +10 -7
  167. package/template/apps/web/src/pages/system/components/user-account/RolePermissionSummary.vue +81 -57
  168. package/template/apps/web/src/pages/system/components/user-account/UserRoleSelector.vue +6 -4
  169. package/template/apps/web/src/pages/system/components/user-account/UserRoleSelector.webtest.ts +20 -4
  170. package/template/apps/web/src/pages/system/resource-config.ts +9 -0
  171. package/template/apps/web/src/pages/work-orders/WorkOrderListPage.vue +1 -1
  172. package/template/apps/web/src/router/disabled-feature-loading.webtest.ts +78 -0
  173. package/template/apps/web/src/router/index.ts +3 -6
  174. package/template/apps/web/src/router/manifest-routes.ts +2 -3
  175. package/template/apps/web/src/router/platform-admin-pages.ts +21 -62
  176. package/template/apps/web/src/router/platform-admin-route-bindings.ts +4 -4
  177. package/template/apps/web/src/services/diagnostics-api.ts +13 -1
  178. package/template/apps/web/src/services/operations-api.ts +4 -0
  179. package/template/apps/web/src/services/platform-settings-api.ts +35 -1
  180. package/template/apps/web/vite.config.ts +9 -3
  181. package/template/deploy/environment/windy.environment.example.yaml +53 -0
  182. package/template/docker-compose.yml +4 -2
  183. package/template/docs/architecture/agent-admin-workbench-implementation.md +1 -1
  184. package/template/docs/architecture/agent-admin-workbench-plan.md +1 -1
  185. package/template/docs/architecture/agent-workflow.md +8 -0
  186. package/template/docs/architecture/durable-jobs.md +17 -0
  187. package/template/docs/architecture/embedded-dsh-integration.md +1 -1
  188. package/template/docs/development/dsh-plugins.md +1 -1
  189. package/template/docs/development/performance-engineering.md +24 -0
  190. package/template/docs/operations/admin-settings-yaml.md +29 -0
  191. package/template/docs/operations/dsh-agent-migration.md +2 -2
  192. package/template/docs/operations/environment-capabilities.md +185 -0
  193. package/template/docs/platform/module-configuration.md +18 -3
  194. package/template/docs/platform/platform-authentication.md +1 -1
  195. package/template/package.json +10 -8
  196. package/template/packages/config/package.json +2 -2
  197. package/template/packages/config/src/definition.ts +14 -4
  198. package/template/packages/config/src/environment-loader.ts +27 -3
  199. package/template/packages/config/src/platform-ai-runtime.test.ts +94 -0
  200. package/template/packages/config/src/platform.ts +5 -2
  201. package/template/packages/database/drizzle/0065_durable_job_lease_snapshot_lookup.sql +1 -0
  202. package/template/packages/database/drizzle/0066_agent_source_sequence_bigint.sql +3 -0
  203. package/template/packages/database/drizzle/0067_media_runtime_access.sql +46 -0
  204. package/template/packages/database/drizzle/meta/0065_snapshot.json +15370 -0
  205. package/template/packages/database/drizzle/meta/0066_snapshot.json +15370 -0
  206. package/template/packages/database/drizzle/meta/0067_snapshot.json +15370 -0
  207. package/template/packages/database/drizzle/meta/_journal.json +21 -0
  208. package/template/packages/database/package.json +3 -3
  209. package/template/packages/database/src/schema/agent-session.ts +4 -3
  210. package/template/packages/database/src/schema/durable-jobs.ts +4 -0
  211. package/template/packages/database/src/schema/index.ts +3 -1
  212. package/template/packages/database/src/schema-durable-recovery.test.ts +21 -1
  213. package/template/packages/database/src/schema-source-sequence.test.ts +24 -0
  214. package/template/packages/example-work-order/package.json +2 -2
  215. package/template/packages/modules/package.json +1 -1
  216. package/template/packages/modules/src/admin-ui-boundary.test.ts +33 -0
  217. package/template/packages/modules/src/composition.ts +11 -28
  218. package/template/packages/modules/src/manifest.ts +2 -0
  219. package/template/packages/modules/src/menu-composition.ts +24 -0
  220. package/template/packages/modules/src/system-admin-routes.ts +2 -2
  221. package/template/packages/modules/src/system-admin-ui-catalog.ts +6 -1
  222. package/template/packages/modules/src/system-ai-api-bindings.ts +0 -16
  223. package/template/packages/modules/src/system-api-control-plane.ts +9 -1
  224. package/template/packages/modules/src/system-api-permissions.ts +7 -0
  225. package/template/packages/modules/src/system-api-watermark.ts +21 -0
  226. package/template/packages/modules/src/system-audit-actions.ts +3 -1
  227. package/template/packages/modules/src/system-features.ts +21 -3
  228. package/template/packages/modules/src/system-media-api-bindings.ts +97 -0
  229. package/template/packages/modules/src/system-module-audit-selection.ts +2 -0
  230. package/template/packages/modules/src/system-module-catalog.test.ts +13 -6
  231. package/template/packages/modules/src/system-module-catalog.ts +6 -241
  232. package/template/packages/modules/src/system-module-definitions.ts +260 -0
  233. package/template/packages/modules/src/system-modules-access.test.ts +2 -0
  234. package/template/packages/modules/src/system-modules.test.ts +7 -1
  235. package/template/packages/modules/src/system-modules.ts +25 -13
  236. package/template/packages/modules/src/system-permissions.ts +12 -0
  237. package/template/packages/server-sdk/package.json +1 -1
  238. package/template/packages/shared/index.ts +3 -0
  239. package/template/packages/shared/package.json +1 -1
  240. package/template/packages/shared/src/environment-assessment.ts +68 -0
  241. package/template/packages/shared/src/environment-policy.ts +205 -0
  242. package/template/packages/shared/src/license-console-capabilities.ts +7 -0
  243. package/template/packages/shared/src/license.ts +1 -0
  244. package/template/packages/shared/src/module-configuration.ts +2 -1
  245. package/template/scripts/operations/collect-device-info.py +230 -0
  246. package/template/apps/server/src/agent/module-ai-port-factory.ts +0 -41
  247. /package/template/apps/server/src/{agent → ai-runtime-security}/execution-grants.ts +0 -0
  248. /package/template/apps/server/src/{agent → ai-runtime-security}/idempotency-key.test.ts +0 -0
  249. /package/template/apps/server/src/{agent → ai-runtime-security}/idempotency-key.ts +0 -0
  250. /package/template/apps/server/src/{agent → ai-runtime-security}/provider-input-governance.test.ts +0 -0
  251. /package/template/apps/server/src/{agent → ai-runtime-security}/provider-input-governance.ts +0 -0
  252. /package/template/apps/server/src/{agent → media-runtime}/required-environment.ts +0 -0
  253. /package/template/apps/server/src/{agent → media-runtime}/runtime-readiness-sources.test.ts +0 -0
@@ -1,4 +1,6 @@
1
1
  process.argv.push("--validate-module-composition");
2
2
  delete process.env.DATABASE_URL;
3
+ // This isolated process validates declarations and handlers, not durable production startup.
4
+ process.env.NODE_ENV = "test";
3
5
 
4
6
  await import("../src/index.js");
@@ -1,11 +1,15 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
 
3
+ const environment = { ...process.env };
4
+ delete environment.PLATFORM_ENABLE_AI;
5
+ delete environment.WINDY_ENVIRONMENT_PROFILE_FILE;
6
+
3
7
  const variants = [
4
- { name: "default", env: { PLATFORM_ENABLE_AI: "false" } },
8
+ { name: "default", env: { PLATFORM_ENABLE_AI_RUNTIME: "false" } },
5
9
  {
6
10
  name: "ai-enabled",
7
11
  env: {
8
- PLATFORM_ENABLE_AI: "true",
12
+ PLATFORM_ENABLE_AI_RUNTIME: "true",
9
13
  AGENT_SERVER_INTERNAL_TOKEN: "0123456789abcdef0123456789abcdef",
10
14
  AI_ARTIFACT_ENCRYPTION_SECRET: "abcdef0123456789abcdef0123456789",
11
15
  },
@@ -15,10 +19,10 @@ const variants = [
15
19
  for (const variant of variants) {
16
20
  const result = spawnSync(
17
21
  process.execPath,
18
- ["run", "scripts/validate-module-composition-once.ts"],
22
+ ["--no-env-file", "run", "scripts/validate-module-composition-once.ts"],
19
23
  {
20
24
  cwd: process.cwd(),
21
- env: { ...process.env, ...variant.env, DATABASE_URL: "" },
25
+ env: { ...environment, ...variant.env, DATABASE_URL: "" },
22
26
  stdio: "inherit",
23
27
  },
24
28
  );
@@ -48,8 +48,36 @@ describe("Windy 管理面准入", () => {
48
48
  });
49
49
  });
50
50
 
51
- async function invoke(actor: RequestActor): Promise<unknown> {
51
+ // @windy-module system.agent begin
52
+ test.each([false, true])(
53
+ "AI 装配为 %s 时同步管理菜单与页面授权",
54
+ async (enabled) => {
55
+ const result = (await invoke(adminActor(), enabled)) as {
56
+ menus: Array<{ key: string; children?: Array<{ key: string }> }>;
57
+ access: { features: Array<{ key: string; enabled: boolean }> };
58
+ };
59
+ const group = result.menus.find(({ key }) => key === "system.ai");
60
+ if (enabled) {
61
+ expect(group?.children?.map(({ key }) => key)).toContain(
62
+ "system.agent-workbench",
63
+ );
64
+ } else {
65
+ expect(group).toBeUndefined();
66
+ expect(result.access.features).toContainEqual(
67
+ expect.objectContaining({
68
+ key: "system.agent-workbench",
69
+ enabled: false,
70
+ allowed: false,
71
+ }),
72
+ );
73
+ }
74
+ },
75
+ );
76
+ // @windy-module system.agent end
77
+
78
+ async function invoke(actor: RequestActor, enableAI = false): Promise<unknown> {
52
79
  const snapshot = createFoundationSnapshot();
80
+ snapshot.platform.enableAI = enableAI;
53
81
  const runtime = createServerRuntime(
54
82
  {},
55
83
  {
@@ -65,10 +65,28 @@ export function registerAdminBootstrapRoute(
65
65
  }
66
66
 
67
67
  const routes = source.modules.flatMap((module) => module.adminRoutes);
68
- const routeKeys = new Set(routes.map(({ key }) => key));
68
+ const disabledAiFeatures = new Set(
69
+ source.snapshot.platform.enableAI
70
+ ? []
71
+ : source.modules.flatMap(({ menus }) =>
72
+ menus
73
+ .filter(({ key }) => key === "system.ai")
74
+ .flatMap(
75
+ ({ children }) =>
76
+ children?.map(({ featureKey }) => featureKey) ?? [],
77
+ ),
78
+ ),
79
+ );
80
+ const routeKeys = new Set(
81
+ routes
82
+ .filter(({ featureKey }) => !disabledAiFeatures.has(featureKey))
83
+ .map(({ key }) => key),
84
+ );
69
85
  const resolvedFeatures = await Promise.all(
70
86
  source.snapshot.features.map((feature) =>
71
- runtime.resolveFeature(guardContext, feature),
87
+ disabledAiFeatures.has(feature.key)
88
+ ? { ...feature, enabled: false, visible: "hidden" as const }
89
+ : runtime.resolveFeature(guardContext, feature),
72
90
  ),
73
91
  );
74
92
  return {
@@ -21,7 +21,7 @@ import {
21
21
  ClientAgentRequestError,
22
22
  parseClientAgentCompletion,
23
23
  } from "./request.js";
24
- import { ProviderInputGovernanceError } from "../provider-input-governance.js";
24
+ import { ProviderInputGovernanceError } from "../../ai-runtime-security/provider-input-governance.js";
25
25
  import {
26
26
  enforceClientSessionAttemptAuthority,
27
27
  enforceClientSessionProviderBoundary,
@@ -10,7 +10,7 @@ import {
10
10
  governProviderInput,
11
11
  projectProviderInput,
12
12
  ProviderInputGovernanceError,
13
- } from "../provider-input-governance.js";
13
+ } from "../../ai-runtime-security/provider-input-governance.js";
14
14
  import {
15
15
  buildRegisteredOperations,
16
16
  type RegisteredOperation,
@@ -1,6 +1,6 @@
1
1
  import type { AiProviderDataPolicy } from "@southwind-ai/modules";
2
2
  import type { ModuleAiJsonValue } from "@southwind-ai/server-sdk";
3
- import { governProviderEnvelope } from "../provider-input-governance.js";
3
+ import { governProviderEnvelope } from "../../ai-runtime-security/provider-input-governance.js";
4
4
 
5
5
  const allowedKeys = new Set([
6
6
  "model",
@@ -16,7 +16,7 @@ import type {
16
16
  CommitAttemptContextInput,
17
17
  } from "./context-version-runtime.js";
18
18
  import { assertNoPrivateReasoning } from "./session-log/events.js";
19
- import { validInternalToken } from "./run-routes.js";
19
+ import { validInternalToken } from "../ai-runtime-security/internal-token.js";
20
20
 
21
21
  const MAX_TEXT = 512;
22
22
  const PATH = "/internal/agent/context-versions";
@@ -80,9 +80,15 @@ export function registerUnavailableAgentContextVersionRoute(
80
80
  });
81
81
  }
82
82
 
83
- function parseInput(value: unknown, requestId: string):
83
+ function parseInput(
84
+ value: unknown,
85
+ requestId: string,
86
+ ):
84
87
  | { phase: "prepare"; input: AttemptContextBaseInput }
85
- | { phase: "abort"; input: AttemptContextBaseInput & { preparationId: string } }
88
+ | {
89
+ phase: "abort";
90
+ input: AttemptContextBaseInput & { preparationId: string };
91
+ }
86
92
  | { phase: "commit"; input: CommitAttemptContextInput } {
87
93
  if (!isRecord(value) || value.contractVersion !== 2) throw invalid();
88
94
  const base = parseBase(value, requestId);
@@ -91,7 +97,8 @@ function parseInput(value: unknown, requestId: string):
91
97
  if (value.phase === "abort") {
92
98
  return { phase: "abort", input: { ...base, preparationId } };
93
99
  }
94
- if (value.phase !== "commit" || !isRecord(value.governedEnvelope)) throw invalid();
100
+ if (value.phase !== "commit" || !isRecord(value.governedEnvelope))
101
+ throw invalid();
95
102
  const provider = parseProvider(value.governedEnvelope.provider);
96
103
  if (!Array.isArray(value.governedEnvelope.messages)) throw invalid();
97
104
  const messages = value.governedEnvelope.messages.map(parseMessage);
@@ -105,7 +112,8 @@ function parseBase(
105
112
  value: Record<string, unknown>,
106
113
  requestId: string,
107
114
  ): AttemptContextBaseInput {
108
- if (!Array.isArray(value.sources) || value.sources.length !== 1) throw invalid();
115
+ if (!Array.isArray(value.sources) || value.sources.length !== 1)
116
+ throw invalid();
109
117
  return {
110
118
  sessionId: text(value.sessionId),
111
119
  turnId: text(value.turnId),
@@ -150,16 +158,25 @@ function parseMessage(value: unknown): GovernedContextMessage {
150
158
  function parseOrigin(value: unknown): ContextMessageOrigin {
151
159
  if (!isRecord(value)) throw invalid();
152
160
  const type = value.type;
153
- if (![
154
- "instructions", "user-prompt", "artifact", "context-source",
155
- "provider-output", "tool-receipt",
156
- ].includes(String(type))) throw invalid();
161
+ if (
162
+ ![
163
+ "instructions",
164
+ "user-prompt",
165
+ "artifact",
166
+ "context-source",
167
+ "provider-output",
168
+ "tool-receipt",
169
+ ].includes(String(type))
170
+ )
171
+ throw invalid();
157
172
  return {
158
173
  type: type as ContextMessageOrigin["type"],
159
174
  sourceId: text(value.sourceId),
160
175
  sourceVersion: text(value.sourceVersion),
161
176
  digest: digest(value.digest),
162
- ...(value.source === undefined ? {} : { source: parseReference(value.source) }),
177
+ ...(value.source === undefined
178
+ ? {}
179
+ : { source: parseReference(value.source) }),
163
180
  };
164
181
  }
165
182
 
@@ -190,7 +207,11 @@ function parseReference(value: unknown): ContextSourceReference {
190
207
  : { expiresAt: timestamp(value.validity.expiresAt) }),
191
208
  ...(value.validity.reason === undefined
192
209
  ? {}
193
- : { reason: text(value.validity.reason) as ContextSourceReference["validity"]["reason"] }),
210
+ : {
211
+ reason: text(
212
+ value.validity.reason,
213
+ ) as ContextSourceReference["validity"]["reason"],
214
+ }),
194
215
  },
195
216
  };
196
217
  }
@@ -213,7 +234,8 @@ function positiveInteger(value: unknown) {
213
234
  }
214
235
 
215
236
  function digest(value: unknown) {
216
- if (typeof value !== "string" || !/^[a-f0-9]{64}$/.test(value)) throw invalid();
237
+ if (typeof value !== "string" || !/^[a-f0-9]{64}$/.test(value))
238
+ throw invalid();
217
239
  return value;
218
240
  }
219
241
 
@@ -234,7 +256,9 @@ function isRecord(value: unknown): value is Record<string, unknown> {
234
256
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
235
257
  }
236
258
 
237
- function invalid() { return new Error("Context Version 请求无效"); }
259
+ function invalid() {
260
+ return new Error("Context Version 请求无效");
261
+ }
238
262
 
239
263
  function errorResponse(code: string, message: string, status: number) {
240
264
  return Response.json({ code, message, retryable: status >= 500 }, { status });
@@ -35,7 +35,7 @@ import {
35
35
  mapAgentRuntimeError,
36
36
  } from "./run-definition-support.js";
37
37
  import { AgentRunFacadeError } from "./run-errors.js";
38
- import { scopedRuntimeIdempotencyKey } from "./idempotency-key.js";
38
+ import { scopedRuntimeIdempotencyKey } from "../ai-runtime-security/idempotency-key.js";
39
39
  import { verifyCommittedRunInput } from "./run-input-verification.js";
40
40
  import {
41
41
  buildRegisteredOperations,
@@ -4,7 +4,7 @@ import type { RequestGuardContext } from "../guards.js";
4
4
  import {
5
5
  projectProviderInput,
6
6
  ProviderInputGovernanceError,
7
- } from "./provider-input-governance.js";
7
+ } from "../ai-runtime-security/provider-input-governance.js";
8
8
  import {
9
9
  AgentRunAuthorizationError,
10
10
  type AgentRunAuthorizationStore,
@@ -4,7 +4,7 @@ import {
4
4
  required,
5
5
  isRecord,
6
6
  } from "./run-route-response.js";
7
- import { timingSafeEqual } from "node:crypto";
7
+ import { validInternalToken } from "../ai-runtime-security/internal-token.js";
8
8
  import type { RequestGuardContext } from "../guards.js";
9
9
  import type { RouteApp } from "../system/routes.js";
10
10
  import type { AgentRunFacade } from "./run-facade.js";
@@ -326,16 +326,3 @@ async function completeSessionRun(
326
326
  }
327
327
  await port.completeForRun({ sessionId, runId, outcome, context });
328
328
  }
329
-
330
- export function validInternalToken(
331
- request: Request,
332
- expected: string,
333
- ): boolean {
334
- const provided = request.headers
335
- .get("authorization")
336
- ?.replace(/^Bearer /, "");
337
- if (!provided || !expected) return false;
338
- const left = Buffer.from(provided);
339
- const right = Buffer.from(expected);
340
- return left.length === right.length && timingSafeEqual(left, right);
341
- }
@@ -6,7 +6,7 @@ import {
6
6
  governProviderInput,
7
7
  projectProviderInput,
8
8
  ProviderInputGovernanceError,
9
- } from "./provider-input-governance.js";
9
+ } from "../ai-runtime-security/provider-input-governance.js";
10
10
  import { assertInputSize } from "./run-definition-support.js";
11
11
  import { AgentRunFacadeError } from "./run-errors.js";
12
12
  import type { RegisteredOperation } from "./run-operation-registry.js";
@@ -1,3 +1,4 @@
1
+ import type { AiRuntimeSwitches } from "@southwind-ai/shared";
1
2
  import type { PlatformConfig } from "@southwind-ai/config";
2
3
  import type { DurableHandlerRegistry } from "@southwind-ai/jobs";
3
4
  import type { ModuleManifest } from "@southwind-ai/modules";
@@ -24,6 +25,7 @@ import type { AgentWorkflowWorkspacePolicyDefinition } from "../agent-workflow/w
24
25
  export interface AgentPlatformRuntimeInput {
25
26
  app: RouteApp;
26
27
  platformConfig: PlatformConfig;
28
+ aiRuntimeSwitches?: AiRuntimeSwitches;
27
29
  modules: readonly ModuleManifest[];
28
30
  hosts: InitializedModuleHosts;
29
31
  runtime: ReturnType<typeof createServerRuntime>;
@@ -1,38 +1,7 @@
1
1
  import { resolveCurrentSystemActor } from "../system/actor.js";
2
2
  import type { AgentAdminReadinessRef } from "./admin-catalog-bootstrap.js";
3
3
  import { registerAgentAdminCatalogRuntime } from "./admin-catalog-bootstrap.js";
4
- import { AiArtifactCodec } from "../ai-artifact/codec.js";
5
- import { registerAiArtifactRoutes } from "../ai-artifact/routes.js";
6
- import { AiArtifactRuntime } from "../ai-artifact/runtime.js";
7
- import { DocumentRunFacade } from "../document/facade.js";
8
- import {
9
- RemoteDocumentRuntime,
10
- unavailableDocumentRuntime,
11
- } from "../document/remote-runtime.js";
12
- import { registerDocumentRunRoutes } from "../document/routes.js";
13
- import { createDocumentOutputRuntime } from "../document/output-artifact-composition.js";
14
- import { installDocumentOutputRuntimeRoutes } from "../document/output-artifact-route-composition.js";
15
- import { createModuleAiOperationReadinessPort } from "../ai-runtime-readiness/operation-port.js";
16
- import { AiRuntimeReadinessTrustStoreV1 } from "../ai-runtime-readiness/trust-store.js";
17
- import { EmbeddingFacade } from "../embedding/facade.js";
18
- import { EmbeddingRunAuthorizations } from "../embedding/run-authorization.js";
19
- import { InMemoryEmbeddingRunBindingRepository } from "../embedding/run-binding-repository.js";
20
- import { DrizzleEmbeddingRunBindingRepository } from "../embedding/drizzle-run-binding-repository.js";
21
- import {
22
- RemoteEmbeddingRuntime,
23
- unavailableEmbeddingRuntime,
24
- } from "../embedding/remote-runtime.js";
25
- import {
26
- registerEmbeddingRoutes,
27
- registerEmbeddingRunArtifactRoutes,
28
- } from "../embedding/routes.js";
29
- import { SpeechRunFacade } from "../speech/facade.js";
30
- import {
31
- RemoteSpeechRuntime,
32
- unavailableSpeechRuntime,
33
- } from "../speech/remote-runtime.js";
34
- import { registerSpeechRunRoutes } from "../speech/routes.js";
35
- import { ExecutionGrantStore } from "./execution-grants.js";
4
+ import { composeSharedAiRuntime } from "../media-runtime/shared-composition.js";
36
5
  import { RemoteAgentRuntime } from "./remote-runtime.js";
37
6
  import { AgentRunFacade } from "./run-facade.js";
38
7
  import { registerAgentRunRoutes } from "./run-routes.js";
@@ -41,18 +10,16 @@ import { DrizzleAgentRunBindingRepository } from "./drizzle-run-binding-reposito
41
10
  import { AgentRunAuthorizationStore } from "./run-authorization.js";
42
11
  import { InMemoryAgentRunBindingRepository } from "./run-binding-repository.js";
43
12
  import { AgentBackgroundOperationExecutor } from "./background-operation.js";
44
- import { createModuleAiPorts } from "./module-ai-port-factory.js";
45
13
  import { createManifestAuthorizedRouteApp } from "../module-host/static-route-authorization.js";
46
14
  import { disableAgentRuntime } from "./runtime-disabled-composition.js";
47
15
  import { createClientAgentSessionRuntime } from "./client-session/bootstrap.js";
48
16
  import { attachClientAgentWorkflowArtifactRuntime } from "./client-workflow-artifact/runtime-composition.js";
49
17
  import { bindClientAgentHostRevocations } from "./client-session/host-revocation-composition.js";
50
18
  import { registerAgentSessions } from "./agent-session-bootstrap.js";
51
- import { createAiRuntimeReadinessSources } from "./runtime-readiness-sources.js";
52
19
  import { registerAiRuntimeReporting } from "./runtime-reporting-bootstrap.js";
53
20
  import { registerScreenshotIntakeRoutes } from "./screenshot-intake-routes.js";
54
21
  import { createScreenshotIntakeRuntimeV2 } from "./screenshot-intake-bootstrap.js";
55
- import { requiredEnvironment } from "./required-environment.js";
22
+ import { requiredEnvironment } from "../media-runtime/required-environment.js";
56
23
  import { composeAgentLongSessionRuntime } from "./session-runtime-composition.js";
57
24
  import { registerAgentWorkbenchRuntime } from "./workbench-bootstrap.js";
58
25
  import type { AgentPlatformRuntimeInput } from "./runtime-bootstrap-input.js";
@@ -73,31 +40,59 @@ export function registerAgentPlatformRuntime(input: AgentPlatformRuntimeInput) {
73
40
  input.app,
74
41
  routeAuthorizer,
75
42
  );
76
- if (!input.platformConfig.enableAI) {
43
+ if (
44
+ !input.platformConfig.enableAI ||
45
+ (input.aiRuntimeSwitches?.agent === false &&
46
+ input.aiRuntimeSwitches.document === false &&
47
+ input.aiRuntimeSwitches.speech === false &&
48
+ input.aiRuntimeSwitches.embedding === false)
49
+ ) {
77
50
  return disableAgentRuntime(input, aiRouteApp, tools);
78
51
  }
79
52
  const sessionSecret = requiredEnvironment("AI_ARTIFACT_ENCRYPTION_SECRET");
80
53
  const internalToken = requiredEnvironment("AGENT_SERVER_INTERNAL_TOKEN");
81
- const businessSessions = registerAgentSessions({
82
- ...input,
83
- secret: internalToken,
84
- });
85
- const aiArtifacts = new AiArtifactRuntime(
86
- new AiArtifactCodec(sessionSecret),
87
- input.hosts,
88
- input.runtime,
89
- features,
90
- input.storage,
91
- resolveAgentActor,
92
- );
93
- registerAiArtifactRoutes(aiRouteApp, aiArtifacts, internalToken);
94
54
  const agentServerUrl =
95
55
  process.env.AGENT_SERVER_URL || "http://agent-server:8080";
96
- const remote = new RemoteAgentRuntime({
97
- baseUrl: agentServerUrl,
56
+ const remote =
57
+ input.aiRuntimeSwitches?.agent === false
58
+ ? undefined
59
+ : new RemoteAgentRuntime({
60
+ baseUrl: agentServerUrl,
61
+ internalToken,
62
+ toolHostUrl: process.env.AGENT_TOOL_HOST_URL || "http://server:9747",
63
+ compatibilityMode: "prefer-v1",
64
+ });
65
+ const sharedAi = composeSharedAiRuntime({
66
+ app: aiRouteApp,
67
+ switches: input.aiRuntimeSwitches,
68
+ modules: input.modules,
69
+ hosts: input.hosts,
70
+ runtime: input.runtime,
71
+ storage: input.storage,
72
+ db: input.db,
73
+ features,
74
+ resolveActor: resolveAgentActor,
75
+ secret: sessionSecret,
98
76
  internalToken,
99
- toolHostUrl: process.env.AGENT_TOOL_HOST_URL || "http://server:9747",
100
- compatibilityMode: "prefer-v1",
77
+ agent: remote,
78
+ });
79
+ const { aiArtifacts } = sharedAi;
80
+ catalogReadiness.current = sharedAi.catalogReadiness;
81
+ if (!remote) {
82
+ const disabledAgent = disableAgentRuntime(input, aiRouteApp, tools, {
83
+ mediaAvailable: true,
84
+ });
85
+ const moduleAi = sharedAi.modulePorts();
86
+ return {
87
+ ...disabledAgent,
88
+ aiArtifacts,
89
+ aiOperations: moduleAi.operations,
90
+ aiReadiness: moduleAi.readiness,
91
+ };
92
+ }
93
+ const businessSessions = registerAgentSessions({
94
+ ...input,
95
+ secret: internalToken,
101
96
  });
102
97
  const clientAgentSessionRuntime = createClientAgentSessionRuntime({
103
98
  app: input.app,
@@ -142,7 +137,6 @@ export function registerAgentPlatformRuntime(input: AgentPlatformRuntimeInput) {
142
137
  agentServerUrl,
143
138
  internalToken,
144
139
  });
145
- const grants = new ExecutionGrantStore();
146
140
  const runAuthorizations = new AgentRunAuthorizationStore(
147
141
  input.db
148
142
  ? new DrizzleAgentRunBindingRepository(input.db)
@@ -165,93 +159,6 @@ export function registerAgentPlatformRuntime(input: AgentPlatformRuntimeInput) {
165
159
  ],
166
160
  memoryObjects: input.sessionMemoryObjects,
167
161
  });
168
- const documentRemote = process.env.DOCUMENT_SERVER_URL?.trim()
169
- ? new RemoteDocumentRuntime({
170
- baseUrl: process.env.DOCUMENT_SERVER_URL,
171
- internalToken,
172
- })
173
- : unavailableDocumentRuntime;
174
- const speechRemote = process.env.SPEECH_SERVER_URL?.trim()
175
- ? new RemoteSpeechRuntime({
176
- baseUrl: process.env.SPEECH_SERVER_URL,
177
- internalToken,
178
- })
179
- : unavailableSpeechRuntime;
180
- const embeddingRemote = process.env.EMBEDDING_SERVER_URL?.trim()
181
- ? new RemoteEmbeddingRuntime({
182
- baseUrl: process.env.EMBEDDING_SERVER_URL,
183
- internalToken,
184
- })
185
- : unavailableEmbeddingRuntime;
186
- const readinessSources = createAiRuntimeReadinessSources({
187
- agent: remote,
188
- document: documentRemote,
189
- speech: speechRemote,
190
- embedding: embeddingRemote,
191
- });
192
- const readinessTrustStore = new AiRuntimeReadinessTrustStoreV1();
193
- const aiReadiness = createModuleAiOperationReadinessPort({
194
- modules: input.modules,
195
- sources: readinessSources,
196
- trustStore: readinessTrustStore,
197
- runtime: input.runtime,
198
- });
199
- catalogReadiness.current = aiReadiness;
200
- const outputRuntime = createDocumentOutputRuntime({
201
- modules: input.modules,
202
- readiness: aiReadiness,
203
- runtime: input.runtime,
204
- storage: input.storage,
205
- resolveActor: resolveAgentActor,
206
- db: input.db,
207
- });
208
- const documentFacadeWithOutput = new DocumentRunFacade(
209
- input.modules,
210
- input.hosts,
211
- input.runtime,
212
- documentRemote,
213
- aiArtifacts,
214
- grants,
215
- outputRuntime?.host,
216
- aiReadiness,
217
- );
218
- installDocumentOutputRuntimeRoutes(outputRuntime, aiRouteApp, internalToken);
219
- registerDocumentRunRoutes(
220
- aiRouteApp,
221
- documentFacadeWithOutput,
222
- documentRemote,
223
- );
224
- const speechFacade = new SpeechRunFacade(
225
- input.modules,
226
- input.hosts,
227
- input.runtime,
228
- speechRemote,
229
- aiArtifacts,
230
- grants,
231
- aiReadiness,
232
- outputRuntime?.speechHost,
233
- );
234
- registerSpeechRunRoutes(aiRouteApp, speechFacade, speechRemote);
235
- const embeddingFacade = new EmbeddingFacade(
236
- input.modules,
237
- input.hosts,
238
- input.runtime,
239
- embeddingRemote,
240
- aiArtifacts,
241
- new EmbeddingRunAuthorizations(
242
- input.db
243
- ? new DrizzleEmbeddingRunBindingRepository(input.db)
244
- : new InMemoryEmbeddingRunBindingRepository(),
245
- internalToken,
246
- ),
247
- aiReadiness,
248
- );
249
- registerEmbeddingRoutes(aiRouteApp, embeddingFacade);
250
- registerEmbeddingRunArtifactRoutes(
251
- aiRouteApp,
252
- embeddingFacade,
253
- internalToken,
254
- );
255
162
  const agentFacade = new AgentRunFacade(
256
163
  input.modules,
257
164
  input.hosts,
@@ -314,22 +221,7 @@ export function registerAgentPlatformRuntime(input: AgentPlatformRuntimeInput) {
314
221
  remote,
315
222
  ),
316
223
  );
317
- const moduleAi = createModuleAiPorts({
318
- modules: input.modules,
319
- runtime: input.runtime,
320
- sources: readinessSources,
321
- trustStore: readinessTrustStore,
322
- operations: {
323
- agentFacade,
324
- agentRemote: remote,
325
- documentFacade: documentFacadeWithOutput,
326
- documentRemote,
327
- speechFacade,
328
- speechRemote,
329
- embeddingFacade,
330
- modules: input.modules,
331
- },
332
- });
224
+ const moduleAi = sharedAi.modulePorts({ agentFacade, agentRemote: remote });
333
225
  return {
334
226
  toolHandlers,
335
227
  aiArtifacts,
@@ -9,10 +9,11 @@ export function disableAgentRuntime(
9
9
  input: AgentPlatformRuntimeInput,
10
10
  app: RouteApp,
11
11
  tools: ReturnType<typeof composeAgentToolRuntime>,
12
+ options?: { mediaAvailable: boolean },
12
13
  ) {
13
14
  registerAgentWorkbenchRuntime(input, app);
14
15
  registerProductionAgentWorkflowRuntime(input, app, tools.toolRegistry);
15
- registerUnavailableAiRoutes(app);
16
+ registerUnavailableAiRoutes(app, options);
16
17
  return {
17
18
  toolHandlers: tools.toolHandlers,
18
19
  aiArtifacts: undefined,
@@ -0,0 +1,54 @@
1
+ import { agentSourceInvalidations } from "@southwind-ai/database";
2
+ import { and, desc, eq } from "drizzle-orm";
3
+ import type { AgentAuthorizationInvalidationInput } from "../../system/authorization-invalidation-bridge.js";
4
+ import type { ModuleAuthoritativeSourceAccessRegistry } from "../context-memory/authoritative-source-registry.js";
5
+ import type { DrizzleSessionInvalidationOutbox } from "../session-persistence/drizzle-invalidation-outbox.js";
6
+ import { authorizationCoordinateForActor } from "../session-projection-access.js";
7
+
8
+ export async function invalidateActorAuthorization(
9
+ input: AgentAuthorizationInvalidationInput,
10
+ sources: ModuleAuthoritativeSourceAccessRegistry,
11
+ outbox: DrizzleSessionInvalidationOutbox,
12
+ ) {
13
+ const authorization = authorizationCoordinateForActor(input.previousActor);
14
+ const sourceKind = "authorization-scope";
15
+ const [latest] = await input.database
16
+ .select({ sequence: agentSourceInvalidations.sequence })
17
+ .from(agentSourceInvalidations)
18
+ .where(
19
+ and(
20
+ eq(agentSourceInvalidations.sourceModuleKey, "system.agent"),
21
+ eq(agentSourceInvalidations.sourceKind, sourceKind),
22
+ eq(agentSourceInvalidations.sourceId, authorization.actorId),
23
+ ),
24
+ )
25
+ .orderBy(desc(agentSourceInvalidations.sequence))
26
+ .limit(1);
27
+ const occurredAt = Date.parse(input.occurredAt);
28
+ if (!Number.isSafeInteger(occurredAt) || occurredAt < 1) {
29
+ throw new Error("Agent 授权失效时间坐标无效");
30
+ }
31
+ const sequence = Math.max(occurredAt, (latest?.sequence ?? 0) + 1);
32
+ const producer = sources.bindInvalidationProducer({
33
+ moduleKey: "system.agent",
34
+ authorization,
35
+ append: {
36
+ append: (envelope) =>
37
+ outbox.appendInTransaction(input.database, envelope),
38
+ },
39
+ });
40
+ const result = await producer.authorizationRevoked({
41
+ source: {
42
+ sourceKind,
43
+ sourceId: authorization.actorId,
44
+ version: authorization.scopeDigest,
45
+ sequence,
46
+ },
47
+ event: {
48
+ eventId: input.eventId,
49
+ eventSequence: sequence,
50
+ occurredAt: input.occurredAt,
51
+ },
52
+ });
53
+ if (result === "stale") throw new Error("Agent 授权失效序列未收敛");
54
+ }