@1agents/dreammate-network 0.2.0 → 0.3.1

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.
@@ -8,7 +8,11 @@
8
8
  *
9
9
  * @packageDocumentation
10
10
  */
11
- export declare const PROTOCOL_VERSION = "0.1.0";
11
+ /**
12
+ * 协议版本,与本包的 package.json 版本保持一致——两个版本号会让人永远猜不准
13
+ * 该看哪个。服务在 `manifest.metadata.protocol_version` 里报告它遵循的版本。
14
+ */
15
+ export declare const PROTOCOL_VERSION = "0.3.1";
12
16
  /** `session://<node>/<runtime>/<session_id>` */
13
17
  export type SessionURI = `session://${string}/${string}/${string}`;
14
18
  /** 资源寻址,如 `recording://tingqi-01/abc`、`speaker://tingqi-001/speaker-23` */
@@ -33,6 +37,11 @@ export type Protocol = "mcp" | "http" | "cli" | "acp";
33
37
  /**
34
38
  * 同一组能力可以同时有多种 access,调用方不必关心底层是谁。
35
39
  * 包装优先级 MCP > CLI > HTTP,但第一版不做自动 fallback 编排,手工声明即可。
40
+ *
41
+ * **数组是有序的:靠前的优先,靠后的是 fallback。** 同一个 protocol 出现多次
42
+ * 是合法且有用的——比如 HTTP 先给 MagicDNS 名(可读、IP 变了也不用改),
43
+ * 再给 tailnet IP 兜底:调用方的 DNS 可能被代理软件劫持(实测一台装了
44
+ * fake-ip 代理的 Mac 会把 MagicDNS 名解析到 198.18.x.x),那时只有 IP 能用。
36
45
  */
37
46
  export interface AccessDescriptor {
38
47
  protocol: Protocol;
@@ -57,6 +66,11 @@ export interface AccessDescriptor {
57
66
  * 与 docs/protocol.md §6 的表格一一对应,改动必须同步两边。
58
67
  */
59
68
  export declare const DEFAULT_PORTS: {
69
+ /**
70
+ * 本机 node agent(`@1agents/dreammate-node`)。**固定端口**,不可改:
71
+ * 它是整台机器对网络的唯一入口,外部节点靠"探 36908"找到这台机器上的一切。
72
+ */
73
+ readonly "node-agent": 36908;
60
74
  readonly "session-registry": 7777;
61
75
  readonly "data-service": 7778;
62
76
  readonly "control-plane": 7779;
@@ -82,6 +96,8 @@ export type ServiceKind = "generic" | "agent_runtime" | "session_registry" | "re
82
96
  * 三层结构是 Node → Service → Capability / Resource,
83
97
  * 不要把 Capability 扁平铺到 Node 上。
84
98
  */
99
+ /** 这个 Service 能被谁连上。省略时按 `network` 理解。 */
100
+ export type Reachability = "localhost" | "network";
85
101
  export interface Service {
86
102
  id: string;
87
103
  name?: string | null;
@@ -91,6 +107,15 @@ export interface Service {
91
107
  capabilities: Capability[];
92
108
  resources?: ResourceDescriptor[];
93
109
  access?: AccessDescriptor[];
110
+ /**
111
+ * 只监听回环的服务,外部节点发现得了但连不上。node agent 如实转述这个
112
+ * 声明而**不做代理**——调用方看到 `localhost` 就知道不用白跑一趟。
113
+ */
114
+ reachability?: Reachability;
115
+ /** 实际监听端口。不必等于 {@link DEFAULT_PORTS} 里的默认值。 */
116
+ port?: number | null;
117
+ /** 存活探测路径,默认 `/health`。 */
118
+ health?: string | null;
94
119
  metadata?: Record<string, unknown>;
95
120
  }
96
121
  /**
@@ -8,7 +8,11 @@
8
8
  *
9
9
  * @packageDocumentation
10
10
  */
11
- export const PROTOCOL_VERSION = "0.1.0";
11
+ /**
12
+ * 协议版本,与本包的 package.json 版本保持一致——两个版本号会让人永远猜不准
13
+ * 该看哪个。服务在 `manifest.metadata.protocol_version` 里报告它遵循的版本。
14
+ */
15
+ export const PROTOCOL_VERSION = "0.3.1";
12
16
  /* ------------------------------------------------------------------ *
13
17
  * 发现
14
18
  * ------------------------------------------------------------------ */
@@ -22,6 +26,11 @@ export const PROTOCOL_VERSION = "0.1.0";
22
26
  * 与 docs/protocol.md §6 的表格一一对应,改动必须同步两边。
23
27
  */
24
28
  export const DEFAULT_PORTS = {
29
+ /**
30
+ * 本机 node agent(`@1agents/dreammate-node`)。**固定端口**,不可改:
31
+ * 它是整台机器对网络的唯一入口,外部节点靠"探 36908"找到这台机器上的一切。
32
+ */
33
+ "node-agent": 36908,
25
34
  "session-registry": 7777,
26
35
  "data-service": 7778,
27
36
  "control-plane": 7779,
package/docs/protocol.md CHANGED
@@ -106,7 +106,10 @@ GET /health
106
106
  POST /capabilities/:name/invoke
107
107
  ```
108
108
 
109
- 外加向 Control Plane 的三个动作:`register` / `heartbeat` / `update manifest`。
109
+ 外加向**本机 node agent** 报备(见 §6)。注意这里已经不是早期草案里的
110
+ `register / heartbeat / update manifest` 三件套:heartbeat 被砍了——存活由
111
+ tailnet(Node 层)加 agent 探 `/health`(Service 层)负责,push 心跳既多余
112
+ 又让 L2 反过来依赖 L3。
110
113
 
111
114
  **不要为了统一而把一切硬塞进 `/invoke`。** 特殊 Service 保留原生协议:
112
115
 
@@ -142,6 +145,7 @@ pull 要知道探哪儿,所以端口是公共词汇的一部分:
142
145
 
143
146
  | 端口 | Service | 层 |
144
147
  |------|---------|-----|
148
+ | **36908** | **`node-agent`(dreammate-node)** | **本机基础设施** |
145
149
  | 7777 | `session-registry`(session-reader) | L2 |
146
150
  | 7778 | `data-service` | L2 |
147
151
  | 7779 | `control-plane` | L3 |
@@ -152,6 +156,61 @@ pull 要知道探哪儿,所以端口是公共词汇的一部分:
152
156
  得由它自己 `POST /nodes/register` 告知——register 因此是可选的加速/兜底,
153
157
  不是必需品。
154
158
 
159
+ ### 本机 node agent
160
+
161
+ 每台机器跑一个 `@1agents/dreammate-node`,固定监听 **36908**。它是这台机器
162
+ 对网络的唯一入口:
163
+
164
+ ```
165
+ Control Plane / 任意节点
166
+ │ 探 36908(每台机器只探一个端口)
167
+
168
+ node-agent :36908
169
+ ├─ GET /manifest 本机聚合视图:节点身份 + 所有已报备的服务
170
+ ├─ GET /health
171
+ ├─ GET /services 各服务的存活与可达性
172
+ └─ POST /services 服务报备(**仅接受 localhost**)
173
+
174
+ ┌───────────┴───────────┐ localhost 报备
175
+ session-reader :7777 task-service :xxxx
176
+ ```
177
+
178
+ 这把 pull 探测的成本从「N 个节点 × M 个端口」降到「N × 1」。
179
+
180
+ **报备是可选的。** 服务不报备也能工作,只是外部得靠约定端口才找得到它。
181
+ 报备时必须声明**可达性**:
182
+
183
+ | `reachability` | 含义 |
184
+ |---|---|
185
+ | `localhost` | 只监听回环,外部节点发现得了但连不上 |
186
+ | `network` | 监听 0.0.0.0 或 tailnet 地址,外部可直连 |
187
+
188
+ agent 如实转述这个声明,**不做代理**。调用方看到 `localhost` 就知道这个能力
189
+ 只对本机开放,不用白跑一趟。
190
+
191
+ > ⚠️ `POST /services` 只接受来自回环的请求。否则网络上任何人都能往你的节点
192
+ > 里塞一个假服务,把调用方引到别处去。
193
+
194
+ ### Control Plane 不是一个进程
195
+
196
+ node 注册与探活下沉成了每台机器的基础设施(就是上面的 agent),原本设想中
197
+ Control Plane 的其余职责——Task、Agent 编排、Execution 账本——**降级为平级的
198
+ 普通服务**,各自独立起进程、各自向本机 agent 报备。
199
+
200
+ ```
201
+ 以前:Control Plane = Node Registry + Execution Ledger + Task + Orchestrator
202
+ (一个大进程)
203
+
204
+ 现在:node-agent = Node Registry + 探活 (每机一个,基础设施)
205
+ task-service ┐
206
+ agent-service ├ 平级服务,各自解耦运行,都向本机 agent 报备
207
+ execution-* ┘
208
+ ```
209
+
210
+ 好处是任何一个服务挂掉都不会让整个控制面消失,也不存在"必须先起 Control Plane
211
+ 才能用"的启动顺序。代价是全网视图需要有人聚合——那也只是另一个服务,它靠
212
+ tailnet + 探 36908 自己拼出来。
213
+
155
214
  ### 节点身份取自 tailnet
156
215
 
157
216
  节点的 `node_id` / `name` / `type` 应该直接用 tailnet 的事实,而不是自己生成:
@@ -200,6 +259,23 @@ pull 要知道探哪儿,所以端口是公共词汇的一部分:
200
259
 
201
260
  包装优先级 `MCP > CLI > HTTP`,但**第一版不做自动 fallback 编排**,手工声明即可。
202
261
 
262
+ **`access` 数组是有序的:靠前的优先,靠后的是 fallback。** 同一个 protocol
263
+ 出现多次是合法且有用的:
264
+
265
+ ```json
266
+ "access": [
267
+ { "protocol": "http", "base_url": "http://scott-mac.tailfb4720.ts.net:7777/v1" },
268
+ { "protocol": "http", "base_url": "http://100.88.227.56:7777/v1" }
269
+ ]
270
+ ```
271
+
272
+ MagicDNS 名放前面(可读,IP 变了也不用改 manifest),tailnet IP 兜底。
273
+ **这不是多余的**——调用方的 DNS 可能被劫持:实测一台装了 fake-ip 代理的 Mac
274
+ 会把 `iclaw-6e78b1` 解析到 `198.18.1.113`,直连 `100.75.105.79` 才通。
275
+ 只给 DNS 名的话,那台机器就永远连不上这个服务。
276
+
277
+ 调用方按顺序试,第一个连通的就用。
278
+
203
279
  `acp` 与前三者不是一回事:MCP/CLI/HTTP 面向 Capability 与 Resource,
204
280
  ACP 面向 Agent Runtime 的 Session 控制(`agent.session.new` / `prompt` / `cancel` /
205
281
  `status` / `resume`)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1agents/dreammate-network",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "DreamMate Network L0 协议包:schema + types,零运行时依赖,不含业务逻辑。",
5
5
  "keywords": [
6
6
  "dreammate",
@@ -4,82 +4,165 @@
4
4
  "title": "DreamMate Service",
5
5
  "description": "Node 上的一组能力聚合。三层结构是 Node → Service → Capability / Resource,不要把 Capability 扁平铺到 Node 上。Agent Runtime 是 Service 的一种特殊 kind。",
6
6
  "type": "object",
7
- "required": ["id", "capabilities"],
7
+ "required": [
8
+ "id",
9
+ "capabilities"
10
+ ],
8
11
  "properties": {
9
12
  "id": {
10
13
  "type": "string",
11
14
  "description": "Service 在所属 Node 内唯一。",
12
15
  "minLength": 1,
13
- "examples": ["session-registry", "reminders", "recordings", "agent-runtime"]
16
+ "examples": [
17
+ "session-registry",
18
+ "reminders",
19
+ "recordings",
20
+ "agent-runtime"
21
+ ]
14
22
  },
15
23
  "name": {
16
- "type": ["string", "null"],
24
+ "type": [
25
+ "string",
26
+ "null"
27
+ ],
17
28
  "description": "人类可读名称,可省略。"
18
29
  },
19
30
  "kind": {
20
31
  "type": "string",
21
32
  "description": "Service 类型。generic 之外的几种在传输层保留原生协议(见 protocol.md §5),不强行塞进 /capabilities/:name/invoke。",
22
- "enum": ["generic", "agent_runtime", "session_registry", "resource_provider", "mcp"],
33
+ "enum": [
34
+ "generic",
35
+ "agent_runtime",
36
+ "session_registry",
37
+ "resource_provider",
38
+ "mcp"
39
+ ],
23
40
  "default": "generic"
24
41
  },
25
42
  "capabilities": {
26
43
  "type": "array",
27
44
  "description": "该 Service 能做什么。第一版就是点分命名的字符串,不带权限/可用性等元数据。可以为空数组(纯 Resource Provider)。",
28
- "items": { "type": "string", "minLength": 1 },
29
- "examples": [["sessions.list", "sessions.read"], ["reminders.create"], ["recordings.list", "recording.read"]]
45
+ "items": {
46
+ "type": "string",
47
+ "minLength": 1
48
+ },
49
+ "examples": [
50
+ [
51
+ "sessions.list",
52
+ "sessions.read"
53
+ ],
54
+ [
55
+ "reminders.create"
56
+ ],
57
+ [
58
+ "recordings.list",
59
+ "recording.read"
60
+ ]
61
+ ]
30
62
  },
31
63
  "resources": {
32
64
  "type": "array",
33
65
  "description": "该 Service 拥有什么(可选,声明资源 URI 的 scheme 而非逐条枚举)。Capability=能做什么,Resource=拥有什么,两者不要混:检索侧走 resource.search/read,调度侧走 capability.invoke。",
34
66
  "items": {
35
67
  "type": "object",
36
- "required": ["scheme"],
68
+ "required": [
69
+ "scheme"
70
+ ],
37
71
  "properties": {
38
72
  "scheme": {
39
73
  "type": "string",
40
74
  "description": "资源 URI 的 scheme。",
41
- "examples": ["recording", "transcript", "speaker", "session"]
75
+ "examples": [
76
+ "recording",
77
+ "transcript",
78
+ "speaker",
79
+ "session"
80
+ ]
42
81
  },
43
- "description": { "type": ["string", "null"] }
82
+ "description": {
83
+ "type": [
84
+ "string",
85
+ "null"
86
+ ]
87
+ }
44
88
  }
45
89
  }
46
90
  },
47
91
  "access": {
48
92
  "type": "array",
49
93
  "description": "怎么访问这个 Service。传输方式只出现在这里,绝不进数据模型——不存在所谓 MCP Capability / CLI Capability。同一组能力可以同时有多种 access,调用方不必关心底层是谁。包装优先级 MCP > CLI > HTTP,但第一版不做自动 fallback 编排,手工声明即可。",
50
- "items": { "$ref": "#/$defs/access" }
94
+ "items": {
95
+ "$ref": "#/$defs/access"
96
+ }
51
97
  },
52
98
  "metadata": {
53
99
  "type": "object",
54
100
  "description": "Service 级自由扩展字段。协议不解释其内容。",
55
101
  "additionalProperties": true
102
+ },
103
+ "reachability": {
104
+ "type": "string",
105
+ "enum": [
106
+ "localhost",
107
+ "network"
108
+ ],
109
+ "description": "这个 Service 能被谁连上。localhost=只监听回环,外部节点发现得了但连不上;network=监听 0.0.0.0 或 tailnet 地址,外部可直连。node agent 如实转述服务自己的声明,不做代理——调用方看到 localhost 就知道不用白跑一趟。省略时按 network 理解。"
110
+ },
111
+ "port": {
112
+ "type": [
113
+ "integer",
114
+ "null"
115
+ ],
116
+ "minimum": 1,
117
+ "maximum": 65535,
118
+ "description": "服务实际监听的端口。向本机 node agent 报备时带上,外部才知道该连哪儿;它不必等于 DEFAULT_PORTS 里的默认值。"
119
+ },
120
+ "health": {
121
+ "type": [
122
+ "string",
123
+ "null"
124
+ ],
125
+ "description": "存活探测路径,默认 /health。node agent 定期探它来判断服务死活——注意 Node 在线不代表 Service 在线。"
56
126
  }
57
127
  },
58
128
  "$defs": {
59
129
  "access": {
60
130
  "type": "object",
61
131
  "title": "Access Descriptor",
62
- "required": ["protocol"],
132
+ "required": [
133
+ "protocol"
134
+ ],
63
135
  "properties": {
64
136
  "protocol": {
65
137
  "type": "string",
66
- "enum": ["mcp", "http", "cli", "acp"],
138
+ "enum": [
139
+ "mcp",
140
+ "http",
141
+ "cli",
142
+ "acp"
143
+ ],
67
144
  "description": "acp 只用于 kind=agent_runtime 的 Session 控制,与前三者不是一回事:MCP/CLI/HTTP 面向 Capability / Resource,ACP 面向 Agent Runtime / Session Control。"
68
145
  },
69
146
  "base_url": {
70
147
  "type": "string",
71
148
  "description": "protocol=http 时的服务基址。",
72
- "examples": ["http://scott-mac:7777/v1"]
149
+ "examples": [
150
+ "http://scott-mac:7777/v1"
151
+ ]
73
152
  },
74
153
  "endpoint": {
75
154
  "type": "string",
76
155
  "description": "protocol=http 时单个 capability 的相对路由。",
77
- "examples": ["GET /transcripts/:id"]
156
+ "examples": [
157
+ "GET /transcripts/:id"
158
+ ]
78
159
  },
79
160
  "tool": {
80
161
  "type": "string",
81
162
  "description": "protocol=mcp 时对应的 tool 名。",
82
- "examples": ["transcript_read"]
163
+ "examples": [
164
+ "transcript_read"
165
+ ]
83
166
  },
84
167
  "server": {
85
168
  "type": "string",
@@ -88,7 +171,10 @@
88
171
  "command": {
89
172
  "type": "string",
90
173
  "description": "protocol=cli 时的命令。",
91
- "examples": ["tingqi transcript get", "1session overview"]
174
+ "examples": [
175
+ "tingqi transcript get",
176
+ "1session overview"
177
+ ]
92
178
  }
93
179
  }
94
180
  }
@@ -96,11 +182,22 @@
96
182
  "examples": [
97
183
  {
98
184
  "id": "transcripts",
99
- "capabilities": ["transcript.read"],
185
+ "capabilities": [
186
+ "transcript.read"
187
+ ],
100
188
  "access": [
101
- { "protocol": "mcp", "tool": "transcript_read" },
102
- { "protocol": "http", "endpoint": "GET /transcripts/:id" },
103
- { "protocol": "cli", "command": "tingqi transcript get" }
189
+ {
190
+ "protocol": "mcp",
191
+ "tool": "transcript_read"
192
+ },
193
+ {
194
+ "protocol": "http",
195
+ "endpoint": "GET /transcripts/:id"
196
+ },
197
+ {
198
+ "protocol": "cli",
199
+ "command": "tingqi transcript get"
200
+ }
104
201
  ]
105
202
  },
106
203
  {
@@ -113,7 +210,11 @@
113
210
  "agent.session.status",
114
211
  "agent.session.resume"
115
212
  ],
116
- "access": [{ "protocol": "acp" }]
213
+ "access": [
214
+ {
215
+ "protocol": "acp"
216
+ }
217
+ ]
117
218
  }
118
219
  ]
119
220
  }