create-windy 0.2.20 → 0.2.22

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 (58) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +93 -11
  3. package/package.json +1 -1
  4. package/template/.windy-template.json +2 -2
  5. package/template/README.md +1 -0
  6. package/template/apps/agent-server/Dockerfile +20 -0
  7. package/template/apps/agent-server/README.md +46 -0
  8. package/template/apps/agent-server/pyproject.toml +44 -0
  9. package/template/apps/agent-server/src/southwind_agent_server/__init__.py +5 -0
  10. package/template/apps/agent-server/src/southwind_agent_server/adk_adapter.py +39 -0
  11. package/template/apps/agent-server/src/southwind_agent_server/app.py +263 -0
  12. package/template/apps/agent-server/src/southwind_agent_server/config.py +141 -0
  13. package/template/apps/agent-server/src/southwind_agent_server/contracts.py +99 -0
  14. package/template/apps/agent-server/src/southwind_agent_server/deterministic.py +52 -0
  15. package/template/apps/agent-server/src/southwind_agent_server/engine.py +85 -0
  16. package/template/apps/agent-server/src/southwind_agent_server/openai_engine.py +197 -0
  17. package/template/apps/agent-server/src/southwind_agent_server/openai_provider.py +246 -0
  18. package/template/apps/agent-server/src/southwind_agent_server/service.py +180 -0
  19. package/template/apps/agent-server/src/southwind_agent_server/sqlite_store.py +317 -0
  20. package/template/apps/agent-server/src/southwind_agent_server/sse.py +41 -0
  21. package/template/apps/agent-server/src/southwind_agent_server/store.py +144 -0
  22. package/template/apps/agent-server/src/southwind_agent_server/tool_host.py +80 -0
  23. package/template/apps/agent-server/tests/test_api.py +207 -0
  24. package/template/apps/agent-server/tests/test_config.py +56 -0
  25. package/template/apps/agent-server/tests/test_openai_provider.py +224 -0
  26. package/template/apps/agent-server/tests/test_sqlite_store.py +93 -0
  27. package/template/apps/agent-server/uv.lock +925 -0
  28. package/template/apps/server/src/agent/execution-grants.ts +89 -0
  29. package/template/apps/server/src/agent/remote-runtime.ts +128 -0
  30. package/template/apps/server/src/agent/run-contracts.ts +43 -0
  31. package/template/apps/server/src/agent/run-facade.test.ts +257 -0
  32. package/template/apps/server/src/agent/run-facade.ts +190 -0
  33. package/template/apps/server/src/agent/run-routes.ts +222 -0
  34. package/template/apps/server/src/agent/runtime-bootstrap.ts +53 -0
  35. package/template/apps/server/src/agent/tool-host.test.ts +242 -0
  36. package/template/apps/server/src/agent/tool-host.ts +153 -0
  37. package/template/apps/server/src/index.ts +26 -22
  38. package/template/apps/server/src/module-composition.test.ts +9 -0
  39. package/template/apps/server/src/module-composition.ts +13 -0
  40. package/template/apps/server/src/module-host/initialize-contracts.ts +67 -0
  41. package/template/apps/server/src/module-host/initialize.test.ts +69 -0
  42. package/template/apps/server/src/module-host/initialize.ts +50 -53
  43. package/template/apps/server/src/module-host.ts +7 -0
  44. package/template/apps/server/src/module-runtime-validation.ts +39 -0
  45. package/template/docker-compose.yml +25 -0
  46. package/template/docs/architecture/ai-runtime.md +744 -0
  47. package/template/docs/platform/agent-runtime.md +128 -0
  48. package/template/docs/platform/agent-tools.md +8 -1
  49. package/template/package.json +1 -0
  50. package/template/packages/config/index.test.ts +43 -0
  51. package/template/packages/config/index.ts +1 -0
  52. package/template/packages/config/package.json +2 -1
  53. package/template/packages/config/src/ai-openai-compatible.ts +131 -0
  54. package/template/packages/server-sdk/index.ts +11 -0
  55. package/template/packages/server-sdk/package.json +4 -2
  56. package/template/packages/server-sdk/src/ai-operation-registration.ts +31 -0
  57. package/template/packages/server-sdk/src/module-host.ts +5 -0
  58. package/template/packages/server-sdk/src/tool-host-port.ts +22 -0
