create-windy 0.6.18 → 0.6.20
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 +133 -54
- package/package.json +1 -1
- package/template/.windy-template.json +2 -2
- package/template/README.md +5 -2
- package/template/apps/agent-server/README.md +6 -0
- package/template/apps/agent-server/src/southwind_agent_server/app.py +23 -0
- package/template/apps/agent-server/src/southwind_agent_server/health_routes.py +9 -0
- package/template/apps/agent-server/src/southwind_agent_server/runtime_compatibility.py +89 -0
- package/template/apps/agent-server/tests/test_runtime_compatibility.py +119 -0
- package/template/apps/server/src/agent/remote-runtime-compatibility.test.ts +147 -0
- package/template/apps/server/src/agent/remote-runtime.ts +66 -4
- package/template/apps/server/src/agent/runtime-bootstrap.ts +1 -0
- package/template/apps/web/src/components/common/FormDialog.vue +1 -0
- package/template/apps/web/src/components/common/ModalLayout.webtest.ts +1 -0
- package/template/apps/web/src/components/ui/dialog/DialogContent.vue +1 -1
- package/template/apps/web/src/components/ui/dialog/DialogScrollContent.vue +1 -1
- package/template/apps/web/src/pages/system/components/SystemResourceEditor.vue +1 -0
- package/template/apps/web/src/pages/system/components/user-account/CreateDepartmentDialog.vue +182 -0
- package/template/apps/web/src/pages/system/components/user-account/CreateDepartmentDialog.webtest.ts +93 -0
- package/template/apps/web/src/pages/system/components/user-account/RolePermissionSummary.vue +59 -0
- package/template/apps/web/src/pages/system/components/user-account/UserAccountForm.vue +21 -71
- package/template/apps/web/src/pages/system/components/user-account/UserAccountForm.webtest.ts +71 -0
- package/template/apps/web/src/pages/system/components/user-account/UserRoleSelector.vue +48 -27
- package/template/apps/web/src/pages/system/components/user-account/UserRoleSelector.webtest.ts +30 -1
- package/template/docs/architecture/agent-runtime-compatibility.md +107 -0
- package/template/docs/architecture/ai-runtime.md +8 -15
- package/template/docs/platform/agent-runtime.md +5 -0
- package/template/packages/server-sdk/contracts/agent-runtime-compatibility/v1/offer.json +22 -0
- package/template/packages/server-sdk/index.ts +19 -0
- package/template/packages/server-sdk/package.json +2 -0
- package/template/packages/server-sdk/src/agent-runtime-compatibility.contract.test.ts +143 -0
- package/template/packages/server-sdk/src/agent-runtime-compatibility.public-api.test.ts +19 -0
- package/template/packages/server-sdk/src/agent-runtime-compatibility.ts +284 -0
package/template/apps/web/src/pages/system/components/user-account/UserRoleSelector.webtest.ts
CHANGED
|
@@ -22,13 +22,38 @@ describe("UserRoleSelector", () => {
|
|
|
22
22
|
code: "department-manager",
|
|
23
23
|
name: "部门负责人",
|
|
24
24
|
dataScope: "department-and-children",
|
|
25
|
+
permissionKeys: ["system.user.read", "system.user.manage"],
|
|
25
26
|
},
|
|
26
27
|
],
|
|
27
28
|
total: 1,
|
|
28
29
|
page: 1,
|
|
29
30
|
pageSize: 100,
|
|
30
31
|
});
|
|
31
|
-
const wrapper = mount(UserRoleSelector, {
|
|
32
|
+
const wrapper = mount(UserRoleSelector, {
|
|
33
|
+
props: {
|
|
34
|
+
modelValue: [],
|
|
35
|
+
permissions: [
|
|
36
|
+
{
|
|
37
|
+
key: "system.user.read",
|
|
38
|
+
label: "查看用户",
|
|
39
|
+
module: "system",
|
|
40
|
+
moduleLabel: "系统管理",
|
|
41
|
+
resource: "user",
|
|
42
|
+
resourceLabel: "用户账号",
|
|
43
|
+
action: "read",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
key: "system.user.manage",
|
|
47
|
+
label: "管理用户",
|
|
48
|
+
module: "system",
|
|
49
|
+
moduleLabel: "系统管理",
|
|
50
|
+
resource: "user",
|
|
51
|
+
resourceLabel: "用户账号",
|
|
52
|
+
action: "manage",
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
});
|
|
32
57
|
await flushPromises();
|
|
33
58
|
|
|
34
59
|
expect(api.list).toHaveBeenCalledWith(
|
|
@@ -37,6 +62,10 @@ describe("UserRoleSelector", () => {
|
|
|
37
62
|
);
|
|
38
63
|
expect(wrapper.text()).toContain("部门负责人");
|
|
39
64
|
expect(wrapper.text()).toContain("本部门及下级");
|
|
65
|
+
expect(wrapper.text()).toContain("查看 2 项功能权限");
|
|
66
|
+
await wrapper.get("summary").trigger("click");
|
|
67
|
+
expect(wrapper.text()).toContain("用户账号");
|
|
68
|
+
expect(wrapper.text()).toContain("查看用户、管理用户");
|
|
40
69
|
await wrapper.get('[role="checkbox"]').trigger("click");
|
|
41
70
|
expect(wrapper.emitted("update:modelValue")?.at(-1)).toEqual([
|
|
42
71
|
["role_manager"],
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Remote Agent Runtime 兼容契约
|
|
2
|
+
|
|
3
|
+
更新时间:2026-09-02。
|
|
4
|
+
|
|
5
|
+
状态:WAI-1 V1 兼容协商基础已实现。它不开放新的业务 Run 入口,也不代表外部业务系统已经可以
|
|
6
|
+
直接接入生产 Agent Server。
|
|
7
|
+
|
|
8
|
+
## 目标
|
|
9
|
+
|
|
10
|
+
Windy 继续只有一套 Agent Runtime。业务系统保留领域治理,中央 Windy Runtime 负责模型执行和
|
|
11
|
+
运行事实,两者通过显式版本化的 Remote Agent Runtime Interface 演进,不能依赖“HTTP 能通”或
|
|
12
|
+
共享源码版本猜测兼容性。
|
|
13
|
+
|
|
14
|
+
WAI-1 只冻结兼容 Offer 和 Run 创建时的版本 pin:
|
|
15
|
+
|
|
16
|
+
- Agent Server 通过受认证的 `GET /api/agent/runtime/compatibility` 返回 V1 Offer;
|
|
17
|
+
- Windy Server 选择共同协议版本,并校验 Engine release、协议能力和完整 Offer 摘要;
|
|
18
|
+
- 创建 Run 时用请求头固定所选协议版本和 Offer digest;
|
|
19
|
+
- Agent Server 在写 Store 或执行 Engine 前重新校验两个 pin;
|
|
20
|
+
- 旧 Server 不发送 pin 时,新 Agent Server 保持兼容;新 Server 在 `prefer-v1` 模式下只对旧
|
|
21
|
+
Agent Server 的明确 `404` 回退,畸形或不兼容 Offer 不降级。
|
|
22
|
+
|
|
23
|
+
公开 TypeScript 契约位于 `@southwind-ai/server-sdk`。它是 Server Adapter 的 Interface,不进入
|
|
24
|
+
浏览器 Bundle,也不让业务 Module 直接访问 Agent Server。
|
|
25
|
+
|
|
26
|
+
## 所有权
|
|
27
|
+
|
|
28
|
+
| 所有者 | 保留职责 |
|
|
29
|
+
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
30
|
+
| 外部业务系统 | Manifest、Operation prepare、Feature/RBAC/License/Data Scope、DLP、Artifact 授权、Execution Grant、Tool allowlist、审批、幂等、审计与 Tool Handler |
|
|
31
|
+
| 中央 Windy | Run 生命周期、Remote execution、Provider/模型路由、canonical SSE/result/cancel、Usage 与可观测事实 |
|
|
32
|
+
| Agent Server | 受控 Model/Tool loop、Run Store、Engine identity、协议 Offer 与 pin 校验;禁止访问业务数据库或 Repository |
|
|
33
|
+
|
|
34
|
+
中央 Runtime 只接收业务 Host 已治理的输入、ArtifactRef、版本化 Operation Release、Tool schema
|
|
35
|
+
和 opaque Execution Grant。数据库 URL、Repository Adapter、业务 Secret、Actor 自报权限或“已审批”
|
|
36
|
+
标志都不属于此 Interface。
|
|
37
|
+
|
|
38
|
+
## V1 Offer
|
|
39
|
+
|
|
40
|
+
Offer 使用以下稳定结构:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"schemaVersion": 1,
|
|
45
|
+
"runtimeKind": "agent",
|
|
46
|
+
"engineId": "windy.direct.openai-compatible.v1",
|
|
47
|
+
"protocolVersions": [1],
|
|
48
|
+
"protocolFeatures": ["canonical-sse-replay.v1", "run-lifecycle.v1"],
|
|
49
|
+
"toolCallback": {
|
|
50
|
+
"contractVersion": 1,
|
|
51
|
+
"authentication": "windy-internal-bearer-v1"
|
|
52
|
+
},
|
|
53
|
+
"releaseDigest": "sha256:<engine-and-protocol-digest>",
|
|
54
|
+
"capabilitiesDigest": "sha256:<features-and-callback-digest>",
|
|
55
|
+
"offerDigest": "sha256:<complete-offer-digest>"
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`protocolFeatures` 描述 Runtime 传输与治理语义,不复用 Operation 的
|
|
60
|
+
`requiredCapabilities`。后者仍表示模型或执行能力,例如 `text-generation`、`vision`。
|
|
61
|
+
|
|
62
|
+
三个摘要使用稳定字段顺序、排序后的集合和紧凑 JSON 计算:
|
|
63
|
+
|
|
64
|
+
- `releaseDigest` 绑定 `runtimeKind + engineId + protocolVersions`;
|
|
65
|
+
- `capabilitiesDigest` 绑定 `protocolFeatures + toolCallback`;
|
|
66
|
+
- `offerDigest` 绑定前述全部字段与两个子摘要。
|
|
67
|
+
|
|
68
|
+
调用方必须重新计算摘要。只修改数组顺序不会改变语义;重复、超界、非法稳定 Key、未知
|
|
69
|
+
`schemaVersion`、无共同协议、能力缺失、callback auth 不匹配或摘要漂移都失败关闭。
|
|
70
|
+
|
|
71
|
+
## 协商与滚动升级
|
|
72
|
+
|
|
73
|
+
Windy 项目组合使用 `prefer-v1`:
|
|
74
|
+
|
|
75
|
+
1. 每次创建 Run 前读取当前 Offer,不缓存跨进程替换后的事实;
|
|
76
|
+
2. Offer 为合法 V1 时发送:
|
|
77
|
+
- `X-Windy-Agent-Runtime-Protocol: 1`
|
|
78
|
+
- `X-Windy-Agent-Runtime-Offer-Digest: sha256:...`
|
|
79
|
+
3. 只有兼容端点明确返回 `404` 才按旧协议创建 Run;网络失败、403、5xx、畸形响应和不兼容
|
|
80
|
+
V1 均拒绝启动;
|
|
81
|
+
4. Agent Server 接受“两个 pin 都缺失”的旧客户端;只有一个 pin、未知版本或 digest 不一致均在
|
|
82
|
+
Run admission 前拒绝。
|
|
83
|
+
|
|
84
|
+
供后续外部业务 Host 使用的 Adapter 必须选择 `require-v1`,不能允许 404 legacy fallback。
|
|
85
|
+
Run body 保持原样,避免给 Pydantic `extra=forbid` 的旧 Agent Server 增加字段而破坏滚动升级。
|
|
86
|
+
|
|
87
|
+
## 当前认证限制
|
|
88
|
+
|
|
89
|
+
V1 Offer 如实声明 `windy-internal-bearer-v1`。它只适用于同一受控部署内的 Windy Server 与
|
|
90
|
+
Agent Server,仍使用 `AGENT_SERVER_INTERNAL_TOKEN` 保护兼容查询、Run 接口和 Tool callback。
|
|
91
|
+
|
|
92
|
+
不得把该 token 复制给 Sharp S-Word 或其它业务系统,也不得把当前 callback URL 暴露为跨系统
|
|
93
|
+
公共入口。外部生产接入前必须新增独立受众、短时或可轮换凭据、重放保护和按业务 Host 撤销的
|
|
94
|
+
callback auth profile;届时外部 Adapter 以 `require-v1` 要求新 profile,旧内部 Adapter 继续并行。
|
|
95
|
+
|
|
96
|
+
## 非目标
|
|
97
|
+
|
|
98
|
+
WAI-1 不实现:
|
|
99
|
+
|
|
100
|
+
- LiteLLM 或多 Provider Gateway;
|
|
101
|
+
- 完整 RAG、向量索引或跨系统检索;
|
|
102
|
+
- Sharp S-Word 的 Manifest、Tool、数据或 UI 接线;
|
|
103
|
+
- 远程业务 Host 注册表、租户路由或共享 Copilot UI;
|
|
104
|
+
- callback 认证升级。
|
|
105
|
+
|
|
106
|
+
这些能力必须在新切片中复用本 Interface,不得新增第二套 Agent Server、Run/Event DTO 或业务数据库
|
|
107
|
+
访问路径。
|
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
# Windy AI Runtime 架构
|
|
2
2
|
|
|
3
|
-
更新时间:2026-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
PostgreSQL 多副本 Store 尚未实现。Document AI 已提供独立
|
|
12
|
-
Server、授权 ArtifactRef、结构化页级结果、本地/内网 Adapter 契约,以及搜索型 PDF
|
|
13
|
-
等派生文件的受治理 Output Artifact producer 协议;项目 composition 尚未接线,不能
|
|
14
|
-
视为生产可用。Speech 已提供批量转写、归一化、VAD/时间戳契约、稳定分段和结果
|
|
15
|
-
Artifact,但未提供实时 ASR、TTS 或 Dictation。不能据此宣称完整 AI Runtime 已交付。
|
|
16
|
-
Vision 已提供 `agent.vision` 的多图片受控输入、像素限制、逐次发送审计和 JSON Schema
|
|
17
|
-
终态校验;视频帧输入与实时视觉不在当前切片。
|
|
3
|
+
更新时间:2026-09-02。
|
|
4
|
+
|
|
5
|
+
状态:文本 Agent、Document、Embedding、批量 Speech/ASR、受控 Vision Artifact、PostgreSQL
|
|
6
|
+
Run Store、持久 Session/Memory 与 Workflow/委派切片均已实现。WAI-1 已为 Windy Server 到
|
|
7
|
+
Agent Server 增加 additive 的 V1 兼容 Offer、release/capability digest 和 Run 创建 pin;详见
|
|
8
|
+
[Remote Agent Runtime 兼容契约](./agent-runtime-compatibility.md)。当前 Provider 仍是单一 Direct
|
|
9
|
+
OpenAI-compatible Adapter;LiteLLM 多 Provider 路由、完整 RAG、外部业务 Host callback 认证与
|
|
10
|
+
Sharp S-Word 接线均未实现。未配置任一可选 Runtime 时继续安全拒绝对应 Operation,不阻止普通业务。
|
|
18
11
|
|
|
19
12
|
## 目标
|
|
20
13
|
|
|
@@ -18,6 +18,11 @@ POST /api/agent/runs/:runId/cancel
|
|
|
18
18
|
创建 Run 必须携带幂等键;SSE 支持 cursor 重放和 heartbeat。Engine 切换不改变 Run DTO、
|
|
19
19
|
canonical Event、唯一终态、取消或 Tool 授权语义。
|
|
20
20
|
|
|
21
|
+
Windy Server 与 Agent Server 之间已经提供 additive 的 V1 兼容 Offer、release/capability digest
|
|
22
|
+
和 Run 创建请求头 pin。当前同部署 Adapter 使用滚动兼容的 `prefer-v1`;任何外部业务 Host Adapter
|
|
23
|
+
必须使用 `require-v1`,且在独立 callback 认证 profile 落地前不得投入生产。完整职责与失败语义见
|
|
24
|
+
[Remote Agent Runtime 兼容契约](../architecture/agent-runtime-compatibility.md)。
|
|
25
|
+
|
|
21
26
|
## 能力与治理边界
|
|
22
27
|
|
|
23
28
|
Direct Engine 已覆盖:
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"runtimeKind": "agent",
|
|
4
|
+
"engineId": "windy.direct.openai-compatible.v1",
|
|
5
|
+
"protocolVersions": [1],
|
|
6
|
+
"protocolFeatures": [
|
|
7
|
+
"canonical-sse-replay.v1",
|
|
8
|
+
"execution-grant-forwarding.v1",
|
|
9
|
+
"operation-release-attestation.v1",
|
|
10
|
+
"run-cancel.v1",
|
|
11
|
+
"run-lifecycle.v1",
|
|
12
|
+
"runtime-reporting.v1",
|
|
13
|
+
"tool-callback.v1"
|
|
14
|
+
],
|
|
15
|
+
"toolCallback": {
|
|
16
|
+
"contractVersion": 1,
|
|
17
|
+
"authentication": "windy-internal-bearer-v1"
|
|
18
|
+
},
|
|
19
|
+
"releaseDigest": "sha256:410c49040a4e4de40dd2fed60877b2b08e9bbcb23198f10a7cbb3cf7d1135ce0",
|
|
20
|
+
"capabilitiesDigest": "sha256:bcdd3822f9077ac6d1cd42f8a53dbdb46d9a5bc908821e92bd6fb28e2ec6ab65",
|
|
21
|
+
"offerDigest": "sha256:1375cd3a1511f2d5fdde3cac123d9d6bc603f80ad746758508d0a3da67ed8fad"
|
|
22
|
+
}
|
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
export type { ModuleActor } from "./src/actor.js";
|
|
2
|
+
export {
|
|
3
|
+
AGENT_RUNTIME_COMPATIBILITY_PATH_V1,
|
|
4
|
+
AGENT_RUNTIME_COMPATIBILITY_SCHEMA_VERSION,
|
|
5
|
+
AGENT_RUNTIME_INTERNAL_CALLBACK_AUTH_V1,
|
|
6
|
+
AGENT_RUNTIME_KIND_V1,
|
|
7
|
+
AGENT_RUNTIME_OFFER_DIGEST_HEADER_V1,
|
|
8
|
+
AGENT_RUNTIME_PROTOCOL_FEATURES_V1,
|
|
9
|
+
AGENT_RUNTIME_PROTOCOL_HEADER_V1,
|
|
10
|
+
AGENT_RUNTIME_PROTOCOL_VERSION_V1,
|
|
11
|
+
AGENT_RUNTIME_TOOL_CALLBACK_CONTRACT_VERSION_V1,
|
|
12
|
+
createAgentRuntimeCompatibilityOfferV1,
|
|
13
|
+
localAgentRuntimeCompatibilityRequirementsV1,
|
|
14
|
+
negotiateAgentRuntimeCompatibilityV1,
|
|
15
|
+
parseAgentRuntimeCompatibilityOfferV1,
|
|
16
|
+
type AgentRuntimeCompatibilityDigestV1,
|
|
17
|
+
type AgentRuntimeCompatibilityNegotiationV1,
|
|
18
|
+
type AgentRuntimeCompatibilityOfferV1,
|
|
19
|
+
type AgentRuntimeCompatibilityRequirementsV1,
|
|
20
|
+
} from "./src/agent-runtime-compatibility.js";
|
|
2
21
|
export { pageAgentClientRunCapability } from "./src/client-agent-session.js";
|
|
3
22
|
export type {
|
|
4
23
|
ModuleClientAgentHostAuthAdapter,
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"src/ai-runtime-readiness-validation.ts",
|
|
27
27
|
"src/ai-runtime-readiness-validation-values.ts",
|
|
28
28
|
"src/ai-runtime-readiness-values.ts",
|
|
29
|
+
"src/agent-runtime-compatibility.ts",
|
|
29
30
|
"src/agent-session-memory-port.ts",
|
|
30
31
|
"src/agent-session-provider.ts",
|
|
31
32
|
"src/agent-source-invalidation-port.ts",
|
|
@@ -71,6 +72,7 @@
|
|
|
71
72
|
},
|
|
72
73
|
"exports": {
|
|
73
74
|
".": "./index.ts",
|
|
75
|
+
"./contracts/agent-runtime-compatibility/v1/*": "./contracts/agent-runtime-compatibility/v1/*",
|
|
74
76
|
"./contracts/ai-runtime-readiness/v1/*": "./contracts/ai-runtime-readiness/v1/*"
|
|
75
77
|
},
|
|
76
78
|
"publishConfig": {
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
import {
|
|
4
|
+
AGENT_RUNTIME_PROTOCOL_FEATURES_V1,
|
|
5
|
+
type AgentRuntimeCompatibilityOfferV1,
|
|
6
|
+
createAgentRuntimeCompatibilityOfferV1,
|
|
7
|
+
localAgentRuntimeCompatibilityRequirementsV1,
|
|
8
|
+
negotiateAgentRuntimeCompatibilityV1,
|
|
9
|
+
parseAgentRuntimeCompatibilityOfferV1,
|
|
10
|
+
} from "./agent-runtime-compatibility.js";
|
|
11
|
+
|
|
12
|
+
const fixture = await loadFixture();
|
|
13
|
+
|
|
14
|
+
describe("Remote Agent Runtime compatibility V1", () => {
|
|
15
|
+
test("公共 builder 与跨语言 fixture 字节语义一致", () => {
|
|
16
|
+
expect(
|
|
17
|
+
createAgentRuntimeCompatibilityOfferV1(
|
|
18
|
+
"windy.direct.openai-compatible.v1",
|
|
19
|
+
),
|
|
20
|
+
).toEqual(fixture);
|
|
21
|
+
expect(parseAgentRuntimeCompatibilityOfferV1(fixture)).toEqual(fixture);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("协商最高共同版本并要求完整协议能力与 callback 认证", () => {
|
|
25
|
+
const result = negotiateAgentRuntimeCompatibilityV1(
|
|
26
|
+
fixture,
|
|
27
|
+
localAgentRuntimeCompatibilityRequirementsV1(),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
expect(result).toEqual({ ok: true, protocolVersion: 1, offer: fixture });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("未知 schema、缺失能力和 callback 认证漂移均失败关闭", () => {
|
|
34
|
+
expect(
|
|
35
|
+
negotiateAgentRuntimeCompatibilityV1(
|
|
36
|
+
{ ...fixture, schemaVersion: 2 },
|
|
37
|
+
localAgentRuntimeCompatibilityRequirementsV1(),
|
|
38
|
+
),
|
|
39
|
+
).toEqual({ ok: false, code: "CONTRACT_UNSUPPORTED" });
|
|
40
|
+
expect(
|
|
41
|
+
negotiateAgentRuntimeCompatibilityV1(fixture, {
|
|
42
|
+
...localAgentRuntimeCompatibilityRequirementsV1(),
|
|
43
|
+
requiredProtocolFeatures: [
|
|
44
|
+
...AGENT_RUNTIME_PROTOCOL_FEATURES_V1,
|
|
45
|
+
"future-required.v1",
|
|
46
|
+
],
|
|
47
|
+
}),
|
|
48
|
+
).toEqual({
|
|
49
|
+
ok: false,
|
|
50
|
+
code: "CAPABILITY_MISSING",
|
|
51
|
+
subject: "future-required.v1",
|
|
52
|
+
});
|
|
53
|
+
expect(
|
|
54
|
+
negotiateAgentRuntimeCompatibilityV1(fixture, {
|
|
55
|
+
...localAgentRuntimeCompatibilityRequirementsV1(),
|
|
56
|
+
requiredToolCallbackAuthentication: "signed-callback-v1",
|
|
57
|
+
}),
|
|
58
|
+
).toEqual({
|
|
59
|
+
ok: false,
|
|
60
|
+
code: "CALLBACK_AUTH_UNSUPPORTED",
|
|
61
|
+
subject: "signed-callback-v1",
|
|
62
|
+
});
|
|
63
|
+
expect(
|
|
64
|
+
negotiateAgentRuntimeCompatibilityV1(fixture, {
|
|
65
|
+
...localAgentRuntimeCompatibilityRequirementsV1(),
|
|
66
|
+
protocolVersions: [2],
|
|
67
|
+
}),
|
|
68
|
+
).toEqual({ ok: false, code: "CONTRACT_UNSUPPORTED" });
|
|
69
|
+
expect(
|
|
70
|
+
negotiateAgentRuntimeCompatibilityV1(fixture, {
|
|
71
|
+
...localAgentRuntimeCompatibilityRequirementsV1(),
|
|
72
|
+
expectedReleaseDigest: `sha256:${"0".repeat(64)}`,
|
|
73
|
+
}),
|
|
74
|
+
).toEqual({ ok: false, code: "RELEASE_DIGEST_MISMATCH" });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("任一受摘要保护字段漂移都不能沿用旧 Offer", () => {
|
|
78
|
+
expect(
|
|
79
|
+
parseAgentRuntimeCompatibilityOfferV1({
|
|
80
|
+
...fixture,
|
|
81
|
+
protocolFeatures: [...fixture.protocolFeatures, "tampered.v1"],
|
|
82
|
+
}),
|
|
83
|
+
).toBeNull();
|
|
84
|
+
expect(
|
|
85
|
+
parseAgentRuntimeCompatibilityOfferV1({
|
|
86
|
+
...fixture,
|
|
87
|
+
engineId: "windy.other-engine.v1",
|
|
88
|
+
}),
|
|
89
|
+
).toBeNull();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("重复或超界集合与非稳定 identity 被拒绝", () => {
|
|
93
|
+
expect(
|
|
94
|
+
parseAgentRuntimeCompatibilityOfferV1({
|
|
95
|
+
...fixture,
|
|
96
|
+
protocolVersions: [1, 1],
|
|
97
|
+
}),
|
|
98
|
+
).toBeNull();
|
|
99
|
+
expect(
|
|
100
|
+
parseAgentRuntimeCompatibilityOfferV1({
|
|
101
|
+
...fixture,
|
|
102
|
+
protocolFeatures: Array.from(
|
|
103
|
+
{ length: 65 },
|
|
104
|
+
(_, index) => `feature-${index}.v1`,
|
|
105
|
+
),
|
|
106
|
+
}),
|
|
107
|
+
).toBeNull();
|
|
108
|
+
expect(() =>
|
|
109
|
+
createAgentRuntimeCompatibilityOfferV1("https://provider.invalid"),
|
|
110
|
+
).toThrow("engineId invalid");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("恶意 getter 或循环对象只形成安全的契约拒绝", () => {
|
|
114
|
+
const throwing = Object.defineProperty({}, "schemaVersion", {
|
|
115
|
+
get() {
|
|
116
|
+
throw new Error("secret getter body");
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
const cyclic: Record<string, unknown> = { ...fixture };
|
|
120
|
+
cyclic.toolCallback = cyclic;
|
|
121
|
+
|
|
122
|
+
expect(parseAgentRuntimeCompatibilityOfferV1(throwing)).toBeNull();
|
|
123
|
+
expect(parseAgentRuntimeCompatibilityOfferV1(cyclic)).toBeNull();
|
|
124
|
+
expect(
|
|
125
|
+
negotiateAgentRuntimeCompatibilityV1(
|
|
126
|
+
throwing,
|
|
127
|
+
localAgentRuntimeCompatibilityRequirementsV1(),
|
|
128
|
+
),
|
|
129
|
+
).toEqual({ ok: false, code: "CONTRACT_INVALID" });
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
async function loadFixture(): Promise<AgentRuntimeCompatibilityOfferV1> {
|
|
134
|
+
return JSON.parse(
|
|
135
|
+
await readFile(
|
|
136
|
+
new URL(
|
|
137
|
+
"../contracts/agent-runtime-compatibility/v1/offer.json",
|
|
138
|
+
import.meta.url,
|
|
139
|
+
),
|
|
140
|
+
"utf8",
|
|
141
|
+
),
|
|
142
|
+
) as AgentRuntimeCompatibilityOfferV1;
|
|
143
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
createAgentRuntimeCompatibilityOfferV1,
|
|
4
|
+
localAgentRuntimeCompatibilityRequirementsV1,
|
|
5
|
+
negotiateAgentRuntimeCompatibilityV1,
|
|
6
|
+
} from "../index.js";
|
|
7
|
+
|
|
8
|
+
test("@southwind-ai/server-sdk 根入口公开 Remote Agent Runtime V1 协商", () => {
|
|
9
|
+
const offer = createAgentRuntimeCompatibilityOfferV1(
|
|
10
|
+
"windy.direct.openai-compatible.v1",
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
expect(
|
|
14
|
+
negotiateAgentRuntimeCompatibilityV1(
|
|
15
|
+
offer,
|
|
16
|
+
localAgentRuntimeCompatibilityRequirementsV1(),
|
|
17
|
+
),
|
|
18
|
+
).toMatchObject({ ok: true, protocolVersion: 1 });
|
|
19
|
+
});
|