@agentunion/fastaun-browser 0.4.5 → 0.4.6
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/CHANGELOG.md +28 -0
- package/_packed_docs/CHANGELOG.md +28 -0
- package/_packed_docs/INDEX.md +2 -2
- package/_packed_docs/KITE_DOCS_GUIDE.md +1 -1
- package/_packed_docs/agent.md//350/277/234/347/250/213agent.md/347/274/223/345/255/230/344/270/216etag/351/200/217/344/274/240/346/226/271/346/241/210.md +73 -84
- package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +15 -14
- package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +2 -2
- package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +22 -5
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +42 -26
- package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +1 -1
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +61 -35
- package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +3 -3
- package/_packed_docs/sdk/09-message-rpc-manual.md +6 -6
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +6 -4
- package/_packed_docs/sdk/INDEX.md +2 -2
- package/_packed_docs/sdk/README.md +3 -3
- package/dist/agent-md.d.ts +111 -0
- package/dist/agent-md.d.ts.map +1 -0
- package/dist/agent-md.js +656 -0
- package/dist/agent-md.js.map +1 -0
- package/dist/aid-store.d.ts +7 -40
- package/dist/aid-store.d.ts.map +1 -1
- package/dist/aid-store.js +71 -171
- package/dist/aid-store.js.map +1 -1
- package/dist/auth.js +1 -1
- package/dist/auth.js.map +1 -1
- package/dist/bundle.js +4224 -4151
- package/dist/client.d.ts +3 -61
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +136 -703
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/keystore/index.d.ts +6 -2
- package/dist/keystore/index.d.ts.map +1 -1
- package/dist/keystore/indexeddb-identity-store.d.ts +59 -0
- package/dist/keystore/indexeddb-identity-store.d.ts.map +1 -0
- package/dist/keystore/indexeddb-identity-store.js +489 -0
- package/dist/keystore/indexeddb-identity-store.js.map +1 -0
- package/dist/keystore/indexeddb-shared.d.ts +76 -0
- package/dist/keystore/indexeddb-shared.d.ts.map +1 -0
- package/dist/keystore/indexeddb-shared.js +382 -0
- package/dist/keystore/indexeddb-shared.js.map +1 -0
- package/dist/keystore/indexeddb-token-store.d.ts +119 -0
- package/dist/keystore/indexeddb-token-store.d.ts.map +1 -0
- package/dist/keystore/indexeddb-token-store.js +1086 -0
- package/dist/keystore/indexeddb-token-store.js.map +1 -0
- package/dist/register-flow.d.ts +22 -3
- package/dist/register-flow.d.ts.map +1 -1
- package/dist/register-flow.js +49 -3
- package/dist/register-flow.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,34 @@
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## 0.4.6 — 2026-06-01
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **IndexedDB 存储拆分**:将单一 `IndexedDBKeyStore` 拆分为两个专职类:
|
|
13
|
+
- `IndexedDBIdentityStore`(`keystore/indexeddb-identity-store.ts`):负责私钥、密钥对、完整身份、pending 注册、证书、metadata KV。
|
|
14
|
+
- `IndexedDBTokenStore`(`keystore/indexeddb-token-store.ts`):负责 prekeys、群组密钥、sessions、seq 跟踪、agent.md 缓存、群组状态、信任根。
|
|
15
|
+
- 共享基础设施提取到 `keystore/indexeddb-shared.ts`。
|
|
16
|
+
- **`AgentMdManager` 类**(`agent-md.ts`):独立的 agent.md 管理器,支持 ETag/Last-Modified 缓存验证、TTL 策略、内容签名验证、信任根存储(虚拟路径 `indexeddb://trust-roots/...`)。
|
|
17
|
+
- **`KeyStore` 接口新增方法**:`loadCert(aid, certFingerprint?)` / `saveCert(aid, certPem, certFingerprint?, opts?)`。
|
|
18
|
+
- **`RegisterFlow` 新增公开方法**:`validateAidName`、`fetchPeerCert`、`shortRpc`、`generateIdentity`、`newClientNonce`、`verifyPhase1Response`。
|
|
19
|
+
- **`AIDStore` 新增方法**:`downloadAgentMd`(替代 `fetchAgentMd`)、`checkAgentMd`(替代 `headAgentMd`)。
|
|
20
|
+
- **`AUNClient.uploadAgentMd()`**:新增方法,签名并上传当前 AID 的 agent.md。
|
|
21
|
+
- **IndexedDB 新增对象仓库**:`agent_md_cache`、`group_state`、`pending_identities`、`e2ee_sessions`;数据库版本升级至 v7。
|
|
22
|
+
- **导出新增**:`IndexedDBIdentityStore`、`IndexedDBTokenStore`、`TokenStore` 类型。
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
- **`AUNClient` 架构**:移除内部 agent.md 缓存字段(`_agentMdCache`、`_agentMdFetchInflight` 等),改由 `AgentMdManager` 统一管理。
|
|
26
|
+
- **`AIDStore`**:使用 `IndexedDBIdentityStore` 替代 `IndexedDBKeyStore`;移除内部 `AuthFlow` 实例,改为按需创建。
|
|
27
|
+
- **`AIDStore.fetchAgentMd()`** 重命名为 `downloadAgentMd()`,返回类型同步更新为 `DownloadAgentMdResult`。
|
|
28
|
+
- **私钥加密**:默认启用 AES-256-GCM + PBKDF2(100,000 迭代);支持 `changeSeed()` 密码迁移。
|
|
29
|
+
|
|
30
|
+
### Removed
|
|
31
|
+
- **`IndexedDBKeyStore` 类**(`keystore/indexeddb.ts`,2280 行):完整移除,功能分解为 `IndexedDBIdentityStore` 和 `IndexedDBTokenStore`。
|
|
32
|
+
- **`FullKeyStore` 类型别名**:不再需要。
|
|
33
|
+
- **`AIDStore.headAgentMd()`**:功能整合到 `AgentMdManager.check()`。
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
9
37
|
## 0.4.5 — 2026-05-31
|
|
10
38
|
|
|
11
39
|
### Added
|
|
@@ -6,6 +6,34 @@
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## 0.4.6 — 2026-06-01
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **IndexedDB 存储拆分**:将单一 `IndexedDBKeyStore` 拆分为两个专职类:
|
|
13
|
+
- `IndexedDBIdentityStore`(`keystore/indexeddb-identity-store.ts`):负责私钥、密钥对、完整身份、pending 注册、证书、metadata KV。
|
|
14
|
+
- `IndexedDBTokenStore`(`keystore/indexeddb-token-store.ts`):负责 prekeys、群组密钥、sessions、seq 跟踪、agent.md 缓存、群组状态、信任根。
|
|
15
|
+
- 共享基础设施提取到 `keystore/indexeddb-shared.ts`。
|
|
16
|
+
- **`AgentMdManager` 类**(`agent-md.ts`):独立的 agent.md 管理器,支持 ETag/Last-Modified 缓存验证、TTL 策略、内容签名验证、信任根存储(虚拟路径 `indexeddb://trust-roots/...`)。
|
|
17
|
+
- **`KeyStore` 接口新增方法**:`loadCert(aid, certFingerprint?)` / `saveCert(aid, certPem, certFingerprint?, opts?)`。
|
|
18
|
+
- **`RegisterFlow` 新增公开方法**:`validateAidName`、`fetchPeerCert`、`shortRpc`、`generateIdentity`、`newClientNonce`、`verifyPhase1Response`。
|
|
19
|
+
- **`AIDStore` 新增方法**:`downloadAgentMd`(替代 `fetchAgentMd`)、`checkAgentMd`(替代 `headAgentMd`)。
|
|
20
|
+
- **`AUNClient.uploadAgentMd()`**:新增方法,签名并上传当前 AID 的 agent.md。
|
|
21
|
+
- **IndexedDB 新增对象仓库**:`agent_md_cache`、`group_state`、`pending_identities`、`e2ee_sessions`;数据库版本升级至 v7。
|
|
22
|
+
- **导出新增**:`IndexedDBIdentityStore`、`IndexedDBTokenStore`、`TokenStore` 类型。
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
- **`AUNClient` 架构**:移除内部 agent.md 缓存字段(`_agentMdCache`、`_agentMdFetchInflight` 等),改由 `AgentMdManager` 统一管理。
|
|
26
|
+
- **`AIDStore`**:使用 `IndexedDBIdentityStore` 替代 `IndexedDBKeyStore`;移除内部 `AuthFlow` 实例,改为按需创建。
|
|
27
|
+
- **`AIDStore.fetchAgentMd()`** 重命名为 `downloadAgentMd()`,返回类型同步更新为 `DownloadAgentMdResult`。
|
|
28
|
+
- **私钥加密**:默认启用 AES-256-GCM + PBKDF2(100,000 迭代);支持 `changeSeed()` 密码迁移。
|
|
29
|
+
|
|
30
|
+
### Removed
|
|
31
|
+
- **`IndexedDBKeyStore` 类**(`keystore/indexeddb.ts`,2280 行):完整移除,功能分解为 `IndexedDBIdentityStore` 和 `IndexedDBTokenStore`。
|
|
32
|
+
- **`FullKeyStore` 类型别名**:不再需要。
|
|
33
|
+
- **`AIDStore.headAgentMd()`**:功能整合到 `AgentMdManager.check()`。
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
9
37
|
## 0.4.5 — 2026-05-31
|
|
10
38
|
|
|
11
39
|
### Added
|
package/_packed_docs/INDEX.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
| [跨语言容器E2E测试方案](design/跨语言容器E2E测试方案.md) | 多语言 SDK 同网同服、test-runner 控制面的跨语言 E2E 方案 |
|
|
14
14
|
| [E2EE V2 简化为 1DH + Per-AID Wrap 方案](design/E2EE_V2简化为1DH加Per-AID_Wrap方案.md) | SDK bootstrap 能力声明 + 服务端 policy 控制 1DH/per-AID wrap 的兼容方案 |
|
|
15
15
|
| [AUN RPC Trace 增强设计](design/2026-05-22-aun-rpc-trace-enhancement.md) | RPC trace 诊断字段与 enter/exit span 设计 |
|
|
16
|
-
| [远程 agent.md 缓存与 ETag 透传方案](agent.md/远程agent.md缓存与etag透传方案.md) | 远程 agent.md per-AID
|
|
16
|
+
| [远程 agent.md 缓存与 ETag 透传方案](agent.md/远程agent.md缓存与etag透传方案.md) | 远程 agent.md per-AID 本地文件/IndexedDB 缓存、消息信封与 RPC 响应 ETag 透传方案 |
|
|
17
17
|
| [SDK 文档索引](sdk/INDEX.md) | SDK 使用手册、RPC 手册、E2EE 手册的子索引 |
|
|
18
18
|
| [SDK 查阅指南](sdk/AUN_DOCS_GUIDE.md) | SDK 文档按行区间渐进式查阅方法 |
|
|
19
19
|
| [协议文档目录](protocol/) | AUN 协议相关文档 |
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
|
|
77
77
|
### 远程 agent.md 缓存与 ETag 透传方案
|
|
78
78
|
|
|
79
|
-
定义每个远程 AID 在 SDK
|
|
79
|
+
定义每个远程 AID 在 SDK 内存和本地持久化记录中维护一条 agent.md 状态:Python / TypeScript / Go 使用 `{aun_path}/AIDs/{aid}/agent.md` 与 `agentmd.json`,浏览器 JavaScript 使用 IndexedDB logical key。方案同时规定 `message.send` 响应向发送端透传 `to` 的 agent.md ETag,消息信封向接收端透传 `from` 的 agent.md ETag,并给出按需下载、无条件 GET、304 兼容、竞态和跨 SDK 一致性规则。
|
|
80
80
|
|
|
81
81
|
### SDK 文档索引
|
|
82
82
|
|
|
@@ -52,5 +52,5 @@ AUN SDK Core 文档在 `docs/` 下。根级索引为 `docs/INDEX.md`,SDK API
|
|
|
52
52
|
| agent.md 远程缓存目标与字段 | `docs/agent.md/远程agent.md缓存与etag透传方案.md` L5-86 |
|
|
53
53
|
| agent.md ETag 透传时序图 | `docs/agent.md/远程agent.md缓存与etag透传方案.md` L88-185 |
|
|
54
54
|
| agent.md 服务端与 SDK 实现流程 | `docs/agent.md/远程agent.md缓存与etag透传方案.md` L187-268 |
|
|
55
|
-
| agent.md
|
|
55
|
+
| agent.md 本地持久化、竞态和测试点 | `docs/agent.md/远程agent.md缓存与etag透传方案.md` L269-317 |
|
|
56
56
|
| SDK API、RPC、E2EE 使用细节 | `docs/sdk/AUN_DOCS_GUIDE.md` |
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
核心目标:
|
|
10
10
|
|
|
11
|
-
- 每个远程 AID 在 SDK
|
|
12
|
-
-
|
|
11
|
+
- 每个远程 AID 在 SDK 本地维护一条 `agent.md` 记录,包含 `remote_etag`、`local_etag`、`content`、`last_modified` 等字段。
|
|
12
|
+
- Python / TypeScript / Go 将正文和元数据持久化到 `{aun_path}/AIDs/{aid}/agent.md` 与 `agentmd.json`;浏览器 JavaScript 使用 IndexedDB 等价 key,存储不可用时退化为内存缓存。
|
|
13
13
|
- `message.send` 的 RPC 响应携带接收方 `agent.md` ETag,让发送方更新 `to` 的云端版本。
|
|
14
14
|
- 接收端收到消息信封时携带发送方 `agent.md` ETag,让接收方更新 `from` 的云端版本。
|
|
15
15
|
- ETag 只作为版本提示,不替代 `agent.md` 内容下载和验签。
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
1. `message.send` / V2 P2P send:把 `from` 的 `agent.md` ETag 注入消息信封,随消息到达接收端。
|
|
22
22
|
2. `message.send` RPC response:把 `to` 的 `agent.md` ETag 注入响应 `_meta`,返回发送端。
|
|
23
23
|
|
|
24
|
-
注意:服务端注入的 ETag 只能代表云端版本。SDK 本地必须区分“观察到的远端云端 ETag”和“当前本地内容对应的 ETag”。字段命名固定为 `remote_etag` 和 `local_etag`,其中 `remote_etag` 表示远端云端版本,`local_etag` 表示本地 `content`
|
|
24
|
+
注意:服务端注入的 ETag 只能代表云端版本。SDK 本地必须区分“观察到的远端云端 ETag”和“当前本地内容对应的 ETag”。字段命名固定为 `remote_etag` 和 `local_etag`,其中 `remote_etag` 表示远端云端版本,`local_etag` 表示本地 `content` 对应版本。下载必须始终使用无条件 GET,不能把 `remote_etag` 或 `local_etag` 放进 `If-None-Match` / `If-Modified-Since`,否则会把版本提示误用成 HTTP 缓存状态。
|
|
25
25
|
|
|
26
26
|
## 字段建议
|
|
27
27
|
|
|
@@ -63,26 +63,29 @@
|
|
|
63
63
|
|
|
64
64
|
## SDK 缓存模型
|
|
65
65
|
|
|
66
|
-
每个远程 AID
|
|
66
|
+
每个远程 AID 在内存和本地持久化记录中都维护一条 agent.md 状态。Python / TypeScript / Go 的持久化记录是 `{aun_path}/AIDs/{aid}/agentmd.json`;浏览器 JavaScript 是 IndexedDB 中的 `{aid}/agentmd.json` logical key。SDK 启动或内存 miss 时按需加载该记录。
|
|
67
67
|
|
|
68
68
|
| 字段 | 含义 |
|
|
69
69
|
| --- | --- |
|
|
70
70
|
| `aid` | 远程 AID |
|
|
71
|
-
| `content` | 本地缓存的完整 `agent.md` 内容,可为空 |
|
|
72
|
-
| `local_etag` | 当前 `content` 对应的 ETag
|
|
73
|
-
| `remote_etag` | 从消息信封或 RPC `_meta` 观察到的远端云端 ETag |
|
|
74
|
-
| `last_modified` | GET 响应的 `Last-Modified` |
|
|
75
|
-
| `fetched_at` | 最近一次成功确认内容的本机时间 |
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
71
|
+
| `content` | 本地缓存的完整 `agent.md` 内容,可为空 |
|
|
72
|
+
| `local_etag` | 当前 `content` 对应的 ETag,由成功 GET 200 内容确认;304 复用本地内容时沿用原值 |
|
|
73
|
+
| `remote_etag` | 从消息信封或 RPC `_meta` 观察到的远端云端 ETag |
|
|
74
|
+
| `last_modified` | GET 响应的 `Last-Modified` |
|
|
75
|
+
| `fetched_at` | 最近一次成功确认内容的本机时间 |
|
|
76
|
+
| `checked_at` | 最近一次 HEAD / GET 确认远端状态的本机时间 |
|
|
77
|
+
| `observed_at` | 最近一次观察到远端 ETag 的本机时间 |
|
|
78
|
+
| `remote_status` | `found` / `missing` / `error` |
|
|
79
|
+
| `verify_status` | 最近一次 agent.md 验签结果:`ok` / `unsigned` / `invalid` 等 |
|
|
80
|
+
| `verify_error` | 最近一次验签失败原因 |
|
|
81
|
+
| `last_error` | 最近一次网络或 HTTP 错误 |
|
|
79
82
|
|
|
80
83
|
状态规则:
|
|
81
84
|
|
|
82
85
|
- 收到远端 ETag 时,只更新 `remote_etag` 和 `observed_at`。
|
|
83
86
|
- 只有下载到内容并完成验签后,才能更新 `content`、`local_etag`、`last_modified`、`fetched_at`。
|
|
84
|
-
- `remote_etag == local_etag`
|
|
85
|
-
- `remote_etag != local_etag` 或 `content`
|
|
87
|
+
- `remote_etag == local_etag` 时视为同步。
|
|
88
|
+
- `remote_etag != local_etag` 或 `content` 为空时视为需要更新;该状态可由字段推导,不要求单独存储 `stale` 字段。
|
|
86
89
|
- `verify_status=invalid` 时内容可以缓存但应用层应能看到无效状态;是否拒绝展示由上层策略决定。
|
|
87
90
|
|
|
88
91
|
## 时序图
|
|
@@ -107,7 +110,7 @@ sequenceDiagram
|
|
|
107
110
|
GW->>NS: HEAD https://B/agent.md<br/>取 to ETag(缓存命中则不请求)
|
|
108
111
|
NS-->>GW: ETag(B)
|
|
109
112
|
GW-->>A: RPC response + _meta.agent_md_etags.to
|
|
110
|
-
A->>A: observeRemoteAgentMdEtag(B, etag)<br/>更新内存 +
|
|
113
|
+
A->>A: observeRemoteAgentMdEtag(B, etag)<br/>更新内存 + agentmd.json/IndexedDB remote_etag
|
|
111
114
|
```
|
|
112
115
|
|
|
113
116
|
### 接收消息时,接收端获得 from 的 agent.md ETag
|
|
@@ -127,11 +130,11 @@ sequenceDiagram
|
|
|
127
130
|
alt 本地 local_etag == remote_etag
|
|
128
131
|
Cache-->>B: 只刷新 observed_at,不下载
|
|
129
132
|
else 本地无内容或 ETag 不一致
|
|
130
|
-
Cache->>Cache:
|
|
131
|
-
B->>NS: GET https://A/agent.md<br
|
|
132
|
-
NS-->>B: 200 content + ETag
|
|
133
|
+
Cache->>Cache: 标记需要更新,按需或后台拉取
|
|
134
|
+
B->>NS: GET https://A/agent.md<br/>不带条件请求头
|
|
135
|
+
NS-->>B: 200 content + ETag,或异常 304/404/error
|
|
133
136
|
B->>B: verify_agent_md(content, aid=A)
|
|
134
|
-
B->>Cache: 写内存 +
|
|
137
|
+
B->>Cache: 写内存 + agentmd.json/IndexedDB
|
|
135
138
|
end
|
|
136
139
|
```
|
|
137
140
|
|
|
@@ -142,25 +145,25 @@ sequenceDiagram
|
|
|
142
145
|
participant App
|
|
143
146
|
participant SDK
|
|
144
147
|
participant Mem as Memory Cache
|
|
145
|
-
participant
|
|
148
|
+
participant Store as agentmd.json / IndexedDB
|
|
146
149
|
participant NS as NameService
|
|
147
150
|
|
|
148
|
-
App->>SDK:
|
|
151
|
+
App->>SDK: downloadAgentMd(A)
|
|
149
152
|
SDK->>Mem: 查 A
|
|
150
153
|
alt 内存有可用 content
|
|
151
154
|
Mem-->>SDK: content, local_etag
|
|
152
155
|
else 内存缺失
|
|
153
|
-
SDK->>
|
|
154
|
-
|
|
156
|
+
SDK->>Store: load {aid}/agentmd.json
|
|
157
|
+
Store-->>SDK: content/remote_etag/local_etag/last_modified
|
|
155
158
|
SDK->>Mem: 回填内存
|
|
156
159
|
end
|
|
157
160
|
|
|
158
161
|
alt 内容缺失或 remote_etag != local_etag
|
|
159
|
-
SDK->>NS: GET /agent.md<br
|
|
160
|
-
NS-->>SDK: 200/304/404/error
|
|
161
|
-
SDK->>SDK: 200 时验签;304
|
|
162
|
+
SDK->>NS: GET /agent.md<br/>不带条件请求头
|
|
163
|
+
NS-->>SDK: 200/304/404/error
|
|
164
|
+
SDK->>SDK: 200 时验签;304 时有本地 content 则复用,无 content 则再无条件 GET 一次
|
|
162
165
|
SDK->>Mem: upsert
|
|
163
|
-
SDK->>
|
|
166
|
+
SDK->>Store: upsert agent.md + agentmd.json
|
|
164
167
|
end
|
|
165
168
|
|
|
166
169
|
SDK-->>App: agent.md content + signature + sync 状态
|
|
@@ -251,67 +254,53 @@ observe_remote_agent_md_etag(aid, etag, source)
|
|
|
251
254
|
|
|
252
255
|
- aid 或 etag 为空时忽略。
|
|
253
256
|
- etag 与当前 `remote_etag` 相同:只刷新 `observed_at`。
|
|
254
|
-
- etag 变化:更新 `remote_etag`、`observed_at`,并根据 `local_etag`
|
|
255
|
-
- 变更需要同时写入内存和
|
|
257
|
+
- etag 变化:更新 `remote_etag`、`observed_at`,并根据 `local_etag` 推导是否需要更新。
|
|
258
|
+
- 变更需要同时写入内存和 agentmd.json / IndexedDB。
|
|
256
259
|
|
|
257
260
|
### 按需下载
|
|
258
261
|
|
|
259
|
-
当应用调用 `
|
|
260
|
-
|
|
261
|
-
-
|
|
262
|
-
-
|
|
263
|
-
-
|
|
264
|
-
-
|
|
265
|
-
-
|
|
266
|
-
-
|
|
267
|
-
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
|
275
|
-
|
|
|
276
|
-
| `aid` |
|
|
277
|
-
| `
|
|
278
|
-
| `
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
|
283
|
-
|
|
|
284
|
-
| `
|
|
285
|
-
| `
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
remote_etag TEXT NOT NULL DEFAULT '',
|
|
295
|
-
last_modified TEXT NOT NULL DEFAULT '',
|
|
296
|
-
fetched_at INTEGER NOT NULL DEFAULT 0,
|
|
297
|
-
observed_at INTEGER NOT NULL DEFAULT 0,
|
|
298
|
-
verify_status TEXT NOT NULL DEFAULT '',
|
|
299
|
-
verify_error TEXT NOT NULL DEFAULT '',
|
|
300
|
-
updated_at INTEGER NOT NULL DEFAULT 0
|
|
301
|
-
);
|
|
302
|
-
|
|
303
|
-
CREATE INDEX IF NOT EXISTS idx_remote_agent_md_cache_observed_at
|
|
304
|
-
ON remote_agent_md_cache(observed_at);
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
五个 SDK 应保持字段语义一致。浏览器 JS 可落 IndexedDB;Node TS、Python、Go、C++ 落各自现有本地存储。
|
|
262
|
+
当应用调用 `downloadAgentMd(aid)` 或 SDK 需要展示远程 agent 信息时:
|
|
263
|
+
|
|
264
|
+
- 可先查内存,miss 时查 agentmd.json / IndexedDB,用于展示本地已有状态。
|
|
265
|
+
- `downloadAgentMd(aid)` 的远端下载请求一律发起无条件 GET,不用本地 ETag 决定请求头。
|
|
266
|
+
- GET 请求不得发送 `If-None-Match` / `If-Modified-Since`。
|
|
267
|
+
- 200:验签,更新内容和 `local_etag`。
|
|
268
|
+
- 304:本地已有 content 时复用该 content;本地没有 content 时再发起一次无条件 GET。第二次仍非 2xx 时按错误返回。
|
|
269
|
+
- 404:标记远端未发布 `agent.md`,不要删除已有内容,除非产品要求严格同步。
|
|
270
|
+
- 网络错误:保留旧内容,记录 `fetch_error` 或更新失败时间。
|
|
271
|
+
|
|
272
|
+
### 本地文件 / 浏览器持久化
|
|
273
|
+
|
|
274
|
+
agent.md 不写入 SQLite。当前 SDK 使用以下持久化位置:
|
|
275
|
+
|
|
276
|
+
| SDK | 正文 | 元数据 |
|
|
277
|
+
| --- | --- | --- |
|
|
278
|
+
| Python | `{aun_path}/AIDs/{aid}/agent.md` | `{aun_path}/AIDs/{aid}/agentmd.json` |
|
|
279
|
+
| TypeScript / Node | `{aun_path}/AIDs/{aid}/agent.md` | `{aun_path}/AIDs/{aid}/agentmd.json` |
|
|
280
|
+
| Go | `{aun_path}/AIDs/{aid}/agent.md` | `{aun_path}/AIDs/{aid}/agentmd.json` |
|
|
281
|
+
| JavaScript / 浏览器 | IndexedDB logical key `{root}/{aid}/agent.md` | IndexedDB logical key `{root}/{aid}/agentmd.json` |
|
|
282
|
+
|
|
283
|
+
`agentmd.json` 至少承载以下语义字段:
|
|
284
|
+
|
|
285
|
+
| 字段 | 说明 |
|
|
286
|
+
| --- | --- |
|
|
287
|
+
| `aid` | AID |
|
|
288
|
+
| `content` | 正文副本;文件系统 SDK 也会单独写 `agent.md` |
|
|
289
|
+
| `local_etag` / `remote_etag` | 本地内容版本 / 远端观察版本 |
|
|
290
|
+
| `last_modified` | 远端 Last-Modified |
|
|
291
|
+
| `fetched_at` / `checked_at` / `observed_at` / `updated_at` | 本机时间戳 |
|
|
292
|
+
| `remote_status` | `found` / `missing` / `error` |
|
|
293
|
+
| `verify_status` / `verify_error` | 最近一次验签状态 |
|
|
294
|
+
| `last_error` | 最近一次错误 |
|
|
295
|
+
|
|
296
|
+
四个已实现 SDK 应保持字段语义一致。旧 `agent_md_cache` / `remote_agent_md_cache` SQLite 表不再作为 agent.md 缓存来源;迁移逻辑可清理旧表,但不得把新 agent.md 写回 SQLite。
|
|
308
297
|
|
|
309
298
|
## 异常与竞态处理
|
|
310
299
|
|
|
311
300
|
- 多个消息同时观察同一 AID 的新 ETag:按 ETag 值幂等 upsert。
|
|
312
301
|
- 多个协程同时触发同一 AID 下载:需要 per-AID in-flight 去重。
|
|
313
|
-
- 观察到 ETag A 后开始下载,期间又观察到 ETag B:下载完成时只更新 `local_etag=A
|
|
314
|
-
- 304 但本地 content
|
|
302
|
+
- 观察到 ETag A 后开始下载,期间又观察到 ETag B:下载完成时只更新 `local_etag=A`,随后仍可由 `remote_etag != local_etag` 推导为需要更新,下一轮继续拉 B。
|
|
303
|
+
- 304 但本地 content 缺失:不能返回空内容,必须再无条件 GET 一次。
|
|
315
304
|
- 信封里的 ETag 不参与 AAD,不作为安全声明;安全性仍依赖 `agent.md` 签名和证书校验。
|
|
316
305
|
- HEAD/GET 超时不影响 message send 和 message pull。
|
|
317
306
|
- 跨域场景中,目标域 Message Service 注入 sender ETag 时可能需要跨域 HEAD;失败时允许缺字段。
|
|
@@ -320,9 +309,9 @@ ON remote_agent_md_cache(observed_at);
|
|
|
320
309
|
|
|
321
310
|
- 发送方收到 `message.send` 响应后,能把 `to` 的 ETag 写入本地缓存 `remote_etag`。
|
|
322
311
|
- 接收方 `message.v2.pull` 后,能从 `envelope.agent_md.sender` 写入 `from` 的 `remote_etag`。
|
|
323
|
-
- ETag
|
|
324
|
-
-
|
|
325
|
-
- 304 且本地有内容时复用内容;304
|
|
312
|
+
- ETag 变化但内容未下载时,可由 `remote_etag != local_etag` 推导为需要更新。
|
|
313
|
+
- 本地文件 / IndexedDB 有缓存、内存为空时,SDK 能按需加载。
|
|
314
|
+
- 304 且本地有内容时复用内容;304 但本地无内容时再无条件 GET 一次。
|
|
326
315
|
- `agent.md` 上传后,Gateway 缓存失效,后续消息能看到新 ETag。
|
|
327
316
|
- HEAD/GET 404、超时、网络错误不影响消息收发主链路。
|
|
328
|
-
- Python / TS / JS / Go
|
|
317
|
+
- Python / TS / JS / Go 四个 SDK 对 `remote_etag`、`local_etag`、`content`、`remote_status`、`verify_status` 语义一致。
|
|
@@ -26,7 +26,7 @@ pip install fastaun
|
|
|
26
26
|
|
|
27
27
|
- `AUNClient()`:无身份客户端,状态为 `no_identity`,随后用 `load_identity(AID对象)` 加载身份。
|
|
28
28
|
- `AUNClient(aid)`:带身份客户端,`aid` 必须是 `AIDStore.load()` 返回的 AID 对象,状态为 `standby`。
|
|
29
|
-
- TS/JS/Go
|
|
29
|
+
- TS/JS/Go 同构:只允许无参或 `AID` 对象;字符串 AID、把 aid 放进 options、旧 `(config, debug)` / `(aid, options)` 都不是公开入口。
|
|
30
30
|
|
|
31
31
|
---
|
|
32
32
|
|
|
@@ -67,7 +67,7 @@ async def load_or_register(store: AIDStore, aid: str):
|
|
|
67
67
|
async def create_client(aid: str) -> AUNClient:
|
|
68
68
|
store = AIDStore(aun_path="~/.aun/myapp", encryption_seed="")
|
|
69
69
|
identity = await load_or_register(store, aid)
|
|
70
|
-
client = AUNClient(identity
|
|
70
|
+
client = AUNClient(identity)
|
|
71
71
|
await client.connect({"slot_id": "main", "auto_reconnect": True})
|
|
72
72
|
return client
|
|
73
73
|
|
|
@@ -116,13 +116,14 @@ store = AIDStore(
|
|
|
116
116
|
)
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
-
`AUNClient`
|
|
119
|
+
`AUNClient` 只接收可选 AID 对象。调试、TLS、根证书和默认 slot 等配置由 `AIDStore` 注入到 AID 对象,再由 client 继承;`protected_headers` 通过 setter 配置:
|
|
120
120
|
|
|
121
121
|
```python
|
|
122
|
-
identity = store.load("alice.agentid.pub")["data"]["aid"]
|
|
123
|
-
client = AUNClient(identity
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
identity = store.load("alice.agentid.pub")["data"]["aid"]
|
|
123
|
+
client = AUNClient(identity)
|
|
124
|
+
client.set_protected_headers({"sdk": "python"})
|
|
125
|
+
await client.connect({"slot_id": "main", "connection_kind": "long"})
|
|
126
|
+
```
|
|
126
127
|
|
|
127
128
|
`verify_ssl` 在 `AIDStore` 构造时配置,不在 `AUNClient` 中配置。自动模式下 SDK 根据环境变量决定:
|
|
128
129
|
|
|
@@ -165,17 +166,17 @@ SDK 使用 `{aun_path}/AIDs/{aid}/` 存储每个 AID 的专属数据:
|
|
|
165
166
|
TypeScript / Node:
|
|
166
167
|
|
|
167
168
|
```ts
|
|
168
|
-
const store = new AIDStore({ aunPath: "~/.aun/myapp", encryptionSeed: "" });
|
|
169
|
-
const loaded = store.load("alice.agentid.pub");
|
|
170
|
-
const client = new AUNClient(loaded.data!.aid
|
|
169
|
+
const store = new AIDStore({ aunPath: "~/.aun/myapp", encryptionSeed: "" });
|
|
170
|
+
const loaded = store.load("alice.agentid.pub");
|
|
171
|
+
const client = new AUNClient(loaded.data!.aid);
|
|
171
172
|
```
|
|
172
173
|
|
|
173
174
|
JavaScript / 浏览器:
|
|
174
175
|
|
|
175
176
|
```js
|
|
176
|
-
const store = new AIDStore({ aunPath: "browser-demo", encryptionSeed: "" });
|
|
177
|
-
const loaded = await store.load("alice.agentid.pub");
|
|
178
|
-
const client = new AUNClient(loaded.data.aid
|
|
177
|
+
const store = new AIDStore({ aunPath: "browser-demo", encryptionSeed: "" });
|
|
178
|
+
const loaded = await store.load("alice.agentid.pub");
|
|
179
|
+
const client = new AUNClient(loaded.data.aid);
|
|
179
180
|
```
|
|
180
181
|
|
|
181
182
|
Go:
|
|
@@ -195,7 +196,7 @@ client := aun.NewAUNClient(aid.Data.AID)
|
|
|
195
196
|
|
|
196
197
|
1. 创建 `AIDStore`。
|
|
197
198
|
2. `store.load(aid)` 加载本地 AID;本地不存在时调用 `store.register(aid)` 后再次加载。
|
|
198
|
-
3. 用 AID 对象构造 `AUNClient(aid
|
|
199
|
+
3. 用 AID 对象构造 `AUNClient(aid)`,或先 `AUNClient()` 再 `load_identity(aid)`。
|
|
199
200
|
4. 先注册事件处理器,再调用 `connect(options)`。
|
|
200
201
|
5. 通过 `call(method, params)` 执行业务 RPC。
|
|
201
202
|
6. 通过 `close()` 释放连接和后台任务。
|
|
@@ -73,9 +73,9 @@ sequenceDiagram
|
|
|
73
73
|
|
|
74
74
|
- `protocol.min/max` 在 `auth.connect` 阶段完成 Gateway 会话版本协商;详细规则见协议文档 `03-Gateway-连接模式.md`。
|
|
75
75
|
- `device.id` 是设备级稳定标识,Python SDK 默认从 `~/.aun/.device_id` 读取。
|
|
76
|
-
- `client.slot_id` 由应用层显式传入,用于区分同设备上的多个实例槽位。
|
|
76
|
+
- `client.slot_id` 由应用层显式传入,用于区分同设备上的多个实例槽位。SDK 允许 `/`、`:`、空格作为隔离键分隔符,例如 `evolclaw cli`、`evolclaw/cli`、`evolclaw:cli` 的隔离键都是 `evolclaw`。
|
|
77
77
|
- `delivery_mode` 决定该 AID 当前连接的投递语义;同一 AID 的所有在线连接必须保持一致。
|
|
78
|
-
- `options.kind` 声明连接类型:`"long"`(默认)= 长连接,承担服务端推送 / 事件订阅;`"short"` = 短连接,仅用于发送 RPC 并等待响应即断开。同 `(aid, device.id, client.slot_id)`
|
|
78
|
+
- `options.kind` 声明连接类型:`"long"`(默认)= 长连接,承担服务端推送 / 事件订阅;`"short"` = 短连接,仅用于发送 RPC 并等待响应即断开。同 `(aid, device.id, slotIsolationKey(client.slot_id))` 隔离槽下,长连接最多 1 条,短连接最多 10 条;短连接不会顶掉长连接。
|
|
79
79
|
- `options.short_ttl_ms` 仅在 `kind="short"` 时有效,可选;服务端兜底超时后主动关闭短连接,防止占名额。
|
|
80
80
|
- `capabilities` 是客户端能力声明;`hello-ok.result.capabilities` 是服务端能力公告,不是双方能力交集。
|
|
81
81
|
|
|
@@ -36,11 +36,28 @@ assert me.is_private_key_valid()
|
|
|
36
36
|
| `AID` | 身份值对象,负责签名、验签、agent.md 签验 | 否 |
|
|
37
37
|
| `AUNClient` | 会话对象,负责认证、连接、重连、事件和 RPC | 是 |
|
|
38
38
|
|
|
39
|
-
`AUNClient` 不再通过配置字典持有某个字符串 AID;它只接收已加载并校验过私钥的 AID 对象。
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
##
|
|
39
|
+
`AUNClient` 不再通过配置字典持有某个字符串 AID;它只接收已加载并校验过私钥的 AID 对象。
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## device_id 与 slot_id
|
|
44
|
+
|
|
45
|
+
`device_id` 是设备级稳定标识,默认写在 `{aun_path}/.device_id`,用于 token、实例状态、V2 设备密钥和消息游标的一级隔离。它是单段安全 token,只允许字母、数字、`.`、`_`、`-`。
|
|
46
|
+
|
|
47
|
+
`slot_id` 是同一设备下的连接/消费槽位,允许用 `/`、`:`、空格表达共享隔离键。隔离键取第一个分隔符前的部分:
|
|
48
|
+
|
|
49
|
+
| slot_id | slotIsolationKey |
|
|
50
|
+
|---------|------------------|
|
|
51
|
+
| `evolclaw daemon` | `evolclaw` |
|
|
52
|
+
| `evolclaw cli` | `evolclaw` |
|
|
53
|
+
| `evolclaw/netcheck` | `evolclaw` |
|
|
54
|
+
| `evolclaw-daemon` | `evolclaw-daemon` |
|
|
55
|
+
|
|
56
|
+
SDK 在 `message.pull` / `message.ack` 中自动注入当前 `device_id` / `slot_id`,并按隔离键校验显式传入的 `slot_id`。因此共享消费槽位时应使用 `/`、`:` 或空格作为分隔符。
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 连接状态机
|
|
44
61
|
|
|
45
62
|
```mermaid
|
|
46
63
|
stateDiagram-v2
|
|
@@ -16,9 +16,9 @@ if not loaded["ok"]:
|
|
|
16
16
|
raise RuntimeError(registered["error"]["message"])
|
|
17
17
|
loaded = store.load(aid)
|
|
18
18
|
|
|
19
|
-
me = loaded["data"]["aid"]
|
|
20
|
-
client = AUNClient(me
|
|
21
|
-
await client.connect({"slot_id": "main", "auto_reconnect": True})
|
|
19
|
+
me = loaded["data"]["aid"]
|
|
20
|
+
client = AUNClient(me)
|
|
21
|
+
await client.connect({"slot_id": "main", "auto_reconnect": True})
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
关键约束:
|
|
@@ -62,9 +62,9 @@ store = AIDStore(
|
|
|
62
62
|
| `register(aid)` | 注册新 AID,返回 Result |
|
|
63
63
|
| `list()` | 列出本地身份 |
|
|
64
64
|
| `exists(aid)` | HEAD PKI 端点,判断 AID 是否已注册 |
|
|
65
|
-
| `resolve(aid, opts=None)` |
|
|
66
|
-
| `
|
|
67
|
-
| `check_agent_md(aid, ttl_days=1)` | 检查本地缓存与远端 agent.md 状态 |
|
|
65
|
+
| `resolve(aid, opts=None)` | 拉取并缓存对端证书;默认同时下载并验签 agent.md,可传 `skip_agent_md=True` 跳过 |
|
|
66
|
+
| `download_agent_md(aid)` | 下载并验签对端 agent.md |
|
|
67
|
+
| `check_agent_md(aid, ttl_days=1)` | 检查本地缓存与远端 agent.md 状态 |
|
|
68
68
|
| `diagnose(aid)` | 本地和远端一致性诊断 |
|
|
69
69
|
| `renew_cert(aid)` / `rekey(aid)` | 证书续签和换钥,成功后重新 `load()` |
|
|
70
70
|
|
|
@@ -125,7 +125,7 @@ await client.connect({
|
|
|
125
125
|
|
|
126
126
|
| 选项 | 说明 |
|
|
127
127
|
|------|------|
|
|
128
|
-
| `slot_id` |
|
|
128
|
+
| `slot_id` | 同一设备内的实例槽位;允许 `/`、`:`、空格作为共享隔离键分隔符 |
|
|
129
129
|
| `connection_kind` | `"long"` 或 `"short"` |
|
|
130
130
|
| `short_ttl_ms` | 短连接服务端兜底超时 |
|
|
131
131
|
| `delivery_mode` | 连接级投递语义 |
|
|
@@ -141,7 +141,7 @@ await client.connect({
|
|
|
141
141
|
|
|
142
142
|
## 长短连接共存
|
|
143
143
|
|
|
144
|
-
同一 `(aid, device_id, slot_id)`
|
|
144
|
+
同一 `(aid, device_id, slotIsolationKey(slot_id))` 隔离槽下允许 1 条长连接 + 最多 10 条短连接。`slot_id` 的第一个 `/`、`:` 或空格之前的部分是共享隔离键,因此 `evolclaw daemon`、`evolclaw cli`、`evolclaw/netcheck` 会共享 `evolclaw` 隔离槽。
|
|
145
145
|
|
|
146
146
|
长连接:
|
|
147
147
|
|
|
@@ -199,27 +199,43 @@ print(client.can_connect, client.can_send, client.is_online)
|
|
|
199
199
|
|
|
200
200
|
---
|
|
201
201
|
|
|
202
|
-
## Agent Web / agent.md
|
|
203
|
-
|
|
204
|
-
发布自己的 agent.md:
|
|
205
|
-
|
|
206
|
-
```python
|
|
207
|
-
await client.
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
解析对端并验签 agent.md:
|
|
202
|
+
## Agent Web / agent.md
|
|
203
|
+
|
|
204
|
+
发布自己的 agent.md:
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
await client.upload_agent_md(content)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
解析对端并验签 agent.md:
|
|
211
211
|
|
|
212
212
|
```python
|
|
213
213
|
resolved = await store.resolve("bob.agentid.pub")
|
|
214
|
-
if resolved["ok"]:
|
|
215
|
-
print(resolved["data"]["agent_md"]["
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
```python
|
|
221
|
-
|
|
222
|
-
```
|
|
214
|
+
if resolved["ok"]:
|
|
215
|
+
print(resolved["data"]["agent_md"]["verification"]["status"])
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
只下载对端 agent.md:
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
downloaded = await store.download_agent_md("bob.agentid.pub")
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
只检查本地缓存与远端状态:
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
state = await store.check_agent_md("bob.agentid.pub", ttl_days=1)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
当前公开入口分工固定:
|
|
231
|
+
|
|
232
|
+
| 主体 | 公开能力 |
|
|
233
|
+
|------|----------|
|
|
234
|
+
| `AUNClient` | `upload_agent_md(content=None)`:读取/签名/上传当前 AID 的 agent.md |
|
|
235
|
+
| `AIDStore` | `download_agent_md(aid)`、`check_agent_md(aid, ttl_days=1)`、`resolve(aid, opts)` |
|
|
236
|
+
| `AID` | `sign_agent_md(content)`、`verify_agent_md(content)` 低层签验能力 |
|
|
237
|
+
|
|
238
|
+
本地落盘位置由 SDK 管理。Python / TypeScript / Go 写入 `{aun_path}/AIDs/{aid}/agent.md` 和同目录 `agentmd.json`;浏览器 JavaScript 写入 IndexedDB 的等价 logical key,存储不可用时退化为内存缓存。agent.md 不写入 SQLite,也不再使用旧 `{aun_path}/AgentMDs` 目录。
|
|
223
239
|
|
|
224
240
|
---
|
|
225
241
|
|
|
@@ -596,7 +596,7 @@ class MySecretStore:
|
|
|
596
596
|
client = AUNClient(aid)
|
|
597
597
|
```
|
|
598
598
|
|
|
599
|
-
`AUNClient` 构造函数只接收可选 AID
|
|
599
|
+
`AUNClient` 构造函数只接收可选 AID 对象。`SecretStore` / `KeyStore` / `SQLiteBackup` 属于 SDK 内部基础设施,不作为应用层构造参数暴露。
|
|
600
600
|
|
|
601
601
|
---
|
|
602
602
|
|