@@ -21,6 +21,7 @@ import {
21
21
  createSystemToolHandlers,
22
22
  registerAgentRoutes,
23
23
  } from "./agent/routes.js";
24
+ import { registerAgentPlatformRuntime } from "./agent/runtime-bootstrap.js";
24
25
  // @windy-module system.agent end
25
26
  import { registerAuthRoutes, registerAuthSessionRoute } from "./auth/routes.js";
26
27
  import { createAuthRuntime } from "./auth/runtime.js";
@@ -55,13 +56,17 @@ import { registerNotificationRoutes } from "./notification/routes.js";
55
56
  import { createStorageRuntime } from "./storage/runtime.js";
56
57
  import { registerStorageRoutes } from "./storage/routes.js";
57
58
  // @windy-module system.storage end
58
- import { validateServerModuleComposition } from "./module-composition.js";
59
- import {
60
- adaptModuleSearchProviders,
61
- createModuleTaskDefinitions,
62
- createModuleToolHandlerMap,
63
- installModuleRoutes,
64
- } from "./module-host.js";
59
+ import { validateInstalledRuntime } from "./module-runtime-validation.js";
60
+ import { installModuleRoutes } from "./module-host.js";
61
+ // @windy-module system.search begin
62
+ import { adaptModuleSearchProviders } from "./module-host.js";
63
+ // @windy-module system.search end
64
+ // @windy-module system.scheduler begin
65
+ import { createModuleTaskDefinitions } from "./module-host.js";
66
+ // @windy-module system.scheduler end
67
+ // @windy-module system.agent begin
68
+ import { createModuleToolHandlerMap } from "./module-host.js";
69
+ // @windy-module system.agent end
65
70
  // @windy-module system.search begin
66
71
  import { createSearchHost } from "./search/host.js";
67
72
  // @windy-module system.search end
@@ -288,6 +293,14 @@ const toolHandlers = new Map([
288
293
  ...createModuleToolHandlerMap(moduleHosts),
289
294
  ]);
290
295
  registerAgentRoutes(app, foundationModules, toolHandlers, runtime);
296
+ registerAgentPlatformRuntime({
297
+ app,
298
+ platformConfig,
299
+ modules: foundationModules,
300
+ hosts: moduleHosts,
301
+ runtime,
302
+ toolHandlers,
303
+ });
291
304
  // @windy-module system.agent end
292
305
  // @windy-module system.scheduler begin
293
306
  registerSchedulerRoutes(app, scheduler, runtime, foundationSnapshot.features);
@@ -300,19 +313,21 @@ registerBulkDataRoutes(
300
313
  foundationSnapshot.features,
301
314
  );
302
315
  // @windy-module system.bulk-data end
303
- // @windy-module work-order begin
304
316
  installModuleRoutes(moduleHosts, app, {
305
317
  runtime,
306
318
  features: foundationSnapshot.features,
319
+ // @windy-module system.scheduler begin
307
320
  jobs: durableJobRuntime.service,
321
+ // @windy-module system.scheduler end
322
+ // @windy-module system.storage begin
308
323
  storage: {
309
324
  uploads: storageRuntime.uploads,
310
325
  files: storageRuntime.files,
311
326
  },
327
+ // @windy-module system.storage end
312
328
  });
313
- // @windy-module work-order end
314
329
 
