create-windy 0.2.23 → 0.2.25
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 +100 -2
- 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
package/dist/cli.js
CHANGED
|
@@ -231,14 +231,28 @@ var optionalModuleManifests = [
|
|
|
231
231
|
"system",
|
|
232
232
|
"system.audit"
|
|
233
233
|
]),
|
|
234
|
+
registryDependencies: [
|
|
235
|
+
{
|
|
236
|
+
sourceName: "@modelcontextprotocol/sdk",
|
|
237
|
+
name: "@modelcontextprotocol/sdk",
|
|
238
|
+
version: "^1.29.0"
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
sourceName: "ajv",
|
|
242
|
+
name: "ajv",
|
|
243
|
+
version: "^8.20.0"
|
|
244
|
+
}
|
|
245
|
+
],
|
|
234
246
|
fileRoots: [
|
|
235
247
|
"apps/agent-server",
|
|
236
248
|
"apps/server/src/agent",
|
|
249
|
+
"apps/server/src/mcp",
|
|
237
250
|
"apps/server/src/module-host/tool-handlers.ts",
|
|
238
251
|
"packages/modules/src/system-agent-tools.ts"
|
|
239
252
|
],
|
|
240
253
|
documentation: [
|
|
241
254
|
"docs/architecture/ai-runtime.md",
|
|
255
|
+
"docs/architecture/mcp-business-capabilities.md",
|
|
242
256
|
"docs/platform/agent-runtime.md",
|
|
243
257
|
"docs/platform/agent-tools.md"
|
|
244
258
|
]
|
|
@@ -1689,6 +1703,10 @@ bun run dev:agent
|
|
|
1689
1703
|
公网 Provider URL 必须使用 HTTPS;本机、容器服务名和内网地址允许 HTTP。
|
|
1690
1704
|
\`QWEN_API_KEY\` 只进入 Agent Server,不会进入 Web 环境或返回给客户端。运行与模块
|
|
1691
1705
|
接入细节见 [Agent Runtime 接入](./docs/platform/agent-runtime.md)。
|
|
1706
|
+
|
|
1707
|
+
MCP 客户端使用 \`http://localhost:9747/api/mcp\`,并通过平台 Bearer Token 或 Session
|
|
1708
|
+
Cookie 鉴权。该入口只发现当前账号获准的只读 Tool;每次调用都会重新执行 Feature、
|
|
1709
|
+
RBAC 和数据范围校验。
|
|
1692
1710
|
` : ""}
|
|
1693
1711
|
|
|
1694
1712
|
## 页面与登录
|
|
@@ -1861,6 +1879,7 @@ var templateRootFiles = [
|
|
|
1861
1879
|
"package.json"
|
|
1862
1880
|
];
|
|
1863
1881
|
var templateDirectories = [
|
|
1882
|
+
".agents/skills/windy-business-module",
|
|
1864
1883
|
"apps/server",
|
|
1865
1884
|
"apps/agent-server",
|
|
1866
1885
|
"apps/web",
|
|
@@ -4288,7 +4307,9 @@ var recipes = [
|
|
|
4288
4307
|
{ id: "starter-0.2.19-to-0.2.20", from: "0.2.19", to: "0.2.20" },
|
|
4289
4308
|
{ id: "starter-0.2.20-to-0.2.21", from: "0.2.20", to: "0.2.21" },
|
|
4290
4309
|
{ id: "starter-0.2.21-to-0.2.22", from: "0.2.21", to: "0.2.22" },
|
|
4291
|
-
{ id: "starter-0.2.22-to-0.2.23", from: "0.2.22", to: "0.2.23" }
|
|
4310
|
+
{ id: "starter-0.2.22-to-0.2.23", from: "0.2.22", to: "0.2.23" },
|
|
4311
|
+
{ id: "starter-0.2.23-to-0.2.24", from: "0.2.23", to: "0.2.24" },
|
|
4312
|
+
{ id: "starter-0.2.24-to-0.2.25", from: "0.2.24", to: "0.2.25" }
|
|
4292
4313
|
];
|
|
4293
4314
|
function resolveRecipeChain(sourceVersion, targetVersion) {
|
|
4294
4315
|
if (sourceVersion === targetVersion)
|
|
@@ -4380,13 +4401,39 @@ import { access as access3, readFile as readFile15 } from "node:fs/promises";
|
|
|
4380
4401
|
import { join as join16 } from "node:path";
|
|
4381
4402
|
|
|
4382
4403
|
// src/update/known-repairs.ts
|
|
4404
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
4383
4405
|
var wrongTextVersions = new Set(["0.2.12", "0.2.13"]);
|
|
4384
4406
|
var formattedBaselineVersions = new Set(["0.2.14", "0.2.15"]);
|
|
4407
|
+
var known023DownstreamHashes = new Map([
|
|
4408
|
+
[
|
|
4409
|
+
"apps/server/src/http-app.ts",
|
|
4410
|
+
"9fca4e4ef68d942993f8176f2d6c508607d7c54c71d80982c966132b3a2572ce"
|
|
4411
|
+
],
|
|
4412
|
+
[
|
|
4413
|
+
"apps/server/src/observability/http-observability.test.ts",
|
|
4414
|
+
"55598b65792606fe80bb5355b2004440ee05aa64f5eedc2662680219d3678fe2"
|
|
4415
|
+
],
|
|
4416
|
+
[
|
|
4417
|
+
"apps/server/src/observability/http-observability.ts",
|
|
4418
|
+
"90a2ee14a2b905c705deb086d8fbeeded5c1e9d345e7f83141c33fcd73f794f3"
|
|
4419
|
+
],
|
|
4420
|
+
[
|
|
4421
|
+
"docs/operations/observability.md",
|
|
4422
|
+
"f7618d173f9f985b15bf71f0c1c00338dad53395ba6c249c3ce3f9443e5e756f"
|
|
4423
|
+
],
|
|
4424
|
+
[
|
|
4425
|
+
"packages/modules/src/system-modules.ts",
|
|
4426
|
+
"81fb08f26e5baa3238606d62b9c46761a68c90f3967ea639b178c80c74c69fc1"
|
|
4427
|
+
]
|
|
4428
|
+
]);
|
|
4385
4429
|
var wrongExampleDescription = "创建时包含了 `work-order` 示例模块,用于演示 Module Manifest 接入;它不是生产工单产品,可以在正式开发前替换。";
|
|
4386
4430
|
var correctCoreDescription = "创建时未选择 `work-order` 示例模块,当前项目只包含平台模块。";
|
|
4387
4431
|
var wrongExampleRow = /^\|\s*示例工单\s*\|\s*已包含\s*\|$/m;
|
|
4388
4432
|
var correctCoreRow = /^\|\s*示例工单\s*\|\s*未包含\s*\|$/m;
|
|
4389
4433
|
function repairKnownDerivedFile(input) {
|
|
4434
|
+
const downstreamRepair = repairKnown023Downstream(input);
|
|
4435
|
+
if (downstreamRepair !== undefined)
|
|
4436
|
+
return downstreamRepair;
|
|
4390
4437
|
if (input.path !== "README.md" || input.includeExample || !input.targetContent.includes(correctCoreDescription) || !correctCoreRow.test(input.targetContent)) {
|
|
4391
4438
|
return;
|
|
4392
4439
|
}
|
|
@@ -4404,6 +4451,57 @@ function repairKnownDerivedFile(input) {
|
|
|
4404
4451
|
}
|
|
4405
4452
|
return input.content.replace(wrongExampleDescription, correctCoreDescription).replace(wrongExampleRow, targetRow);
|
|
4406
4453
|
}
|
|
4454
|
+
function repairKnown023Downstream(input) {
|
|
4455
|
+
if (input.sourceVersion !== "0.2.23" || known023DownstreamHashes.get(input.path) !== contentHash(input.content)) {
|
|
4456
|
+
return;
|
|
4457
|
+
}
|
|
4458
|
+
if (input.path === "apps/server/src/http-app.ts" || input.path === "packages/modules/src/system-modules.ts") {
|
|
4459
|
+
return input.targetContent;
|
|
4460
|
+
}
|
|
4461
|
+
if (input.path === "apps/server/src/observability/http-observability.ts") {
|
|
4462
|
+
return replaceKnown(input.content, ` if (!isImportantRequest(input.request.method, statusCode, durationMs)) {`, ` if (durationMs === undefined) return;
|
|
4463
|
+
if (!isImportantRequest(input.request.method, statusCode, durationMs)) {`);
|
|
4464
|
+
}
|
|
4465
|
+
if (input.path === "apps/server/src/observability/http-observability.test.ts") {
|
|
4466
|
+
const targetTest = extractTest(input.targetContent, "同一次 observation 重复完成时不重复记录日志");
|
|
4467
|
+
return targetTest ? replaceKnown(input.content, `
|
|
4468
|
+
});
|
|
4469
|
+
`, `
|
|
4470
|
+
${targetTest}
|
|
4471
|
+
});
|
|
4472
|
+
`) : undefined;
|
|
4473
|
+
}
|
|
4474
|
+
if (input.path === "docs/operations/observability.md") {
|
|
4475
|
+
const lifecycle = extractSectionLead(input.targetContent, "请求观测在全局 `onRequest` 建立", "## Server 指标");
|
|
4476
|
+
const verification = extractSectionLead(input.targetContent, "已验证:共享关联标识校验", "尚未由本页证明");
|
|
4477
|
+
const withLifecycle = lifecycle ? replaceKnown(input.content, `
|
|
4478
|
+
## Server 指标`, `
|
|
4479
|
+
${lifecycle}
|
|
4480
|
+
## Server 指标`) : undefined;
|
|
4481
|
+
return withLifecycle && verification ? replaceKnown(withLifecycle, /^已验证:共享关联标识校验.*Dockerfile 已声明 readiness `HEALTHCHECK`。$/m, verification.trim()) : undefined;
|
|
4482
|
+
}
|
|
4483
|
+
}
|
|
4484
|
+
function contentHash(content) {
|
|
4485
|
+
return createHash2("sha256").update(content).digest("hex");
|
|
4486
|
+
}
|
|
4487
|
+
function replaceKnown(content, search, replacement) {
|
|
4488
|
+
const replaced = content.replace(search, replacement);
|
|
4489
|
+
return replaced === content ? undefined : replaced;
|
|
4490
|
+
}
|
|
4491
|
+
function extractTest(content, title) {
|
|
4492
|
+
const marker = ` test("${title}"`;
|
|
4493
|
+
const start = content.indexOf(marker);
|
|
4494
|
+
if (start < 0)
|
|
4495
|
+
return;
|
|
4496
|
+
const end = content.indexOf(`
|
|
4497
|
+
});`, start);
|
|
4498
|
+
return end < 0 ? undefined : content.slice(start, end + 6);
|
|
4499
|
+
}
|
|
4500
|
+
function extractSectionLead(content, startText, endText) {
|
|
4501
|
+
const start = content.indexOf(startText);
|
|
4502
|
+
const end = content.indexOf(endText, start);
|
|
4503
|
+
return start < 0 || end < 0 ? undefined : content.slice(start, end);
|
|
4504
|
+
}
|
|
4407
4505
|
function normalizeMarkdownTables(content) {
|
|
4408
4506
|
return content.split(`
|
|
4409
4507
|
`).map((line) => {
|
|
@@ -4960,7 +5058,7 @@ async function planFile(path2, project, baseRoot, targetRoot, source, target, pr
|
|
|
4960
5058
|
targetContent: new TextDecoder().decode(theirs)
|
|
4961
5059
|
}) : undefined;
|
|
4962
5060
|
if (repaired !== undefined) {
|
|
4963
|
-
return ready(path2, ownership, "update", "
|
|
5061
|
+
return ready(path2, ownership, "update", "修复已发布版本的确定性派生差异", new TextEncoder().encode(repaired));
|
|
4964
5062
|
}
|
|
4965
5063
|
if (path2.startsWith("packages/database/drizzle/") && path2.endsWith(".sql")) {
|
|
4966
5064
|
if (source && !target)
|
package/package.json
CHANGED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: windy-business-module
|
|
3
|
+
description: "Windy 业务模块、Manifest、Repository、Service、Agent Tool 与 MCP 接入开发。"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Windy 业务模块开发
|
|
7
|
+
|
|
8
|
+
用于新增或修改业务能力。先读取仓库 `AGENTS.md`、`CONTEXT.md` 和直接相关的
|
|
9
|
+
`docs/architecture/`、`docs/development/`、`docs/platform/` 文档。
|
|
10
|
+
|
|
11
|
+
## 工作流
|
|
12
|
+
|
|
13
|
+
1. 选择一个稳定的能力基础 Key 和模块命名空间。先定义业务边界、Actor、数据归属与
|
|
14
|
+
失败语义,再改代码。
|
|
15
|
+
2. 在业务 package 的 Module Manifest 一次性注册 Feature、Permission、API、
|
|
16
|
+
菜单/入口、审计动作,以及需要的 Task、Provider、JSON Schema 和 Tool。
|
|
17
|
+
3. 数据层使用 Drizzle Schema、前向 Migration 和 Repository;Service 持有业务规则,
|
|
18
|
+
其它模块只能通过 Service、公开 Port 或模块注册机制协作。
|
|
19
|
+
4. Server 通过 `InstallableServerModule` 和 `ModuleHost` 注册 Guarded Route、Task、
|
|
20
|
+
Provider 或 Tool Handler。Handler 调用 Service,不直接访问其它模块 Repository。
|
|
21
|
+
5. Tool 输入输出只使用 Manifest 已注册的顶层 object JSON Schema。Actor、Permission、
|
|
22
|
+
Feature、License 和 Data Scope 只来自 `ModuleToolContext` 与平台 Runtime。
|
|
23
|
+
6. 若项目包含 `system.agent`,`operation=query` 的 Tool 会由同一 Registry 自动进入
|
|
24
|
+
MCP;不要编辑 MCP Adapter 或复制 Tool 定义。写操作在幂等、审批和结果追踪闭环前
|
|
25
|
+
不通过 MCP 暴露。
|
|
26
|
+
7. 补 Manifest/Composition、Repository/Service、Guard 拒绝和 Handler 契约测试。
|
|
27
|
+
涉及 Tool 时还要覆盖 Schema 拒绝、数据范围与未授权发现/调用;涉及可选模块时覆盖
|
|
28
|
+
Starter 的文件、依赖和数据库裁剪。
|
|
29
|
+
8. 更新长期文档,运行受影响测试、`bun run typecheck` 和 `bun run lint`,按一个完整
|
|
30
|
+
小切片提交。
|
|
31
|
+
|
|
32
|
+
## 代码事实源
|
|
33
|
+
|
|
34
|
+
- Manifest 与 Tool 类型:`packages/modules/src/manifest.ts`
|
|
35
|
+
- Tool 组合校验:`packages/modules/src/tool-composition.ts`
|
|
36
|
+
- 模块 Host 契约:`packages/server-sdk/src/module-host.ts`
|
|
37
|
+
- Tool Handler 契约:`packages/server-sdk/src/tool-registration.ts`
|
|
38
|
+
- 可移除业务示例:`packages/example-work-order/`、`apps/server/src/work-order/`
|
|
39
|
+
- 业务模块安装入口:`apps/server/src/installed-business-modules.ts`
|
|
40
|
+
- MCP 与统一执行边界:`docs/architecture/mcp-business-capabilities.md`
|
|
41
|
+
|
|
42
|
+
缺少 `system.agent` 或某个事实源文件时,先说明当前生成组合的物理边界,不在业务层补一套
|
|
43
|
+
替代实现。
|
package/template/AGENTS.md
CHANGED
|
@@ -42,6 +42,8 @@
|
|
|
42
42
|
|
|
43
43
|
## 常用入口
|
|
44
44
|
|
|
45
|
+
- `.agents/skills/windy-business-module/SKILL.md`:新增或修改业务模块、Agent Tool
|
|
46
|
+
或 MCP 暴露时使用的仓库内开发流程。
|
|
45
47
|
- `bun run dev`:复用 Docker 镜像启动 PostgreSQL、自动 migration、Server 与支持热更新的 Web。
|
|
46
48
|
- `bun run dev:build`:依赖、Dockerfile 或 lockfile 变化后重建并启动开发环境。
|
|
47
49
|
- `bun run dev:web`:启动 Web 开发服务。
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@elysiajs/swagger": "^1.3.1",
|
|
13
|
+
"@modelcontextprotocol/sdk": "^1",
|
|
14
|
+
"@noble/ed25519": "^3.1.0",
|
|
13
15
|
"@southwind-ai/config": "workspace:*",
|
|
14
16
|
"@southwind-ai/database": "workspace:*",
|
|
15
17
|
"@southwind-ai/example-work-order": "workspace:*",
|
|
@@ -19,7 +21,7 @@
|
|
|
19
21
|
"@southwind-ai/server-sdk": "workspace:*",
|
|
20
22
|
"@southwind-ai/shared": "workspace:*",
|
|
21
23
|
"@southwind-ai/storage": "workspace:*",
|
|
22
|
-
"
|
|
24
|
+
"ajv": "^8.20.0",
|
|
23
25
|
"argon2": "^0.44.0",
|
|
24
26
|
"drizzle-orm": "^0.45.2",
|
|
25
27
|
"elysia": "^1.4.29",
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type ModuleToolHandlers,
|
|
13
13
|
registerAgentRoutes,
|
|
14
14
|
} from "./routes.js";
|
|
15
|
+
import { GovernedToolRegistry } from "./tool-registry.js";
|
|
15
16
|
|
|
16
17
|
describe("Agent Tool data scope", () => {
|
|
17
18
|
test("启动时拒绝 Manifest 与 Handler 不一致", () => {
|
|
@@ -27,21 +28,16 @@ describe("Agent Tool data scope", () => {
|
|
|
27
28
|
execute: () => ({ items: [], total: 0, page: 1, pageSize: 10 }),
|
|
28
29
|
});
|
|
29
30
|
|
|
30
|
-
expect(
|
|
31
|
-
|
|
32
|
-
new
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
expect(
|
|
32
|
+
() =>
|
|
33
|
+
new GovernedToolRegistry(
|
|
34
|
+
loadFoundationModules(),
|
|
35
|
+
missing as ModuleToolHandlers,
|
|
36
|
+
runtime,
|
|
37
|
+
),
|
|
37
38
|
).toThrow("缺少 Handler");
|
|
38
|
-
expect(
|
|
39
|
-
|
|
40
|
-
new FakeRouteApp(),
|
|
41
|
-
loadFoundationModules(),
|
|
42
|
-
extra,
|
|
43
|
-
runtime,
|
|
44
|
-
),
|
|
39
|
+
expect(
|
|
40
|
+
() => new GovernedToolRegistry(loadFoundationModules(), extra, runtime),
|
|
45
41
|
).toThrow("Handler 未声明");
|
|
46
42
|
});
|
|
47
43
|
|
|
@@ -70,9 +66,11 @@ describe("Agent Tool data scope", () => {
|
|
|
70
66
|
);
|
|
71
67
|
registerAgentRoutes(
|
|
72
68
|
app,
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
69
|
+
new GovernedToolRegistry(
|
|
70
|
+
loadFoundationModules(),
|
|
71
|
+
createSystemToolHandlers(repositories, runtime),
|
|
72
|
+
runtime,
|
|
73
|
+
),
|
|
76
74
|
);
|
|
77
75
|
guardContext.actor = {
|
|
78
76
|
...guardContext.actor,
|
|
@@ -103,9 +101,11 @@ describe("Agent Tool data scope", () => {
|
|
|
103
101
|
const app = new FakeRouteApp();
|
|
104
102
|
registerAgentRoutes(
|
|
105
103
|
app,
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
104
|
+
new GovernedToolRegistry(
|
|
105
|
+
loadFoundationModules(),
|
|
106
|
+
createSystemToolHandlers(createSystemRepositories(snapshot), runtime),
|
|
107
|
+
runtime,
|
|
108
|
+
),
|
|
109
109
|
);
|
|
110
110
|
const guardContext = await runtime.createGuardContext(
|
|
111
111
|
authenticatedRequest(),
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import type { AgentRuntimeOperation } from "./run-contracts.js";
|
|
3
|
+
import { RemoteAgentRuntime } from "./remote-runtime.js";
|
|
4
|
+
|
|
5
|
+
const servers: Bun.Server<unknown>[] = [];
|
|
6
|
+
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
for (const server of servers.splice(0)) server.stop(true);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
describe("RemoteAgentRuntime", () => {
|
|
12
|
+
test("只向 Agent Server 序列化远端支持的运行限制", async () => {
|
|
13
|
+
let received: unknown;
|
|
14
|
+
const server = Bun.serve({
|
|
15
|
+
port: 0,
|
|
16
|
+
async fetch(request) {
|
|
17
|
+
received = await request.json();
|
|
18
|
+
return Response.json({
|
|
19
|
+
runId: "run_1",
|
|
20
|
+
status: "accepted",
|
|
21
|
+
eventsUrl: "/api/agent/runs/run_1/events",
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
servers.push(server);
|
|
26
|
+
|
|
27
|
+
const manifestLimits = {
|
|
28
|
+
maxDurationSeconds: 60,
|
|
29
|
+
maxInputBytes: 4_096,
|
|
30
|
+
maxOutputBytes: 8_192,
|
|
31
|
+
};
|
|
32
|
+
const operation: AgentRuntimeOperation = {
|
|
33
|
+
operationKey: "business.answer",
|
|
34
|
+
input: { question: "你好" },
|
|
35
|
+
instructions: "只回答当前业务问题。",
|
|
36
|
+
userPrompt: "你好",
|
|
37
|
+
allowedTools: [],
|
|
38
|
+
limits: manifestLimits,
|
|
39
|
+
executionGrant: "grant_1",
|
|
40
|
+
};
|
|
41
|
+
const runtime = new RemoteAgentRuntime({
|
|
42
|
+
baseUrl: server.url.toString(),
|
|
43
|
+
internalToken: "test-token",
|
|
44
|
+
toolHostUrl: "http://server.internal/api/agent/tools",
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
await runtime.start(operation, "request-1");
|
|
48
|
+
|
|
49
|
+
expect(received).toMatchObject({
|
|
50
|
+
limits: {
|
|
51
|
+
maxDurationSeconds: 60,
|
|
52
|
+
maxOutputBytes: 8_192,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
expect(
|
|
56
|
+
(received as { limits: Record<string, unknown> }).limits,
|
|
57
|
+
).not.toHaveProperty("maxInputBytes");
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -27,16 +27,26 @@ export class RemoteAgentRuntime implements AgentRuntimePort {
|
|
|
27
27
|
request: AgentRuntimeOperation,
|
|
28
28
|
idempotencyKey: string,
|
|
29
29
|
): Promise<AgentRuntimeStartResponse> {
|
|
30
|
+
const remoteRequest: RemoteStartRunRequest = {
|
|
31
|
+
operationKey: request.operationKey,
|
|
32
|
+
input: request.input,
|
|
33
|
+
instructions: request.instructions,
|
|
34
|
+
userPrompt: request.userPrompt,
|
|
35
|
+
allowedTools: request.allowedTools,
|
|
36
|
+
limits: {
|
|
37
|
+
maxDurationSeconds: request.limits.maxDurationSeconds,
|
|
38
|
+
maxOutputBytes: request.limits.maxOutputBytes,
|
|
39
|
+
},
|
|
40
|
+
executionGrant: request.executionGrant,
|
|
41
|
+
toolHostUrl: this.options.toolHostUrl,
|
|
42
|
+
};
|
|
30
43
|
const response = await this.request("/api/agent/runs", {
|
|
31
44
|
method: "POST",
|
|
32
45
|
headers: {
|
|
33
46
|
"content-type": "application/json",
|
|
34
47
|
"idempotency-key": idempotencyKey,
|
|
35
48
|
},
|
|
36
|
-
body: JSON.stringify(
|
|
37
|
-
...request,
|
|
38
|
-
toolHostUrl: this.options.toolHostUrl,
|
|
39
|
-
}),
|
|
49
|
+
body: JSON.stringify(remoteRequest),
|
|
40
50
|
});
|
|
41
51
|
if (!response.ok) throw await remoteError(response);
|
|
42
52
|
return (await response.json()) as AgentRuntimeStartResponse;
|
|
@@ -96,6 +106,14 @@ export class RemoteAgentRuntime implements AgentRuntimePort {
|
|
|
96
106
|
}
|
|
97
107
|
}
|
|
98
108
|
|
|
109
|
+
interface RemoteStartRunRequest extends Omit<AgentRuntimeOperation, "limits"> {
|
|
110
|
+
limits: {
|
|
111
|
+
maxDurationSeconds: number;
|
|
112
|
+
maxOutputBytes: number;
|
|
113
|
+
};
|
|
114
|
+
toolHostUrl: string;
|
|
115
|
+
}
|
|
116
|
+
|
|
99
117
|
function normalizeInternalUrl(raw: string): string {
|
|
100
118
|
const url = new URL(raw);
|
|
101
119
|
if (!["http:", "https:"].includes(url.protocol)) {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
entitlementKey,
|
|
4
|
+
type FeatureFlagDefinition,
|
|
5
|
+
} from "@southwind-ai/shared";
|
|
3
6
|
import {
|
|
4
7
|
createFoundationSnapshot,
|
|
5
8
|
loadFoundationModules,
|
|
@@ -8,6 +11,7 @@ import { createServerRuntime } from "../runtime.js";
|
|
|
8
11
|
import type { RouteApp } from "../system/routes.js";
|
|
9
12
|
import { createSystemRepositories } from "../system/seed.js";
|
|
10
13
|
import { createSystemToolHandlers, registerAgentRoutes } from "./routes.js";
|
|
14
|
+
import { GovernedToolRegistry } from "./tool-registry.js";
|
|
11
15
|
|
|
12
16
|
class FakeRouteApp implements RouteApp {
|
|
13
17
|
readonly handlers = new Map<string, (context: unknown) => unknown>();
|
|
@@ -44,12 +48,14 @@ function routeFixture(features?: FeatureFlagDefinition[]) {
|
|
|
44
48
|
}
|
|
45
49
|
registerAgentRoutes(
|
|
46
50
|
app,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
new GovernedToolRegistry(
|
|
52
|
+
modules,
|
|
53
|
+
createSystemToolHandlers(
|
|
54
|
+
createSystemRepositories(createFoundationSnapshot()),
|
|
55
|
+
runtime,
|
|
56
|
+
),
|
|
50
57
|
runtime,
|
|
51
58
|
),
|
|
52
|
-
runtime,
|
|
53
59
|
);
|
|
54
60
|
return { app, runtime };
|
|
55
61
|
}
|
|
@@ -191,9 +197,11 @@ describe("agent routes", () => {
|
|
|
191
197
|
const app = new FakeRouteApp();
|
|
192
198
|
registerAgentRoutes(
|
|
193
199
|
app,
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
200
|
+
new GovernedToolRegistry(
|
|
201
|
+
loadFoundationModules(),
|
|
202
|
+
createSystemToolHandlers(repositories, runtime),
|
|
203
|
+
runtime,
|
|
204
|
+
),
|
|
197
205
|
);
|
|
198
206
|
const request = () =>
|
|
199
207
|
new Request(
|