create-windy 0.4.1 → 0.5.0
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 +258 -121
- package/package.json +1 -1
- package/template/.windy-template.json +2 -2
- package/template/apps/agent-server/adk_web_agents/canonical_debug/agent.py +29 -9
- package/template/apps/agent-server/adk_web_agents/services.py +25 -0
- package/template/apps/agent-server/src/southwind_agent_server/adk_adapter.py +40 -11
- package/template/apps/agent-server/src/southwind_agent_server/adk_debug_sessions.py +325 -0
- package/template/apps/agent-server/src/southwind_agent_server/adk_model.py +70 -6
- package/template/apps/agent-server/src/southwind_agent_server/adk_tools.py +1 -1
- package/template/apps/agent-server/src/southwind_agent_server/app.py +16 -4
- package/template/apps/agent-server/src/southwind_agent_server/approval_resolution.py +30 -0
- package/template/apps/agent-server/src/southwind_agent_server/contracts.py +9 -0
- package/template/apps/agent-server/src/southwind_agent_server/deterministic.py +2 -1
- package/template/apps/agent-server/src/southwind_agent_server/engine.py +3 -1
- package/template/apps/agent-server/src/southwind_agent_server/openai_engine.py +95 -130
- package/template/apps/agent-server/src/southwind_agent_server/openai_engine_validation.py +133 -0
- package/template/apps/agent-server/src/southwind_agent_server/openai_provider.py +89 -20
- package/template/apps/agent-server/src/southwind_agent_server/postgres_report_insert.py +34 -0
- package/template/apps/agent-server/src/southwind_agent_server/postgres_report_queries.py +234 -0
- package/template/apps/agent-server/src/southwind_agent_server/postgres_report_replay.py +44 -0
- package/template/apps/agent-server/src/southwind_agent_server/postgres_report_status.py +83 -0
- package/template/apps/agent-server/src/southwind_agent_server/postgres_report_store.py +331 -0
- package/template/apps/agent-server/src/southwind_agent_server/postgres_report_summary.py +304 -0
- package/template/apps/agent-server/src/southwind_agent_server/report_lifecycle.py +217 -0
- package/template/apps/agent-server/src/southwind_agent_server/report_metrics.py +149 -0
- package/template/apps/agent-server/src/southwind_agent_server/report_presenter.py +280 -0
- package/template/apps/agent-server/src/southwind_agent_server/report_routes.py +126 -0
- package/template/apps/agent-server/src/southwind_agent_server/report_store.py +344 -0
- package/template/apps/agent-server/src/southwind_agent_server/report_types.py +111 -0
- package/template/apps/agent-server/src/southwind_agent_server/runtime_factory.py +31 -2
- package/template/apps/agent-server/src/southwind_agent_server/service.py +56 -51
- package/template/apps/agent-server/src/southwind_agent_server/signal_events.py +18 -1
- package/template/apps/agent-server/tests/test_adk_adapter.py +7 -9
- package/template/apps/agent-server/tests/test_adk_debug_sessions.py +215 -0
- package/template/apps/agent-server/tests/test_adk_web_canonical_debug.py +48 -1
- package/template/apps/agent-server/tests/test_openai_provider.py +58 -6
- package/template/apps/agent-server/tests/test_postgres_report_status.py +59 -0
- package/template/apps/agent-server/tests/test_postgres_report_summary.py +298 -0
- package/template/apps/agent-server/tests/test_provider_attempt_reporting.py +152 -0
- package/template/apps/agent-server/tests/test_report_attempt_lifecycle.py +126 -0
- package/template/apps/agent-server/tests/test_report_resilience.py +181 -0
- package/template/apps/agent-server/tests/test_report_store.py +286 -0
- package/template/apps/agent-server/tests/test_report_summary_parity.py +115 -0
- package/template/apps/agent-server/tests/test_report_terminal_sync.py +177 -0
- package/template/apps/server/src/agent/debug-session-facade-provider-failure.test.ts +269 -0
- package/template/apps/server/src/agent/debug-session-facade.test.ts +335 -0
- package/template/apps/server/src/agent/debug-session-facade.ts +302 -0
- package/template/apps/server/src/agent/debug-session-id.test.ts +37 -0
- package/template/apps/server/src/agent/debug-session-id.ts +37 -0
- package/template/apps/server/src/agent/debug-session-provider-reader.ts +54 -0
- package/template/apps/server/src/agent/debug-session-routes.test.ts +64 -0
- package/template/apps/server/src/agent/debug-session-routes.ts +73 -0
- package/template/apps/server/src/agent/debug-session-validation.ts +106 -0
- package/template/apps/server/src/agent/drizzle-run-binding-repository.ts +2 -0
- package/template/apps/server/src/agent/provider-input-governance.test.ts +18 -0
- package/template/apps/server/src/agent/provider-input-governance.ts +37 -1
- package/template/apps/server/src/agent/remote-runtime.test.ts +7 -0
- package/template/apps/server/src/agent/remote-runtime.ts +1 -0
- package/template/apps/server/src/agent/run-authorization.test.ts +98 -0
- package/template/apps/server/src/agent/run-authorization.ts +32 -1
- package/template/apps/server/src/agent/run-binding-repository.ts +1 -0
- package/template/apps/server/src/agent/run-contracts.ts +5 -0
- package/template/apps/server/src/agent/run-facade-adk-debug.test.ts +39 -0
- package/template/apps/server/src/agent/run-facade.test.ts +78 -0
- package/template/apps/server/src/agent/run-facade.ts +33 -3
- package/template/apps/server/src/agent/run-input-commitment.ts +50 -0
- package/template/apps/server/src/agent/run-input-verification.ts +41 -0
- package/template/apps/server/src/agent/runtime-bootstrap-report.test.ts +136 -0
- package/template/apps/server/src/agent/runtime-bootstrap.ts +66 -1
- package/template/apps/server/src/agent/tool-host.test.ts +1 -0
- package/template/apps/server/src/agent/unavailable-routes.ts +4 -0
- package/template/apps/server/src/ai-runtime-report/actor-directory.ts +94 -0
- package/template/apps/server/src/ai-runtime-report/actor-handle.test.ts +27 -0
- package/template/apps/server/src/ai-runtime-report/actor-handle.ts +81 -0
- package/template/apps/server/src/ai-runtime-report/contracts.ts +91 -0
- package/template/apps/server/src/ai-runtime-report/remote-parser.ts +212 -0
- package/template/apps/server/src/ai-runtime-report/remote-port.ts +138 -0
- package/template/apps/server/src/ai-runtime-report/routes.ts +172 -0
- package/template/apps/server/src/ai-runtime-report/scope.ts +63 -0
- package/template/apps/server/src/ai-runtime-report/service.test.ts +271 -0
- package/template/apps/server/src/ai-runtime-report/service.ts +206 -0
- package/template/apps/server/src/license/runtime-audit-reliability.test.ts +2 -0
- package/template/apps/server/src/license/runtime-service.test.ts +6 -0
- package/template/apps/server/src/module-host/ai-operation-port.test.ts +63 -0
- package/template/apps/server/src/module-host/ai-operation-port.ts +30 -5
- package/template/apps/server/src/module-host/debug-session-provider-registration.test.ts +162 -0
- package/template/apps/server/src/module-host/debug-session-provider-registration.ts +84 -0
- package/template/apps/server/src/module-host/initialize-contracts.ts +7 -0
- package/template/apps/server/src/module-host/initialize.ts +6 -197
- package/template/apps/server/src/module-host/request-context.ts +1 -0
- package/template/apps/server/src/module-host/scoped-host.ts +214 -0
- package/template/apps/server/src/module-host.ts +1 -0
- package/template/apps/server/src/runtime-adk-debug.test.ts +25 -1
- package/template/apps/server/src/runtime-adk-debug.ts +8 -2
- package/template/apps/server/src/system/operations.test.ts +54 -0
- package/template/apps/server/src/system/operations.ts +23 -1
- package/template/apps/server/src/system/seed.test.ts +4 -0
- package/template/apps/server/src/system/system-resource-monitoring.test.ts +82 -0
- package/template/apps/server/src/system/system-resource-monitoring.ts +300 -0
- package/template/apps/web/src/layout/AppSidebar.vue +6 -1
- package/template/apps/web/src/layout/AppSidebar.webtest.ts +2 -0
- package/template/apps/web/src/pages/auth/LoginPage.vue +6 -1
- package/template/apps/web/src/pages/settings/BrandingSettingsSection.vue +20 -3
- package/template/apps/web/src/pages/settings/BrandingSettingsSection.webtest.ts +80 -0
- package/template/apps/web/src/pages/system/AiRuntimePage.vue +129 -0
- package/template/apps/web/src/pages/system/SystemOperationsPage.vue +2 -1
- package/template/apps/web/src/pages/system/ai-runtime-presenter.ts +60 -0
- package/template/apps/web/src/pages/system/ai-runtime-presenter.webtest.ts +48 -0
- package/template/apps/web/src/pages/system/audit-time-range.ts +34 -2
- package/template/apps/web/src/pages/system/components/AuditLogFilters.vue +4 -3
- package/template/apps/web/src/pages/system/components/AuditLogFilters.webtest.ts +26 -1
- package/template/apps/web/src/pages/system/components/OperationGpuMonitor.vue +98 -0
- package/template/apps/web/src/pages/system/components/OperationGpuMonitor.webtest.ts +61 -0
- package/template/apps/web/src/pages/system/components/OperationServiceMonitor.vue +53 -0
- package/template/apps/web/src/pages/system/components/OperationServiceMonitor.webtest.ts +20 -1
- package/template/apps/web/src/pages/system/components/SystemResourcePage.webtest.ts +14 -1
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeActivityChart.vue +85 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeCharts.webtest.ts +82 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeFilters.vue +98 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeFilters.webtest.ts +27 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeRunDetailDialog.vue +168 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeRunTable.vue +130 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeSummaryCards.vue +82 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeSummaryCards.webtest.ts +102 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeTokenByModelChart.vue +101 -0
- package/template/apps/web/src/pages/system/composables/useAiRuntimeReport.ts +185 -0
- package/template/apps/web/src/pages/system/composables/useAiRuntimeReport.webtest.ts +158 -0
- package/template/apps/web/src/pages/system/composables/useSystemResource.ts +21 -7
- package/template/apps/web/src/router/error-navigation.ts +22 -0
- package/template/apps/web/src/router/index.ts +12 -23
- package/template/apps/web/src/router/platform-admin-route-bindings.ts +4 -0
- package/template/apps/web/src/services/ai-runtime-api.ts +164 -0
- package/template/apps/web/src/services/ai-runtime-api.webtest.ts +111 -0
- package/template/apps/web/src/services/operations-api.ts +28 -0
- package/template/docker-compose.agent-debug.yml +1 -1
- package/template/docs/architecture/ai-runtime.md +2 -1
- package/template/docs/architecture/module-host.md +3 -0
- package/template/docs/platform/agent-run-attestation.md +65 -0
- package/template/docs/platform/agent-runtime-observability.md +135 -0
- package/template/docs/platform/agent-runtime.md +47 -3
- package/template/docs/platform/operations-overview.md +13 -0
- package/template/docs/platform/platform-authentication.md +4 -3
- package/template/packages/config/package.json +1 -1
- package/template/packages/database/drizzle/0045_tiny_cassandra_nova.sql +65 -0
- package/template/packages/database/drizzle/0046_tranquil_living_mummy.sql +6 -0
- package/template/packages/database/drizzle/0047_oval_luke_cage.sql +18 -0
- package/template/packages/database/drizzle/meta/0045_snapshot.json +8814 -0
- package/template/packages/database/drizzle/meta/0046_snapshot.json +8820 -0
- package/template/packages/database/drizzle/meta/0047_snapshot.json +8835 -0
- package/template/packages/database/drizzle/meta/_journal.json +21 -0
- package/template/packages/database/package.json +1 -1
- package/template/packages/database/src/agent-runtime-schema.test.ts +54 -0
- package/template/packages/database/src/schema/agent-runtime.ts +114 -0
- package/template/packages/jobs/package.json +1 -1
- package/template/packages/modules/package.json +1 -1
- package/template/packages/modules/src/system-admin-routes.ts +10 -0
- package/template/packages/modules/src/system-admin-ui-catalog.ts +1 -1
- package/template/packages/modules/src/system-api-control-plane.ts +17 -1
- package/template/packages/modules/src/system-audit-actions.ts +10 -0
- package/template/packages/modules/src/system-features.ts +8 -0
- package/template/packages/modules/src/system-module-catalog.test.ts +35 -0
- package/template/packages/modules/src/system-module-catalog.ts +15 -4
- package/template/packages/modules/src/system-modules-access.test.ts +69 -0
- package/template/packages/modules/src/system-modules.ts +13 -0
- package/template/packages/modules/src/system-permissions.ts +2 -0
- package/template/packages/server-sdk/index.ts +11 -0
- package/template/packages/server-sdk/package.json +2 -1
- package/template/packages/server-sdk/src/ai-operation-port.ts +5 -0
- package/template/packages/server-sdk/src/debug-session-provider.ts +63 -0
- package/template/packages/server-sdk/src/module-host.ts +5 -0
- package/template/packages/shared/index.ts +3 -0
- package/template/packages/shared/package.json +1 -1
- package/template/packages/shared/src/ai-runtime-report.ts +134 -0
- package/template/scripts/lib/postgres-regression.ts +201 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @windy-module system.audit begin
|
|
1
2
|
import { generateKeyPairSync, sign } from "node:crypto";
|
|
2
3
|
import { describe, expect, test } from "bun:test";
|
|
3
4
|
import {
|
|
@@ -94,3 +95,4 @@ function signedLicense() {
|
|
|
94
95
|
publicKeyPem: publicKey.export({ type: "spki", format: "pem" }).toString(),
|
|
95
96
|
};
|
|
96
97
|
}
|
|
98
|
+
// @windy-module system.audit end
|
|
@@ -12,11 +12,13 @@ import {
|
|
|
12
12
|
type SignedLicenseV1,
|
|
13
13
|
} from "@southwind-ai/shared";
|
|
14
14
|
import { evaluateAccessGuards } from "../access-guards.js";
|
|
15
|
+
// @windy-module system.agent begin
|
|
15
16
|
import {
|
|
16
17
|
createSystemToolHandlers,
|
|
17
18
|
registerAgentRoutes,
|
|
18
19
|
} from "../agent/routes.js";
|
|
19
20
|
import { GovernedToolRegistry } from "../agent/tool-registry.js";
|
|
21
|
+
// @windy-module system.agent end
|
|
20
22
|
import { runGuardedBackgroundTask } from "../background/task-guard.js";
|
|
21
23
|
import { createFoundationSnapshot, loadSystemModules } from "../foundation.js";
|
|
22
24
|
import {
|
|
@@ -61,6 +63,7 @@ describe("客户业务平台 License 运行闭环", () => {
|
|
|
61
63
|
expect(await status(fixture.app, "GET", "/api/system/users", true)).toBe(
|
|
62
64
|
403,
|
|
63
65
|
);
|
|
66
|
+
// @windy-module system.agent begin
|
|
64
67
|
expect(
|
|
65
68
|
await status(
|
|
66
69
|
fixture.app,
|
|
@@ -69,6 +72,7 @@ describe("客户业务平台 License 运行闭环", () => {
|
|
|
69
72
|
true,
|
|
70
73
|
),
|
|
71
74
|
).toBe(403);
|
|
75
|
+
// @windy-module system.agent end
|
|
72
76
|
expect(
|
|
73
77
|
await status(fixture.app, "GET", "/api/system/users/export", true),
|
|
74
78
|
).toBe(403);
|
|
@@ -264,6 +268,7 @@ function createFixture(
|
|
|
264
268
|
const routeAuthorizer = createStaticRouteAuthorizer(runtime, modules);
|
|
265
269
|
registerPlatformLicenseRoutes(app, licenseRuntime, routeAuthorizer);
|
|
266
270
|
registerSystemRoutes(app, repositories, runtime, snapshot.features);
|
|
271
|
+
// @windy-module system.agent begin
|
|
267
272
|
registerAgentRoutes(
|
|
268
273
|
app,
|
|
269
274
|
new GovernedToolRegistry(
|
|
@@ -273,6 +278,7 @@ function createFixture(
|
|
|
273
278
|
),
|
|
274
279
|
routeAuthorizer,
|
|
275
280
|
);
|
|
281
|
+
// @windy-module system.agent end
|
|
276
282
|
return { app, runtime, licenseRuntime };
|
|
277
283
|
}
|
|
278
284
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { createModuleAiOperationPort } from "./ai-operation-port.js";
|
|
3
|
+
|
|
4
|
+
describe("Module AI Operation canonical result", () => {
|
|
5
|
+
test("只把尚未完成的 RUN_CONFLICT 映射为 pending", async () => {
|
|
6
|
+
const port = agentPort(() =>
|
|
7
|
+
Response.json(
|
|
8
|
+
{ code: "RUN_CONFLICT", message: "尚未完成" },
|
|
9
|
+
{ status: 409 },
|
|
10
|
+
),
|
|
11
|
+
);
|
|
12
|
+
await expect(
|
|
13
|
+
port.result({
|
|
14
|
+
kind: "agent",
|
|
15
|
+
operationKey: "analysis.copilot.case",
|
|
16
|
+
runId: "run-1",
|
|
17
|
+
}),
|
|
18
|
+
).rejects.toMatchObject({ code: "AI_OPERATION_PENDING" });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("failed 与 cancelled 的 409 映射为 canonical failure", async () => {
|
|
22
|
+
for (const code of ["RUNTIME_UNAVAILABLE", "RUN_CANCELLED"]) {
|
|
23
|
+
const port = agentPort(() =>
|
|
24
|
+
Response.json({ code, message: "终态" }, { status: 409 }),
|
|
25
|
+
);
|
|
26
|
+
await expect(
|
|
27
|
+
port.result({
|
|
28
|
+
kind: "agent",
|
|
29
|
+
operationKey: "analysis.copilot.case",
|
|
30
|
+
runId: "run-1",
|
|
31
|
+
}),
|
|
32
|
+
).rejects.toMatchObject({ code: "AI_OPERATION_FAILED" });
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("503 暂不可用不能伪装成 Run 终态", async () => {
|
|
37
|
+
const port = agentPort(() =>
|
|
38
|
+
Response.json(
|
|
39
|
+
{ code: "RUNTIME_UNAVAILABLE", message: "暂不可用" },
|
|
40
|
+
{ status: 503 },
|
|
41
|
+
),
|
|
42
|
+
);
|
|
43
|
+
await expect(
|
|
44
|
+
port.result({
|
|
45
|
+
kind: "agent",
|
|
46
|
+
operationKey: "analysis.copilot.case",
|
|
47
|
+
runId: "run-1",
|
|
48
|
+
}),
|
|
49
|
+
).rejects.toMatchObject({ code: "AI_OPERATION_UNAVAILABLE" });
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
function agentPort(result: () => Response) {
|
|
54
|
+
return createModuleAiOperationPort(
|
|
55
|
+
"analysis",
|
|
56
|
+
{} as never,
|
|
57
|
+
AbortSignal.timeout(1_000),
|
|
58
|
+
{
|
|
59
|
+
agentFacade: { authorizeRun: async () => undefined },
|
|
60
|
+
agentRemote: { result: async () => result() },
|
|
61
|
+
} as never,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -49,6 +49,15 @@ export function createModuleAiOperationPort(
|
|
|
49
49
|
signal,
|
|
50
50
|
);
|
|
51
51
|
},
|
|
52
|
+
async verifyAgentRun(input) {
|
|
53
|
+
own(input.operationKey);
|
|
54
|
+
await services.agentFacade.verifyRunInput(
|
|
55
|
+
input.operationKey,
|
|
56
|
+
input.runId,
|
|
57
|
+
input.input,
|
|
58
|
+
context,
|
|
59
|
+
);
|
|
60
|
+
},
|
|
52
61
|
async startDocument(input) {
|
|
53
62
|
own(input.operationKey);
|
|
54
63
|
return services.documentFacade.start(
|
|
@@ -148,22 +157,38 @@ async function responseJson(response: Response): Promise<ModuleAiJsonValue> {
|
|
|
148
157
|
|
|
149
158
|
async function responseError(response: Response) {
|
|
150
159
|
let message = `AI Runtime 返回 ${response.status}`;
|
|
160
|
+
let remoteCode = "";
|
|
151
161
|
try {
|
|
152
162
|
const value = (await response.json()) as Record<string, unknown>;
|
|
153
163
|
if (typeof value.message === "string") message = value.message;
|
|
164
|
+
if (typeof value.code === "string") remoteCode = value.code;
|
|
154
165
|
} catch {
|
|
155
166
|
// Runtime 正文不受信任;只保留稳定状态。
|
|
156
167
|
}
|
|
157
168
|
return new ModuleAiOperationError(
|
|
158
|
-
response.status
|
|
159
|
-
? "AI_OPERATION_INVALID"
|
|
160
|
-
: response.status === 409
|
|
161
|
-
? "AI_OPERATION_PENDING"
|
|
162
|
-
: "AI_OPERATION_FAILED",
|
|
169
|
+
operationErrorCode(response.status, remoteCode),
|
|
163
170
|
message,
|
|
164
171
|
);
|
|
165
172
|
}
|
|
166
173
|
|
|
174
|
+
function operationErrorCode(status: number, remoteCode: string) {
|
|
175
|
+
if (status === 403) return "AI_OPERATION_FORBIDDEN" as const;
|
|
176
|
+
if (status === 404 || status === 400 || status === 422) {
|
|
177
|
+
return "AI_OPERATION_INVALID" as const;
|
|
178
|
+
}
|
|
179
|
+
if (status >= 500) return "AI_OPERATION_UNAVAILABLE" as const;
|
|
180
|
+
if (status === 409 && remoteCode === "RUN_CONFLICT") {
|
|
181
|
+
return "AI_OPERATION_PENDING" as const;
|
|
182
|
+
}
|
|
183
|
+
if (
|
|
184
|
+
status === 409 &&
|
|
185
|
+
(remoteCode === "RUN_CANCELLED" || remoteCode === "RUNTIME_UNAVAILABLE")
|
|
186
|
+
) {
|
|
187
|
+
return "AI_OPERATION_FAILED" as const;
|
|
188
|
+
}
|
|
189
|
+
return "AI_OPERATION_INVALID" as const;
|
|
190
|
+
}
|
|
191
|
+
|
|
167
192
|
function jsonValue(value: unknown): ModuleAiJsonValue {
|
|
168
193
|
return JSON.parse(JSON.stringify(value)) as ModuleAiJsonValue;
|
|
169
194
|
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import type { ModuleManifest } from "@southwind-ai/modules";
|
|
3
|
+
import {
|
|
4
|
+
MODULE_DEBUG_SESSION_SCHEMA_VERSION,
|
|
5
|
+
type ModuleDebugSessionProviderV1,
|
|
6
|
+
} from "@southwind-ai/server-sdk";
|
|
7
|
+
import { featureKey } from "@southwind-ai/shared";
|
|
8
|
+
import { initializeModuleHosts } from "./initialize.js";
|
|
9
|
+
import { manifest } from "./test-fixtures.js";
|
|
10
|
+
|
|
11
|
+
describe("ADK 调试会话 Provider 注册", () => {
|
|
12
|
+
test("只接受同模块 Agent Operation 的 Feature 与权限绑定", async () => {
|
|
13
|
+
const module = debugManifest();
|
|
14
|
+
const provider = debugProvider();
|
|
15
|
+
const hosts = await initializeModuleHosts({
|
|
16
|
+
modules: [module],
|
|
17
|
+
installations: [
|
|
18
|
+
{
|
|
19
|
+
manifest: module,
|
|
20
|
+
setup(host) {
|
|
21
|
+
host.registerDebugSessionProviders([provider]);
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
expect(hosts.debugSessionProviders).toEqual([
|
|
28
|
+
{ moduleName: "known", provider },
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
await expect(
|
|
32
|
+
initializeWith({
|
|
33
|
+
...provider,
|
|
34
|
+
permissionKey: "known.copilot.manage",
|
|
35
|
+
}),
|
|
36
|
+
).rejects.toThrow("必须复用 Operation 的 Feature 与权限");
|
|
37
|
+
await expect(
|
|
38
|
+
initializeWith({
|
|
39
|
+
...provider,
|
|
40
|
+
operationKeys: ["known.missing"],
|
|
41
|
+
}),
|
|
42
|
+
).rejects.toThrow("未声明为 Agent");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("协议版本、Manifest 审计动作、命名空间与重复 Key fail closed", async () => {
|
|
46
|
+
await expect(
|
|
47
|
+
initializeWith({
|
|
48
|
+
...debugProvider(),
|
|
49
|
+
key: "other.history",
|
|
50
|
+
}),
|
|
51
|
+
).rejects.toThrow("不属于模块 known");
|
|
52
|
+
await expect(
|
|
53
|
+
initializeWith({
|
|
54
|
+
...debugProvider(),
|
|
55
|
+
auditAction: "known.missing",
|
|
56
|
+
}),
|
|
57
|
+
).rejects.toThrow("审计动作未在 Manifest 声明");
|
|
58
|
+
await expect(
|
|
59
|
+
initializeWith({
|
|
60
|
+
...debugProvider(),
|
|
61
|
+
schemaVersion: 2 as 1,
|
|
62
|
+
}),
|
|
63
|
+
).rejects.toThrow("协议版本不受支持");
|
|
64
|
+
|
|
65
|
+
const module = debugManifest();
|
|
66
|
+
const provider = debugProvider();
|
|
67
|
+
await expect(
|
|
68
|
+
initializeModuleHosts({
|
|
69
|
+
modules: [module],
|
|
70
|
+
installations: [
|
|
71
|
+
{
|
|
72
|
+
manifest: module,
|
|
73
|
+
setup(host) {
|
|
74
|
+
host.registerDebugSessionProviders([provider, provider]);
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
}),
|
|
79
|
+
).rejects.toThrow("重复注册");
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
async function initializeWith(provider: ModuleDebugSessionProviderV1) {
|
|
84
|
+
const module = debugManifest();
|
|
85
|
+
return initializeModuleHosts({
|
|
86
|
+
modules: [module],
|
|
87
|
+
installations: [
|
|
88
|
+
{
|
|
89
|
+
manifest: module,
|
|
90
|
+
setup(host) {
|
|
91
|
+
host.registerDebugSessionProviders([provider]);
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function debugProvider(): ModuleDebugSessionProviderV1 {
|
|
99
|
+
return {
|
|
100
|
+
schemaVersion: MODULE_DEBUG_SESSION_SCHEMA_VERSION,
|
|
101
|
+
key: "known.copilot.history",
|
|
102
|
+
operationKeys: ["known.copilot.global"],
|
|
103
|
+
featureKey: "known.copilot",
|
|
104
|
+
permissionKey: "known.copilot.execute",
|
|
105
|
+
auditAction: "known.copilot.history.read",
|
|
106
|
+
list: async () => ({ sessions: [], truncated: false }),
|
|
107
|
+
get: async () => undefined,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function debugManifest(): ModuleManifest {
|
|
112
|
+
return manifest("known", {
|
|
113
|
+
features: [
|
|
114
|
+
{
|
|
115
|
+
key: featureKey("known.copilot"),
|
|
116
|
+
label: "Copilot",
|
|
117
|
+
module: "known",
|
|
118
|
+
visible: "visible",
|
|
119
|
+
enabled: true,
|
|
120
|
+
stage: "stable",
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
permissions: [
|
|
124
|
+
permission("known.copilot.execute"),
|
|
125
|
+
permission("known.copilot.manage"),
|
|
126
|
+
],
|
|
127
|
+
aiOperations: [
|
|
128
|
+
{
|
|
129
|
+
key: "known.copilot.global",
|
|
130
|
+
kind: "agent",
|
|
131
|
+
featureKey: "known.copilot",
|
|
132
|
+
permissionKey: "known.copilot.execute",
|
|
133
|
+
auditAction: "known.copilot.execute",
|
|
134
|
+
inputSchemaKey: "known.copilot.input.v1",
|
|
135
|
+
outputSchemaKey: "known.copilot.output.v1",
|
|
136
|
+
requiredCapabilities: ["text-generation"],
|
|
137
|
+
allowedToolKeys: [],
|
|
138
|
+
dataClassification: "internal",
|
|
139
|
+
execution: "streaming",
|
|
140
|
+
publicNetworkAccess: "forbidden",
|
|
141
|
+
limits: {
|
|
142
|
+
maxDurationSeconds: 60,
|
|
143
|
+
maxInputBytes: 4096,
|
|
144
|
+
maxOutputBytes: 4096,
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
auditActions: ["known.copilot.execute", "known.copilot.history.read"],
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function permission(key: string) {
|
|
153
|
+
return {
|
|
154
|
+
key,
|
|
155
|
+
label: key,
|
|
156
|
+
module: "known",
|
|
157
|
+
moduleLabel: "Known",
|
|
158
|
+
resource: "copilot",
|
|
159
|
+
resourceLabel: "Copilot",
|
|
160
|
+
action: "execute" as const,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { ModuleManifest } from "@southwind-ai/modules";
|
|
2
|
+
import {
|
|
3
|
+
MODULE_DEBUG_SESSION_SCHEMA_VERSION,
|
|
4
|
+
requireModuleNamespace,
|
|
5
|
+
type ModuleDebugSessionProviderV1,
|
|
6
|
+
} from "@southwind-ai/server-sdk";
|
|
7
|
+
|
|
8
|
+
export function validateDebugSessionProviderRegistration(
|
|
9
|
+
manifest: ModuleManifest,
|
|
10
|
+
provider: ModuleDebugSessionProviderV1,
|
|
11
|
+
): void {
|
|
12
|
+
const moduleName = manifest.name;
|
|
13
|
+
requireModuleNamespace("调试会话 Provider", provider.key, moduleName);
|
|
14
|
+
requireModuleNamespace(
|
|
15
|
+
"调试会话 Provider Feature",
|
|
16
|
+
provider.featureKey,
|
|
17
|
+
moduleName,
|
|
18
|
+
);
|
|
19
|
+
requireModuleNamespace(
|
|
20
|
+
"调试会话 Provider 权限",
|
|
21
|
+
provider.permissionKey,
|
|
22
|
+
moduleName,
|
|
23
|
+
);
|
|
24
|
+
requireModuleNamespace(
|
|
25
|
+
"调试会话 Provider 审计动作",
|
|
26
|
+
provider.auditAction,
|
|
27
|
+
moduleName,
|
|
28
|
+
);
|
|
29
|
+
if (provider.schemaVersion !== MODULE_DEBUG_SESSION_SCHEMA_VERSION) {
|
|
30
|
+
throw new Error(`调试会话 Provider 协议版本不受支持:${provider.key}`);
|
|
31
|
+
}
|
|
32
|
+
if (!manifest.features.some(({ key }) => key === provider.featureKey)) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`调试会话 Provider Feature 未在 Manifest 声明:${provider.featureKey}`,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
if (!manifest.permissions.some(({ key }) => key === provider.permissionKey)) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`调试会话 Provider 权限未在 Manifest 声明:${provider.permissionKey}`,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
if (!manifestAuditActions(manifest).has(provider.auditAction)) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`调试会话 Provider 审计动作未在 Manifest 声明:${provider.auditAction}`,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
if (provider.operationKeys.length === 0) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`调试会话 Provider 必须绑定至少一个 Operation:${provider.key}`,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
const operationKeys = new Set<string>();
|
|
53
|
+
for (const operationKey of provider.operationKeys) {
|
|
54
|
+
requireModuleNamespace("调试会话 Operation", operationKey, moduleName);
|
|
55
|
+
if (operationKeys.has(operationKey)) {
|
|
56
|
+
throw new Error(`调试会话 Provider Operation 重复:${operationKey}`);
|
|
57
|
+
}
|
|
58
|
+
operationKeys.add(operationKey);
|
|
59
|
+
const operation = manifest.aiOperations?.find(
|
|
60
|
+
({ key }) => key === operationKey,
|
|
61
|
+
);
|
|
62
|
+
if (!operation || operation.kind !== "agent") {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`调试会话 Provider Operation 未声明为 Agent:${operationKey}`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
if (
|
|
68
|
+
operation.featureKey !== provider.featureKey ||
|
|
69
|
+
operation.permissionKey !== provider.permissionKey
|
|
70
|
+
) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
`调试会话 Provider 必须复用 Operation 的 Feature 与权限:${operationKey}`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function manifestAuditActions(manifest: ModuleManifest): ReadonlySet<string> {
|
|
79
|
+
return new Set(
|
|
80
|
+
manifest.auditActions.map((action) =>
|
|
81
|
+
typeof action === "string" ? action : action.key,
|
|
82
|
+
),
|
|
83
|
+
);
|
|
84
|
+
}
|
|
@@ -3,6 +3,7 @@ import type { ModuleManifest } from "@southwind-ai/modules";
|
|
|
3
3
|
import type {
|
|
4
4
|
InstallableServerModule,
|
|
5
5
|
ModuleAiOperationHandlerRegistration,
|
|
6
|
+
ModuleDebugSessionProviderV1,
|
|
6
7
|
ModuleDurableHandlerDefinition,
|
|
7
8
|
ModuleInitializer,
|
|
8
9
|
ModuleGovernedResourceRegistration,
|
|
@@ -64,6 +65,11 @@ export interface RegisteredModuleSearchProvider {
|
|
|
64
65
|
readonly provider: ModuleSearchProvider;
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
export interface RegisteredModuleDebugSessionProvider {
|
|
69
|
+
readonly moduleName: string;
|
|
70
|
+
readonly provider: ModuleDebugSessionProviderV1;
|
|
71
|
+
}
|
|
72
|
+
|
|
67
73
|
export interface InitializeModuleHostsInput {
|
|
68
74
|
modules: readonly ModuleManifest[];
|
|
69
75
|
/** Manifest 与 setup 由同一业务模块安装单元贡献。 */
|
|
@@ -86,6 +92,7 @@ export interface InitializedModuleHosts {
|
|
|
86
92
|
readonly governedResources: readonly RegisteredModuleGovernedResource[];
|
|
87
93
|
readonly rolePresets: readonly RegisteredModuleRolePreset[];
|
|
88
94
|
readonly searchProviders: readonly RegisteredModuleSearchProvider[];
|
|
95
|
+
readonly debugSessionProviders: readonly RegisteredModuleDebugSessionProvider[];
|
|
89
96
|
manifestOf(moduleName: string): ModuleManifest;
|
|
90
97
|
/** 该模块经 Host 注册的上传用途集合(不含平台基础用途)。 */
|
|
91
98
|
uploadPurposesOf(moduleName: string): ReadonlySet<string>;
|
|
@@ -1,38 +1,20 @@
|
|
|
1
|
-
import { DurableHandlerRegistry } from "@southwind-ai/jobs";
|
|
2
|
-
import type { ModuleManifest } from "@southwind-ai/modules";
|
|
3
1
|
import {
|
|
4
|
-
requireModuleNamespace,
|
|
5
|
-
type ModuleHost,
|
|
6
2
|
type ModuleResourceFileAccessPolicy,
|
|
7
3
|
type ModuleUploadPolicy,
|
|
8
4
|
} from "@southwind-ai/server-sdk";
|
|
9
5
|
import type {
|
|
10
6
|
InitializedModuleHosts,
|
|
11
7
|
InitializeModuleHostsInput,
|
|
12
|
-
RegisteredModuleAiOperationHandler,
|
|
13
|
-
RegisteredModuleDurableHandler,
|
|
14
|
-
RegisteredModuleGovernedResource,
|
|
15
|
-
RegisteredModuleRolePreset,
|
|
16
|
-
RegisteredModuleRoute,
|
|
17
|
-
RegisteredModuleSearchProvider,
|
|
18
|
-
RegisteredModuleSandboxToolHandler,
|
|
19
|
-
RegisteredModuleTaskHandler,
|
|
20
|
-
RegisteredModuleToolHandler,
|
|
21
8
|
} from "./initialize-contracts.js";
|
|
22
|
-
import {
|
|
23
|
-
import { registerGovernedResources } from "./governed-resources.js";
|
|
24
|
-
import {
|
|
25
|
-
registerResourceFileAccessPolicies,
|
|
26
|
-
validateResourceFileAccessPurposes,
|
|
27
|
-
} from "./resource-file-policies.js";
|
|
28
|
-
import { validateRouteDeclaration } from "./route-declaration.js";
|
|
9
|
+
import { validateResourceFileAccessPurposes } from "./resource-file-policies.js";
|
|
29
10
|
import { createScopedSandboxHost } from "./sandbox-tool-registration.js";
|
|
30
|
-
import {
|
|
11
|
+
import { createScopedHost, type HostCollections } from "./scoped-host.js";
|
|
31
12
|
|
|
32
13
|
export type {
|
|
33
14
|
InitializedModuleHosts,
|
|
34
15
|
InitializeModuleHostsInput,
|
|
35
16
|
RegisteredModuleAiOperationHandler,
|
|
17
|
+
RegisteredModuleDebugSessionProvider,
|
|
36
18
|
RegisteredModuleDurableHandler,
|
|
37
19
|
RegisteredModuleGovernedResource,
|
|
38
20
|
RegisteredModuleRolePreset,
|
|
@@ -43,8 +25,6 @@ export type {
|
|
|
43
25
|
RegisteredModuleToolHandler,
|
|
44
26
|
} from "./initialize-contracts.js";
|
|
45
27
|
|
|
46
|
-
type HandlerInstaller = (registry: DurableHandlerRegistry) => void;
|
|
47
|
-
|
|
48
28
|
export async function initializeModuleHosts(
|
|
49
29
|
input: InitializeModuleHostsInput,
|
|
50
30
|
): Promise<InitializedModuleHosts> {
|
|
@@ -92,6 +72,8 @@ export async function initializeModuleHosts(
|
|
|
92
72
|
rolePresetKeys: new Set<string>(),
|
|
93
73
|
searchProviders: [],
|
|
94
74
|
searchProviderKeys: new Set<string>(),
|
|
75
|
+
debugSessionProviders: [],
|
|
76
|
+
debugSessionProviderKeys: new Set<string>(),
|
|
95
77
|
};
|
|
96
78
|
|
|
97
79
|
const installations = [
|
|
@@ -149,6 +131,7 @@ export async function initializeModuleHosts(
|
|
|
149
131
|
governedResources: [...state.governedResources],
|
|
150
132
|
rolePresets: [...state.rolePresets],
|
|
151
133
|
searchProviders: [...state.searchProviders],
|
|
134
|
+
debugSessionProviders: [...state.debugSessionProviders],
|
|
152
135
|
manifestOf(moduleName) {
|
|
153
136
|
const manifest = manifests.get(moduleName);
|
|
154
137
|
if (!manifest) throw new Error(`模块未安装:${moduleName}`);
|
|
@@ -172,177 +155,3 @@ export async function initializeModuleHosts(
|
|
|
172
155
|
},
|
|
173
156
|
};
|
|
174
157
|
}
|
|
175
|
-
|
|
176
|
-
interface HostCollections {
|
|
177
|
-
uploadPolicies: ModuleUploadPolicy[];
|
|
178
|
-
uploadPurposes: Set<string>;
|
|
179
|
-
modulePurposes: Map<string, Set<string>>;
|
|
180
|
-
moduleDependencies: ReadonlyMap<string, readonly string[]>;
|
|
181
|
-
resourceFileAccessPolicies: ModuleResourceFileAccessPolicy[];
|
|
182
|
-
moduleResourceFileAccessPolicies: Map<
|
|
183
|
-
string,
|
|
184
|
-
ModuleResourceFileAccessPolicy[]
|
|
185
|
-
>;
|
|
186
|
-
resourceFileAccessPolicyKeys: Set<string>;
|
|
187
|
-
handlerKeys: Set<string>;
|
|
188
|
-
handlerInstallers: HandlerInstaller[];
|
|
189
|
-
durableHandlers: RegisteredModuleDurableHandler[];
|
|
190
|
-
routes: RegisteredModuleRoute[];
|
|
191
|
-
routeKeys: Set<string>;
|
|
192
|
-
taskHandlers: RegisteredModuleTaskHandler[];
|
|
193
|
-
taskKeys: Set<string>;
|
|
194
|
-
toolHandlers: RegisteredModuleToolHandler[];
|
|
195
|
-
sandboxToolHandlers: RegisteredModuleSandboxToolHandler[];
|
|
196
|
-
toolKeys: Set<string>;
|
|
197
|
-
aiOperationHandlers: RegisteredModuleAiOperationHandler[];
|
|
198
|
-
aiOperationKeys: Set<string>;
|
|
199
|
-
governedResources: RegisteredModuleGovernedResource[];
|
|
200
|
-
governedResourceKeys: Set<string>;
|
|
201
|
-
rolePresets: RegisteredModuleRolePreset[];
|
|
202
|
-
rolePresetKeys: Set<string>;
|
|
203
|
-
searchProviders: RegisteredModuleSearchProvider[];
|
|
204
|
-
searchProviderKeys: Set<string>;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
function createScopedHost(
|
|
208
|
-
manifest: ModuleManifest,
|
|
209
|
-
state: HostCollections,
|
|
210
|
-
): ModuleHost {
|
|
211
|
-
const moduleName = manifest.name;
|
|
212
|
-
return {
|
|
213
|
-
moduleName,
|
|
214
|
-
registerUploadPolicy(policy) {
|
|
215
|
-
requireModuleNamespace("上传用途", policy.purpose, moduleName);
|
|
216
|
-
if (state.uploadPurposes.has(policy.purpose)) {
|
|
217
|
-
throw new Error(`上传用途重复注册:${policy.purpose}`);
|
|
218
|
-
}
|
|
219
|
-
state.uploadPurposes.add(policy.purpose);
|
|
220
|
-
state.uploadPolicies.push(policy);
|
|
221
|
-
const purposes =
|
|
222
|
-
state.modulePurposes.get(moduleName) ?? new Set<string>();
|
|
223
|
-
purposes.add(policy.purpose);
|
|
224
|
-
state.modulePurposes.set(moduleName, purposes);
|
|
225
|
-
},
|
|
226
|
-
registerResourceFileAccessPolicies(policies) {
|
|
227
|
-
registerResourceFileAccessPolicies(manifest, state, policies);
|
|
228
|
-
},
|
|
229
|
-
registerDurableHandler(definition) {
|
|
230
|
-
requireModuleNamespace("任务 handler", definition.key, moduleName);
|
|
231
|
-
registerModuleDurableHandler(manifest, state, definition);
|
|
232
|
-
},
|
|
233
|
-
registerGovernedResources(registrations) {
|
|
234
|
-
registerGovernedResources(manifest, state, registrations);
|
|
235
|
-
},
|
|
236
|
-
registerRoutes(routes) {
|
|
237
|
-
for (const definition of routes) {
|
|
238
|
-
requireModuleNamespace(
|
|
239
|
-
"路由权限",
|
|
240
|
-
definition.permissionKey,
|
|
241
|
-
moduleName,
|
|
242
|
-
);
|
|
243
|
-
validateRouteDeclaration(manifest, definition);
|
|
244
|
-
const key = `${definition.method} ${definition.path}`;
|
|
245
|
-
if (state.routeKeys.has(key)) {
|
|
246
|
-
throw new Error(`模块路由重复注册:${key}`);
|
|
247
|
-
}
|
|
248
|
-
state.routeKeys.add(key);
|
|
249
|
-
state.routes.push({ moduleName, definition });
|
|
250
|
-
}
|
|
251
|
-
},
|
|
252
|
-
registerTaskHandlers(registrations) {
|
|
253
|
-
for (const registration of registrations) {
|
|
254
|
-
requireModuleNamespace("定时任务", registration.taskKey, moduleName);
|
|
255
|
-
if (!manifest.tasks.some(({ key }) => key === registration.taskKey)) {
|
|
256
|
-
throw new Error(
|
|
257
|
-
`定时任务 Handler 未在 Manifest 声明:${registration.taskKey}`,
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
if (state.taskKeys.has(registration.taskKey)) {
|
|
261
|
-
throw new Error(`定时任务 Handler 重复注册:${registration.taskKey}`);
|
|
262
|
-
}
|
|
263
|
-
state.taskKeys.add(registration.taskKey);
|
|
264
|
-
state.taskHandlers.push({ moduleName, registration });
|
|
265
|
-
}
|
|
266
|
-
},
|
|
267
|
-
registerToolHandlers(registrations) {
|
|
268
|
-
for (const registration of registrations) {
|
|
269
|
-
requireModuleNamespace("Agent Tool", registration.toolKey, moduleName);
|
|
270
|
-
if (!manifest.tools.some(({ key }) => key === registration.toolKey)) {
|
|
271
|
-
throw new Error(
|
|
272
|
-
`Agent Tool Handler 未在 Manifest 声明:${registration.toolKey}`,
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
if (state.toolKeys.has(registration.toolKey)) {
|
|
276
|
-
throw new Error(
|
|
277
|
-
`Agent Tool Handler 重复注册:${registration.toolKey}`,
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
state.toolKeys.add(registration.toolKey);
|
|
281
|
-
state.toolHandlers.push({ moduleName, registration });
|
|
282
|
-
}
|
|
283
|
-
},
|
|
284
|
-
registerAiOperationHandlers(registrations) {
|
|
285
|
-
for (const registration of registrations) {
|
|
286
|
-
requireModuleNamespace(
|
|
287
|
-
"AI Operation",
|
|
288
|
-
registration.operationKey,
|
|
289
|
-
moduleName,
|
|
290
|
-
);
|
|
291
|
-
const operation = manifest.aiOperations?.find(
|
|
292
|
-
({ key }) => key === registration.operationKey,
|
|
293
|
-
);
|
|
294
|
-
if (!operation) {
|
|
295
|
-
throw new Error(
|
|
296
|
-
`AI Operation Handler 未在 Manifest 声明:${registration.operationKey}`,
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
if (
|
|
300
|
-
(operation.kind === "agent" && !registration.prepare) ||
|
|
301
|
-
(operation.kind === "document" && !registration.prepareDocument) ||
|
|
302
|
-
(operation.kind === "speech" && !registration.prepareSpeech) ||
|
|
303
|
-
(operation.kind === "embedding" && !registration.prepareEmbedding)
|
|
304
|
-
) {
|
|
305
|
-
throw new Error(
|
|
306
|
-
`AI Operation Handler 与 kind 不匹配:${registration.operationKey}`,
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
if (state.aiOperationKeys.has(registration.operationKey)) {
|
|
310
|
-
throw new Error(
|
|
311
|
-
`AI Operation Handler 重复注册:${registration.operationKey}`,
|
|
312
|
-
);
|
|
313
|
-
}
|
|
314
|
-
state.aiOperationKeys.add(registration.operationKey);
|
|
315
|
-
state.aiOperationHandlers.push({ moduleName, registration });
|
|
316
|
-
}
|
|
317
|
-
},
|
|
318
|
-
registerRolePresets(presets) {
|
|
319
|
-
for (const preset of presets) {
|
|
320
|
-
requireModuleNamespace("角色预设", preset.key, moduleName);
|
|
321
|
-
if (state.rolePresetKeys.has(preset.key)) {
|
|
322
|
-
throw new Error(`角色预设重复注册:${preset.key}`);
|
|
323
|
-
}
|
|
324
|
-
for (const permissionKey of preset.permissionKeys) {
|
|
325
|
-
requireModuleNamespace("角色预设权限", permissionKey, moduleName);
|
|
326
|
-
if (!manifest.permissions.some(({ key }) => key === permissionKey)) {
|
|
327
|
-
throw new Error(
|
|
328
|
-
`角色预设引用了模块 ${moduleName} 未声明权限:${permissionKey}`,
|
|
329
|
-
);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
state.rolePresetKeys.add(preset.key);
|
|
333
|
-
state.rolePresets.push({ moduleName, preset });
|
|
334
|
-
}
|
|
335
|
-
},
|
|
336
|
-
registerSearchProviders(providers) {
|
|
337
|
-
for (const provider of providers) {
|
|
338
|
-
requireModuleNamespace("搜索 Provider", provider.key, moduleName);
|
|
339
|
-
validateSearchProviderRegistration(manifest, provider);
|
|
340
|
-
if (state.searchProviderKeys.has(provider.key)) {
|
|
341
|
-
throw new Error(`搜索 Provider 重复注册:${provider.key}`);
|
|
342
|
-
}
|
|
343
|
-
state.searchProviderKeys.add(provider.key);
|
|
344
|
-
state.searchProviders.push({ moduleName, provider });
|
|
345
|
-
}
|
|
346
|
-
},
|
|
347
|
-
};
|
|
348
|
-
}
|