315
- validateServerModuleComposition({
330
+ validateInstalledRuntime({
316
331
  modules: foundationModules,
317
332
  routes: app.routes,
318
333
  // @windy-module system.scheduler begin
@@ -324,18 +339,7 @@ validateServerModuleComposition({
324
339
  // @windy-module system.search begin
325
340
  providers: searchHost.providerKeys,
326
341
  // @windy-module system.search end
327
- moduleRegistrations: {
328
- routes: moduleHosts.routes.map(
329
- ({ definition }) => `${definition.method} ${definition.path}`,
330
- ),
331
- tasks: moduleHosts.taskHandlers.map(
332
- ({ registration }) => registration.taskKey,
333
- ),
334
- tools: moduleHosts.toolHandlers.map(
335
- ({ registration }) => registration.toolKey,
336
- ),
337
- providers: moduleHosts.searchProviders.map(({ provider }) => provider.key),
338
- },
342
+ hosts: moduleHosts,
339
343
  });
340
344
 
341
345
  const port = process.env.PORT ? Number(process.env.PORT) : 9_747;
@@ -73,6 +73,15 @@ describe("Server Module Composition", () => {
73
73
  moduleRegistrations: { providers: ["system.unknown-search"] },
74
74
  }),
75
75
  ).toThrow("搜索 Provider已注册但未在 Manifest 声明:system.unknown-search");
76
+
77
+ expect(() =>
78
+ validateServerModuleComposition({
79
+ ...base,
80
+ moduleRegistrations: { aiOperations: ["system.unknown-operation"] },
81
+ }),
82
+ ).toThrow(
83
+ "AI Operation Handler已注册但未在 Manifest 声明:system.unknown-operation",
84
+ );
76
85
  });
77
86
  });
78
87
 
@@ -16,6 +16,7 @@ export interface ModuleImplementationRegistrations {
16
16
  routes?: readonly string[];
17
17
  tasks?: readonly string[];
18
18
  tools?: readonly string[];
19
+ aiOperations?: readonly string[];
19
20
  providers?: readonly string[];
20
21
  }
21
22
 
@@ -24,6 +25,7 @@ export interface ServerCompositionInput {
24
25
  routes: readonly RegisteredHttpRoute[];
25
26
  tasks?: readonly { key: string }[];
26
27
  tools?: ReadonlyMap<string, unknown>;
28
+ aiOperations?: readonly string[];
27
29
  providers?: readonly string[];
28
30
  moduleRegistrations?: ModuleImplementationRegistrations;
29
31
  }
@@ -35,6 +37,7 @@ export function validateServerModuleComposition(
35
37
  apiRoutes: input.routes.map(apiRouteKey),
36
38
  tasks: (input.tasks || []).map(({ key }) => key),
37
39
  tools: [...(input.tools?.keys() || [])],
40
+ aiOperations: input.aiOperations,
38
41
  providers: input.providers || [],
39
42
  });
