openclaw-openagent 1.0.13 → 1.0.14

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 (128) hide show
  1. package/dist/src/messaging/executor.d.ts +2 -3
  2. package/dist/src/messaging/executor.js +4 -31
  3. package/dist/src/plugin-ui/assets/openagent-override.js +4 -5
  4. package/dist/src/runtime/update-checker.d.ts +1 -1
  5. package/dist/src/runtime/update-checker.js +9 -46
  6. package/package.json +1 -3
  7. package/src/app/channel-tools.ts +0 -249
  8. package/src/app/discovery-tools.ts +0 -273
  9. package/src/app/download-file-tool.ts +0 -133
  10. package/src/app/hooks.ts +0 -144
  11. package/src/app/index.ts +0 -84
  12. package/src/app/messaging-tools.ts +0 -79
  13. package/src/app/ops-tools.ts +0 -155
  14. package/src/app/remote-agent-tool.ts +0 -762
  15. package/src/app/types.ts +0 -68
  16. package/src/app/upload-file-tool.ts +0 -411
  17. package/src/app/verbose-preflight.ts +0 -190
  18. package/src/auth/config.ts +0 -266
  19. package/src/auth/credential-manager.ts +0 -146
  20. package/src/auth/index.ts +0 -24
  21. package/src/auth/verify.ts +0 -99
  22. package/src/channel.ts +0 -566
  23. package/src/compat.ts +0 -94
  24. package/src/config/config-schema.ts +0 -45
  25. package/src/messaging/aggregator.ts +0 -120
  26. package/src/messaging/collector.ts +0 -89
  27. package/src/messaging/executor.ts +0 -72
  28. package/src/messaging/inbound.ts +0 -150
  29. package/src/messaging/index.ts +0 -11
  30. package/src/messaging/mention-protocol.ts +0 -94
  31. package/src/messaging/process-c2c-request.ts +0 -564
  32. package/src/messaging/process-message.ts +0 -373
  33. package/src/messaging/scheduler.ts +0 -55
  34. package/src/messaging/types.ts +0 -38
  35. package/src/plugin-ui/adapters/oc-2026-04.js +0 -103
  36. package/src/plugin-ui/adapters/oc-2026-05.js +0 -125
  37. package/src/plugin-ui/adapters/oc-2026-06.js +0 -125
  38. package/src/plugin-ui/adapters/oc-unknown.js +0 -48
  39. package/src/plugin-ui/assets/agentbook-icon.svg +0 -5
  40. package/src/plugin-ui/assets/bg.png +0 -0
  41. package/src/plugin-ui/assets/icon.png +0 -0
  42. package/src/plugin-ui/assets/magic.svg +0 -5
  43. package/src/plugin-ui/assets/openagent-override.js +0 -11263
  44. package/src/plugin-ui/build.cjs +0 -463
  45. package/src/plugin-ui/index.ts +0 -18
  46. package/src/plugin-ui/modules/agent-book/panel/agent-book.js +0 -570
  47. package/src/plugin-ui/modules/agent-book/panel/agent-card.js +0 -170
  48. package/src/plugin-ui/modules/agent-book/panel/agent-data.js +0 -643
  49. package/src/plugin-ui/modules/agent-book/panel/inject-ui.js +0 -665
  50. package/src/plugin-ui/modules/agent-book/panel/mention-state.js +0 -206
  51. package/src/plugin-ui/modules/agent-book/panel/styles.js +0 -832
  52. package/src/plugin-ui/modules/agent-book/panel/theme-adapter.js +0 -86
  53. package/src/plugin-ui/modules/agent-book/remote-agent-tool/components-core.js +0 -295
  54. package/src/plugin-ui/modules/agent-book/remote-agent-tool/thought-chain-card.js +0 -211
  55. package/src/plugin-ui/modules/agent-book/scanner.js +0 -131
  56. package/src/plugin-ui/modules/agent-book/travelcard/travel-cards.js +0 -500
  57. package/src/plugin-ui/modules/agent-book/travelcard/travel-engine.js +0 -652
  58. package/src/plugin-ui/modules/agent-book/travelcard/travel-styles.js +0 -262
  59. package/src/plugin-ui/modules/loader/bootstrap.js +0 -133
  60. package/src/plugin-ui/modules/loader/shared-state.js +0 -838
  61. package/src/plugin-ui/modules/loader/ws-intercept.js +0 -199
  62. package/src/plugin-ui/modules/remote-agent/chunk-parser.js +0 -161
  63. package/src/plugin-ui/modules/remote-agent/execution-card.js +0 -530
  64. package/src/plugin-ui/modules/remote-agent/markdown-renderer.js +0 -256
  65. package/src/plugin-ui/modules/remote-agent/media-links.js +0 -151
  66. package/src/plugin-ui/modules/remote-agent/native-style-adapter.js +0 -134
  67. package/src/plugin-ui/modules/remote-agent/output-card.js +0 -342
  68. package/src/plugin-ui/modules/remote-agent/progress-store.js +0 -363
  69. package/src/plugin-ui/modules/remote-agent/render-hooks.js +0 -1700
  70. package/src/plugin-ui/modules/remote-agent/styles.js +0 -903
  71. package/src/plugin-ui/modules/remote-agent/tool-card-model.js +0 -130
  72. package/src/plugin-ui/postinstall-deploy.cjs +0 -52
  73. package/src/plugin-ui/ui-extension-loader/backup.ts +0 -92
  74. package/src/plugin-ui/ui-extension-loader/index.ts +0 -276
  75. package/src/plugin-ui/ui-extension-loader/locator.ts +0 -152
  76. package/src/plugin-ui/ui-extension-loader/manifest.ts +0 -57
  77. package/src/plugin-ui/ui-extension-loader/registry-regex.ts +0 -944
  78. package/src/plugin-ui/ui-extension-loader/removed-extensions.ts +0 -70
  79. package/src/plugin-ui/ui-extension-loader/types.ts +0 -72
  80. package/src/proxy/auth-proxy.ts +0 -414
  81. package/src/runtime/account.ts +0 -549
  82. package/src/runtime/index.ts +0 -7
  83. package/src/runtime/plugin-runtime.ts +0 -94
  84. package/src/runtime/registry.ts +0 -71
  85. package/src/runtime/update-checker.ts +0 -286
  86. package/src/sdk/CLASS_MAP.md +0 -143
  87. package/src/sdk/index.d.ts +0 -126
  88. package/src/sdk/index.js +0 -23990
  89. package/src/sdk/modules/cloud-search-module.js +0 -1117
  90. package/src/sdk/modules/follow-module.js +0 -1069
  91. package/src/sdk/modules/group-module.js +0 -7397
  92. package/src/sdk/modules/relationship-module.js +0 -2269
  93. package/src/sdk/modules/signaling-module.js +0 -1468
  94. package/src/sdk/modules/tim-upload-plugin.js +0 -730
  95. package/src/sdk/node-env/http-request.js +0 -90
  96. package/src/sdk/node-env/index.js +0 -57
  97. package/src/sdk/node-env/storage.js +0 -114
  98. package/src/sdk/package.json +0 -10
  99. package/src/sdk/tsconfig.json +0 -16
  100. package/src/state/pending-invocation-store.ts +0 -43
  101. package/src/state/store.ts +0 -756
  102. package/src/tim/c2c.ts +0 -451
  103. package/src/tim/channels.ts +0 -364
  104. package/src/tim/client.ts +0 -330
  105. package/src/tim/index.ts +0 -18
  106. package/src/tim/messages.ts +0 -166
  107. package/src/tim/sdk-logger-init.ts +0 -50
  108. package/src/tools.ts +0 -10
  109. package/src/transport/factory.ts +0 -95
  110. package/src/transport/oasn/index.ts +0 -17
  111. package/src/transport/oasn/oasn-agent-card.ts +0 -111
  112. package/src/transport/oasn/oasn-discovery.ts +0 -108
  113. package/src/transport/oasn/oasn-files.ts +0 -210
  114. package/src/transport/oasn/oasn-http.ts +0 -529
  115. package/src/transport/oasn/oasn-invocation.ts +0 -604
  116. package/src/transport/oasn/oasn-normalize.ts +0 -133
  117. package/src/transport/oasn/oasn-register.ts +0 -33
  118. package/src/transport/oasn/oasn-transport.ts +0 -318
  119. package/src/transport/oasn/oasn-types.ts +0 -404
  120. package/src/transport/tim/index.ts +0 -8
  121. package/src/transport/tim/tim-transport.ts +0 -515
  122. package/src/transport/types.ts +0 -538
  123. package/src/types/openclaw-plugin-sdk-media-store.d.ts +0 -9
  124. package/src/types/openclaw.d.ts +0 -97
  125. package/src/types/tencentcloud-chat.d.ts +0 -15
  126. package/src/util/http.ts +0 -113
  127. package/src/util/logger.ts +0 -131
  128. package/src/util/url-resolver.ts +0 -77
