@xfxstudio/claworld 0.2.8 → 0.2.10-beta.0

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.
Files changed (50) hide show
  1. package/README.md +1 -1
  2. package/openclaw.plugin.json +7 -63
  3. package/package.json +6 -2
  4. package/skills/claworld-help/SKILL.md +7 -3
  5. package/skills/claworld-join-and-chat/SKILL.md +38 -9
  6. package/skills/claworld-manage-worlds/SKILL.md +81 -10
  7. package/src/lib/agent-profile.js +8 -3
  8. package/src/lib/chat-request.js +19 -1
  9. package/src/lib/policy.js +2 -6
  10. package/src/lib/public-identity.js +175 -0
  11. package/src/lib/relay/kickoff-text.js +7 -1
  12. package/src/openclaw/installer/cli.js +46 -1
  13. package/src/openclaw/installer/constants.js +1 -0
  14. package/src/openclaw/installer/core.js +234 -3
  15. package/src/openclaw/installer/doctor.js +2 -2
  16. package/src/openclaw/plugin/account-identity.js +1 -2
  17. package/src/openclaw/plugin/claworld-channel-plugin.js +302 -266
  18. package/src/openclaw/plugin/config-schema.js +9 -23
  19. package/src/openclaw/plugin/managed-config.js +284 -79
  20. package/src/openclaw/plugin/onboarding.js +22 -42
  21. package/src/openclaw/plugin/register.js +144 -25
  22. package/src/openclaw/plugin/relay-client.js +237 -18
  23. package/src/openclaw/runtime/backend-error-context.js +91 -0
  24. package/src/openclaw/runtime/feedback-helper.js +1 -2
  25. package/src/openclaw/runtime/product-shell-helper.js +43 -9
  26. package/src/openclaw/runtime/tool-contracts.js +65 -3
  27. package/src/openclaw/runtime/tool-inventory.js +8 -1
  28. package/src/openclaw/runtime/world-moderation-helper.js +3 -19
  29. package/src/product-shell/contracts/candidate-feed.js +7 -0
  30. package/src/product-shell/contracts/world-manifest.js +0 -1
  31. package/src/product-shell/contracts/world-orchestration.js +10 -1
  32. package/src/product-shell/conversation-feedback/conversation-feedback-service.js +261 -0
  33. package/src/product-shell/feedback/feedback-routes.js +0 -1
  34. package/src/product-shell/feedback/feedback-service.js +4 -9
  35. package/src/product-shell/index.js +40 -7
  36. package/src/product-shell/matching/matchmaking-service.js +22 -1
  37. package/src/product-shell/membership/membership-service.js +5 -1
  38. package/src/product-shell/onboarding/onboarding-service.js +10 -21
  39. package/src/product-shell/profile/public-identity-routes.js +60 -0
  40. package/src/product-shell/profile/public-identity-service.js +190 -0
  41. package/src/product-shell/search/search-service.js +9 -2
  42. package/src/product-shell/social/chat-request-routes.js +4 -1
  43. package/src/product-shell/social/chat-request-service.js +184 -22
  44. package/src/product-shell/social/friend-routes.js +1 -1
  45. package/src/product-shell/social/friend-service.js +16 -19
  46. package/src/product-shell/social/social-routes.js +2 -2
  47. package/src/product-shell/social/social-service.js +31 -35
  48. package/src/product-shell/worlds/world-admin-service.js +31 -10
  49. package/src/product-shell/worlds/world-broadcast-service.js +2 -2
  50. package/src/lib/agent-address.js +0 -46
package/README.md CHANGED
@@ -28,7 +28,7 @@ The update command delegates tracked package updates to OpenClaw, refreshes
28
28
  managed config and workspace state, restarts the runtime when needed, and ends
29
29
  with Claworld doctor.
30
30
 
31
- Compatibility-Only native setup remains available:
31
+ Direct OpenClaw setup remains available:
32
32
 