40
43
  if (input.moduleRegistrations) {
@@ -55,6 +58,11 @@ function validateRegistrationConsistency(
55
58
  const declaredTools = new Set(
56
59
  modules.flatMap((module) => module.tools.map(({ key }) => key)),
57
60
  );
61
+ const declaredAiOperations = new Set(
62
+ modules.flatMap((module) =>
63
+ (module.aiOperations ?? []).map(({ key }) => key),
64
+ ),
65
+ );
58
66
  const declaredProviders = new Set(
59
67
  modules.flatMap((module) => module.providers.map(({ key }) => key)),
60
68
  );
@@ -62,6 +70,11 @@ function validateRegistrationConsistency(
62
70
  ["模块路由", registrations.routes || [], declaredRoutes],
63
71
  ["定时任务 Handler", registrations.tasks || [], declaredTasks],
64
72
  ["Agent Tool Handler", registrations.tools || [], declaredTools],
73
+ [
74
+ "AI Operation Handler",
75
+ registrations.aiOperations || [],
76
+ declaredAiOperations,
77
+ ],
65
78
  ["搜索 Provider", registrations.providers || [], declaredProviders],
66
79
  ] as const) {
67
80
  for (const key of registered) {
@@ -0,0 +1,67 @@
1
+ import type { DurableHandlerRegistry } from "@southwind-ai/jobs";
2
+ import type { ModuleManifest } from "@southwind-ai/modules";
3
+ import type {
4
+ InstallableServerModule,
5
+ ModuleAiOperationHandlerRegistration,
6
+ ModuleInitializer,
7
+ ModuleRolePreset,
8
+ ModuleRouteDefinition,
9
+ ModuleSearchProvider,
10
+ ModuleTaskHandlerRegistration,
11
+ ModuleToolHandlerRegistration,
12
+ ModuleUploadPolicy,
13
+ } from "@southwind-ai/server-sdk";
14
+
15
+ export interface RegisteredModuleRoute {
16
+ readonly moduleName: string;
17
+ readonly definition: ModuleRouteDefinition;
18
+ }
19
+
20
+ export interface RegisteredModuleTaskHandler {
21
+ readonly moduleName: string;
22
+ readonly registration: ModuleTaskHandlerRegistration;
23
+ }
24
+
25
+ export interface RegisteredModuleToolHandler {
26
+ readonly moduleName: string;
27
+ readonly registration: ModuleToolHandlerRegistration;
28
+ }
29
+
30
+ export interface RegisteredModuleAiOperationHandler {
31
+ readonly moduleName: string;
32
+ readonly registration: ModuleAiOperationHandlerRegistration;
33
+ }
34
+
35
+ export interface RegisteredModuleRolePreset {
36
+ readonly moduleName: string;
37
+ readonly preset: ModuleRolePreset;
38
+ }
39
+
40
+ export interface RegisteredModuleSearchProvider {
41
+ readonly moduleName: string;
42
+ readonly provider: ModuleSearchProvider;
43
+ }
44
+
45
+ export interface InitializeModuleHostsInput {
46
+ modules: readonly ModuleManifest[];
47
+ /** Manifest 与 setup 由同一业务模块安装单元贡献。 */
48
+ installations?: readonly InstallableServerModule<ModuleManifest>[];
49
+ /** @deprecated 仅供旧模块迁移;新模块必须使用 installations。 */
50
+ initializers?: readonly ModuleInitializer[];
51
+ baseUploadPolicies?: readonly ModuleUploadPolicy[];
52
+ }
53
+
54
+ export interface InitializedModuleHosts {
55
+ readonly manifests: readonly ModuleManifest[];
56
+ readonly uploadPolicies: readonly ModuleUploadPolicy[];
57
+ readonly routes: readonly RegisteredModuleRoute[];
58
+ readonly taskHandlers: readonly RegisteredModuleTaskHandler[];
59
+ readonly toolHandlers: readonly RegisteredModuleToolHandler[];
60
+ readonly aiOperationHandlers: readonly RegisteredModuleAiOperationHandler[];
61
+ readonly rolePresets: readonly RegisteredModuleRolePreset[];
62
+ readonly searchProviders: readonly RegisteredModuleSearchProvider[];
63
+ manifestOf(moduleName: string): ModuleManifest;
64
+ /** 该模块经 Host 注册的上传用途集合(不含平台基础用途)。 */
65
+ uploadPurposesOf(moduleName: string): ReadonlySet<string>;
66
+ installDurableHandlers(registry: DurableHandlerRegistry): void;
67
+ }
@@ -102,6 +102,75 @@ describe("模块 Host 初始化", () => {
102
102
  }),
103
103
  ).rejects.toThrow("Server Module Manifest 与能力目录不是同一声明:known");
104
104
  });
105
+
106
+ test("AI Operation Handler 只注册本模块 Manifest 声明", async () => {
107
+ const operation = {
108
+ key: "known.summarize",
109
+ kind: "agent" as const,
110
+ featureKey: "known.ai",
111
+ permissionKey: "known.ai.execute",
112
+ auditAction: "known.ai.execute",
113
+ inputSchemaKey: "known.input.v1",
114
+ outputSchemaKey: "known.output.v1",
115
+ requiredCapabilities: ["text-generation"],
116
+ allowedToolKeys: [],
117
+ dataClassification: "internal" as const,
118
+ execution: "streaming" as const,
119
+ publicNetworkAccess: "forbidden" as const,
120
+ limits: {
121
+ maxDurationSeconds: 60,
122
+ maxInputBytes: 4096,
123
+ maxOutputBytes: 4096,
124
+ },
125
+ };
126
+ const known = manifest("known", { aiOperations: [operation] });
127
+ const initialized = await initializeModuleHosts({
128
+ modules: [known],
129
+ installations: [
130
+ {
131
+ manifest: known,
132
+ setup(host) {
133
+ host.registerAiOperationHandlers([
134
+ {
135
+ operationKey: operation.key,
136
+ prepare: () => ({
137
+ instructions: "按要求回答。",
138
+ userPrompt: "你好",
139
+ }),
140
+ },
141
+ ]);
142
+ },
143
+ },
144
+ ],
145
+ });
146
+ expect(
147
+ initialized.aiOperationHandlers.map(
148
+ ({ registration }) => registration.operationKey,
149
+ ),
150
+ ).toEqual(["known.summarize"]);
151
+
152
+ await expect(
153
+ initializeModuleHosts({
154
+ modules: [known],
155
+ installations: [
156
+ {
157
+ manifest: known,
158
+ setup(host) {
159
+ host.registerAiOperationHandlers([
160
+ {
161
+ operationKey: "known.unknown",
162
+ prepare: () => ({
163
+ instructions: "x",
164
+ userPrompt: "x",
165
+ }),
166
+ },
167
+ ]);
168
+ },
169
+ },
170
+ ],
171
+ }),
172
+ ).rejects.toThrow("AI Operation Handler 未在 Manifest 声明");
173
+ });
105
174
  });