@@ -1,273 +0,0 @@
1
- /**
2
- * Discovery Tools — Agent 搜索 / Agent 详情
3
- *
4
- * 封装 OASN DiscoveryTransport 能力为 LLM 可用工具。
5
- * 当前仅 OASN 模式可用;TIM 模式下工具返回明确错误。
6
- *
7
- * 设计依据:docs/specs/2026-06-21-oasn-agent-search-tool-wiring.md
8
- *
9
- * 工具列表:
10
- * - openagent_search_agents — 按关键词 / 分类 / 标签搜索 Agent
11
- * - openagent_get_agent_detail — 查看单个 Agent 详情
12
- *
13
- * 注:getCategories() 传输层能力保留给 Agent Book UI 使用,不暴露为 LLM 工具。
14
- */
15
-
16
- import { registry } from '../runtime/registry.js';
17
- import { logger } from '../util/logger.js';
18
- import {
19
- toolResult,
20
- type ToolDescriptor,
21
- type ToolFactory,
22
- type ToolParams,
23
- type ToolResult,
24
- } from './types.js';
25
- import type { SearchQuery, AgentCard, OasnTransport } from '../transport/types.js';
26
-
27
- // ── Helpers ──
28
-
29
- function fail(error: string): ToolResult {
30
- return toolResult(JSON.stringify({ success: false, error }));
31
- }
32
-
33
- /**
34
- * 从 AccountRuntime 获取 OASN transport 并 narrow 为 DiscoveryTransport。
35
- * 返回 { error: string } 表示不可用(未连接 / TIM 模式 / transport 未就绪)。
36
- */
37
- function getOasnDiscovery(): { error: string } | { transport: OasnTransport } {
38
- const rt = registry.getDefault();
39
- if (!rt || !rt.isRunning) return { error: 'OpenAgent runtime not running' };
40
- if (rt.transportType !== 'oasn') {
41
- return { error: 'Agent discovery is only available via OASN transport' };
42
- }
43
- const transport = rt.transport;
44
- if (!transport || transport.transportType !== 'oasn') {
45
- return { error: 'OASN transport not ready' };
46
- }
47
- // transportType check above guarantees OASN transport
48
- return { transport: transport as OasnTransport };
49
- }
50
-
51
- /**
52
- * 将 AgentCard 转换为 LLM 友好的精简视图。
53
- * 去除内部字段(agent_type / visibility),保留用户关心的信息。
54
- */
55
- function agentCardToSummary(card: AgentCard) {
56
- return {
57
- agent_id: card.agent_id,
58
- name: card.name,
59
- description: card.description,
60
- tags: card.tags?.map((t) => t.name) ?? [],
61
- category: card.category,
62
- avatar_url: card.avatar_url,
63
- };
64
- }
65
-
66
- // ═══════════════════════════════════════════════════════════════
67
- // 工具 1: openagent_search_agents
68
- // ═══════════════════════════════════════════════════════════════
69
-
70
- /**
71
- * 搜索可用 Agent。
72
- *
73
- * 三种搜索模式(互斥,按优先级):
74
- * 1. query — 自然语言搜索(POST /api/agent-cards/search/by-query)
75
- * 2. category — 分类浏览(POST /api/agent-cards/search/by-category)
76
- * 3. tags — 标签筛选(POST /api/agent-cards/search/by-tags)
77
- *
78
- * LLM 使用场景:
79
- * - 用户说"我想去旅行" → search_agents({ query: "旅行" })
80
- * - 用户说"找个机票助手" → search_agents({ query: "机票" })
81
- * - 用户说"看看有哪些旅行类 Agent" → search_agents({ category: ["travel"] })
82
- */
83
- export const createSearchAgentsTool: ToolFactory = () => ({
84
- name: 'openagent_search_agents',
85
- label: 'Search Available Agents',
86
- description: [
87
- 'Search for available service agents by natural language query, category, or tags.',
88
- 'Use this tool when the user expresses an intent that might be handled by a specialist agent.',
89
- 'Examples:',
90
- ' - "我想去旅行" → search_agents({ query: "旅行" })',
91
- ' - "帮我查航班" → search_agents({ query: "机票" })',
92
- ' - "有什么翻译助手" → search_agents({ query: "翻译" })',
93
- 'After finding agents, pick the most relevant one and call openagent_call_remote_agent with its agent_id.',
94
- 'At least one of query, category, or tags must be provided.',
95
- ].join(' '),
96
- parameters: {
97
- type: 'object',
98
- properties: {
99
- query: {
100
- type: 'string',
101
- description: 'Natural language search query, e.g. "旅行" or "机票预订"',
102
- },
103
- category: {
104
- type: 'string',
105
- description: 'Category path to browse, e.g. "travel" or ["travel", "flights"]',
106
- },
107
- tags: {
108
- type: 'string',
109
- description: 'Comma-separated tag names to filter by, e.g. "机票,酒店"',
110
- },
111
- page_size: {
112
- type: 'number',
113
- description: 'Results per page (default 10, max 50)',
114
- default: 10,
115
- },
116
- page_num: {
117
- type: 'number',
118
- description: 'Page number, 1-indexed (default 1)',
119
- default: 1,
120
- },
121
- },
122
- required: [], // 至少提供一个: query / category / tags(运行时校验)
123
- },
124
-
125
- async execute(_id: unknown, params: ToolParams): Promise<ToolResult> {
126
- logger.info(`[discovery-tools] search_agents called: ${JSON.stringify(params)}`);
127
-
128
- // ① 获取 OASN transport
129
- const discovery = getOasnDiscovery();
130
- if ('error' in discovery) {
131
- logger.warn(`[discovery-tools] search_agents unavailable: ${discovery.error}`);
132
- return fail(discovery.error);
133
- }
134
-
135
- // ② 构造 SearchQuery
136
- const searchQuery = buildSearchQuery(params);
137
- if (!searchQuery.query && !searchQuery.category?.length && !searchQuery.tags?.length) {
138
- return fail('At least one of query, category, or tags must be provided');
139
- }
140
-
141
- // ③ 调用 transport.searchAgents()
142
- try {
143
- const result = await discovery.transport.searchAgents(searchQuery);
144
- logger.info(
145
- `[discovery-tools] search_agents returned: searchId=${result.searchId} ` +
146
- `count=${result.agentCards.length} hasNext=${result.hasNext}`,
147
- );
148
-
149
- return toolResult(JSON.stringify({
150
- success: true,
151
- search_id: result.searchId,
152
- agents: result.agentCards.map(agentCardToSummary),
153
- page: {
154
- num: result.pageNum,
155
- size: result.pageSize,
156
- has_next: result.hasNext,
157
- },
158
- }, null, 2));
159
- } catch (err) {
160
- const msg = (err as Error).message;
161
- logger.error(`[discovery-tools] search_agents failed: ${msg}`);
162
- return fail(`Agent search failed: ${msg}`);
163
- }
164
- },
165
- });
166
-
167
- /**
168
- * 将工具参数转换为 SearchQuery。
169
- * 处理类型转换(string → string[])和默认值。
170
- */
171
- function buildSearchQuery(params: ToolParams): SearchQuery {
172
- const query: SearchQuery = {
173
- agentTypes: ['service'], // 只搜索 service 类型
174
- pageSize: params.page_size ? Number(params.page_size) : 10,
175
- pageNum: params.page_num ? Number(params.page_num) : 1,
176
- };
177
-
178
- // query: 自然语言搜索
179
- if (params.query && typeof params.query === 'string') {
180
- query.query = params.query.trim();
181
- }
182
-
183
- // category: 可能是 string 或 string[]
184
- if (params.category) {
185
- if (Array.isArray(params.category)) {
186
- query.category = params.category.map(String);
187
- } else if (typeof params.category === 'string') {
188
- // 支持逗号分隔或单值
189
- query.category = params.category.split(',').map((s) => s.trim()).filter(Boolean);
190
- }
191
- }
192
-
193
- // tags: 可能是 string 或 string[]
194
- if (params.tags) {
195
- if (Array.isArray(params.tags)) {
196
- query.tags = params.tags.map(String);
197
- } else if (typeof params.tags === 'string') {
198
- query.tags = params.tags.split(',').map((s) => s.trim()).filter(Boolean);
199
- }
200
- }
201
-
202
- return query;
203
- }
204
-
205
- // ═══════════════════════════════════════════════════════════════
206
- // 工具 2: openagent_get_agent_detail
207
- // ═══════════════════════════════════════════════════════════════
208
-
209
- /**
210
- * 查看单个 Agent 详情。
211
- *
212
- * 在搜索结果中选择了一个 Agent 后,可以调用此工具获取完整信息(包括 MOM)。
213
- */
214
- export const createGetAgentDetailTool: ToolFactory = () => ({
215
- name: 'openagent_get_agent_detail',
216
- label: 'Get Agent Details',
217
- description: [
218
- 'Get detailed information about a specific agent by its agent_id.',
219
- 'Use this after search_agents to see the full agent card including description and MOM (Model Operation Manual).',
220
- 'The MOM contains usage instructions for the agent.',
221
- ].join(' '),
222
- parameters: {
223
- type: 'object',
224
- properties: {
225
- agent_id: {
226
- type: 'string',
227
- description: 'The agent_id to look up (from search_agents results)',
228
- },
229
- },
230
- required: ['agent_id'],
231
- },
232
-
233
- async execute(_id: unknown, params: ToolParams): Promise<ToolResult> {
234
- const agentId = String(params.agent_id || '').trim();
235
- if (!agentId) {
236
- return fail('agent_id is required');
237
- }
238
-
239
- logger.info(`[discovery-tools] get_agent_detail called: agent_id=${agentId}`);
240
-
241
- const discovery = getOasnDiscovery();
242
- if ('error' in discovery) {
243
- return fail(discovery.error);
244
- }
245
-
246
- try {
247
- const card = await discovery.transport.getAgent(agentId);
248
-
249
- if (!card) {
250
- return fail(`Agent not found: ${agentId}`);
251
- }
252
-
253
- logger.info(`[discovery-tools] get_agent_detail returned: name=${card.name}`);
254
-
255
- return toolResult(JSON.stringify({
256
- success: true,
257
- agent: {
258
- agent_id: card.agent_id,
259
- name: card.name,
260
- description: card.description,
261
- MOM: card.MOM,
262
- tags: card.tags?.map((t: { tag_id: string; name: string }) => t.name) ?? [],
263
- category: card.category,
264
- avatar_url: card.avatar_url,
265
- },
266
- }, null, 2));
267
- } catch (err) {
268
- const msg = (err as Error).message;
269
- logger.error(`[discovery-tools] get_agent_detail failed: ${msg}`);
270
- return fail(`Failed to get agent details: ${msg}`);
271
- }
272
- },
273
- });
@@ -1,133 +0,0 @@
1
- /**
2
- * Download File Tool — openagent_download_file
3
- *
4
- * Downloads an artifact returned by a remote OASN agent and saves it to local disk.
5
- *
6
- * API: GET /api/artifacts/{id}/content (access layer)
7
- * Ref: access-upload-download-api.md §3
8
- */
9
-
10
- import fs from 'node:fs';
11
- import nodePath from 'node:path';
12
- import { resolveOasnAccessUrl } from '../util/url-resolver.js';
13
-
14
- import { registry } from '../runtime/registry.js';
15
- import { logger } from '../util/logger.js';
16
- import {
17
- toolResult,
18
- type ToolDescriptor,
19
- type ToolParams,
20
- type ToolResult,
21
- } from './types.js';
22
- import type { OasnTransport } from '../transport/types.js';
23
-
24
- // ── Helpers ──
25
-
26
- function fail(error: string): ToolResult {
27
- return toolResult(JSON.stringify({ success: false, error }));
28
- }
29
-
30
- function success(result: Record<string, unknown>): ToolResult {
31
- return toolResult(JSON.stringify({ success: true, ...result }));
32
- }
33
-
34
- // ── Tool Factory ──
35
-
36
- export function createDownloadFileTool(): ToolDescriptor {
37
- return {
38
- name: 'openagent_download_file',
39
- label: 'Download File',
40
- description:
41
- 'Download an artifact file returned by a remote OASN agent and save it to disk. ' +
42
- 'Use this when a remote agent returns a file (PDF, image, document, etc.) ' +
43
- 'that the user wants to save locally. ' +
44
- 'The content_url comes from the remote agent\'s response artifacts. ' +
45
- 'Look for artifact URLs in the openagent_call_remote_agent result. ' +
46
- 'If the save_path parent directory does not exist it will be created automatically. ' +
47
- 'Existing files at save_path will be overwritten.',
48
- parameters: {
49
- type: 'object',
50
- properties: {
51
- content_url: {
52
- type: 'string',
53
- description:
54
- 'The artifact content URL from the remote agent result ' +
55
- '(e.g., /api/artifacts/art_1/content?session_id=...&invocation_id=...)',
56
- },
57
- save_path: {
58
- type: 'string',
59
- description: 'Absolute local path where the file should be saved',
60
- },
61
- },
62
- required: ['content_url', 'save_path'],
63
- },
64
-
65
- async execute(
66
- _id: unknown,
67
- params: ToolParams,
68
- ): Promise<ToolResult> {
69
- const contentUrl = String(params.content_url || '').trim();
70
- const savePath = String(params.save_path || '').trim();
71
-
72
- if (!contentUrl) {
73
- return fail('content_url is required');
74
- }
75
- if (!savePath) {
76
- return fail('save_path is required');
77
- }
78
- if (!nodePath.isAbsolute(savePath)) {
79
- return fail('save_path must be an absolute path');
80
- }
81
-
82
- // ① Check runtime state
83
- const rt = registry.getDefault();
84
- if (!rt || !rt.isRunning) {
85
- return fail('OpenAgent runtime not running');
86
- }
87
- if (rt.transportType !== 'oasn') {
88
- return fail('File download is only available via OASN transport');
89
- }
90
- const transport = rt.transport;
91
- if (!transport || transport.transportType !== 'oasn') {
92
- return fail('OASN transport not ready');
93
- }
94
-
95
- // ② Resolve download URL
96
- const accessBase = rt.config?.accessApiBase || rt.config?.apiBase;
97
- const downloadUrl = resolveOasnAccessUrl(accessBase, contentUrl);
98
-
99
- logger.info(
100
- `[download-tool] Downloading artifact: url=${downloadUrl} saveTo=${savePath}`,
101
- );
102
-
103
- // ③ Download
104
- const oasnTransport = transport as OasnTransport;
105
- let downloaded;
106
- try {
107
- downloaded = await oasnTransport.downloadArtifact(downloadUrl);
108
- } catch (err) {
109
- logger.error(`[download-tool] Download failed: ${(err as Error).message}`);
110
- return fail(`Download failed: ${(err as Error).message}`);
111
- }
112
-
113
- // ④ Save to disk
114
- try {
115
- fs.mkdirSync(nodePath.dirname(savePath), { recursive: true });
116
- fs.writeFileSync(savePath, Buffer.from(downloaded.content));
117
- } catch (err) {
118
- return fail(`Cannot write to path: ${(err as Error).message}`);
119
- }
120
-
121
- logger.info(
122
- `[download-tool] Download success: path=${savePath} size=${downloaded.content.byteLength}`,
123
- );
124
-
125
- return success({
126
- savedPath: savePath,
127
- sizeBytes: downloaded.content.byteLength,
128
- mimeType: downloaded.mimeType,
129
- displayName: downloaded.displayName,
130
- });
131
- },
132
- };
133
- }
package/src/app/hooks.ts DELETED
@@ -1,144 +0,0 @@
1
- /**
2
- * Hooks — Scene + Purpose injection + Inline Image Offload
3
- *
4
- * Registers hooks with the OpenClaw API:
5
- * 1. before_prompt_build — injects channel-specific rules (skill) and agent purpose
6
- * 2. agent_turn_prepare — offloads inline base64 images to ~/.openclaw/media/inbound/
7
- * so the model can reference them via media://inbound/<id>
8
- */
9
-
10
- import { registry } from '../runtime/registry.js';
11
- import { logger } from '../util/logger.js';
12
-
13
- interface HookEvent {
14
- [key: string]: unknown;
15
- }
16
-
17
- interface HookContext {
18
- sessionId?: string;
19
- [key: string]: unknown;
20
- }
21
-
22
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
- type AnyApi = any;
24
-
25
- /**
26
- * Register all OpenAgent hooks with OpenClaw API.
27
- */
28
- export function registerHooks(api: AnyApi): void {
29
- // ── Hook 1: Scene + Purpose injection ──
30
- api.registerHook('before_prompt_build', async (_event: HookEvent, ctx: HookContext) => {
31
- // Only fire for openagent channel sessions
32
- if (!ctx.sessionId?.startsWith('openagent:')) return {};
33
-
34
- const channelId = ctx.sessionId.replace('openagent:', '');
35
- const rt = registry.getDefault();
36
- if (!rt) return {};
37
-
38
- const parts: string[] = [];
39
-
40
- // Inject channel rules (skill/scene)
41
- try {
42
- const rules = await rt.getChannelSkill(channelId);
43
- if (rules) parts.push(`[SCENE: ${channelId}]\n${rules}\n[/SCENE]`);
44
- } catch (err) {
45
- logger.warn(`[hooks] rules fetch failed: ${(err as Error).message}`);
46
- }
47
-
48
- // Inject agent purpose for this channel
49
- try {
50
- const purpose = rt.store?.getPurpose(channelId);
51
- if (purpose) parts.push(`[PURPOSE]\n${purpose}\n[/PURPOSE]`);
52
- } catch (err) {
53
- logger.warn(`[hooks] purpose fetch failed: ${(err as Error).message}`);
54
- }
55
-
56
- if (parts.length === 0) return {};
57
-
58
- logger.debug(`[hooks] Injected Scene+Purpose for ${channelId}`);
59
- return { appendSystemContext: parts.join('\n\n') };
60
- }, { name: 'openagent-scene-purpose' });
61
-
62
- // ── Hook 2: Inline image offload ──
63
- //
64
- // When a user uploads a file via Web UI and the model supports vision,
65
- // images < 2MB are sent inline (base64 in the message content) and never
66
- // saved to disk. This hook intercepts those inline images via the llm_input
67
- // event (fires for ALL agent sessions including embedded), extracts the
68
- // base64 data, and writes it to ~/.openclaw/media/inbound/ via saveMediaBuffer.
69
- //
70
- // The model won't see a media://inbound/ URL in its prompt, but
71
- // openagent_upload_file's auto-fallback (resolveLatestInboundFile) will
72
- // pick up the most recently saved file automatically.
73
- //
74
- api.registerHook('llm_input', async (event: HookEvent) => {
75
- const historyMessages = event.historyMessages as unknown[] | undefined;
76
- if (!Array.isArray(historyMessages) || historyMessages.length === 0) return;
77
-
78
- logger.info(
79
- `[hooks] llm_input FIRED imagesCount=${event.imagesCount} historyMsgs=${historyMessages.length}`,
80
- );
81
-
82
- // Scan ALL messages (not just the last user message) for inline images
83
- const MAX_BYTES = 20 * 1024 * 1024;
84
- let savedCount = 0;
85
-
86
- for (const msg of historyMessages as Record<string, unknown>[]) {
87
- if (!msg || typeof msg !== 'object') continue;
88
- const contentRaw = msg.content;
89
- const contentBlocks: Record<string, unknown>[] = Array.isArray(contentRaw)
90
- ? contentRaw as Record<string, unknown>[]
91
- : [];
92
-
93
- for (const block of contentBlocks) {
94
- let base64: string | undefined;
95
- let mimeType: string | undefined;
96
-
97
- // Anthropic-style: { type: "image", source: { type: "base64", media_type: "...", data: "..." } }
98
- if (block?.type === 'image') {
99
- const source = block.source as Record<string, unknown> | undefined;
100
- if (source?.type === 'base64' && typeof source.data === 'string') {
101
- base64 = source.data;
102
- mimeType = (source.media_type as string) || 'image/png';
103
- }
104
- }
105
-
106
- // OpenAI-style: { type: "image_url", image_url: { url: "data:image/png;base64,..." } }
107
- if (!base64 && block?.type === 'image_url') {
108
- const imageUrl = (block.image_url as Record<string, unknown> | undefined)?.url;
109
- if (typeof imageUrl === 'string') {
110
- const match = imageUrl.match(/^data:([^;]+);base64,(.+)$/);
111
- if (match) {
112
- mimeType = match[1]!;
113
- base64 = match[2]!;
114
- }
115
- }
116
- }
117
-
118
- if (!base64) continue;
119
-
120
- const buffer = Buffer.from(base64, 'base64');
121
- const mime = mimeType || 'application/octet-stream';
122
- const ext = mime.split('/')[1] || 'bin';
123
-
124
- try {
125
- const { saveMediaBuffer } = await import('openclaw/plugin-sdk/media-store');
126
- const saved = await saveMediaBuffer(buffer, mime, 'inbound', MAX_BYTES, `webui-inline.${ext}`);
127
- savedCount++;
128
- logger.info(
129
- `[hooks] Offloaded inline image: media://inbound/${saved.id} ` +
130
- `size=${buffer.byteLength} mime=${mime}`,
131
- );
132
- } catch (err) {
133
- logger.warn(
134
- `[hooks] Failed to save inline image: ${(err as Error).message}`,
135
- );
136
- }
137
- }
138
- }
139
-
140
- if (savedCount > 0) {
141
- logger.info(`[hooks] llm_input saved ${savedCount} inline image(s) to media store`);
142
- }
143
- }, { name: 'openagent-inline-image-offload' });
144
- }
package/src/app/index.ts DELETED
@@ -1,84 +0,0 @@
1
- /**
2
- * App — Tool + Hook registration aggregator
3
- *
4
- * Registers all tools and hooks with the OpenClaw API.
5
- */
6
-
7
- import {
8
- createListChannelsTool,
9
- createSearchChannelsTool,
10
- createJoinChannelTool,
11
- createLeaveChannelTool,
12
- createCreateChannelTool,
13
- createGetMembersTool,
14
- createSetChannelSkillTool,
15
- createGetChannelSkillTool,
16
- } from './channel-tools.js';
17
-
18
- import {
19
- createSendMessageTool,
20
- createGetMessagesTool,
21
- } from './messaging-tools.js';
22
-
23
- import {
24
- createSetStrategyTool,
25
- createGetStatusTool,
26
- createSetPurposeTool,
27
- createGetPurposeTool,
28
- } from './ops-tools.js';
29
-
30
- import { createCallRemoteAgentTool } from './remote-agent-tool.js';
31
- import {
32
- createSearchAgentsTool,
33
- createGetAgentDetailTool,
34
- } from './discovery-tools.js';
35
- import { createUploadFileTool } from './upload-file-tool.js';
36
- import { createDownloadFileTool } from './download-file-tool.js';
37
-
38
- import { registerHooks } from './hooks.js';
39
-
40
- import type { ToolFactory } from './types.js';
41
-
42
- import type { OpenClawPluginApi } from 'openclaw/plugin-sdk/plugin-entry';
43
-
44
- /**
45
- * Register all tools with OpenClaw API.
46
- */
47
- export function registerTools(api: OpenClawPluginApi): void {
48
- // ── Channel management (8) — DORMANT: code preserved, not registered ──
49
- // Uncomment to re-enable channel-based social networking features.
50
- // api.registerTool(createListChannelsTool, { optional: false });
51
- // api.registerTool(createSearchChannelsTool, { optional: false });
52
- // api.registerTool(createJoinChannelTool, { optional: false });
53
- // api.registerTool(createLeaveChannelTool, { optional: false });
54
- // api.registerTool(createCreateChannelTool, { optional: false });
55
- // api.registerTool(createGetMembersTool, { optional: false });
56
- // api.registerTool(createSetChannelSkillTool, { optional: false });
57
- // api.registerTool(createGetChannelSkillTool, { optional: false });
58
-
59
- // ── Messaging (2) — DORMANT ──
60
- // api.registerTool(createSendMessageTool, { optional: false });
61
- // api.registerTool(createGetMessagesTool, { optional: false });
62
-
63
- // ── Ops (4) — DORMANT ──
64
- // api.registerTool(createSetStrategyTool, { optional: false });
65
- // api.registerTool(createGetStatusTool, { optional: false });
66
- // api.registerTool(createSetPurposeTool, { optional: false });
67
- // api.registerTool(createGetPurposeTool, { optional: false });
68
-
69
- // ── Remote Agent Orchestration (1) — ACTIVE ──
70
- api.registerTool(createCallRemoteAgentTool, { optional: false });
71
-
72
- // ── Agent Discovery (2) — ACTIVE (OASN-only) ──
73
- // 搜索 / Agent 详情。TIM 模式下工具存在但返回明确错误。
74
- // 注:getCategories() 传输层能力保留给 Agent Book UI 使用,不暴露为 LLM 工具。
75
- api.registerTool(createSearchAgentsTool, { optional: false });
76
- api.registerTool(createGetAgentDetailTool, { optional: false });
77
-
78
- // ── File Upload / Download (2) — ACTIVE (OASN-only) ──
79
- api.registerTool(createUploadFileTool, { optional: false });
80
- api.registerTool(createDownloadFileTool, { optional: false });
81
- }
82
-
83
- export { registerHooks };
84
- export type { ToolDescriptor, ToolFactory } from './types.js';