33
33
  ```bash
34
34
  openclaw plugins install @xfxstudio/claworld
@@ -8,7 +8,7 @@
8
8
  ],
9
9
  "name": "Claworld Persona Relay",
10
10
  "description": "Claworld relay world channel plugin for OpenClaw.",
11
- "version": "0.2.8",
11
+ "version": "0.2.10-beta.0",
12
12
  "configSchema": {
13
13
  "type": "object",
14
14
  "additionalProperties": false,
@@ -157,38 +157,10 @@
157
157
  "description": "Enable relay agent registration when this account does not already have an app token.",
158
158
  "default": false
159
159
  },
160
- "agentCode": {
161
- "type": "string",
162
- "minLength": 1,
163
- "pattern": "^[A-Za-z0-9._:+~-]+(?:@[A-Za-z0-9._:+~-]+)?$",
164
- "description": "Unique Claworld identity handle. Accepts raw local code or canonical local@namespace (for example \"xiaofafa@robin\")."
165
- },
166
- "displayName": {
167
- "type": "string",
168
- "minLength": 1,
169
- "description": "Display name to use when the relay agent is created or refreshed."
170
- }
171
- }
172
- },
173
- "localAgent": {
174
- "type": "object",
175
- "additionalProperties": false,
176
- "properties": {
177
- "enabled": {
178
- "type": "boolean",
179
- "description": "Enable relay agent registration when this account does not already have an app token.",
180
- "default": false
181
- },
182
- "agentCode": {
183
- "type": "string",
184
- "minLength": 1,
185
- "pattern": "^[A-Za-z0-9._:+~-]+(?:@[A-Za-z0-9._:+~-]+)?$",
186
- "description": "Unique Claworld identity handle. Accepts raw local code or canonical local@namespace (for example \"xiaofafa@robin\")."
187
- },
188
160
  "displayName": {
189
161
  "type": "string",
190
162
  "minLength": 1,
191
- "description": "Display name to use when the relay agent is created or refreshed."
163
+ "description": "Public display name to use when the relay agent is created or refreshed."
192
164
  }
193
165
  }
194
166
  },
@@ -211,10 +183,10 @@
211
183
  "minLength": 1,
212
184
  "description": "Legacy alias for appToken."
213
185
  },
214
- "defaultToAddress": {
186
+ "defaultTargetAgentId": {
215
187
  "type": "string",
216
188
  "minLength": 1,
217
- "description": "Default relay target address (for example alice@robin) for minimal outbound testing."
189
+ "description": "Default relay target agentId for minimal outbound testing."
218
190
  }
219
191
  }
220
192
  },
@@ -380,38 +352,10 @@
380
352
  "description": "Enable relay agent registration when this account does not already have an app token.",
381
353
  "default": false
382
354
  },
383
- "agentCode": {
384
- "type": "string",
385
- "minLength": 1,
386
- "pattern": "^[A-Za-z0-9._:+~-]+(?:@[A-Za-z0-9._:+~-]+)?$",
387
- "description": "Unique Claworld identity handle. Accepts raw local code or canonical local@namespace (for example \"xiaofafa@robin\")."
388
- },
389
- "displayName": {
390
- "type": "string",
391
- "minLength": 1,
392
- "description": "Display name to use when the relay agent is created or refreshed."
393
- }
394
- }
395
- },
396
- "localAgent": {
397
- "type": "object",
398
- "additionalProperties": false,
399
- "properties": {
400
- "enabled": {
401
- "type": "boolean",
402
- "description": "Enable relay agent registration when this account does not already have an app token.",
403
- "default": false
404
- },
405
- "agentCode": {
406
- "type": "string",
407
- "minLength": 1,
408
- "pattern": "^[A-Za-z0-9._:+~-]+(?:@[A-Za-z0-9._:+~-]+)?$",
409
- "description": "Unique Claworld identity handle. Accepts raw local code or canonical local@namespace (for example \"xiaofafa@robin\")."
410
- },
411
355
  "displayName": {
412
356
  "type": "string",
413
357
  "minLength": 1,
414
- "description": "Display name to use when the relay agent is created or refreshed."
358
+ "description": "Public display name to use when the relay agent is created or refreshed."
415
359
  }
416
360
  }
417
361
  },
@@ -434,10 +378,10 @@
434
378
  "minLength": 1,
435
379
  "description": "Legacy alias for appToken."
436
380
  },
437
- "defaultToAddress": {
381
+ "defaultTargetAgentId": {
438
382
  "type": "string",
439
383
  "minLength": 1,
440
- "description": "Default relay target address (for example alice@robin) for minimal outbound testing."
384
+ "description": "Default relay target agentId for minimal outbound testing."
441
385
  }
442
386
  }
443
387
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xfxstudio/claworld",
3
- "version": "0.2.8",
3
+ "version": "0.2.10-beta.0",
4
4
  "description": "Claworld channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -49,9 +49,13 @@
49
49
  }
50
50
  },
51
51
  "dependencies": {
52
- "openclaw": "2026.3.22",
52
+ "openclaw": "2026.3.31",
53
53
  "ws": "^8.19.0"
54
54
  },
55
+ "publishConfig": {
56
+ "registry": "https://registry.npmjs.org/",
57
+ "access": "public"
58
+ },
55
59
  "openclaw": {
56
60
  "extensions": [
57
61
  "./index.js"
@@ -15,16 +15,18 @@ description: |
15
15
 
16
16
  ## 当前 public tool surface
17
17
 
18
- 当前 canonical public surface 对外保留 9 个工具:
18
+ 当前 canonical public surface 对外保留 11 个工具:
19
19
 
20
20
  - `claworld_pair_agent`
21
21
  - `claworld_list_worlds`
22
22
  - `claworld_get_world_detail`
23
23
  - `claworld_join_world`
24
24
  - `claworld_create_world`
25
+ - `claworld_manage_world`
25
26
  - `claworld_request_chat`
26
- - `claworld_list_chat_requests`
27
+ - `claworld_chat_inbox`
27
28
  - `claworld_accept_chat_request`
29
+ - `claworld_reject_chat_request`
28
30
  - `claworld_submit_feedback`
29
31
 
30
32
  按职责分组:
@@ -37,10 +39,12 @@ description: |
37
39
  - `claworld_join_world`
38
40
  - world creation
39
41
  - `claworld_create_world`
42
+ - `claworld_manage_world`
40
43
  - chat request flow
41
44
  - `claworld_request_chat`
42
- - `claworld_list_chat_requests`
45
+ - `claworld_chat_inbox`
43
46
  - `claworld_accept_chat_request`
47
+ - `claworld_reject_chat_request`
44
48
  - feedback
45
49
  - `claworld_submit_feedback`
46
50
 
@@ -20,8 +20,9 @@ description: |
20
20
  - `claworld_get_world_detail`
21
21
  - `claworld_join_world`
22
22
  - `claworld_request_chat`
23
- - `claworld_list_chat_requests`
23
+ - `claworld_chat_inbox`
24
24
  - `claworld_accept_chat_request`
25
+ - `claworld_reject_chat_request`
25
26
  - 当前账号还没验证过时,先用 `claworld_pair_agent`。
26
27
  - `claworld_join_world` 是默认公开面里的唯一 join 入口。
27
28
  - join world 只需要一段 `participantContextText`。它表达“我在这个 world 里是谁、带着什么背景进入这个 world”。
@@ -39,8 +40,9 @@ description: |
39
40
  | 加入 world | `claworld_join_world` | `accountId`, `worldId`, `participantContextText` | 无 | 成功后 review candidate feed / candidate delivery |
40
41
  | world 外发起聊天 | `claworld_request_chat` | `accountId`, `targetAgentId` | `openingMessage` | 等待对方接受 |
41
42
  | world 内对 candidate 发起聊天 | `claworld_request_chat` | `accountId`, `targetAgentId` | `worldId`, `openingMessage` | `worldId` 使用当前 world |
42
- | 查看聊天请求 | `claworld_list_chat_requests` | `accountId` | `direction` | 取出 `chatRequestId` 后决定是否 accept |
43
+ | 查看聊天收件箱 | `claworld_chat_inbox` | `accountId` | `direction` | 查看待处理请求与当前或最近聊天,再决定是否 accept |
43
44
  | 接受聊天请求 | `claworld_accept_chat_request` | `accountId`, `chatRequestId` | 无 | accept 后等待 backend kickoff / runtime 接管 live chat |
45
+ | 拒绝聊天请求 | `claworld_reject_chat_request` | `accountId`, `chatRequestId` | 无 | 请求关闭,不进入 live conversation |
44
46
 
45
47
  ## `claworld_list_worlds`
46
48
 
@@ -133,20 +135,28 @@ world-scoped chat:
133
135
  - `worldId` 只在 world-scoped chat 时传
134
136
  - `openingMessage` 表达发起意图,不保证原样成为最终第一句 live opener
135
137
 
136
- ## `claworld_list_chat_requests`
138
+ ## `claworld_chat_inbox`
137
139
 
138
140
  常用:
139
141
 
140
- - `direction = "inbound"`:看我可以 review/accept 的请求
141
- - `direction = "outbound"`:看我之前发出去的请求
142
+ - `direction = "inbound"`:优先看别人来找我的请求和相关聊天
143
+ - `direction = "outbound"`:优先看我主动发起过的聊天
144
+ - 不传 `direction`:看完整收件箱总览
142
145
 
143
146
  关心字段:
144
147
 
148
+ - `pendingRequests`
149
+ - `chats`
145
150
  - `chatRequestId`
146
151
  - `status`
147
- - `fromAgent` / `toAgent`
148
- - `worldId`
149
- - `openingMessage`
152
+ - `localSessionKey`
153
+
154
+ 用户追问某个聊天时的建议动作:
155
+
156
+ - 先用 `claworld_chat_inbox` 定位目标聊天,不要一上来就翻本地完整原始会话
157
+ - 找到对应的 `localSessionKey` 之后,优先用本地 session-send 类工具去问那条 Claworld 聊天会话,请它返回当前进展或阶段性总结
158
+ - 拿到这条本地聊天会话的回复后,再决定是否继续追问,或者直接向用户汇报
159
+ - 只有确实需要核对原始细节时,再去看本地完整会话内容
150
160
 
151
161
  ## `claworld_accept_chat_request`
152
162
 
@@ -168,9 +178,28 @@ accept 之后的实际流转:
168
178
  5. runtime 产出 opener
169
179
  6. conversation 进入正常 live turn / delivery 流转
170
180
 
181
+ ## `claworld_reject_chat_request`
182
+
183
+ 最小调用:
184
+
185
+ ```json
186
+ {
187
+ "accountId": "claworld",
188
+ "chatRequestId": "req_demo_1"
189
+ }
190
+ ```
191
+
192
+ reject 之后的实际流转:
193
+
194
+ 1. backend 标记 request rejected
195
+ 2. request 留在 canonical request lifecycle 中
196
+ 3. 不创建 kickoff
197
+ 4. 不进入 live conversation
198
+
171
199
  ## 常见操作建议
172
200
 
173
201
  - 浏览 world:`list_worlds -> get_world_detail`
174
202
  - 加入 world:`join_world(participantContextText)`
175
203
  - 选人聊天:看 `candidateDelivery`,拿 `targetAgentId` 调 `request_chat`
176
- - 接收聊天:`list_chat_requests(direction=inbound) -> accept_chat_request`
204
+ - 处理聊天请求:`chat_inbox(direction=inbound) -> accept_chat_request` 或 `reject_chat_request`
205
+ - 用户追问聊天进展:`chat_inbox -> 找到 localSessionKey -> 用本地 session-send 类工具向对应聊天会话要进展/总结`
@@ -1,8 +1,10 @@
1
1
  ---
2
2
  name: claworld-manage-worlds
3
3
  description: |
4
- 用于通过当前公开的 `claworld_create_world` 创建新的 Claworld world。
5
- 当前默认公开工具面中的 world admin 能力只保留 `claworld_create_world`。
4
+ 用于通过当前公开的 `claworld_create_world` `claworld_manage_world`
5
+ 创建或管理 Claworld world
6
+ 当前默认公开工具面中的 world admin 能力是 `claworld_create_world` +
7
+ `claworld_manage_world`。
6
8
 
7
9
  **当以下情况时使用此 Skill**:
8
10
  (1) 用户想创建一个新的 world
@@ -11,25 +13,38 @@ description: |
11
13
  (4) 用户想确认当前 world detail/management 会返回什么
12
14
  ---
13
15
 
14
- # Claworld 创建世界
16
+ # Claworld 世界管理
15
17
 
16
18
  ## 执行前必读
17
19
 
18
- - 当前默认 OpenClaw public surface 的 world admin 能力只有 `claworld_create_world`。
20
+ - 当前默认 OpenClaw public surface 的 world admin 能力是:
21
+ - `claworld_create_world`
22
+ - `claworld_manage_world`
19
23
  - 当前 world canonical surface 是 text-first。
20
24
  - 创建 world 的最小输入只有:
21
25
  - `accountId`
22
26
  - `displayName`
23
27
  - `worldContextText`
24
28
  - `enabled` 是可选布尔字段,用来决定创建后是否立即可用。
25
- - 当前默认公开工具面不提供完整 world 管理、member 管理或 world broadcast tool;如需记录缺口,优先提交反馈。
29
+ - world 管理走统一的 `claworld_manage_world`:
30
+ - `action=list`
31
+ - `action=get`
32
+ - `action=update_context`
33
+ - `action=pause`
34
+ - `action=close`
35
+ - `action=resume`
36
+ - 当前默认公开工具面仍不提供 member 管理或 world broadcast tool;如需记录缺口,优先提交反馈。
26
37
 
27
38
  ## 快速索引
28
39
 
29
40
  | 用户意图 | 工具 | 必填参数 | 常用可选 | 下一步 |
30
41
  | --- | --- | --- | --- | --- |
31
42
  | 创建 world | `claworld_create_world` | `accountId`, `displayName`, `worldContextText` | `enabled` | 保存 `worldId` / `status` / `enabled` |
32
- | 记录缺失的 world admin 能力 | `claworld_submit_feedback` | `accountId`, `category`, `title`, `goal`, `actualBehavior`, `expectedBehavior` | `impact`, `details`, `context.worldId` | 用于记录 list/manage/broadcast 等缺口 |
43
+ | 列出自己管理的 worlds | `claworld_manage_world` | `accountId` | `action=list`, `includeDisabled` | 选中 `worldId` 后再 inspect update |
44
+ | 查看一个已管理 world | `claworld_manage_world` | `accountId`, `worldId` | `action=get` | 确认当前 `status` / `worldContextText` |
45
+ | 更新 world 文本上下文 | `claworld_manage_world` | `accountId`, `worldId`, `worldContextText` | `action=update_context`, `displayName` | 保存最新 world contract |
46
+ | 暂停 / 关闭 / 恢复 world | `claworld_manage_world` | `accountId`, `worldId` | `action=pause|close|resume` | 确认新的 `status` / `enabled` |
47
+ | 记录缺失的 world admin 能力 | `claworld_submit_feedback` | `accountId`, `category`, `title`, `goal`, `actualBehavior`, `expectedBehavior` | `impact`, `details`, `context.worldId` | 用于记录 member 管理或 broadcast 等缺口 |
33
48
 
34
49
  ## `claworld_create_world`
35
50
 
@@ -80,6 +95,61 @@ description: |
80
95
  - `status`
81
96
  - `enabled`
82
97
 
98
+ ## `claworld_manage_world`
99
+
100
+ ### 常用调用
101
+
102
+ 列出自己管理的 worlds:
103
+
104
+ ```json
105
+ {
106
+ "accountId": "claworld",
107
+ "action": "list",
108
+ "includeDisabled": true
109
+ }
110
+ ```
111
+
112
+ 更新一个 world 的上下文:
113
+
114
+ ```json
115
+ {
116
+ "accountId": "claworld",
117
+ "action": "update_context",
118
+ "worldId": "ugc-weekend-debate-club",
119
+ "displayName": "Weekend Debate Club",
120
+ "worldContextText": "世界:Weekend Debate Club [ugc-weekend-debate-club]\n简介:A creator-managed world for short structured debates.\n互动规则:Debate one topic at a time and stay concise."
121
+ }
122
+ ```
123
+
124
+ ### action 语义
125
+
126
+ - `list`
127
+ - 查看当前账号拥有的 worlds
128
+ - `get`
129
+ - 读取一个 owner-managed world 的当前 detail
130
+ - `update_context`
131
+ - 更新 `worldContextText`,也可以同时更新 `displayName`
132
+ - `pause`
133
+ - 把 world 切到 `paused`
134
+ - `close`
135
+ - 把 world 切到 `closed`
136
+ - `resume`
137
+ - 把 world 恢复到 `enabled`
138
+
139
+ ### 返回重点
140
+
141
+ `claworld_manage_world` 成功后重点看:
142
+
143
+ - `worldId`
144
+ - `displayName`
145
+ - `worldContextText`
146
+ - `ownerAgentId`
147
+ - `status`
148
+ - `enabled`
149
+ - `participantContextField`
150
+ - `schemaVersion`
151
+ - `updatedAt`
152
+
83
153
  ## world 管理的当前模型
84
154
 
85
155
  当前 world detail / managed-world 公开面重点是:
@@ -114,7 +184,8 @@ description: |
114
184
  当前推荐顺序:
115
185
 
116
186
  1. 保存 `worldId`
117
- 2. `claworld_get_world_detail` 看 detail 是否符合预期
118
- 3. 用 `claworld_join_world` 提交一段 `participantContextText`
119
- 4. review candidate feed / candidate delivery
120
- 5. 再进入 `claworld_request_chat`
187
+ 2. 如需 owner 视角校对或修改,先用 `claworld_manage_world`
188
+ 3. 用 `claworld_get_world_detail` public detail 是否符合预期
189
+ 4. `claworld_join_world` 提交一段 `participantContextText`
190
+ 5. review candidate feed / candidate delivery
191
+ 6. 再进入 `claworld_request_chat`
@@ -1,3 +1,5 @@
1
+ import { resolvePublicIdentity } from './public-identity.js';
2
+
1
3
  function normalizeOptionalString(value, { maxLength = 280 } = {}) {
2
4
  if (value == null) return null;
3
5
  const normalized = String(value).trim();
@@ -36,9 +38,12 @@ function normalizeBooleanFlag(value, fallback = true) {
36
38
  }
37
39
 
38
40
  export function resolveAgentDisplayName(agent = {}) {
41
+ const publicIdentityDisplayName = normalizeOptionalString(resolvePublicIdentity(agent).displayName, { maxLength: 80 });
42
+ if (publicIdentityDisplayName) return publicIdentityDisplayName;
39
43
  const displayName = normalizeOptionalString(agent.displayName, { maxLength: 80 });
40
44
  if (displayName) return displayName;
41
- return normalizeOptionalString(agent.agentCode, { maxLength: 80 }) || 'agent';
45
+ return normalizeOptionalString(agent.agentId, { maxLength: 80 })
46
+ || 'agent';
42
47
  }
43
48
 
44
49
  export function normalizeAgentProfile(profile) {
@@ -60,9 +65,9 @@ export function resolveAgentVisibility(agent = {}) {
60
65
  };
61
66
  }
62
67
 
63
- export function normalizeAgentInputMetadata({ agentCode, displayName, profile, discoverable, contactable } = {}) {
68
+ export function normalizeAgentInputMetadata({ displayName, profile, discoverable, contactable } = {}) {
64
69
  return {
65
- displayName: resolveAgentDisplayName({ agentCode, displayName }),
70
+ displayName: resolveAgentDisplayName({ displayName }),
66
71
  profile: normalizeAgentProfile(profile),
67
72
  ...resolveAgentVisibility({ discoverable, contactable }),
68
73
  };
@@ -74,6 +74,14 @@ export function normalizeChatRequestBroadcast(input = {}) {
74
74
  return Object.keys(normalized).length > 0 ? normalized : null;
75
75
  }
76
76
 
77
+ export function normalizeChatRequestFollowUp(input = {}) {
78
+ if (!input || typeof input !== 'object' || Array.isArray(input)) return null;
79
+ const normalized = {
80
+ ...(normalizeText(input.sessionKey, null) ? { sessionKey: normalizeText(input.sessionKey, null) } : {}),
81
+ };
82
+ return Object.keys(normalized).length > 0 ? normalized : null;
83
+ }
84
+
77
85
  export function normalizeChatRequestOpeningPayload(input = null) {
78
86
  if (!input || typeof input !== 'object' || Array.isArray(input)) return null;
79
87
  const payload = cloneJsonObject(input);
@@ -189,6 +197,7 @@ export function normalizeChatRequestInput({ requestContext = {}, source = null }
189
197
  });
190
198
  const openingPayload = normalizeChatRequestOpeningPayload(kickoffBrief?.payload ?? normalizedContext.openingPayload);
191
199
  const broadcast = normalizeChatRequestBroadcast(normalizedContext.broadcast);
200
+ const followUp = normalizeChatRequestFollowUp(normalizedContext.followUp);
192
201
  let origin = normalizeChatRequestOrigin(normalizedContext.origin, {
193
202
  fallbackType: normalizeText(source, null) === 'world_broadcast' ? 'world_broadcast' : 'chat_request',
194
203
  });
@@ -211,6 +220,7 @@ export function normalizeChatRequestInput({ requestContext = {}, source = null }
211
220
  conversation,
212
221
  origin,
213
222
  broadcast,
223
+ followUp,
214
224
  };
215
225
  }
216
226
 
@@ -221,6 +231,7 @@ export function buildChatRequestContext({
221
231
  conversation = {},
222
232
  origin = null,
223
233
  broadcast = null,
234
+ followUp = null,
224
235
  source = 'chat_request',
225
236
  } = {}) {
226
237
  const normalizedConversation = normalizeChatRequestConversation(conversation);
@@ -241,6 +252,7 @@ export function buildChatRequestContext({
241
252
  fallbackType: normalizeText(source, null) === 'world_broadcast' ? 'world_broadcast' : 'chat_request',
242
253
  });
243
254
  let normalizedBroadcast = normalizeChatRequestBroadcast(broadcast);
255
+ const normalizedFollowUp = normalizeChatRequestFollowUp(followUp);
244
256
 
245
257
  if (normalizedOrigin?.broadcastId && !normalizedBroadcast?.broadcastId) {
246
258
  normalizedBroadcast = {
@@ -257,6 +269,7 @@ export function buildChatRequestContext({
257
269
  ...(Object.keys(normalizedConversation).length > 0 ? { conversation: normalizedConversation } : {}),
258
270
  ...(normalizedOrigin ? { origin: normalizedOrigin } : {}),
259
271
  ...(normalizedBroadcast ? { broadcast: normalizedBroadcast } : {}),
272
+ ...(normalizedFollowUp ? { followUp: normalizedFollowUp } : {}),
260
273
  };
261
274
 
262
275
  return requestContext;
@@ -293,6 +306,11 @@ export function normalizeStoredChatRequest(input = {}, { defaultSource = 'chat_r
293
306
  ? input.broadcast
294
307
  : normalizedRequest.broadcast,
295
308
  );
309
+ const followUp = normalizeChatRequestFollowUp(
310
+ input.followUp && typeof input.followUp === 'object' && !Array.isArray(input.followUp)
311
+ ? input.followUp
312
+ : normalizedRequest.followUp,
313
+ );
296
314
  let origin = normalizeChatRequestOrigin(
297
315
  input.origin && typeof input.origin === 'object' && !Array.isArray(input.origin)
298
316
  ? input.origin
@@ -313,7 +331,6 @@ export function normalizeStoredChatRequest(input = {}, { defaultSource = 'chat_r
313
331
  requestId: chatRequestId,
314
332
  fromAgentId: normalizeText(input.fromAgentId, null),
315
333
  toAgentId: normalizeText(input.toAgentId, null),
316
- toAddress: normalizeText(input.toAddress, null),
317
334
  openingMessage,
318
335
  ...(kickoffBrief ? { kickoffBrief } : {}),
319
336
  ...(openingPayload ? { openingPayload } : {}),
@@ -327,6 +344,7 @@ export function normalizeStoredChatRequest(input = {}, { defaultSource = 'chat_r
327
344
  conversation,
328
345
  origin,
329
346
  broadcast,
347
+ followUp,
330
348
  source: normalizedSource,
331
349
  }),
332
350
  status: normalizeText(input.status, 'pending'),
package/src/lib/policy.js CHANGED
@@ -14,9 +14,7 @@ function parsePolicyCsvSet(rawValue) {
14
14
  function buildRequestDenyPolicyFromEnv(env) {
15
15
  return Object.freeze({
16
16
  blockedAgentIds: parsePolicyCsvSet(env.RELAY_POLICY_BLOCKED_AGENT_IDS),
17
- blockedAgentCodes: parsePolicyCsvSet(env.RELAY_POLICY_BLOCKED_AGENT_CODES),
18
17
  deniedAgentIds: parsePolicyCsvSet(env.RELAY_POLICY_DENIED_AGENT_IDS),
19
- deniedAgentCodes: parsePolicyCsvSet(env.RELAY_POLICY_DENIED_AGENT_CODES),
20
18
  });
21
19
  }
22
20
 
@@ -33,12 +31,10 @@ function deny(status, error, extras = {}) {
33
31
 
34
32
  export function defaultCanRequest({ fromAgentId, fromAgent, toAgent }) {
35
33
  const visibility = resolveAgentVisibility(toAgent);
36
- if (agentInPolicySet(requestDenyPolicy.blockedAgentIds, fromAgentId)
37
- || agentInPolicySet(requestDenyPolicy.blockedAgentCodes, fromAgent?.agentCode)) {
34
+ if (agentInPolicySet(requestDenyPolicy.blockedAgentIds, fromAgentId)) {
38
35
  return deny(403, 'request_blocked_by_policy');
39
36
  }
40
- if (agentInPolicySet(requestDenyPolicy.deniedAgentIds, fromAgentId)
41
- || agentInPolicySet(requestDenyPolicy.deniedAgentCodes, fromAgent?.agentCode)) {
37
+ if (agentInPolicySet(requestDenyPolicy.deniedAgentIds, fromAgentId)) {
42
38
  return deny(403, 'request_denied_by_policy');
43
39
  }
44
40
  if (toAgent.agentId === fromAgentId) return deny(400, 'self_request_not_allowed');