106
175
 
107
176
  function initializer(
@@ -5,64 +5,31 @@ import {
5
5
  import type { ModuleManifest } from "@southwind-ai/modules";
6
6
  import {
7
7
  requireModuleNamespace,
8
- type InstallableServerModule,
9
8
  type ModuleHost,
10
- type ModuleInitializer,
11
- type ModuleRolePreset,
12
9
  type ModuleRouteDefinition,
13
- type ModuleSearchProvider,
14
- type ModuleTaskHandlerRegistration,
15
- type ModuleToolHandlerRegistration,
16
10
  type ModuleUploadPolicy,
17
11
  } from "@southwind-ai/server-sdk";
12
+ import type {
13
+ InitializedModuleHosts,
14
+ InitializeModuleHostsInput,
15
+ RegisteredModuleAiOperationHandler,
16
+ RegisteredModuleRolePreset,
17
+ RegisteredModuleRoute,
18
+ RegisteredModuleSearchProvider,
19
+ RegisteredModuleTaskHandler,
20
+ RegisteredModuleToolHandler,
21
+ } from "./initialize-contracts.js";
18
22
 
19
- export interface RegisteredModuleRoute {
20
- readonly moduleName: string;
21
- readonly definition: ModuleRouteDefinition;
22
- }
23
-
24
- export interface RegisteredModuleTaskHandler {
25
- readonly moduleName: string;
26
- readonly registration: ModuleTaskHandlerRegistration;
27
- }
28
-
29
- export interface RegisteredModuleToolHandler {
30
- readonly moduleName: string;
31
- readonly registration: ModuleToolHandlerRegistration;
32
- }
33
-
34
- export interface RegisteredModuleRolePreset {
35
- readonly moduleName: string;
36
- readonly preset: ModuleRolePreset;
37
- }
38
-
39
- export interface RegisteredModuleSearchProvider {
40
- readonly moduleName: string;
41
- readonly provider: ModuleSearchProvider;
42
- }
43
-
44
- export interface InitializeModuleHostsInput {
45
- modules: readonly ModuleManifest[];
46
- /** Manifest 与 setup 由同一业务模块安装单元贡献。 */
47
- installations?: readonly InstallableServerModule<ModuleManifest>[];
48
- /** @deprecated 仅供旧模块迁移;新模块必须使用 installations。 */
49
- initializers?: readonly ModuleInitializer[];
50
- baseUploadPolicies?: readonly ModuleUploadPolicy[];
51
- }
52
-
53
- export interface InitializedModuleHosts {
54
- readonly manifests: readonly ModuleManifest[];
55
- readonly uploadPolicies: readonly ModuleUploadPolicy[];
56
- readonly routes: readonly RegisteredModuleRoute[];
57
- readonly taskHandlers: readonly RegisteredModuleTaskHandler[];
58
- readonly toolHandlers: readonly RegisteredModuleToolHandler[];
59
- readonly rolePresets: readonly RegisteredModuleRolePreset[];
60
- readonly searchProviders: readonly RegisteredModuleSearchProvider[];
61
- manifestOf(moduleName: string): ModuleManifest;
62
- /** 该模块经 Host 注册的上传用途集合(不含平台基础用途)。 */
63
- uploadPurposesOf(moduleName: string): ReadonlySet<string>;
64
- installDurableHandlers(registry: DurableHandlerRegistry): void;
65
- }
23
+ export type {
24
+ InitializedModuleHosts,
25
+ InitializeModuleHostsInput,
26
+ RegisteredModuleAiOperationHandler,
27
+ RegisteredModuleRolePreset,
28
+ RegisteredModuleRoute,
29
+ RegisteredModuleSearchProvider,
30
+ RegisteredModuleTaskHandler,
31
+ RegisteredModuleToolHandler,
32
+ } from "./initialize-contracts.js";
66
33
 
67
34
  type HandlerInstaller = (registry: DurableHandlerRegistry) => void;
68
35
 
@@ -94,6 +61,8 @@ export async function initializeModuleHosts(
94
61
  taskKeys: new Set<string>(),
95
62
  toolHandlers: [],
96
63
  toolKeys: new Set<string>(),
64
+ aiOperationHandlers: [],
65
+ aiOperationKeys: new Set<string>(),
97
66
  rolePresets: [],
98
67
  rolePresetKeys: new Set<string>(),
99
68
  searchProviders: [],
@@ -142,6 +111,7 @@ export async function initializeModuleHosts(
142
111
  routes: [...state.routes],
143
112
  taskHandlers: [...state.taskHandlers],
144
113
  toolHandlers: [...state.toolHandlers],
114
+ aiOperationHandlers: [...state.aiOperationHandlers],
145
115
  rolePresets: [...state.rolePresets],
146
116
  searchProviders: [...state.searchProviders],
147
117
  manifestOf(moduleName) {
@@ -170,6 +140,8 @@ interface HostCollections {
170
140
  taskKeys: Set<string>;
171
141
  toolHandlers: RegisteredModuleToolHandler[];
172
142
  toolKeys: Set<string>;
143
+ aiOperationHandlers: RegisteredModuleAiOperationHandler[];
144
+ aiOperationKeys: Set<string>;
173
145
  rolePresets: RegisteredModuleRolePreset[];
174
146
  rolePresetKeys: Set<string>;
175
147
  searchProviders: RegisteredModuleSearchProvider[];
@@ -253,6 +225,31 @@ function createScopedHost(
253
225
  state.toolHandlers.push({ moduleName, registration });
254
226
  }
255
227
  },
228
+ registerAiOperationHandlers(registrations) {
229
+ for (const registration of registrations) {
230
+ requireModuleNamespace(
231
+ "AI Operation",
232
+ registration.operationKey,
233
+ moduleName,
234
+ );
235
+ if (
236
+ !manifest.aiOperations?.some(
237
+ ({ key }) => key === registration.operationKey,
238
+ )
239
+ ) {
240
+ throw new Error(
241
+ `AI Operation Handler 未在 Manifest 声明:${registration.operationKey}`,
242
+ );
243
+ }
244
+ if (state.aiOperationKeys.has(registration.operationKey)) {
245
+ throw new Error(
246
+ `AI Operation Handler 重复注册:${registration.operationKey}`,
247
+ );
248
+ }
249
+ state.aiOperationKeys.add(registration.operationKey);
250
+ state.aiOperationHandlers.push({ moduleName, registration });
251
+ }
252
+ },
256
253
  registerRolePresets(presets) {
257
254
  for (const preset of presets) {
258
255
  requireModuleNamespace("角色预设", preset.key, moduleName);
@@ -7,6 +7,7 @@ export {
7
7
  type RegisteredModuleSearchProvider,
8
8
  type RegisteredModuleTaskHandler,
9
9
  type RegisteredModuleToolHandler,
10
+ type RegisteredModuleAiOperationHandler,
10
11
  } from "./module-host/initialize.js";
11
12
  export {
12
13
  buildModuleRequestContext,
@@ -21,14 +22,20 @@ export {
21
22
  moduleRolePresetId,
22
23
  synchronizeModuleRolePresets,
23
24
  } from "./module-host/role-presets.js";
25
+ // @windy-module system.search begin
24
26
  export {
25
27
  adaptModuleSearchProvider,
26
28
  adaptModuleSearchProviders,
27
29
  } from "./module-host/search-providers.js";
30
+ // @windy-module system.search end
28
31
  export {
29
32
  createModuleStoragePort,
30
33
  type ModuleStorageScope,
31
34
  type ModuleStorageServices,
32
35
  } from "./module-host/storage-port.js";
36
+ // @windy-module system.scheduler begin
33
37
  export { createModuleTaskDefinitions } from "./module-host/task-definitions.js";
38
+ // @windy-module system.scheduler end
39
+ // @windy-module system.agent begin
34
40
  export { createModuleToolHandlerMap } from "./module-host/tool-handlers.js";
41
+ // @windy-module system.agent end
@@ -0,0 +1,39 @@
1
+ import type { ModuleManifest } from "@southwind-ai/modules";
2
+ import { validateServerModuleComposition } from "./module-composition.js";
3
+ import type { InitializedModuleHosts } from "./module-host.js";
4
+
5
+ export function validateInstalledRuntime(input: {
6
+ modules: ModuleManifest[];
7
+ routes: readonly { method: string; path: string }[];
8
+ tasks?: readonly { key: string }[];
9
+ tools?: ReadonlyMap<string, unknown>;
10
+ providers?: readonly string[];
11
+ hosts: InitializedModuleHosts;
12
+ }): void {
13
+ const aiOperations = input.hosts.aiOperationHandlers.map(
14
+ ({ registration }) => registration.operationKey,
15
+ );
16
+ validateServerModuleComposition({
17
+ modules: input.modules,
18
+ routes: input.routes,
19
+ tasks: input.tasks,
20
+ tools: input.tools,
21
+ aiOperations,
22
+ providers: input.providers,
23
+ moduleRegistrations: {
24
+ routes: input.hosts.routes.map(
25
+ ({ definition }) => `${definition.method} ${definition.path}`,
26
+ ),
27
+ tasks: input.hosts.taskHandlers.map(
28
+ ({ registration }) => registration.taskKey,
29
+ ),
30
+ tools: input.hosts.toolHandlers.map(
31
+ ({ registration }) => registration.toolKey,
32
+ ),
33
+ aiOperations,
34
+ providers: input.hosts.searchProviders.map(
35
+ ({ provider }) => provider.key,
36
+ ),
37
+ },
38
+ });
39
+ }
@@ -42,6 +42,10 @@ services:
42
42
  WINDY_CONTAINERIZED: "1"
43
43
  BUN_CONFIG_NO_CLEAR_TERMINAL_ON_RELOAD: "true"
44
44
  WINDY_SYSTEM_USAGE_PATHS: /app/data/audit
45
+ PLATFORM_ENABLE_AI: ${PLATFORM_ENABLE_AI:-false}
46
+ AGENT_SERVER_URL: http://agent-server:8080
47
+ AGENT_TOOL_HOST_URL: http://server:9747
48
+ AGENT_SERVER_INTERNAL_TOKEN: ${AGENT_SERVER_INTERNAL_TOKEN:-}
45
49
  ports:
46
50
  - "9747:9747"
47
51
  volumes:
@@ -52,6 +56,26 @@ services:
52
56
  migrate:
53
57
  condition: service_completed_successfully
54
58
 
59
+ agent-server:
60
+ profiles: ["agent"]
61
+ build:
62
+ context: .
63
+ dockerfile: apps/agent-server/Dockerfile
64
+ environment:
65
+ AGENT_SERVER_INTERNAL_TOKEN: ${AGENT_SERVER_INTERNAL_TOKEN:-}
66
+ AGENT_RUN_STORE_PATH: /app/data/agent/runs.sqlite3
67
+ QWEN_API_URL: ${QWEN_API_URL:-}
68
+ QWEN_API_KEY: ${QWEN_API_KEY:-}
69
+ QWEN_MODEL: ${QWEN_MODEL:-}
70
+ AI_PROVIDER_CONNECT_TIMEOUT_MS: ${AI_PROVIDER_CONNECT_TIMEOUT_MS:-5000}
71
+ AI_PROVIDER_REQUEST_TIMEOUT_MS: ${AI_PROVIDER_REQUEST_TIMEOUT_MS:-60000}
72
+ AI_PROVIDER_MAX_RESPONSE_BYTES: ${AI_PROVIDER_MAX_RESPONSE_BYTES:-2097152}
73
+ volumes:
74
+ - agent-run-data:/app/data/agent
75
+ depends_on:
76
+ server:
77
+ condition: service_started
78
+
55
79
  web:
56
80
  build:
57
81
  context: .
@@ -85,3 +109,4 @@ services:
85
109
  volumes:
86
110
  postgres-data:
87
111
  server-audit-data:
112
+ agent-run-data: