create-windy 0.3.17 → 0.3.19
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/README.md +14 -36
- package/dist/cli.js +1534 -3079
- package/package.json +1 -1
- package/template/.windy-template.json +2 -2
- package/template/AGENTS.md +6 -0
- package/template/README.md +5 -10
- package/template/apps/agent-server/Dockerfile +2 -1
- package/template/apps/agent-server/README.md +13 -5
- package/template/apps/agent-server/adk_web_agents/canonical_debug/__init__.py +1 -0
- package/template/apps/agent-server/adk_web_agents/canonical_debug/agent.py +125 -0
- package/template/apps/agent-server/project/adk-web-debug.json +4 -0
- package/template/apps/agent-server/src/southwind_agent_server/adk_debug.py +348 -0
- package/template/apps/agent-server/tests/test_adk_debug_result_polling.py +139 -0
- package/template/apps/agent-server/tests/test_adk_web_canonical_debug.py +235 -0
- package/template/apps/server/src/agent/run-debug-authorization.ts +27 -0
- package/template/apps/server/src/agent/run-facade-adk-debug.test.ts +56 -0
- package/template/apps/server/src/agent/run-facade.test-support.ts +235 -0
- package/template/apps/server/src/agent/run-facade.test.ts +8 -244
- package/template/apps/server/src/agent/run-facade.ts +23 -48
- package/template/apps/server/src/agent/run-operation-registry.ts +51 -0
- package/template/apps/server/src/agent/runtime-bootstrap.ts +5 -2
- package/template/apps/server/src/agent/tool-audit.ts +3 -0
- package/template/apps/server/src/agent/tool-registry.test.ts +29 -0
- package/template/apps/server/src/application-services.ts +83 -15
- package/template/apps/server/src/guards.ts +16 -0
- package/template/apps/server/src/index.ts +23 -20
- package/template/apps/server/src/jobs/legacy-adapter.integration.test.ts +0 -1
- package/template/apps/server/src/jobs/legacy-adapter.test.ts +6 -0
- package/template/apps/server/src/jobs/legacy-service-runtime.ts +18 -0
- package/template/apps/server/src/module-host/durable-context-unavailable.test.ts +84 -0
- package/template/apps/server/src/module-host/durable-context.test.ts +14 -0
- package/template/apps/server/src/module-host/durable-context.ts +17 -15
- package/template/apps/server/src/module-host/request-context.test.ts +30 -1
- package/template/apps/server/src/module-host/request-context.ts +15 -2
- package/template/apps/server/src/module-host/storage-port.ts +1 -1
- package/template/apps/server/src/platform-bootstrap.ts +16 -0
- package/template/apps/server/src/runtime-adk-debug.test.ts +246 -0
- package/template/apps/server/src/runtime-adk-debug.ts +158 -0
- package/template/apps/server/src/runtime-request-actor.test.ts +55 -0
- package/template/apps/server/src/runtime-request-actor.ts +22 -0
- package/template/apps/server/src/runtime.test.ts +3 -0
- package/template/apps/server/src/runtime.ts +36 -3
- package/template/apps/server/src/scheduler/definitions.test.ts +16 -0
- package/template/docker-compose.agent-debug.yml +56 -0
- package/template/docker-compose.yml +1 -39
- package/template/docs/architecture/ai-runtime.md +14 -5
- package/template/docs/architecture/module-manifest-composition.md +14 -0
- package/template/docs/development/custom-login-page.md +2 -16
- package/template/docs/platform/agent-runtime.md +59 -16
- package/template/docs/platform/agent-tool-sandbox.md +4 -3
- package/template/docs/platform/platform-authentication.md +9 -1
- package/template/package.json +1 -1
- package/template/packages/database/drizzle/0043_activate_agent_dependencies.sql +196 -0
- package/template/packages/database/drizzle/meta/0043_snapshot.json +8335 -0
- package/template/packages/database/drizzle/meta/_journal.json +7 -0
- package/template/packages/database/src/schema-notification-delivery.test.ts +15 -6
- package/template/packages/database/src/schema-workflow-notification.test.ts +44 -29
- package/template/scripts/dev-adk-web.ts +107 -0
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import { initializeModuleHosts } from "../module-host.js";
|
|
7
|
-
import { createServerRuntime } from "../runtime.js";
|
|
8
|
-
import { createTestRunAuthorizationStore } from "./run-authorization.test-support.js";
|
|
9
|
-
import { FakeAgentRuntime } from "./fake-runtime.test-support.js";
|
|
10
|
-
import { AgentRunFacade } from "./run-facade.js";
|
|
2
|
+
import {
|
|
3
|
+
createRunFacadeFixture,
|
|
4
|
+
guardContext,
|
|
5
|
+
} from "./run-facade.test-support.js";
|
|
11
6
|
|
|
12
7
|
describe("Guarded Agent Run facade", () => {
|
|
13
8
|
test("Guard 后由宿主 Handler 生成计划并签发短时 Grant", async () => {
|
|
14
|
-
const fixture = await
|
|
9
|
+
const fixture = await createRunFacadeFixture();
|
|
15
10
|
const created = await fixture.facade.start(
|
|
16
11
|
"business.answer",
|
|
17
12
|
{ question: "你好", internalCaseId: "case-secret" },
|
|
@@ -64,7 +59,7 @@ describe("Guarded Agent Run facade", () => {
|
|
|
64
59
|
});
|
|
65
60
|
|
|
66
61
|
test("客户端输入不能覆盖 Operation、Tool 或授权", async () => {
|
|
67
|
-
const fixture = await
|
|
62
|
+
const fixture = await createRunFacadeFixture();
|
|
68
63
|
await fixture.facade.start(
|
|
69
64
|
"business.answer",
|
|
70
65
|
{
|
|
@@ -84,7 +79,7 @@ describe("Guarded Agent Run facade", () => {
|
|
|
84
79
|
});
|
|
85
80
|
|
|
86
81
|
test("缺少 Operation 权限时不调用 Handler 或 Runtime", async () => {
|
|
87
|
-
const fixture = await
|
|
82
|
+
const fixture = await createRunFacadeFixture([]);
|
|
88
83
|
await expect(
|
|
89
84
|
fixture.facade.start(
|
|
90
85
|
"business.answer",
|
|
@@ -98,7 +93,7 @@ describe("Guarded Agent Run facade", () => {
|
|
|
98
93
|
});
|
|
99
94
|
|
|
100
95
|
test("恢复时权限已收回会返回不泄露 Run 存在性的 404", async () => {
|
|
101
|
-
const fixture = await
|
|
96
|
+
const fixture = await createRunFacadeFixture();
|
|
102
97
|
await fixture.facade.start(
|
|
103
98
|
"business.answer",
|
|
104
99
|
{ question: "稍后恢复" },
|
|
@@ -115,234 +110,3 @@ describe("Guarded Agent Run facade", () => {
|
|
|
115
110
|
});
|
|
116
111
|
});
|
|
117
112
|
});
|
|
118
|
-
|
|
119
|
-
async function createFixture(
|
|
120
|
-
permissionKeys: string[] = ["business.ai.execute", "business.records.read"],
|
|
121
|
-
) {
|
|
122
|
-
const module = manifest();
|
|
123
|
-
const prepared: unknown[] = [];
|
|
124
|
-
const hosts = await initializeModuleHosts({
|
|
125
|
-
modules: [module],
|
|
126
|
-
installations: [
|
|
127
|
-
{
|
|
128
|
-
manifest: module,
|
|
129
|
-
setup(host) {
|
|
130
|
-
host.registerAiOperationHandlers([
|
|
131
|
-
{
|
|
132
|
-
operationKey: "business.answer",
|
|
133
|
-
release: {
|
|
134
|
-
workflowVersion: "workflow-2",
|
|
135
|
-
promptVersion: "prompt-3",
|
|
136
|
-
routeVersion: "route-4",
|
|
137
|
-
},
|
|
138
|
-
prepare(input, context) {
|
|
139
|
-
prepared.push({ input, actorId: context.actor.id });
|
|
140
|
-
const question =
|
|
141
|
-
typeof input === "object" &&
|
|
142
|
-
input !== null &&
|
|
143
|
-
!Array.isArray(input) &&
|
|
144
|
-
typeof input.question === "string"
|
|
145
|
-
? input.question
|
|
146
|
-
: "";
|
|
147
|
-
return {
|
|
148
|
-
instructions: "只回答当前业务问题。",
|
|
149
|
-
userPrompt: question,
|
|
150
|
-
};
|
|
151
|
-
},
|
|
152
|
-
},
|
|
153
|
-
]);
|
|
154
|
-
},
|
|
155
|
-
},
|
|
156
|
-
],
|
|
157
|
-
});
|
|
158
|
-
const runtime = createServerRuntime(
|
|
159
|
-
{},
|
|
160
|
-
{
|
|
161
|
-
featureCatalog: module.features,
|
|
162
|
-
licenseResolver: { resolve: () => ({ status: "valid" }) },
|
|
163
|
-
},
|
|
164
|
-
);
|
|
165
|
-
const context = guardContext(permissionKeys);
|
|
166
|
-
const remote = new FakeAgentRuntime();
|
|
167
|
-
const facade = new AgentRunFacade(
|
|
168
|
-
[module],
|
|
169
|
-
hosts,
|
|
170
|
-
runtime,
|
|
171
|
-
remote,
|
|
172
|
-
createTestRunAuthorizationStore(),
|
|
173
|
-
);
|
|
174
|
-
return { context, facade, prepared, remote, runtime };
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function guardContext(permissionKeys: string[]) {
|
|
178
|
-
return createRequestGuardContext(
|
|
179
|
-
new Request("http://localhost/api/agent/runs"),
|
|
180
|
-
{
|
|
181
|
-
resolveActor: () => ({
|
|
182
|
-
id: "actor-1",
|
|
183
|
-
type: "user",
|
|
184
|
-
name: "测试用户",
|
|
185
|
-
roleCodes: ["user"],
|
|
186
|
-
permissionKeys,
|
|
187
|
-
scopeContext: buildScopeContext("actor-1", undefined, [
|
|
188
|
-
{
|
|
189
|
-
id: "role-user",
|
|
190
|
-
code: "user",
|
|
191
|
-
dataScope: "self",
|
|
192
|
-
departmentIds: [],
|
|
193
|
-
},
|
|
194
|
-
]),
|
|
195
|
-
}),
|
|
196
|
-
resolveLicense: () => ({ status: "valid" }),
|
|
197
|
-
},
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function manifest(): ModuleManifest {
|
|
202
|
-
return {
|
|
203
|
-
name: "business",
|
|
204
|
-
version: "0.1.0",
|
|
205
|
-
label: "业务",
|
|
206
|
-
description: "业务",
|
|
207
|
-
menus: [],
|
|
208
|
-
adminRoutes: [],
|
|
209
|
-
permissions: [
|
|
210
|
-
permission("business.ai.execute", "ai", "manage"),
|
|
211
|
-
permission("business.records.read", "records", "read"),
|
|
212
|
-
],
|
|
213
|
-
apiPermissions: [],
|
|
214
|
-
features: [
|
|
215
|
-
enabledFeature("business.ai"),
|
|
216
|
-
enabledFeature("business.records"),
|
|
217
|
-
],
|
|
218
|
-
migrations: [],
|
|
219
|
-
schemaExports: [],
|
|
220
|
-
jsonSchemas: [
|
|
221
|
-
{
|
|
222
|
-
key: "business.records.input.v1",
|
|
223
|
-
schema: { type: "object" },
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
key: "business.records.output.v1",
|
|
227
|
-
schema: { type: "object" },
|
|
228
|
-
},
|
|
229
|
-
{
|
|
230
|
-
key: "business.input.v1",
|
|
231
|
-
schema: { type: "object" },
|
|
232
|
-
},
|
|
233
|
-
{
|
|
234
|
-
key: "business.output.v1",
|
|
235
|
-
schema: { type: "object" },
|
|
236
|
-
},
|
|
237
|
-
],
|
|
238
|
-
tasks: [],
|
|
239
|
-
tools: [
|
|
240
|
-
{
|
|
241
|
-
key: "business.records.list",
|
|
242
|
-
label: "查询记录",
|
|
243
|
-
description: "查询当前范围记录。",
|
|
244
|
-
inputSchemaKey: "business.records.input.v1",
|
|
245
|
-
outputSchemaKey: "business.records.output.v1",
|
|
246
|
-
operation: "query",
|
|
247
|
-
permissionKey: "business.records.read",
|
|
248
|
-
featureKey: "business.records",
|
|
249
|
-
auditAction: "business.records.read",
|
|
250
|
-
scopeMode: "request",
|
|
251
|
-
dataClassification: "internal",
|
|
252
|
-
idempotency: "none",
|
|
253
|
-
approval: "none",
|
|
254
|
-
limits: {
|
|
255
|
-
maxDurationSeconds: 10,
|
|
256
|
-
maxInputBytes: 4096,
|
|
257
|
-
maxOutputBytes: 4096,
|
|
258
|
-
},
|
|
259
|
-
},
|
|
260
|
-
],
|
|
261
|
-
aiOperations: [
|
|
262
|
-
{
|
|
263
|
-
key: "business.answer",
|
|
264
|
-
kind: "agent",
|
|
265
|
-
featureKey: "business.ai",
|
|
266
|
-
permissionKey: "business.ai.execute",
|
|
267
|
-
auditAction: "business.ai.execute",
|
|
268
|
-
inputSchemaKey: "business.input.v1",
|
|
269
|
-
outputSchemaKey: "business.output.v1",
|
|
270
|
-
requiredCapabilities: ["text-generation"],
|
|
271
|
-
allowedToolKeys: ["business.records.list"],
|
|
272
|
-
dataClassification: "internal",
|
|
273
|
-
execution: "streaming",
|
|
274
|
-
providerEgress: {
|
|
275
|
-
mode: "configured-endpoint",
|
|
276
|
-
deploymentKey: "ai.openai-compatible",
|
|
277
|
-
},
|
|
278
|
-
providerDataPolicy: {
|
|
279
|
-
inputFields: [
|
|
280
|
-
{
|
|
281
|
-
jsonPointer: "/question",
|
|
282
|
-
classification: "internal",
|
|
283
|
-
dlpAction: "redact",
|
|
284
|
-
},
|
|
285
|
-
],
|
|
286
|
-
envelopeFields: [
|
|
287
|
-
{
|
|
288
|
-
field: "instructions",
|
|
289
|
-
classification: "internal",
|
|
290
|
-
dlpAction: "reject",
|
|
291
|
-
},
|
|
292
|
-
{
|
|
293
|
-
field: "user-prompt",
|
|
294
|
-
classification: "internal",
|
|
295
|
-
dlpAction: "redact",
|
|
296
|
-
},
|
|
297
|
-
{
|
|
298
|
-
field: "tool-definitions",
|
|
299
|
-
classification: "internal",
|
|
300
|
-
dlpAction: "reject",
|
|
301
|
-
},
|
|
302
|
-
{
|
|
303
|
-
field: "provider-output",
|
|
304
|
-
classification: "internal",
|
|
305
|
-
dlpAction: "redact",
|
|
306
|
-
},
|
|
307
|
-
{
|
|
308
|
-
field: "tool-results",
|
|
309
|
-
classification: "sensitive",
|
|
310
|
-
dlpAction: "redact",
|
|
311
|
-
},
|
|
312
|
-
],
|
|
313
|
-
},
|
|
314
|
-
toolNetworkPolicy: { mode: "forbidden" },
|
|
315
|
-
limits: {
|
|
316
|
-
maxDurationSeconds: 60,
|
|
317
|
-
maxInputBytes: 4096,
|
|
318
|
-
maxOutputBytes: 4096,
|
|
319
|
-
},
|
|
320
|
-
},
|
|
321
|
-
],
|
|
322
|
-
providers: [],
|
|
323
|
-
auditActions: ["business.ai.execute", "business.records.read"],
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
function permission(key: string, resource: string, action: "read" | "manage") {
|
|
328
|
-
return {
|
|
329
|
-
key,
|
|
330
|
-
label: key,
|
|
331
|
-
module: "business",
|
|
332
|
-
moduleLabel: "业务",
|
|
333
|
-
resource,
|
|
334
|
-
resourceLabel: resource,
|
|
335
|
-
action,
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
function enabledFeature(key: string) {
|
|
340
|
-
return {
|
|
341
|
-
key: featureKey(key),
|
|
342
|
-
label: key,
|
|
343
|
-
module: "business",
|
|
344
|
-
visible: "visible" as const,
|
|
345
|
-
enabled: true,
|
|
346
|
-
stage: "stable" as const,
|
|
347
|
-
};
|
|
348
|
-
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { randomUUIDv7 } from "bun";
|
|
2
|
-
import type {
|
|
3
|
-
ModuleAiOperationDefinition,
|
|
4
|
-
ModuleManifest,
|
|
5
|
-
} from "@southwind-ai/modules";
|
|
2
|
+
import type { ModuleManifest } from "@southwind-ai/modules";
|
|
6
3
|
import {
|
|
7
4
|
resolveProviderEgress,
|
|
8
5
|
resolveToolNetworkPolicy,
|
|
@@ -10,14 +7,12 @@ import {
|
|
|
10
7
|
import type {
|
|
11
8
|
ModuleAiArtifactRef,
|
|
12
9
|
ModuleAiJsonValue,
|
|
13
|
-
ModuleAiOperationHandlerRegistration,
|
|
14
10
|
} from "@southwind-ai/server-sdk";
|
|
15
11
|
import { evaluateAccessGuards } from "../access-guards.js";
|
|
16
12
|
import { auditActor, type RequestGuardContext } from "../guards.js";
|
|
17
13
|
import type { InitializedModuleHosts } from "../module-host.js";
|
|
18
14
|
import { toModuleActor } from "../module-host.js";
|
|
19
15
|
import type { createServerRuntime } from "../runtime.js";
|
|
20
|
-
import { findFeature } from "../route-guards.js";
|
|
21
16
|
import type { AiArtifactRuntime } from "../ai-artifact/runtime.js";
|
|
22
17
|
import {
|
|
23
18
|
AgentRunAuthorizationError,
|
|
@@ -26,12 +21,10 @@ import {
|
|
|
26
21
|
} from "./run-authorization.js";
|
|
27
22
|
import type {
|
|
28
23
|
AgentRuntimePort,
|
|
29
|
-
AgentRuntimeOperation,
|
|
30
24
|
AgentRuntimeStartResponse,
|
|
31
25
|
AgentRuntimeStatusResponse,
|
|
32
26
|
} from "./run-contracts.js";
|
|
33
27
|
import { AgentRuntimeError } from "./run-contracts.js";
|
|
34
|
-
import { buildAgentRuntimeRelease } from "./run-release.js";
|
|
35
28
|
import {
|
|
36
29
|
governProviderInput,
|
|
37
30
|
ProviderInputGovernanceError,
|
|
@@ -42,10 +35,17 @@ import {
|
|
|
42
35
|
digest,
|
|
43
36
|
publicRunPath,
|
|
44
37
|
runtimeErrorStatus,
|
|
45
|
-
toolDefinitions,
|
|
46
38
|
} from "./run-definition-support.js";
|
|
47
39
|
import { AgentRunFacadeError, inaccessibleRun } from "./run-errors.js";
|
|
48
40
|
import { scopedRuntimeIdempotencyKey } from "./idempotency-key.js";
|
|
41
|
+
import {
|
|
42
|
+
assertSelectedDebugOperation,
|
|
43
|
+
debugActorAuditMetadata,
|
|
44
|
+
} from "./run-debug-authorization.js";
|
|
45
|
+
import {
|
|
46
|
+
buildRegisteredOperations,
|
|
47
|
+
type RegisteredOperation,
|
|
48
|
+
} from "./run-operation-registry.js";
|
|
49
49
|
export { AgentRunFacadeError } from "./run-errors.js";
|
|
50
50
|
|
|
51
51
|
type ServerRuntime = ReturnType<typeof createServerRuntime>;
|
|
@@ -61,36 +61,7 @@ export class AgentRunFacade {
|
|
|
61
61
|
private readonly authorizations: AgentRunAuthorizationStore,
|
|
62
62
|
private readonly artifacts?: AiArtifactRuntime,
|
|
63
63
|
) {
|
|
64
|
-
|
|
65
|
-
hosts.aiOperationHandlers.map(({ registration, moduleName }) => [
|
|
66
|
-
registration.operationKey,
|
|
67
|
-
{ registration, moduleName },
|
|
68
|
-
]),
|
|
69
|
-
);
|
|
70
|
-
const features = modules.flatMap(({ features }) => features);
|
|
71
|
-
this.#operations = new Map(
|
|
72
|
-
modules.flatMap((module) =>
|
|
73
|
-
(module.aiOperations ?? []).map((definition) => {
|
|
74
|
-
const handler = handlers.get(definition.key);
|
|
75
|
-
const tools = toolDefinitions(modules, definition.allowedToolKeys);
|
|
76
|
-
return [
|
|
77
|
-
definition.key,
|
|
78
|
-
{
|
|
79
|
-
definition,
|
|
80
|
-
...handler,
|
|
81
|
-
feature: findFeature(features, definition.featureKey),
|
|
82
|
-
tools,
|
|
83
|
-
release: buildAgentRuntimeRelease(
|
|
84
|
-
modules,
|
|
85
|
-
module,
|
|
86
|
-
definition,
|
|
87
|
-
handler?.registration,
|
|
88
|
-
),
|
|
89
|
-
},
|
|
90
|
-
] as const;
|
|
91
|
-
}),
|
|
92
|
-
),
|
|
93
|
-
);
|
|
64
|
+
this.#operations = buildRegisteredOperations(modules, hosts);
|
|
94
65
|
}
|
|
95
66
|
|
|
96
67
|
async start(
|
|
@@ -101,6 +72,12 @@ export class AgentRunFacade {
|
|
|
101
72
|
artifacts: readonly ModuleAiArtifactRef[] = [],
|
|
102
73
|
signal: AbortSignal = AbortSignal.timeout(10_000),
|
|
103
74
|
): Promise<AgentRuntimeStartResponse> {
|
|
75
|
+
assertSelectedDebugOperation(
|
|
76
|
+
this.runtime,
|
|
77
|
+
context.actor.id,
|
|
78
|
+
operationKey,
|
|
79
|
+
"forbidden",
|
|
80
|
+
);
|
|
104
81
|
const operation = this.#operations.get(operationKey);
|
|
105
82
|
if (
|
|
106
83
|
!operation?.registration?.prepare ||
|
|
@@ -237,6 +214,7 @@ export class AgentRunFacade {
|
|
|
237
214
|
},
|
|
238
215
|
requestId: context.requestId,
|
|
239
216
|
metadata: {
|
|
217
|
+
...debugActorAuditMetadata(context.actor),
|
|
240
218
|
artifactCount: rotatedArtifacts.length,
|
|
241
219
|
artifactBytes: rotatedArtifacts.reduce(
|
|
242
220
|
(total, artifact) => total + artifact.byteSize,
|
|
@@ -278,6 +256,12 @@ export class AgentRunFacade {
|
|
|
278
256
|
runId: string,
|
|
279
257
|
context: RequestGuardContext,
|
|
280
258
|
) {
|
|
259
|
+
assertSelectedDebugOperation(
|
|
260
|
+
this.runtime,
|
|
261
|
+
context.actor.id,
|
|
262
|
+
operationKey,
|
|
263
|
+
"hidden",
|
|
264
|
+
);
|
|
281
265
|
const operation = this.#operations.get(operationKey);
|
|
282
266
|
if (!operation || operation.definition.kind !== "agent") {
|
|
283
267
|
throw inaccessibleRun();
|
|
@@ -334,12 +318,3 @@ export class AgentRunFacade {
|
|
|
334
318
|
};
|
|
335
319
|
}
|
|
336
320
|
}
|
|
337
|
-
|
|
338
|
-
interface RegisteredOperation {
|
|
339
|
-
definition: ModuleAiOperationDefinition;
|
|
340
|
-
registration?: ModuleAiOperationHandlerRegistration;
|
|
341
|
-
moduleName?: string;
|
|
342
|
-
feature: ReturnType<typeof findFeature>;
|
|
343
|
-
tools: AgentRuntimeOperation["allowedTools"];
|
|
344
|
-
release: AgentRuntimeOperation["release"];
|
|
345
|
-
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ModuleManifest } from "@southwind-ai/modules";
|
|
2
|
+
import type { AgentRuntimeOperation } from "./run-contracts.js";
|
|
3
|
+
import type { ModuleAiOperationHandlerRegistration } from "@southwind-ai/server-sdk";
|
|
4
|
+
import type { InitializedModuleHosts } from "../module-host.js";
|
|
5
|
+
import { findFeature } from "../route-guards.js";
|
|
6
|
+
import { toolDefinitions } from "./run-definition-support.js";
|
|
7
|
+
import { buildAgentRuntimeRelease } from "./run-release.js";
|
|
8
|
+
|
|
9
|
+
export interface RegisteredOperation {
|
|
10
|
+
definition: NonNullable<ModuleManifest["aiOperations"]>[number];
|
|
11
|
+
registration?: ModuleAiOperationHandlerRegistration;
|
|
12
|
+
moduleName?: string;
|
|
13
|
+
feature: ReturnType<typeof findFeature>;
|
|
14
|
+
tools: AgentRuntimeOperation["allowedTools"];
|
|
15
|
+
release: AgentRuntimeOperation["release"];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function buildRegisteredOperations(
|
|
19
|
+
modules: readonly ModuleManifest[],
|
|
20
|
+
hosts: InitializedModuleHosts,
|
|
21
|
+
): Map<string, RegisteredOperation> {
|
|
22
|
+
const handlers = new Map(
|
|
23
|
+
hosts.aiOperationHandlers.map(({ registration, moduleName }) => [
|
|
24
|
+
registration.operationKey,
|
|
25
|
+
{ registration, moduleName },
|
|
26
|
+
]),
|
|
27
|
+
);
|
|
28
|
+
const features = modules.flatMap(({ features }) => features);
|
|
29
|
+
return new Map(
|
|
30
|
+
modules.flatMap((module) =>
|
|
31
|
+
(module.aiOperations ?? []).map((definition) => {
|
|
32
|
+
const handler = handlers.get(definition.key);
|
|
33
|
+
return [
|
|
34
|
+
definition.key,
|
|
35
|
+
{
|
|
36
|
+
definition,
|
|
37
|
+
...handler,
|
|
38
|
+
feature: findFeature(features, definition.featureKey),
|
|
39
|
+
tools: toolDefinitions(modules, definition.allowedToolKeys),
|
|
40
|
+
release: buildAgentRuntimeRelease(
|
|
41
|
+
modules,
|
|
42
|
+
module,
|
|
43
|
+
definition,
|
|
44
|
+
handler?.registration,
|
|
45
|
+
),
|
|
46
|
+
},
|
|
47
|
+
] as const;
|
|
48
|
+
}),
|
|
49
|
+
),
|
|
50
|
+
);
|
|
51
|
+
}
|
|
@@ -60,6 +60,9 @@ export function registerAgentPlatformRuntime(input: {
|
|
|
60
60
|
backgroundAi: DeferredAgentBackgroundOperationPortFactory;
|
|
61
61
|
}) {
|
|
62
62
|
const features = input.modules.flatMap(({ features }) => features);
|
|
63
|
+
const resolveAgentActor = async (actorId: string) =>
|
|
64
|
+
(await input.runtime.resolveEphemeralActor(actorId)) ??
|
|
65
|
+
(await resolveCurrentSystemActor(input.systemRepositories, actorId));
|
|
63
66
|
const toolHandlers = new Map([
|
|
64
67
|
...createSystemToolHandlers(input.systemRepositories, input.runtime),
|
|
65
68
|
...createModuleToolHandlerMap(input.hosts, {
|
|
@@ -95,7 +98,7 @@ export function registerAgentPlatformRuntime(input: {
|
|
|
95
98
|
input.runtime,
|
|
96
99
|
features,
|
|
97
100
|
input.storage,
|
|
98
|
-
|
|
101
|
+
resolveAgentActor,
|
|
99
102
|
);
|
|
100
103
|
registerAiArtifactRoutes(input.app, aiArtifacts, internalToken);
|
|
101
104
|
const remote = new RemoteAgentRuntime({
|
|
@@ -173,7 +176,7 @@ export function registerAgentPlatformRuntime(input: {
|
|
|
173
176
|
input.runtime,
|
|
174
177
|
runAuthorizations,
|
|
175
178
|
input.workflow,
|
|
176
|
-
|
|
179
|
+
resolveAgentActor,
|
|
177
180
|
),
|
|
178
181
|
internalToken,
|
|
179
182
|
);
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
} from "@southwind-ai/server-sdk";
|
|
5
5
|
import type { AuditEvent } from "@southwind-ai/shared";
|
|
6
6
|
import { auditActor, type RequestGuardContext } from "../guards.js";
|
|
7
|
+
import { debugActorAuditMetadata } from "./run-debug-authorization.js";
|
|
7
8
|
import type { RegisteredTool } from "./tool-registry.js";
|
|
8
9
|
|
|
9
10
|
export function toolJsonBytes(value: ModuleToolInput | ModuleToolResult) {
|
|
@@ -26,6 +27,7 @@ export function toolEvent(
|
|
|
26
27
|
module: tool.module,
|
|
27
28
|
requestId: context.requestId,
|
|
28
29
|
metadata: {
|
|
30
|
+
...debugActorAuditMetadata(context.actor),
|
|
29
31
|
inputBytes: toolJsonBytes(input),
|
|
30
32
|
outputBytes: toolJsonBytes(result),
|
|
31
33
|
executionMode,
|
|
@@ -53,6 +55,7 @@ export function sandboxFailureEvent(
|
|
|
53
55
|
module: tool.module,
|
|
54
56
|
requestId: context.requestId,
|
|
55
57
|
metadata: {
|
|
58
|
+
...debugActorAuditMetadata(context.actor),
|
|
56
59
|
inputBytes: toolJsonBytes(input),
|
|
57
60
|
executionMode: "sandbox",
|
|
58
61
|
failureKind: policyDenied ? "policy-denied" : "business-failure",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import type { ModuleToolHandlers } from "./tool-registry.js";
|
|
3
3
|
import { createFoundationSnapshot, loadSystemModules } from "../foundation.js";
|
|
4
|
+
import { bindModuleUserProjection } from "../guards.js";
|
|
4
5
|
import { createServerRuntime } from "../runtime.js";
|
|
5
6
|
import { createSystemRepositories } from "../system/seed.js";
|
|
6
7
|
import { createSystemToolHandlers } from "./routes.js";
|
|
@@ -92,6 +93,34 @@ describe("Governed Tool Registry", () => {
|
|
|
92
93
|
});
|
|
93
94
|
expect(JSON.stringify(event)).not.toContain("permissionKeys");
|
|
94
95
|
});
|
|
96
|
+
|
|
97
|
+
test("调试 Tool 审计保留服务主体并标注真实数据用户", async () => {
|
|
98
|
+
const fixture = await createFixture();
|
|
99
|
+
fixture.context.actor = bindModuleUserProjection(
|
|
100
|
+
{
|
|
101
|
+
...fixture.context.actor,
|
|
102
|
+
id: "service_adk_debug",
|
|
103
|
+
type: "service",
|
|
104
|
+
name: "ADK 开发调试",
|
|
105
|
+
},
|
|
106
|
+
"user_dev_admin",
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
await fixture.registry.execute(
|
|
110
|
+
"system.features.list",
|
|
111
|
+
{ page: 1 },
|
|
112
|
+
fixture.context,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const event = fixture.runtime.auditSink.list().at(-1);
|
|
116
|
+
expect(event?.actor).toMatchObject({
|
|
117
|
+
id: "service_adk_debug",
|
|
118
|
+
type: "service",
|
|
119
|
+
});
|
|
120
|
+
expect(event?.metadata).toMatchObject({
|
|
121
|
+
debugDataActorId: "user_dev_admin",
|
|
122
|
+
});
|
|
123
|
+
});
|
|
95
124
|
});
|
|
96
125
|
|
|
97
126
|
async function createFixture() {
|