create-windy 0.2.22 → 0.2.24
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.
- package/dist/cli.js +25 -3
- package/package.json +1 -1
- package/template/.agents/skills/windy-business-module/SKILL.md +43 -0
- package/template/.windy-template.json +2 -2
- package/template/AGENTS.md +2 -0
- package/template/apps/server/package.json +3 -1
- package/template/apps/server/src/agent/data-scope-routes.test.ts +20 -20
- package/template/apps/server/src/agent/remote-runtime.test.ts +59 -0
- package/template/apps/server/src/agent/remote-runtime.ts +22 -4
- package/template/apps/server/src/agent/routes.test.ts +16 -8
- package/template/apps/server/src/agent/routes.ts +103 -169
- package/template/apps/server/src/agent/run-facade.test.ts +30 -0
- package/template/apps/server/src/agent/runtime-bootstrap.ts +3 -3
- package/template/apps/server/src/agent/tool-host.test.ts +61 -8
- package/template/apps/server/src/agent/tool-host.ts +17 -73
- package/template/apps/server/src/agent/tool-registry.test.ts +117 -0
- package/template/apps/server/src/agent/tool-registry.ts +318 -0
- package/template/apps/server/src/http-app.test.ts +87 -0
- package/template/apps/server/src/http-app.ts +43 -25
- package/template/apps/server/src/index.ts +14 -2
- package/template/apps/server/src/license/runtime-service.test.ts +6 -3
- package/template/apps/server/src/mcp/adapter.test.ts +195 -0
- package/template/apps/server/src/mcp/adapter.ts +204 -0
- package/template/apps/server/src/mcp/routes.ts +21 -0
- package/template/apps/server/src/module-host/tool-handlers.ts +6 -5
- package/template/apps/server/src/observability/http-observability.test.ts +21 -0
- package/template/apps/server/src/observability/http-observability.ts +1 -0
- package/template/apps/server/src/observability/metrics.ts +5 -2
- package/template/docs/operations/observability.md +9 -1
- package/template/docs/platform/agent-runtime.md +15 -3
- package/template/docs/platform/agent-tools.md +88 -28
- package/template/packages/modules/index.ts +1 -0
- package/template/packages/modules/src/ai-operation-composition.test.ts +30 -0
- package/template/packages/modules/src/ai-operation-composition.ts +13 -0
- package/template/packages/modules/src/composition.test.ts +42 -0
- package/template/packages/modules/src/composition.ts +3 -0
- package/template/packages/modules/src/manifest.ts +20 -1
- package/template/packages/modules/src/system-agent-tools.ts +53 -1
- package/template/packages/modules/src/system-api-permissions.ts +5 -0
- package/template/packages/modules/src/system-module-catalog.ts +1 -0
- package/template/packages/modules/src/system-modules.test.ts +32 -0
- package/template/packages/modules/src/system-modules.ts +9 -1
- package/template/packages/modules/src/tool-composition.ts +86 -0
- package/template/packages/server-sdk/index.ts +1 -0
- package/template/packages/server-sdk/package.json +1 -0
- package/template/packages/server-sdk/src/ai-operation-registration.ts +2 -7
- package/template/packages/server-sdk/src/tool-registration.ts +6 -7
- package/template/packages/shared/index.ts +1 -0
- package/template/packages/shared/src/json.ts +8 -0
|
@@ -172,15 +172,45 @@ function fixture(name: string): ModuleManifest {
|
|
|
172
172
|
],
|
|
173
173
|
migrations: [],
|
|
174
174
|
schemaExports: [],
|
|
175
|
+
jsonSchemas: [
|
|
176
|
+
{
|
|
177
|
+
key: `${name}.tool.input.v1`,
|
|
178
|
+
schema: { type: "object" },
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
key: `${name}.tool.output.v1`,
|
|
182
|
+
schema: { type: "object" },
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
key: `${name}.ai.input.v1`,
|
|
186
|
+
schema: { type: "object" },
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
key: `${name}.ai.output.v1`,
|
|
190
|
+
schema: { type: "object" },
|
|
191
|
+
},
|
|
192
|
+
],
|
|
175
193
|
tasks: [],
|
|
176
194
|
tools: [
|
|
177
195
|
{
|
|
178
196
|
key: `${name}.tool`,
|
|
179
197
|
label: "业务工具",
|
|
180
198
|
description: "AI Operation 测试工具。",
|
|
199
|
+
inputSchemaKey: `${name}.tool.input.v1`,
|
|
200
|
+
outputSchemaKey: `${name}.tool.output.v1`,
|
|
201
|
+
operation: "query",
|
|
181
202
|
permissionKey,
|
|
182
203
|
featureKey,
|
|
183
204
|
auditAction: `${name}.ai.execute`,
|
|
205
|
+
scopeMode: "request",
|
|
206
|
+
dataClassification: "internal",
|
|
207
|
+
idempotency: "none",
|
|
208
|
+
approval: "none",
|
|
209
|
+
limits: {
|
|
210
|
+
maxDurationSeconds: 10,
|
|
211
|
+
maxInputBytes: 4096,
|
|
212
|
+
maxOutputBytes: 4096,
|
|
213
|
+
},
|
|
184
214
|
},
|
|
185
215
|
],
|
|
186
216
|
providers: [],
|
|
@@ -8,6 +8,7 @@ interface AiOperationCatalogs {
|
|
|
8
8
|
features: ReadonlySet<string>;
|
|
9
9
|
audits: ReadonlySet<string>;
|
|
10
10
|
tools: ReadonlySet<string>;
|
|
11
|
+
schemas: ReadonlySet<string>;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export function validateAiOperations(
|
|
@@ -85,6 +86,18 @@ function validateOperation(
|
|
|
85
86
|
operation.auditAction,
|
|
86
87
|
catalogs.audits,
|
|
87
88
|
);
|
|
89
|
+
requiredReference(
|
|
90
|
+
operation.key,
|
|
91
|
+
" JSON Schema",
|
|
92
|
+
operation.inputSchemaKey,
|
|
93
|
+
catalogs.schemas,
|
|
94
|
+
);
|
|
95
|
+
requiredReference(
|
|
96
|
+
operation.key,
|
|
97
|
+
" JSON Schema",
|
|
98
|
+
operation.outputSchemaKey,
|
|
99
|
+
catalogs.schemas,
|
|
100
|
+
);
|
|
88
101
|
validateCapabilities(operation);
|
|
89
102
|
validateTools(operation, catalogs.tools);
|
|
90
103
|
validateLimits(operation);
|
|
@@ -170,6 +170,26 @@ describe("Module Composition 契约", () => {
|
|
|
170
170
|
);
|
|
171
171
|
});
|
|
172
172
|
|
|
173
|
+
test("Agent Tool 必须引用已注册 object Schema 并声明正数限制", () => {
|
|
174
|
+
const missing = fixture("system");
|
|
175
|
+
missing.tools[0]!.inputSchemaKey = "system.missing.v1";
|
|
176
|
+
expect(() => validateModuleComposition([missing])).toThrow(
|
|
177
|
+
"引用了未注册 JSON Schema",
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
const invalidSchema = fixture("system");
|
|
181
|
+
invalidSchema.jsonSchemas![0]!.schema = { type: "array" };
|
|
182
|
+
expect(() => validateModuleComposition([invalidSchema])).toThrow(
|
|
183
|
+
"顶层类型必须是 object",
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
const invalidLimit = fixture("system");
|
|
187
|
+
invalidLimit.tools[0]!.limits.maxInputBytes = 0;
|
|
188
|
+
expect(() => validateModuleComposition([invalidLimit])).toThrow(
|
|
189
|
+
"上限必须是正整数",
|
|
190
|
+
);
|
|
191
|
+
});
|
|
192
|
+
|
|
173
193
|
test("字段治理策略校验唯一资源、权限风险和已注册字段", () => {
|
|
174
194
|
const module = fixture("system");
|
|
175
195
|
module.permissions.push({
|
|
@@ -266,6 +286,16 @@ function fixture(name: string, dependencies: string[] = []): ModuleManifest {
|
|
|
266
286
|
migrationIds: [`${name}.001`],
|
|
267
287
|
},
|
|
268
288
|
],
|
|
289
|
+
jsonSchemas: [
|
|
290
|
+
{
|
|
291
|
+
key: `${name}.tool.input.v1`,
|
|
292
|
+
schema: { type: "object" },
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
key: `${name}.tool.output.v1`,
|
|
296
|
+
schema: { type: "object" },
|
|
297
|
+
},
|
|
298
|
+
],
|
|
269
299
|
tasks: [
|
|
270
300
|
{
|
|
271
301
|
key: `${name}.task`,
|
|
@@ -283,9 +313,21 @@ function fixture(name: string, dependencies: string[] = []): ModuleManifest {
|
|
|
283
313
|
key: `${name}.tool`,
|
|
284
314
|
label: name,
|
|
285
315
|
description: name,
|
|
316
|
+
inputSchemaKey: `${name}.tool.input.v1`,
|
|
317
|
+
outputSchemaKey: `${name}.tool.output.v1`,
|
|
318
|
+
operation: "query",
|
|
286
319
|
permissionKey: readPermission,
|
|
287
320
|
featureKey,
|
|
288
321
|
auditAction: "agent.tool.execute",
|
|
322
|
+
scopeMode: "request",
|
|
323
|
+
dataClassification: "internal",
|
|
324
|
+
idempotency: "none",
|
|
325
|
+
approval: "none",
|
|
326
|
+
limits: {
|
|
327
|
+
maxDurationSeconds: 10,
|
|
328
|
+
maxInputBytes: 4096,
|
|
329
|
+
maxOutputBytes: 4096,
|
|
330
|
+
},
|
|
289
331
|
},
|
|
290
332
|
],
|
|
291
333
|
providers: [
|
|
@@ -7,6 +7,7 @@ import { validateAiOperations } from "./ai-operation-composition.js";
|
|
|
7
7
|
import { validateImplementationBindings } from "./implementation-bindings.js";
|
|
8
8
|
import { composeModuleMenus } from "./menu-composition.js";
|
|
9
9
|
import { validateCapabilityCatalogs } from "./catalog-validation.js";
|
|
10
|
+
import { validateToolContracts } from "./tool-composition.js";
|
|
10
11
|
|
|
11
12
|
export interface ValidatedModuleComposition {
|
|
12
13
|
modules: readonly ModuleManifest[];
|
|
@@ -35,6 +36,7 @@ export function validateModuleComposition(
|
|
|
35
36
|
modules.flatMap((module) => module.features.map(({ key }) => key)),
|
|
36
37
|
);
|
|
37
38
|
const catalogs = validateCapabilityCatalogs(modules);
|
|
39
|
+
const jsonSchemas = validateToolContracts(modules);
|
|
38
40
|
const audits = new Set(catalogs.auditActions.map(({ key }) => key));
|
|
39
41
|
const menus = composeModuleMenus(modules).flatMap((menu) =>
|
|
40
42
|
flattenMenus([menu]),
|
|
@@ -69,6 +71,7 @@ export function validateModuleComposition(
|
|
|
69
71
|
features,
|
|
70
72
|
audits,
|
|
71
73
|
tools: toolKeys,
|
|
74
|
+
schemas: new Set(jsonSchemas.keys()),
|
|
72
75
|
});
|
|
73
76
|
const providers = modules.flatMap((module) => module.providers);
|
|
74
77
|
unique(
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
AuditActionType,
|
|
5
5
|
DataGovernancePolicyDefinition,
|
|
6
6
|
FeatureFlagDefinition,
|
|
7
|
+
JsonObject,
|
|
7
8
|
MigrationBundle,
|
|
8
9
|
MigrationDefinition,
|
|
9
10
|
MenuNode,
|
|
@@ -20,9 +21,26 @@ export interface ModuleToolDefinition {
|
|
|
20
21
|
key: string;
|
|
21
22
|
label: string;
|
|
22
23
|
description: string;
|
|
24
|
+
inputSchemaKey: string;
|
|
25
|
+
outputSchemaKey: string;
|
|
26
|
+
operation: "query" | "command" | "background";
|
|
23
27
|
permissionKey: string;
|
|
24
|
-
featureKey
|
|
28
|
+
featureKey: string;
|
|
25
29
|
auditAction: AuditActionType;
|
|
30
|
+
scopeMode: "platform" | "request";
|
|
31
|
+
dataClassification: "public" | "internal" | "sensitive";
|
|
32
|
+
idempotency: "none" | "supported" | "required";
|
|
33
|
+
approval: "none" | "required";
|
|
34
|
+
limits: {
|
|
35
|
+
maxDurationSeconds: number;
|
|
36
|
+
maxInputBytes: number;
|
|
37
|
+
maxOutputBytes: number;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ModuleJsonSchemaDefinition {
|
|
42
|
+
key: string;
|
|
43
|
+
schema: JsonObject;
|
|
26
44
|
}
|
|
27
45
|
|
|
28
46
|
export type AdminRouteSurface = "sidebar" | "account" | "hidden";
|
|
@@ -107,6 +125,7 @@ export interface ModuleManifest {
|
|
|
107
125
|
migrations: MigrationDefinition[];
|
|
108
126
|
migrationBundle?: MigrationBundle;
|
|
109
127
|
schemaExports: ModuleSchemaExportDefinition[];
|
|
128
|
+
jsonSchemas?: ModuleJsonSchemaDefinition[];
|
|
110
129
|
tasks: ModuleScheduledTaskDefinition[];
|
|
111
130
|
tools: ModuleToolDefinition[];
|
|
112
131
|
aiOperations?: ModuleAiOperationDefinition[];
|
|
@@ -1,15 +1,66 @@
|
|
|
1
|
-
import type { ModuleManifest } from "./manifest.js";
|
|
1
|
+
import type { ModuleJsonSchemaDefinition, ModuleManifest } from "./manifest.js";
|
|
2
|
+
|
|
3
|
+
export const systemAgentToolSchemas: ModuleJsonSchemaDefinition[] = [
|
|
4
|
+
{
|
|
5
|
+
key: "system.pagination.input.v1",
|
|
6
|
+
schema: {
|
|
7
|
+
type: "object",
|
|
8
|
+
properties: {
|
|
9
|
+
page: { type: "integer", minimum: 1, default: 1 },
|
|
10
|
+
pageSize: {
|
|
11
|
+
type: "integer",
|
|
12
|
+
minimum: 1,
|
|
13
|
+
maximum: 50,
|
|
14
|
+
default: 10,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
additionalProperties: false,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
key: "system.page.output.v1",
|
|
22
|
+
schema: {
|
|
23
|
+
type: "object",
|
|
24
|
+
properties: {
|
|
25
|
+
items: { type: "array", items: { type: "object" } },
|
|
26
|
+
total: { type: "integer", minimum: 0 },
|
|
27
|
+
page: { type: "integer", minimum: 1 },
|
|
28
|
+
pageSize: { type: "integer", minimum: 1 },
|
|
29
|
+
},
|
|
30
|
+
required: ["items", "total", "page", "pageSize"],
|
|
31
|
+
additionalProperties: false,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const queryContract = {
|
|
37
|
+
inputSchemaKey: "system.pagination.input.v1",
|
|
38
|
+
outputSchemaKey: "system.page.output.v1",
|
|
39
|
+
operation: "query" as const,
|
|
40
|
+
scopeMode: "platform" as const,
|
|
41
|
+
dataClassification: "internal" as const,
|
|
42
|
+
idempotency: "none" as const,
|
|
43
|
+
approval: "none" as const,
|
|
44
|
+
limits: {
|
|
45
|
+
maxDurationSeconds: 10,
|
|
46
|
+
maxInputBytes: 4_096,
|
|
47
|
+
maxOutputBytes: 1_048_576,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
2
50
|
|
|
3
51
|
export const systemAgentTools: ModuleManifest["tools"] = [
|
|
4
52
|
{
|
|
53
|
+
...queryContract,
|
|
5
54
|
key: "system.users.list",
|
|
6
55
|
label: "查询用户",
|
|
7
56
|
description: "分页查询平台用户账号。",
|
|
8
57
|
permissionKey: "system.user.read",
|
|
9
58
|
featureKey: "system.rbac",
|
|
10
59
|
auditAction: "agent.tool.execute",
|
|
60
|
+
scopeMode: "request",
|
|
11
61
|
},
|
|
12
62
|
{
|
|
63
|
+
...queryContract,
|
|
13
64
|
key: "system.features.list",
|
|
14
65
|
label: "查询功能开关",
|
|
15
66
|
description: "分页查询平台功能开关状态。",
|
|
@@ -18,6 +69,7 @@ export const systemAgentTools: ModuleManifest["tools"] = [
|
|
|
18
69
|
auditAction: "agent.tool.execute",
|
|
19
70
|
},
|
|
20
71
|
{
|
|
72
|
+
...queryContract,
|
|
21
73
|
key: "system.audit.list",
|
|
22
74
|
label: "查询审计日志",
|
|
23
75
|
description: "分页查询平台审计事件。",
|
|
@@ -25,6 +25,11 @@ export function systemApiPermissionBindings(): ApiPermissionBinding[] {
|
|
|
25
25
|
),
|
|
26
26
|
binding("POST", "/api/search", "system.search.read", "system.search"),
|
|
27
27
|
// @windy-module system.search end
|
|
28
|
+
// @windy-module system.agent begin
|
|
29
|
+
binding("GET", "/api/mcp", "system.agent.read", "system.agent"),
|
|
30
|
+
binding("POST", "/api/mcp", "system.agent.read", "system.agent"),
|
|
31
|
+
binding("DELETE", "/api/mcp", "system.agent.read", "system.agent"),
|
|
32
|
+
// @windy-module system.agent end
|
|
28
33
|
...resourceBindings({
|
|
29
34
|
path: "users",
|
|
30
35
|
permissionResource: "user",
|
|
@@ -218,6 +218,7 @@ function createSystemCapabilityModule(
|
|
|
218
218
|
.map((feature) => ({ ...feature, module: definition.name })),
|
|
219
219
|
migrations: isCore ? aggregate.migrations : [],
|
|
220
220
|
schemaExports: isCore ? aggregate.schemaExports : [],
|
|
221
|
+
jsonSchemas: isCore ? aggregate.jsonSchemas : [],
|
|
221
222
|
tasks: aggregate.tasks.filter(ownsGuard),
|
|
222
223
|
tools: aggregate.tools.filter(ownsGuard),
|
|
223
224
|
providers: aggregate.providers.filter(ownsGuard),
|
|
@@ -326,6 +326,38 @@ describe("系统模块能力注册", () => {
|
|
|
326
326
|
anonymous: undefined,
|
|
327
327
|
});
|
|
328
328
|
});
|
|
329
|
+
|
|
330
|
+
// @windy-module system.agent begin
|
|
331
|
+
test("MCP Streamable HTTP 入口统一绑定 Agent Feature 与读取权限", () => {
|
|
332
|
+
const bindings = systemFoundationModule().apiPermissions.filter(
|
|
333
|
+
({ path }) => path === "/api/mcp",
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
expect(bindings).toEqual([
|
|
337
|
+
{
|
|
338
|
+
method: "GET",
|
|
339
|
+
path: "/api/mcp",
|
|
340
|
+
permissionKey: "system.agent.read",
|
|
341
|
+
featureKey: "system.agent",
|
|
342
|
+
anonymous: undefined,
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
method: "POST",
|
|
346
|
+
path: "/api/mcp",
|
|
347
|
+
permissionKey: "system.agent.read",
|
|
348
|
+
featureKey: "system.agent",
|
|
349
|
+
anonymous: undefined,
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
method: "DELETE",
|
|
353
|
+
path: "/api/mcp",
|
|
354
|
+
permissionKey: "system.agent.read",
|
|
355
|
+
featureKey: "system.agent",
|
|
356
|
+
anonymous: undefined,
|
|
357
|
+
},
|
|
358
|
+
]);
|
|
359
|
+
});
|
|
360
|
+
// @windy-module system.agent end
|
|
329
361
|
});
|
|
330
362
|
|
|
331
363
|
function flattenMenuKeys(
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { ModuleManifest } from "./manifest.js";
|
|
2
2
|
// @windy-module system.agent begin
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
systemAgentToolSchemas,
|
|
5
|
+
systemAgentTools,
|
|
6
|
+
} from "./system-agent-tools.js";
|
|
4
7
|
// @windy-module system.agent end
|
|
5
8
|
import { systemAdminRoutes } from "./system-admin-routes.js";
|
|
6
9
|
import { systemApiPermissionBindings } from "./system-api-permissions.js";
|
|
@@ -249,6 +252,11 @@ export function systemFoundationModule(): ModuleManifest {
|
|
|
249
252
|
migrationIds: ["0000..0028"],
|
|
250
253
|
},
|
|
251
254
|
],
|
|
255
|
+
jsonSchemas: [
|
|
256
|
+
// @windy-module system.agent begin
|
|
257
|
+
...systemAgentToolSchemas,
|
|
258
|
+
// @windy-module system.agent end
|
|
259
|
+
],
|
|
252
260
|
tasks: [
|
|
253
261
|
// @windy-module system.scheduler begin
|
|
254
262
|
{
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ModuleJsonSchemaDefinition,
|
|
3
|
+
ModuleManifest,
|
|
4
|
+
ModuleToolDefinition,
|
|
5
|
+
} from "./manifest.js";
|
|
6
|
+
|
|
7
|
+
export function validateToolContracts(
|
|
8
|
+
modules: readonly ModuleManifest[],
|
|
9
|
+
): ReadonlyMap<string, ModuleJsonSchemaDefinition> {
|
|
10
|
+
const schemas = new Map<string, ModuleJsonSchemaDefinition>();
|
|
11
|
+
for (const module of modules) {
|
|
12
|
+
for (const definition of module.jsonSchemas ?? []) {
|
|
13
|
+
validateSchema(module.name, definition);
|
|
14
|
+
if (schemas.has(definition.key)) {
|
|
15
|
+
throw new Error(`JSON Schema Key 重复:${definition.key}`);
|
|
16
|
+
}
|
|
17
|
+
schemas.set(definition.key, definition);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
for (const module of modules) {
|
|
21
|
+
for (const tool of module.tools) {
|
|
22
|
+
validateTool(module.name, tool, schemas);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return schemas;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function validateSchema(
|
|
29
|
+
moduleName: string,
|
|
30
|
+
definition: ModuleJsonSchemaDefinition,
|
|
31
|
+
): void {
|
|
32
|
+
if (!definition.key.trim()) throw new Error("JSON Schema Key 不能为空");
|
|
33
|
+
if (!definition.key.startsWith(`${moduleName}.`)) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
`JSON Schema ${definition.key} 必须使用模块命名空间 ${moduleName}`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
if (definition.schema.type !== "object") {
|
|
39
|
+
throw new Error(`JSON Schema ${definition.key} 顶层类型必须是 object`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function validateTool(
|
|
44
|
+
moduleName: string,
|
|
45
|
+
tool: ModuleToolDefinition,
|
|
46
|
+
schemas: ReadonlyMap<string, ModuleJsonSchemaDefinition>,
|
|
47
|
+
): void {
|
|
48
|
+
const fields: Array<string | undefined> = [
|
|
49
|
+
tool.key,
|
|
50
|
+
tool.label,
|
|
51
|
+
tool.description,
|
|
52
|
+
tool.inputSchemaKey,
|
|
53
|
+
tool.outputSchemaKey,
|
|
54
|
+
tool.featureKey,
|
|
55
|
+
tool.permissionKey,
|
|
56
|
+
tool.auditAction,
|
|
57
|
+
];
|
|
58
|
+
if (fields.some((value) => !value?.trim())) {
|
|
59
|
+
throw new Error("Agent Tool Key、展示信息与引用字段不能为空");
|
|
60
|
+
}
|
|
61
|
+
const rootNamespace = moduleName.split(".")[0]!;
|
|
62
|
+
if (!tool.key.startsWith(`${rootNamespace}.`)) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`Agent Tool ${tool.key} 必须使用模块根命名空间 ${rootNamespace}`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
requireSchema(tool.key, tool.inputSchemaKey, schemas);
|
|
68
|
+
requireSchema(tool.key, tool.outputSchemaKey, schemas);
|
|
69
|
+
if (tool.operation === "query" && tool.approval !== "none") {
|
|
70
|
+
throw new Error(`${tool.key} 查询能力不能要求执行审批`);
|
|
71
|
+
}
|
|
72
|
+
const limits = Object.values(tool.limits);
|
|
73
|
+
if (limits.some((value) => !Number.isSafeInteger(value) || value <= 0)) {
|
|
74
|
+
throw new Error(`${tool.key} 运行与输入输出上限必须是正整数`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function requireSchema(
|
|
79
|
+
toolKey: string,
|
|
80
|
+
schemaKey: string,
|
|
81
|
+
schemas: ReadonlyMap<string, ModuleJsonSchemaDefinition>,
|
|
82
|
+
): void {
|
|
83
|
+
if (!schemas.has(schemaKey)) {
|
|
84
|
+
throw new Error(`${toolKey} 引用了未注册 JSON Schema:${schemaKey}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
+
import type { JsonValue } from "@southwind-ai/shared";
|
|
1
2
|
import type { ModuleActor } from "./actor.js";
|
|
2
3
|
|
|
3
|
-
export type ModuleAiJsonValue =
|
|
4
|
-
| string
|
|
5
|
-
| number
|
|
6
|
-
| boolean
|
|
7
|
-
| null
|
|
8
|
-
| ModuleAiJsonValue[]
|
|
9
|
-
| { [key: string]: ModuleAiJsonValue };
|
|
4
|
+
export type ModuleAiJsonValue = JsonValue;
|
|
10
5
|
|
|
11
6
|
export interface ModuleAiOperationContext {
|
|
12
7
|
readonly actor: ModuleActor;
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import type { ModuleActor } from "./actor.js";
|
|
2
|
+
import type { ModuleAiJsonValue } from "./ai-operation-registration.js";
|
|
2
3
|
|
|
3
4
|
export interface ModuleToolContext {
|
|
4
5
|
readonly actor: ModuleActor;
|
|
5
6
|
readonly requestId: string;
|
|
7
|
+
readonly signal: AbortSignal;
|
|
6
8
|
}
|
|
7
9
|
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
readonly page: number;
|
|
12
|
-
readonly pageSize: number;
|
|
13
|
-
}
|
|
10
|
+
export type ModuleToolInput = Readonly<Record<string, ModuleAiJsonValue>>;
|
|
11
|
+
|
|
12
|
+
export type ModuleToolResult = Readonly<Record<string, ModuleAiJsonValue>>;
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
15
|
* 模块 Agent Tool 的 Handler 注册。
|
|
@@ -23,7 +22,7 @@ export interface ModuleToolHandlerRegistration {
|
|
|
23
22
|
readonly toolKey: string;
|
|
24
23
|
readonly scopeMode: "platform" | "request";
|
|
25
24
|
execute(
|
|
26
|
-
|
|
25
|
+
input: ModuleToolInput,
|
|
27
26
|
context: ModuleToolContext,
|
|
28
27
|
): Promise<ModuleToolResult> | ModuleToolResult;
|
|
29
28
|
}
|
|
@@ -4,6 +4,7 @@ export * from "./src/data-governance.js";
|
|
|
4
4
|
export * from "./src/feature.js";
|
|
5
5
|
export * from "./src/license.js";
|
|
6
6
|
export * from "./src/license-catalog.js";
|
|
7
|
+
export * from "./src/json.js";
|
|
7
8
|
export * from "./src/migration.js";
|
|
8
9
|
export * from "./src/migration-bundle-validation.js";
|
|
9
10
|
export * from "./src/module-configuration.js";
|