create-windy 0.4.1 → 0.5.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 (174) hide show
  1. package/dist/cli.js +292 -125
  2. package/package.json +1 -1
  3. package/template/.windy-template.json +2 -2
  4. package/template/apps/agent-server/adk_web_agents/canonical_debug/agent.py +29 -9
  5. package/template/apps/agent-server/adk_web_agents/services.py +25 -0
  6. package/template/apps/agent-server/src/southwind_agent_server/adk_adapter.py +40 -11
  7. package/template/apps/agent-server/src/southwind_agent_server/adk_debug_sessions.py +325 -0
  8. package/template/apps/agent-server/src/southwind_agent_server/adk_model.py +70 -6
  9. package/template/apps/agent-server/src/southwind_agent_server/adk_tools.py +1 -1
  10. package/template/apps/agent-server/src/southwind_agent_server/app.py +16 -4
  11. package/template/apps/agent-server/src/southwind_agent_server/approval_resolution.py +30 -0
  12. package/template/apps/agent-server/src/southwind_agent_server/contracts.py +9 -0
  13. package/template/apps/agent-server/src/southwind_agent_server/deterministic.py +2 -1
  14. package/template/apps/agent-server/src/southwind_agent_server/engine.py +3 -1
  15. package/template/apps/agent-server/src/southwind_agent_server/openai_engine.py +95 -130
  16. package/template/apps/agent-server/src/southwind_agent_server/openai_engine_validation.py +133 -0
  17. package/template/apps/agent-server/src/southwind_agent_server/openai_provider.py +89 -20
  18. package/template/apps/agent-server/src/southwind_agent_server/postgres_report_insert.py +34 -0
  19. package/template/apps/agent-server/src/southwind_agent_server/postgres_report_queries.py +234 -0
  20. package/template/apps/agent-server/src/southwind_agent_server/postgres_report_replay.py +44 -0
  21. package/template/apps/agent-server/src/southwind_agent_server/postgres_report_status.py +83 -0
  22. package/template/apps/agent-server/src/southwind_agent_server/postgres_report_store.py +331 -0
  23. package/template/apps/agent-server/src/southwind_agent_server/postgres_report_summary.py +304 -0
  24. package/template/apps/agent-server/src/southwind_agent_server/report_lifecycle.py +217 -0
  25. package/template/apps/agent-server/src/southwind_agent_server/report_metrics.py +149 -0
  26. package/template/apps/agent-server/src/southwind_agent_server/report_presenter.py +280 -0
  27. package/template/apps/agent-server/src/southwind_agent_server/report_routes.py +126 -0
  28. package/template/apps/agent-server/src/southwind_agent_server/report_store.py +344 -0
  29. package/template/apps/agent-server/src/southwind_agent_server/report_types.py +111 -0
  30. package/template/apps/agent-server/src/southwind_agent_server/runtime_factory.py +31 -2
  31. package/template/apps/agent-server/src/southwind_agent_server/service.py +56 -51
  32. package/template/apps/agent-server/src/southwind_agent_server/signal_events.py +18 -1
  33. package/template/apps/agent-server/tests/test_adk_adapter.py +7 -9
  34. package/template/apps/agent-server/tests/test_adk_debug_sessions.py +215 -0
  35. package/template/apps/agent-server/tests/test_adk_web_canonical_debug.py +48 -1
  36. package/template/apps/agent-server/tests/test_openai_provider.py +58 -6
  37. package/template/apps/agent-server/tests/test_postgres_report_status.py +59 -0
  38. package/template/apps/agent-server/tests/test_postgres_report_summary.py +298 -0
  39. package/template/apps/agent-server/tests/test_provider_attempt_reporting.py +152 -0
  40. package/template/apps/agent-server/tests/test_report_attempt_lifecycle.py +126 -0
  41. package/template/apps/agent-server/tests/test_report_resilience.py +181 -0
  42. package/template/apps/agent-server/tests/test_report_store.py +286 -0
  43. package/template/apps/agent-server/tests/test_report_summary_parity.py +115 -0
  44. package/template/apps/agent-server/tests/test_report_terminal_sync.py +177 -0
  45. package/template/apps/server/src/agent/debug-session-facade-provider-failure.test.ts +269 -0
  46. package/template/apps/server/src/agent/debug-session-facade.test.ts +335 -0
  47. package/template/apps/server/src/agent/debug-session-facade.ts +302 -0
  48. package/template/apps/server/src/agent/debug-session-id.test.ts +37 -0
  49. package/template/apps/server/src/agent/debug-session-id.ts +37 -0
  50. package/template/apps/server/src/agent/debug-session-provider-reader.ts +54 -0
  51. package/template/apps/server/src/agent/debug-session-routes.test.ts +64 -0
  52. package/template/apps/server/src/agent/debug-session-routes.ts +73 -0
  53. package/template/apps/server/src/agent/debug-session-validation.ts +106 -0
  54. package/template/apps/server/src/agent/drizzle-run-binding-repository.ts +2 -0
  55. package/template/apps/server/src/agent/provider-input-governance.test.ts +18 -0
  56. package/template/apps/server/src/agent/provider-input-governance.ts +37 -1
  57. package/template/apps/server/src/agent/remote-runtime.test.ts +7 -0
  58. package/template/apps/server/src/agent/remote-runtime.ts +1 -0
  59. package/template/apps/server/src/agent/run-authorization.test.ts +98 -0
  60. package/template/apps/server/src/agent/run-authorization.ts +32 -1
  61. package/template/apps/server/src/agent/run-binding-repository.ts +1 -0
  62. package/template/apps/server/src/agent/run-contracts.ts +5 -0
  63. package/template/apps/server/src/agent/run-facade-adk-debug.test.ts +39 -0
  64. package/template/apps/server/src/agent/run-facade.test.ts +78 -0
  65. package/template/apps/server/src/agent/run-facade.ts +33 -3
  66. package/template/apps/server/src/agent/run-input-commitment.ts +50 -0
  67. package/template/apps/server/src/agent/run-input-verification.ts +41 -0
  68. package/template/apps/server/src/agent/runtime-bootstrap-report.test.ts +136 -0
  69. package/template/apps/server/src/agent/runtime-bootstrap.ts +66 -1
  70. package/template/apps/server/src/agent/tool-host.test.ts +1 -0
  71. package/template/apps/server/src/agent/unavailable-routes.ts +4 -0
  72. package/template/apps/server/src/ai-runtime-report/actor-directory.ts +94 -0
  73. package/template/apps/server/src/ai-runtime-report/actor-handle.test.ts +27 -0
  74. package/template/apps/server/src/ai-runtime-report/actor-handle.ts +81 -0
  75. package/template/apps/server/src/ai-runtime-report/contracts.ts +91 -0
  76. package/template/apps/server/src/ai-runtime-report/remote-parser.ts +212 -0
  77. package/template/apps/server/src/ai-runtime-report/remote-port.ts +138 -0
  78. package/template/apps/server/src/ai-runtime-report/routes.ts +172 -0
  79. package/template/apps/server/src/ai-runtime-report/scope.ts +63 -0
  80. package/template/apps/server/src/ai-runtime-report/service.test.ts +271 -0
  81. package/template/apps/server/src/ai-runtime-report/service.ts +206 -0
  82. package/template/apps/server/src/license/runtime-audit-reliability.test.ts +2 -0
  83. package/template/apps/server/src/license/runtime-service.test.ts +6 -0
  84. package/template/apps/server/src/module-host/ai-operation-port.test.ts +63 -0
  85. package/template/apps/server/src/module-host/ai-operation-port.ts +30 -5
  86. package/template/apps/server/src/module-host/debug-session-provider-registration.test.ts +162 -0
  87. package/template/apps/server/src/module-host/debug-session-provider-registration.ts +84 -0
  88. package/template/apps/server/src/module-host/initialize-contracts.ts +7 -0
  89. package/template/apps/server/src/module-host/initialize.ts +6 -197
  90. package/template/apps/server/src/module-host/request-context.ts +1 -0
  91. package/template/apps/server/src/module-host/scoped-host.ts +214 -0
  92. package/template/apps/server/src/module-host.ts +1 -0
  93. package/template/apps/server/src/runtime-adk-debug.test.ts +25 -1
  94. package/template/apps/server/src/runtime-adk-debug.ts +8 -2
  95. package/template/apps/server/src/system/operations.test.ts +54 -0
  96. package/template/apps/server/src/system/operations.ts +23 -1
  97. package/template/apps/server/src/system/seed.test.ts +4 -0
  98. package/template/apps/server/src/system/system-resource-monitoring.test.ts +82 -0
  99. package/template/apps/server/src/system/system-resource-monitoring.ts +300 -0
  100. package/template/apps/web/src/layout/AppSidebar.vue +6 -1
  101. package/template/apps/web/src/layout/AppSidebar.webtest.ts +2 -0
  102. package/template/apps/web/src/pages/auth/LoginPage.vue +6 -1
  103. package/template/apps/web/src/pages/settings/BrandingSettingsSection.vue +20 -3
  104. package/template/apps/web/src/pages/settings/BrandingSettingsSection.webtest.ts +80 -0
  105. package/template/apps/web/src/pages/system/AiRuntimePage.vue +129 -0
  106. package/template/apps/web/src/pages/system/SystemOperationsPage.vue +2 -1
  107. package/template/apps/web/src/pages/system/ai-runtime-presenter.ts +60 -0
  108. package/template/apps/web/src/pages/system/ai-runtime-presenter.webtest.ts +48 -0
  109. package/template/apps/web/src/pages/system/audit-time-range.ts +34 -2
  110. package/template/apps/web/src/pages/system/components/AuditLogFilters.vue +4 -3
  111. package/template/apps/web/src/pages/system/components/AuditLogFilters.webtest.ts +26 -1
  112. package/template/apps/web/src/pages/system/components/OperationGpuMonitor.vue +98 -0
  113. package/template/apps/web/src/pages/system/components/OperationGpuMonitor.webtest.ts +61 -0
  114. package/template/apps/web/src/pages/system/components/OperationServiceMonitor.vue +53 -0
  115. package/template/apps/web/src/pages/system/components/OperationServiceMonitor.webtest.ts +20 -1
  116. package/template/apps/web/src/pages/system/components/SystemResourcePage.webtest.ts +14 -1
  117. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeActivityChart.vue +85 -0
  118. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeCharts.webtest.ts +82 -0
  119. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeFilters.vue +98 -0
  120. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeFilters.webtest.ts +27 -0
  121. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeRunDetailDialog.vue +168 -0
  122. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeRunTable.vue +130 -0
  123. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeSummaryCards.vue +82 -0
  124. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeSummaryCards.webtest.ts +102 -0
  125. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeTokenByModelChart.vue +101 -0
  126. package/template/apps/web/src/pages/system/composables/useAiRuntimeReport.ts +185 -0
  127. package/template/apps/web/src/pages/system/composables/useAiRuntimeReport.webtest.ts +158 -0
  128. package/template/apps/web/src/pages/system/composables/useSystemResource.ts +21 -7
  129. package/template/apps/web/src/router/error-navigation.ts +22 -0
  130. package/template/apps/web/src/router/index.ts +12 -23
  131. package/template/apps/web/src/router/platform-admin-route-bindings.ts +4 -0
  132. package/template/apps/web/src/services/ai-runtime-api.ts +164 -0
  133. package/template/apps/web/src/services/ai-runtime-api.webtest.ts +111 -0
  134. package/template/apps/web/src/services/operations-api.ts +28 -0
  135. package/template/docker-compose.agent-debug.yml +1 -1
  136. package/template/docs/architecture/ai-runtime.md +2 -1
  137. package/template/docs/architecture/module-host.md +3 -0
  138. package/template/docs/platform/agent-run-attestation.md +65 -0
  139. package/template/docs/platform/agent-runtime-observability.md +135 -0
  140. package/template/docs/platform/agent-runtime.md +47 -3
  141. package/template/docs/platform/operations-overview.md +13 -0
  142. package/template/docs/platform/platform-authentication.md +4 -3
  143. package/template/packages/config/package.json +1 -1
  144. package/template/packages/database/drizzle/0045_tiny_cassandra_nova.sql +65 -0
  145. package/template/packages/database/drizzle/0046_tranquil_living_mummy.sql +6 -0
  146. package/template/packages/database/drizzle/0047_oval_luke_cage.sql +18 -0
  147. package/template/packages/database/drizzle/meta/0045_snapshot.json +8814 -0
  148. package/template/packages/database/drizzle/meta/0046_snapshot.json +8820 -0
  149. package/template/packages/database/drizzle/meta/0047_snapshot.json +8835 -0
  150. package/template/packages/database/drizzle/meta/_journal.json +21 -0
  151. package/template/packages/database/package.json +1 -1
  152. package/template/packages/database/src/agent-runtime-schema.test.ts +54 -0
  153. package/template/packages/database/src/schema/agent-runtime.ts +114 -0
  154. package/template/packages/jobs/package.json +1 -1
  155. package/template/packages/modules/package.json +1 -1
  156. package/template/packages/modules/src/system-admin-routes.ts +10 -0
  157. package/template/packages/modules/src/system-admin-ui-catalog.ts +1 -1
  158. package/template/packages/modules/src/system-api-control-plane.ts +17 -1
  159. package/template/packages/modules/src/system-audit-actions.ts +10 -0
  160. package/template/packages/modules/src/system-features.ts +8 -0
  161. package/template/packages/modules/src/system-module-catalog.test.ts +35 -0
  162. package/template/packages/modules/src/system-module-catalog.ts +15 -4
  163. package/template/packages/modules/src/system-modules-access.test.ts +69 -0
  164. package/template/packages/modules/src/system-modules.ts +13 -0
  165. package/template/packages/modules/src/system-permissions.ts +2 -0
  166. package/template/packages/server-sdk/index.ts +11 -0
  167. package/template/packages/server-sdk/package.json +2 -1
  168. package/template/packages/server-sdk/src/ai-operation-port.ts +5 -0
  169. package/template/packages/server-sdk/src/debug-session-provider.ts +63 -0
  170. package/template/packages/server-sdk/src/module-host.ts +5 -0
  171. package/template/packages/shared/index.ts +3 -0
  172. package/template/packages/shared/package.json +1 -1
  173. package/template/packages/shared/src/ai-runtime-report.ts +134 -0
  174. package/template/scripts/lib/postgres-regression.ts +201 -0
@@ -133,6 +133,7 @@ function unavailableModuleAiOperationPort(
133
133
  };
134
134
  return {
135
135
  startAgent: async () => unavailable(),
136
+ verifyAgentRun: async () => unavailable(),
136
137
  startDocument: async () => unavailable(),
137
138
  startSpeech: async () => unavailable(),
138
139
  embed: async () => unavailable(),
@@ -0,0 +1,214 @@
1
+ import type { DurableHandlerRegistry } from "@southwind-ai/jobs";
2
+ import type { ModuleManifest } from "@southwind-ai/modules";
3
+ import {
4
+ requireModuleNamespace,
5
+ type ModuleHost,
6
+ type ModuleResourceFileAccessPolicy,
7
+ type ModuleUploadPolicy,
8
+ } from "@southwind-ai/server-sdk";
9
+ import type {
10
+ RegisteredModuleAiOperationHandler,
11
+ RegisteredModuleDebugSessionProvider,
12
+ RegisteredModuleDurableHandler,
13
+ RegisteredModuleGovernedResource,
14
+ RegisteredModuleRolePreset,
15
+ RegisteredModuleRoute,
16
+ RegisteredModuleSandboxToolHandler,
17
+ RegisteredModuleSearchProvider,
18
+ RegisteredModuleTaskHandler,
19
+ RegisteredModuleToolHandler,
20
+ } from "./initialize-contracts.js";
21
+ import { validateDebugSessionProviderRegistration } from "./debug-session-provider-registration.js";
22
+ import { registerModuleDurableHandler } from "./durable-handler-registration.js";
23
+ import { registerGovernedResources } from "./governed-resources.js";
24
+ import { registerResourceFileAccessPolicies } from "./resource-file-policies.js";
25
+ import { validateRouteDeclaration } from "./route-declaration.js";
26
+ import { validateSearchProviderRegistration } from "./search-provider-registration.js";
27
+
28
+ type HandlerInstaller = (registry: DurableHandlerRegistry) => void;
29
+
30
+ export interface HostCollections {
31
+ uploadPolicies: ModuleUploadPolicy[];
32
+ uploadPurposes: Set<string>;
33
+ modulePurposes: Map<string, Set<string>>;
34
+ moduleDependencies: ReadonlyMap<string, readonly string[]>;
35
+ resourceFileAccessPolicies: ModuleResourceFileAccessPolicy[];
36
+ moduleResourceFileAccessPolicies: Map<
37
+ string,
38
+ ModuleResourceFileAccessPolicy[]
39
+ >;
40
+ resourceFileAccessPolicyKeys: Set<string>;
41
+ handlerKeys: Set<string>;
42
+ handlerInstallers: HandlerInstaller[];
43
+ durableHandlers: RegisteredModuleDurableHandler[];
44
+ routes: RegisteredModuleRoute[];
45
+ routeKeys: Set<string>;
46
+ taskHandlers: RegisteredModuleTaskHandler[];
47
+ taskKeys: Set<string>;
48
+ toolHandlers: RegisteredModuleToolHandler[];
49
+ sandboxToolHandlers: RegisteredModuleSandboxToolHandler[];
50
+ toolKeys: Set<string>;
51
+ aiOperationHandlers: RegisteredModuleAiOperationHandler[];
52
+ aiOperationKeys: Set<string>;
53
+ governedResources: RegisteredModuleGovernedResource[];
54
+ governedResourceKeys: Set<string>;
55
+ rolePresets: RegisteredModuleRolePreset[];
56
+ rolePresetKeys: Set<string>;
57
+ searchProviders: RegisteredModuleSearchProvider[];
58
+ searchProviderKeys: Set<string>;
59
+ debugSessionProviders: RegisteredModuleDebugSessionProvider[];
60
+ debugSessionProviderKeys: Set<string>;
61
+ }
62
+
63
+ export function createScopedHost(
64
+ manifest: ModuleManifest,
65
+ state: HostCollections,
66
+ ): ModuleHost {
67
+ const moduleName = manifest.name;
68
+ return {
69
+ moduleName,
70
+ registerUploadPolicy(policy) {
71
+ requireModuleNamespace("上传用途", policy.purpose, moduleName);
72
+ if (state.uploadPurposes.has(policy.purpose)) {
73
+ throw new Error(`上传用途重复注册:${policy.purpose}`);
74
+ }
75
+ state.uploadPurposes.add(policy.purpose);
76
+ state.uploadPolicies.push(policy);
77
+ const purposes =
78
+ state.modulePurposes.get(moduleName) ?? new Set<string>();
79
+ purposes.add(policy.purpose);
80
+ state.modulePurposes.set(moduleName, purposes);
81
+ },
82
+ registerResourceFileAccessPolicies(policies) {
83
+ registerResourceFileAccessPolicies(manifest, state, policies);
84
+ },
85
+ registerDurableHandler(definition) {
86
+ requireModuleNamespace("任务 handler", definition.key, moduleName);
87
+ registerModuleDurableHandler(manifest, state, definition);
88
+ },
89
+ registerGovernedResources(registrations) {
90
+ registerGovernedResources(manifest, state, registrations);
91
+ },
92
+ registerRoutes(routes) {
93
+ for (const definition of routes) {
94
+ requireModuleNamespace(
95
+ "路由权限",
96
+ definition.permissionKey,
97
+ moduleName,
98
+ );
99
+ validateRouteDeclaration(manifest, definition);
100
+ const key = `${definition.method} ${definition.path}`;
101
+ if (state.routeKeys.has(key)) {
102
+ throw new Error(`模块路由重复注册:${key}`);
103
+ }
104
+ state.routeKeys.add(key);
105
+ state.routes.push({ moduleName, definition });
106
+ }
107
+ },
108
+ registerTaskHandlers(registrations) {
109
+ for (const registration of registrations) {
110
+ requireModuleNamespace("定时任务", registration.taskKey, moduleName);
111
+ if (!manifest.tasks.some(({ key }) => key === registration.taskKey)) {
112
+ throw new Error(
113
+ `定时任务 Handler 未在 Manifest 声明:${registration.taskKey}`,
114
+ );
115
+ }
116
+ if (state.taskKeys.has(registration.taskKey)) {
117
+ throw new Error(`定时任务 Handler 重复注册:${registration.taskKey}`);
118
+ }
119
+ state.taskKeys.add(registration.taskKey);
120
+ state.taskHandlers.push({ moduleName, registration });
121
+ }
122
+ },
123
+ registerToolHandlers(registrations) {
124
+ for (const registration of registrations) {
125
+ requireModuleNamespace("Agent Tool", registration.toolKey, moduleName);
126
+ if (!manifest.tools.some(({ key }) => key === registration.toolKey)) {
127
+ throw new Error(
128
+ `Agent Tool Handler 未在 Manifest 声明:${registration.toolKey}`,
129
+ );
130
+ }
131
+ if (state.toolKeys.has(registration.toolKey)) {
132
+ throw new Error(
133
+ `Agent Tool Handler 重复注册:${registration.toolKey}`,
134
+ );
135
+ }
136
+ state.toolKeys.add(registration.toolKey);
137
+ state.toolHandlers.push({ moduleName, registration });
138
+ }
139
+ },
140
+ registerAiOperationHandlers(registrations) {
141
+ for (const registration of registrations) {
142
+ requireModuleNamespace(
143
+ "AI Operation",
144
+ registration.operationKey,
145
+ moduleName,
146
+ );
147
+ const operation = manifest.aiOperations?.find(
148
+ ({ key }) => key === registration.operationKey,
149
+ );
150
+ if (!operation) {
151
+ throw new Error(
152
+ `AI Operation Handler 未在 Manifest 声明:${registration.operationKey}`,
153
+ );
154
+ }
155
+ if (
156
+ (operation.kind === "agent" && !registration.prepare) ||
157
+ (operation.kind === "document" && !registration.prepareDocument) ||
158
+ (operation.kind === "speech" && !registration.prepareSpeech) ||
159
+ (operation.kind === "embedding" && !registration.prepareEmbedding)
160
+ ) {
161
+ throw new Error(
162
+ `AI Operation Handler 与 kind 不匹配:${registration.operationKey}`,
163
+ );
164
+ }
165
+ if (state.aiOperationKeys.has(registration.operationKey)) {
166
+ throw new Error(
167
+ `AI Operation Handler 重复注册:${registration.operationKey}`,
168
+ );
169
+ }
170
+ state.aiOperationKeys.add(registration.operationKey);
171
+ state.aiOperationHandlers.push({ moduleName, registration });
172
+ }
173
+ },
174
+ registerRolePresets(presets) {
175
+ for (const preset of presets) {
176
+ requireModuleNamespace("角色预设", preset.key, moduleName);
177
+ if (state.rolePresetKeys.has(preset.key)) {
178
+ throw new Error(`角色预设重复注册:${preset.key}`);
179
+ }
180
+ for (const permissionKey of preset.permissionKeys) {
181
+ requireModuleNamespace("角色预设权限", permissionKey, moduleName);
182
+ if (!manifest.permissions.some(({ key }) => key === permissionKey)) {
183
+ throw new Error(
184
+ `角色预设引用了模块 ${moduleName} 未声明权限:${permissionKey}`,
185
+ );
186
+ }
187
+ }
188
+ state.rolePresetKeys.add(preset.key);
189
+ state.rolePresets.push({ moduleName, preset });
190
+ }
191
+ },
192
+ registerSearchProviders(providers) {
193
+ for (const provider of providers) {
194
+ requireModuleNamespace("搜索 Provider", provider.key, moduleName);
195
+ validateSearchProviderRegistration(manifest, provider);
196
+ if (state.searchProviderKeys.has(provider.key)) {
197
+ throw new Error(`搜索 Provider 重复注册:${provider.key}`);
198
+ }
199
+ state.searchProviderKeys.add(provider.key);
200
+ state.searchProviders.push({ moduleName, provider });
201
+ }
202
+ },
203
+ registerDebugSessionProviders(providers) {
204
+ for (const provider of providers) {
205
+ validateDebugSessionProviderRegistration(manifest, provider);
206
+ if (state.debugSessionProviderKeys.has(provider.key)) {
207
+ throw new Error(`调试会话 Provider 重复注册:${provider.key}`);
208
+ }
209
+ state.debugSessionProviderKeys.add(provider.key);
210
+ state.debugSessionProviders.push({ moduleName, provider });
211
+ }
212
+ },
213
+ };
214
+ }
@@ -9,6 +9,7 @@ export {
9
9
  type RegisteredModuleTaskHandler,
10
10
  type RegisteredModuleToolHandler,
11
11
  type RegisteredModuleAiOperationHandler,
12
+ type RegisteredModuleDebugSessionProvider,
12
13
  } from "./module-host/initialize.js";
13
14
  export {
14
15
  buildModuleRequestContext,
@@ -7,7 +7,7 @@ import { createAdkDebugRuntime } from "./runtime-adk-debug.js";
7
7
  const token = "debug-token-012345678901234567890123";
8
8
 
9
9
  describe("ADK Web 专用调试身份", () => {
10
- test("只在 canonical Run 路径委托真实用户并收窄权限", async () => {
10
+ test("只在 canonical Run 与只读 debug-session 路径委托真实用户", async () => {
11
11
  const runtime = createRuntime();
12
12
  expect(
13
13
  runtime.claimsRequest(request("/api/foundation", "GET", token)),
@@ -28,6 +28,30 @@ describe("ADK Web 专用调试身份", () => {
28
28
  expect(
29
29
  await runtime.resolveRequest(request("/api/foundation", "GET", token)),
30
30
  ).toBeUndefined();
31
+ expect(
32
+ await runtime.resolveRequest(
33
+ request("/api/agent/debug/sessions/v1", "GET", token),
34
+ ),
35
+ ).toMatchObject({ id: "service_adk_debug" });
36
+ expect(
37
+ await runtime.resolveRequest(
38
+ request(
39
+ "/api/agent/debug/sessions/v1/wdbg1_" + "a".repeat(43),
40
+ "GET",
41
+ token,
42
+ ),
43
+ ),
44
+ ).toMatchObject({ id: "service_adk_debug" });
45
+ expect(
46
+ await runtime.resolveRequest(
47
+ request("/api/agent/debug/sessions/v1", "POST", token),
48
+ ),
49
+ ).toBeUndefined();
50
+ expect(
51
+ await runtime.resolveRequest(
52
+ request("/api/agent/debug/sessions/v1/one/events", "GET", token),
53
+ ),
54
+ ).toBeUndefined();
31
55
  expect(
32
56
  await runtime.resolveRequest(
33
57
  request("/api/agent/runs/run_1/result", "GET"),
@@ -45,7 +45,7 @@ export function createAdkDebugRuntime(
45
45
  return {
46
46
  claimsRequest,
47
47
  async resolveRequest(request: Request): Promise<RequestActor | undefined> {
48
- if (!claimsRequest(request) || !isCanonicalRunRequest(request)) {
48
+ if (!claimsRequest(request) || !isAdkDebugRequest(request)) {
49
49
  return undefined;
50
50
  }
51
51
  return resolveDelegatedActor(actorResolver, dataActorId, permissionKeys);
@@ -123,8 +123,14 @@ function resolvePermissionClosure(
123
123
  return [...new Set([operation.permissionKey, ...toolPermissions])];
124
124
  }
125
125
 
126
- function isCanonicalRunRequest(request: Request): boolean {
126
+ function isAdkDebugRequest(request: Request): boolean {
127
127
  const path = new URL(request.url).pathname.replace(/\/+$/, "");
128
+ if (
129
+ request.method === "GET" &&
130
+ /^\/api\/agent\/debug\/sessions\/v1(?:\/[^/]+)?$/.test(path)
131
+ ) {
132
+ return true;
133
+ }
128
134
  if (request.method === "POST" && path === "/api/agent/runs") return true;
129
135
  const match = path.match(
130
136
  /^\/api\/agent\/runs\/[^/]+\/(events|result|cancel|attach)$/,
@@ -84,6 +84,37 @@ describe("system operation routes", () => {
84
84
  path: "/imports",
85
85
  usedBytes: 500,
86
86
  }),
87
+ cpuMonitorProbe: async () => ({
88
+ status: "available",
89
+ logicalCores: 16,
90
+ hostUsagePercent: 42.5,
91
+ processUsagePercent: 3.25,
92
+ loadAverage1m: 2.1,
93
+ }),
94
+ gpuMonitorProbe: async () => ({
95
+ status: "available",
96
+ provider: "NVIDIA",
97
+ message: "GPU 指标可用。",
98
+ devices: [
99
+ {
100
+ index: 0,
101
+ uuid: "GPU-abc",
102
+ name: "NVIDIA RTX 5000",
103
+ usagePercent: 75,
104
+ memoryUsedBytes: 8_192,
105
+ memoryTotalBytes: 32_768,
106
+ memoryUsagePercent: 25,
107
+ temperatureCelsius: 62,
108
+ processes: [
109
+ {
110
+ pid: 4321,
111
+ name: "python ai-service.py",
112
+ usedMemoryBytes: 7_168,
113
+ },
114
+ ],
115
+ },
116
+ ],
117
+ }),
87
118
  },
88
119
  );
89
120
  const guardContext = await runtime.createGuardContext(
@@ -104,6 +135,15 @@ describe("system operation routes", () => {
104
135
  totalBytes: number;
105
136
  usagePercent: number;
106
137
  };
138
+ cpu: {
139
+ logicalCores: number;
140
+ hostUsagePercent: number;
141
+ processUsagePercent: number;
142
+ };
143
+ };
144
+ gpuMonitor: {
145
+ status: string;
146
+ devices: Array<{ name: string; processes: Array<{ pid: number }> }>;
107
147
  };
108
148
  databaseMonitor: { engine: string; version: string };
109
149
  diskMonitor: {
@@ -118,6 +158,20 @@ describe("system operation routes", () => {
118
158
  expect(snapshot.serviceMonitor.status).toBe("running");
119
159
  expect(snapshot.serviceMonitor.startedAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
120
160
  expect(snapshot.serviceMonitor.memory.usagePercent).toBeGreaterThan(0);
161
+ expect(snapshot.serviceMonitor.cpu).toMatchObject({
162
+ logicalCores: 16,
163
+ hostUsagePercent: 42.5,
164
+ processUsagePercent: 3.25,
165
+ });
166
+ expect(snapshot.gpuMonitor).toMatchObject({
167
+ status: "available",
168
+ devices: [
169
+ {
170
+ name: "NVIDIA RTX 5000",
171
+ processes: [{ pid: 4321 }],
172
+ },
173
+ ],
174
+ });
121
175
  expect(snapshot.databaseMonitor).toMatchObject({
122
176
  engine: "PostgreSQL",
123
177
  version: "16.3",
@@ -18,6 +18,12 @@ import {
18
18
  type DirectoryUsageProbe,
19
19
  type HostDiskUsageProbe,
20
20
  } from "./operation-monitoring.js";
21
+ import {
22
+ createCpuMonitorProbe,
23
+ createGpuMonitorProbe,
24
+ type CpuMonitorProbe,
25
+ type GpuMonitorProbe,
26
+ } from "./system-resource-monitoring.js";
21
27
 
22
28
  type ServerRuntime = ReturnType<typeof createServerRuntime>;
23
29
 
@@ -31,6 +37,8 @@ export interface OperationRouteOptions {
31
37
  hostDiskUsageProbe?: HostDiskUsageProbe;
32
38
  systemUsageProbe?: DirectoryUsageProbe;
33
39
  importDataUsageProbe?: DirectoryUsageProbe;
40
+ cpuMonitorProbe?: CpuMonitorProbe;
41
+ gpuMonitorProbe?: GpuMonitorProbe;
34
42
  onlineUserProvider?: () => Promise<ActiveAuthSession[]>;
35
43
  }
36
44
 
@@ -69,6 +77,8 @@ export function registerSystemOperationRoutes(
69
77
  process.env.WINDY_IMPORT_DATA_PATH,
70
78
  ),
71
79
  );
80
+ const cpuMonitorProbe = options.cpuMonitorProbe ?? createCpuMonitorProbe();
81
+ const gpuMonitorProbe = options.gpuMonitorProbe ?? createGpuMonitorProbe();
72
82
  const onlineUserProvider = options.onlineUserProvider ?? (async () => []);
73
83
  app.get("/system/operations", async (rawContext) => {
74
84
  const { guardContext } = rawContext as OperationRouteContext;
@@ -87,6 +97,8 @@ export function registerSystemOperationRoutes(
87
97
  hostDiskUsageProbe,
88
98
  systemUsageProbe,
89
99
  importDataUsageProbe,
100
+ cpuMonitorProbe,
101
+ gpuMonitorProbe,
90
102
  onlineUserProvider,
91
103
  )
92
104
  : forbidden(decision.reason);
@@ -108,6 +120,8 @@ async function createOperationSnapshot(
108
120
  hostDiskUsageProbe: HostDiskUsageProbe,
109
121
  systemUsageProbe: DirectoryUsageProbe,
110
122
  importDataUsageProbe: DirectoryUsageProbe,
123
+ cpuMonitorProbe: CpuMonitorProbe,
124
+ gpuMonitorProbe: GpuMonitorProbe,
111
125
  onlineUserProvider: () => Promise<ActiveAuthSession[]>,
112
126
  ) {
113
127
  const [
@@ -116,6 +130,8 @@ async function createOperationSnapshot(
116
130
  hostDiskUsage,
117
131
  systemUsage,
118
132
  importDataUsage,
133
+ cpuMonitor,
134
+ gpuMonitor,
119
135
  activeSessions,
120
136
  ] = await Promise.all([
121
137
  redisHealthProbe(),
@@ -123,6 +139,8 @@ async function createOperationSnapshot(
123
139
  hostDiskUsageProbe(),
124
140
  systemUsageProbe(),
125
141
  importDataUsageProbe(),
142
+ cpuMonitorProbe(),
143
+ gpuMonitorProbe(),
126
144
  onlineUserProvider(),
127
145
  ]);
128
146
  return {
@@ -138,7 +156,11 @@ async function createOperationSnapshot(
138
156
  lastSeenAt: (session.lastSeenAt || session.issuedAt).toISOString(),
139
157
  expiresAt: session.expiresAt.toISOString(),
140
158
  })),
141
- serviceMonitor: serviceMonitorSnapshot(),
159
+ serviceMonitor: {
160
+ ...serviceMonitorSnapshot(),
161
+ cpu: cpuMonitor,
162
+ },
163
+ gpuMonitor,
142
164
  databaseMonitor,
143
165
  diskMonitor: diskMonitorSnapshot(
144
166
  hostDiskUsage,
@@ -33,6 +33,7 @@ describe("系统内置角色同步", () => {
33
33
  // @windy-module system.settings end
34
34
  expect(role?.permissionKeys).not.toContain("system.user.reveal");
35
35
  expect(role?.permissionKeys).not.toContain("system.user.export");
36
+ expect(role?.permissionKeys).not.toContain("system.ai-runtime.read");
36
37
  expect(role?.permissionKeys).toContain("custom.permission");
37
38
  // @windy-module system.settings begin
38
39
  expect(role?.menuKeys).toContain("system.settings");
@@ -54,6 +55,9 @@ describe("系统内置角色同步", () => {
54
55
  expect(
55
56
  (await repositories.roles.get("role_developer"))?.permissionKeys,
56
57
  ).not.toEqual(expect.arrayContaining(["system.user.reveal"]));
58
+ expect(
59
+ (await repositories.roles.get("role_developer"))?.permissionKeys,
60
+ ).not.toContain("system.ai-runtime.read");
57
61
 
58
62
  await repositories.roles.update(
59
63
  "role_developer",
@@ -0,0 +1,82 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import {
3
+ calculateCpuMonitor,
4
+ createGpuMonitorProbe,
5
+ type CpuSample,
6
+ } from "./system-resource-monitoring.js";
7
+
8
+ describe("系统 CPU 与 GPU 监控", () => {
9
+ test("CPU 占用按宿主机时间和逻辑核心归一化进程时间计算", () => {
10
+ const previous: CpuSample = {
11
+ hostIdleMilliseconds: 2_000,
12
+ hostTotalMilliseconds: 10_000,
13
+ processMicroseconds: 1_000_000,
14
+ observedAtMicroseconds: 5_000_000,
15
+ logicalCores: 4,
16
+ loadAverage1m: 1,
17
+ };
18
+ const snapshot = calculateCpuMonitor(previous, {
19
+ hostIdleMilliseconds: 2_400,
20
+ hostTotalMilliseconds: 11_000,
21
+ processMicroseconds: 1_800_000,
22
+ observedAtMicroseconds: 6_000_000,
23
+ logicalCores: 4,
24
+ loadAverage1m: 1.5,
25
+ });
26
+
27
+ expect(snapshot).toMatchObject({
28
+ hostUsagePercent: 60,
29
+ processUsagePercent: 20,
30
+ logicalCores: 4,
31
+ loadAverage1m: 1.5,
32
+ });
33
+ });
34
+
35
+ test("NVIDIA 指标关联设备利用率与 AI 计算进程显存", async () => {
36
+ const probe = createGpuMonitorProbe(async (args) =>
37
+ args[0]?.startsWith("--query-gpu=")
38
+ ? {
39
+ exitCode: 0,
40
+ stdout: '0, GPU-abc, "NVIDIA RTX 5000", 75, 8192, 32768, 62\n',
41
+ }
42
+ : {
43
+ exitCode: 0,
44
+ stdout: 'GPU-abc, 4321, "python ai-service.py", 7168\n',
45
+ },
46
+ );
47
+
48
+ await expect(probe()).resolves.toMatchObject({
49
+ status: "available",
50
+ devices: [
51
+ {
52
+ index: 0,
53
+ name: "NVIDIA RTX 5000",
54
+ usagePercent: 75,
55
+ memoryUsagePercent: 25,
56
+ temperatureCelsius: 62,
57
+ processes: [
58
+ {
59
+ pid: 4321,
60
+ name: "python ai-service.py",
61
+ usedMemoryBytes: 7_168 * 1_024 ** 2,
62
+ },
63
+ ],
64
+ },
65
+ ],
66
+ });
67
+ });
68
+
69
+ test("没有 nvidia-smi 时返回可解释的不可探测状态", async () => {
70
+ const probe = createGpuMonitorProbe(async () => ({
71
+ exitCode: null,
72
+ stdout: "",
73
+ missing: true,
74
+ }));
75
+
76
+ await expect(probe()).resolves.toMatchObject({
77
+ status: "not-detected",
78
+ provider: "NVIDIA",
79
+ devices: [],
80
+ });
81
+ });
82
+ });