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
package/src/app/types.ts DELETED
@@ -1,68 +0,0 @@
1
- /**
2
- * Shared tool types
3
- *
4
- * Return format aligned with OpenClaw SDK's AgentToolResult:
5
- * @see https://docs.openclaw.ai (Plugin SDK → Building Plugins)
6
- * @see docs/reference/openclaw/v2026.3.23/dist/plugin-sdk/src/agents/tools/common.d.ts
7
- */
8
-
9
- export interface ToolParams {
10
- [key: string]: unknown;
11
- }
12
-
13
- /** SDK-compatible tool result (matches AgentToolResult structure) */
14
- export interface ToolResult {
15
- content: Array<{ type: 'text'; text: string }>;
16
- /** Optional details for rich media. Matches OpenClaw AgentToolResult<TDetails>. */
17
- details?: {
18
- media?: { mediaUrls: string[] };
19
- [key: string]: unknown;
20
- };
21
- }
22
-
23
- export type ToolUpdateCallback = (partialResult: ToolResult) => void;
24
-
25
- export interface ToolDescriptor {
26
- name: string;
27
- label: string;
28
- description: string;
29
- parameters: {
30
- type: 'object';
31
- properties: Record<string, {
32
- type: string;
33
- description: string;
34
- enum?: string[];
35
- default?: unknown;
36
- items?: { type: string };
37
- }>;
38
- required: string[];
39
- };
40
- /**
41
- * Execute the tool.
42
- *
43
- * OpenClaw runtime usually calls:
44
- * tool.execute(toolCallId, params, signal, onUpdate, ctx)
45
- *
46
- * Older adapters used:
47
- * tool.execute(toolCallId, params, onUpdate, ctx, signal)
48
- *
49
- * The wider signature lets streaming tools normalize both shapes while
50
- * existing 2-param tools continue to work unchanged.
51
- *
52
- * @see docs/reference/openclaw/v2026.3.23/dist/pi-embedded-CswW9luA.js L169823
53
- */
54
- execute: (
55
- _id: unknown,
56
- params: ToolParams,
57
- signalOrUpdate?: AbortSignal | ToolUpdateCallback,
58
- onUpdateOrContext?: ToolUpdateCallback | unknown,
59
- contextOrSignal?: unknown,
60
- ) => Promise<ToolResult>;
61
- }
62
-
63
- export type ToolFactory = () => ToolDescriptor;
64
-
65
- /** Helper: create a successful text result in SDK format */
66
- export function toolResult(text: string): ToolResult {
67
- return { content: [{ type: 'text', text }] };
68
- }
@@ -1,411 +0,0 @@
1
- /**
2
- * Upload File Tool — openagent_upload_file
3
- *
4
- * Uploads a local file to the OASN agent network, returning a fileRef
5
- * that can be passed to openagent_call_remote_agent via input_file_refs.
6
- *
7
- * Supports three input modes:
8
- * - file_path: absolute path to a local file
9
- * - media_ref: OpenClaw media://inbound/<id> reference (from web UI attachments)
10
- * - filename_hint: original filename to scan & match from ~/.openclaw/media/inbound/
11
- *
12
- * API: POST /api/files (access layer)
13
- * Ref: access-upload-download-api.md §2
14
- */
15
-
16
- import fs from 'node:fs';
17
- import os from 'node:os';
18
- import nodePath from 'node:path';
19
-
20
- import { registry } from '../runtime/registry.js';
21
- import { logger } from '../util/logger.js';
22
- import {
23
- toolResult,
24
- type ToolDescriptor,
25
- type ToolParams,
26
- type ToolResult,
27
- } from './types.js';
28
- import type { OasnTransport, UploadFileParams } from '../transport/types.js';
29
-
30
- // ── Helpers ──
31
-
32
- const MAX_FILE_SIZE_BYTES = 100 * 1024 * 1024; // 100 MB
33
-
34
- function fail(error: string): ToolResult {
35
- return toolResult(JSON.stringify({ success: false, error }));
36
- }
37
-
38
- function success(result: Record<string, unknown>): ToolResult {
39
- return toolResult(JSON.stringify({ success: true, ...result }));
40
- }
41
-
42
- /** 根据文件扩展名推断 MIME 类型 */
43
- function inferMimeType(filePath: string): string {
44
- const ext = nodePath.extname(filePath).toLowerCase();
45
- const map: Record<string, string> = {
46
- '.pdf': 'application/pdf',
47
- '.png': 'image/png',
48
- '.jpg': 'image/jpeg',
49
- '.jpeg': 'image/jpeg',
50
- '.gif': 'image/gif',
51
- '.svg': 'image/svg+xml',
52
- '.webp': 'image/webp',
53
- '.txt': 'text/plain',
54
- '.md': 'text/markdown',
55
- '.json': 'application/json',
56
- '.csv': 'text/csv',
57
- '.html': 'text/html',
58
- '.css': 'text/css',
59
- '.js': 'text/javascript',
60
- '.ts': 'text/typescript',
61
- '.py': 'text/x-python',
62
- '.xml': 'application/xml',
63
- '.zip': 'application/zip',
64
- '.tar': 'application/x-tar',
65
- '.gz': 'application/gzip',
66
- '.mp3': 'audio/mpeg',
67
- '.mp4': 'video/mp4',
68
- '.doc': 'application/msword',
69
- '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
70
- '.xls': 'application/vnd.ms-excel',
71
- '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
72
- '.ppt': 'application/vnd.ms-powerpoint',
73
- '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
74
- };
75
- return map[ext] || 'application/octet-stream';
76
- }
77
-
78
- /**
79
- * Resolve a media://inbound/<id> reference to an absolute file path.
80
- * Uses OpenClaw's resolveMediaBufferPath when available, else falls back
81
- * to computing the path from ~/.openclaw/media/inbound/<id>.
82
- */
83
- async function resolveMediaRef(mediaRef: string): Promise<string> {
84
- // Parse media://inbound/<id>
85
- const match = mediaRef.match(/^media:\/\/inbound\/(.+)$/);
86
- if (!match) {
87
- throw new Error(`Unsupported media reference format: ${mediaRef}. Expected media://inbound/<id>`);
88
- }
89
- const id = match[1]!;
90
-
91
- // Try OpenClaw SDK's resolveMediaBufferPath first
92
- try {
93
- const { resolveMediaBufferPath } = await import('openclaw/plugin-sdk/media-runtime');
94
- return await resolveMediaBufferPath(id, 'inbound');
95
- } catch {
96
- // Fallback: compute path manually
97
- const mediaDir = nodePath.join(os.homedir(), '.openclaw', 'media', 'inbound');
98
- const resolved = nodePath.join(mediaDir, id);
99
- // Security: ensure resolved path stays within mediaDir
100
- if (!resolved.startsWith(mediaDir + nodePath.sep) && resolved !== mediaDir) {
101
- throw new Error(`Unsafe media ID: ${id}`);
102
- }
103
- if (!fs.existsSync(resolved)) {
104
- throw new Error(`Media file not found: ${id}. The attachment may have been sent inline (not saved to disk). Use file_path to upload a local file instead.`);
105
- }
106
- return resolved;
107
- }
108
- }
109
-
110
- /** Recency window for filename_hint matching (milliseconds) */
111
- const FILENAME_HINT_RECENCY_MS = 5 * 60 * 1000;
112
-
113
- /**
114
- * Scan ~/.openclaw/media/inbound/ for recently modified files,
115
- * optionally filtered by a predicate. Returns candidates sorted by
116
- * mtime descending (most recent first).
117
- */
118
- function findRecentInboundFiles(
119
- mediaDir: string,
120
- filter?: (name: string) => boolean,
121
- ): { name: string; path: string; mtimeMs: number }[] {
122
- if (!fs.existsSync(mediaDir)) return [];
123
-
124
- let entries: string[];
125
- try {
126
- entries = fs.readdirSync(mediaDir);
127
- } catch {
128
- return [];
129
- }
130
-
131
- const now = Date.now();
132
- return entries
133
- .filter((name) => {
134
- if (name.startsWith('.')) return false;
135
- if (filter && !filter(name)) return false;
136
- try {
137
- const stat = fs.statSync(nodePath.join(mediaDir, name));
138
- return stat.isFile() && (now - stat.mtimeMs) <= FILENAME_HINT_RECENCY_MS;
139
- } catch {
140
- return false;
141
- }
142
- })
143
- .map((name) => {
144
- const fullPath = nodePath.join(mediaDir, name);
145
- const stat = fs.statSync(fullPath);
146
- return { name, path: fullPath, mtimeMs: stat.mtimeMs };
147
- })
148
- .sort((a, b) => b.mtimeMs - a.mtimeMs);
149
- }
150
-
151
- /**
152
- * Resolve a filename hint to an absolute file path by scanning
153
- * ~/.openclaw/media/inbound/ for recently modified files whose
154
- * names contain the hint (case-insensitive substring match).
155
- *
156
- * Handles the disk naming convention: <original-base>---<uuid>.<ext>
157
- * A hint of "photo.png" matches "photo---abc123.png" by extracting
158
- * the base name "photo" and matching it against the stored filename.
159
- */
160
- function resolveByFilenameHint(filenameHint: string): string {
161
- const mediaDir = nodePath.join(os.homedir(), '.openclaw', 'media', 'inbound');
162
-
163
- if (!fs.existsSync(mediaDir)) {
164
- throw new Error(
165
- `Media inbound directory not found: ${mediaDir}. ` +
166
- `Ensure a file has been uploaded via the Web UI first.`,
167
- );
168
- }
169
-
170
- // Normalize hint for matching against <base>---<uuid>.<ext> filenames
171
- const hintLower = filenameHint.toLowerCase();
172
- const lastDot = filenameHint.lastIndexOf('.');
173
- const hintBaseLower = lastDot > 0
174
- ? filenameHint.substring(0, lastDot).toLowerCase()
175
- : hintLower;
176
-
177
- const candidates = findRecentInboundFiles(mediaDir, (name) => {
178
- const nameLower = name.toLowerCase();
179
- return nameLower.includes(hintLower) || nameLower.includes(hintBaseLower);
180
- });
181
-
182
- if (candidates.length === 0) {
183
- throw new Error(
184
- `No recently modified file matching "${filenameHint}" found in the media inbound directory. ` +
185
- `The file may have been renamed, deleted, or uploaded more than 5 minutes ago. ` +
186
- `Try uploading the file again via the Web UI, or use file_path to specify an absolute path.`,
187
- );
188
- }
189
-
190
- // Security: ensure resolved path stays within mediaDir
191
- const resolved = candidates[0]!.path;
192
- const mediaSep = mediaDir + nodePath.sep;
193
- if (!resolved.startsWith(mediaSep) && resolved !== mediaDir) {
194
- throw new Error(`Unsafe path resolved from filename hint: ${filenameHint}`);
195
- }
196
-
197
- return resolved;
198
- }
199
-
200
- /**
201
- * Resolve the most recently uploaded file from ~/.openclaw/media/inbound/.
202
- * Used as a last-resort fallback when the model sees an inline image
203
- * but has no filename or path information at all.
204
- */
205
- function resolveLatestInboundFile(): string {
206
- const mediaDir = nodePath.join(os.homedir(), '.openclaw', 'media', 'inbound');
207
-
208
- if (!fs.existsSync(mediaDir)) {
209
- throw new Error(
210
- `Media inbound directory not found: ${mediaDir}. ` +
211
- `Ensure a file has been uploaded via the Web UI first.`,
212
- );
213
- }
214
-
215
- const candidates = findRecentInboundFiles(mediaDir);
216
-
217
- if (candidates.length === 0) {
218
- throw new Error(
219
- `No recently uploaded files found in the media inbound directory. ` +
220
- `Upload a file via the Web UI first, then retry. ` +
221
- `If you know the file path, use the file_path parameter instead.`,
222
- );
223
- }
224
-
225
- if (candidates.length > 1) {
226
- // Log a warning so the user/operator can audit unexpected matches
227
- logger.warn(
228
- `[upload-tool] auto-resolved latest of ${candidates.length} inbound files: ` +
229
- `"${candidates[0]!.name}" (others: ${candidates.slice(1, 4).map((c) => c.name).join(', ')})`,
230
- );
231
- }
232
-
233
- const resolved = candidates[0]!.path;
234
- const mediaSep = mediaDir + nodePath.sep;
235
- if (!resolved.startsWith(mediaSep) && resolved !== mediaDir) {
236
- throw new Error('Unsafe path resolved from latest inbound file');
237
- }
238
-
239
- return resolved;
240
- }
241
-
242
- // ── Tool Factory ──
243
-
244
- export function createUploadFileTool(): ToolDescriptor {
245
- return {
246
- name: 'openagent_upload_file',
247
- label: 'Upload File',
248
- description:
249
- 'Upload a local file to the OASN agent network before calling a remote agent. ' +
250
- 'Use this when the user wants to send a file (document, image, code, etc.) ' +
251
- 'to a remote specialist agent for processing. ' +
252
- 'Returns a fileRef string. Pass this fileRef in the ' +
253
- 'openagent_call_remote_agent input_file_refs parameter so the remote agent can access the file. ' +
254
- 'Three input modes (use exactly one, or none to auto-detect): ' +
255
- '(1) file_path: absolute local path (e.g., /Users/name/Downloads/photo.png); ' +
256
- '(2) media_ref: a media://inbound/<id> URL visible in the message text; ' +
257
- '(3) filename_hint: the filename as shown in the upload UI (e.g., "photo.png") — ' +
258
- 'use this when you see an inline image in the chat but NO media://inbound/ URL. ' +
259
- 'The tool will automatically scan recently uploaded files to find the match on disk. ' +
260
- 'If NO parameters are provided, the tool auto-resolves the most recently uploaded file ' +
261
- 'from the Web UI — this is the easiest option when the user just attached a file. ' +
262
- 'Prefer media_ref when you see a media://inbound/ URL, ' +
263
- 'filename_hint when you only see the inline content without a URL, ' +
264
- 'and file_path when the user explicitly tells you a specific path.',
265
- parameters: {
266
- type: 'object',
267
- properties: {
268
- file_path: {
269
- type: 'string',
270
- description: 'Absolute path to a local file (e.g., /Users/.../report.pdf). Use this for files on disk.',
271
- },
272
- media_ref: {
273
- type: 'string',
274
- description: 'OpenClaw media reference from web UI attachments (e.g., media://inbound/photo---uuid.png). Use this when the user attached a file in the chat and you see a media://inbound/ URL.',
275
- },
276
- filename_hint: {
277
- type: 'string',
278
- description:
279
- 'The original filename as shown in the upload UI (e.g., "photo.png", "report.pdf"). ' +
280
- 'Use this when you can see the file content inline but do NOT have a media://inbound/ URL. ' +
281
- 'The tool will scan recently uploaded files to find a match.',
282
- },
283
- display_name: {
284
- type: 'string',
285
- description: 'Optional display name for the file (defaults to filename)',
286
- },
287
- },
288
- required: [],
289
- },
290
-
291
- async execute(
292
- _id: unknown,
293
- params: ToolParams,
294
- ): Promise<ToolResult> {
295
- const filePathRaw = String(params.file_path || '').trim();
296
- const mediaRefRaw = String(params.media_ref || '').trim();
297
- const filenameHint = String(params.filename_hint || '').trim();
298
-
299
- // ① Resolve input source
300
- let filePath: string;
301
- let displayName: string;
302
-
303
- if (mediaRefRaw) {
304
- // media://inbound/<id> reference
305
- try {
306
- filePath = await resolveMediaRef(mediaRefRaw);
307
- displayName = String(params.display_name || '').trim()
308
- || nodePath.basename(filePath);
309
- } catch (err) {
310
- return fail((err as Error).message);
311
- }
312
- } else if (filePathRaw) {
313
- // traditional file path
314
- filePath = filePathRaw;
315
- displayName = String(params.display_name || '').trim()
316
- || nodePath.basename(filePath);
317
- } else if (filenameHint) {
318
- // filename hint → scan ~/.openclaw/media/inbound/ for matching file
319
- try {
320
- filePath = resolveByFilenameHint(filenameHint);
321
- displayName = String(params.display_name || '').trim()
322
- || nodePath.basename(filePath);
323
- } catch (err) {
324
- return fail((err as Error).message);
325
- }
326
- } else {
327
- // fallback: auto-resolve the most recently uploaded file
328
- try {
329
- filePath = resolveLatestInboundFile();
330
- displayName = String(params.display_name || '').trim()
331
- || nodePath.basename(filePath);
332
- logger.info(
333
- `[upload-tool] Auto-resolved latest inbound file: ${filePath}`,
334
- );
335
- } catch (err) {
336
- return fail((err as Error).message);
337
- }
338
- }
339
-
340
- // ② 检查运行态
341
- const rt = registry.getDefault();
342
- if (!rt || !rt.isRunning) {
343
- return fail('OpenAgent runtime not running');
344
- }
345
- if (rt.transportType !== 'oasn') {
346
- return fail('File upload is only available via OASN transport');
347
- }
348
- const transport = rt.transport;
349
- if (!transport || transport.transportType !== 'oasn') {
350
- return fail('OASN transport not ready');
351
- }
352
-
353
- // ③ 读取文件
354
- let stat: fs.Stats;
355
- try {
356
- stat = fs.statSync(filePath);
357
- } catch {
358
- return fail(`File not found: ${filePath}`);
359
- }
360
- if (!stat.isFile()) {
361
- return fail(`Not a file: ${filePath}`);
362
- }
363
- if (stat.size > MAX_FILE_SIZE_BYTES) {
364
- return fail(
365
- `File too large: ${(stat.size / (1024 * 1024)).toFixed(1)} MB (max 100 MB)`,
366
- );
367
- }
368
-
369
- let buffer: Buffer;
370
- try {
371
- buffer = fs.readFileSync(filePath);
372
- } catch (err) {
373
- return fail(`Cannot read file: ${(err as Error).message}`);
374
- }
375
-
376
- // ④ 上传
377
- const mimeType = inferMimeType(filePath);
378
-
379
- const oasnTransport = transport as OasnTransport;
380
- const uploadParams: UploadFileParams = {
381
- content: buffer,
382
- displayName,
383
- mimeType,
384
- };
385
-
386
- logger.info(
387
- `[upload-tool] Uploading file: name=${displayName} size=${buffer.byteLength} mime=${mimeType}`,
388
- );
389
-
390
- let result;
391
- try {
392
- result = await oasnTransport.uploadFile(uploadParams);
393
- } catch (err) {
394
- logger.error(`[upload-tool] Upload failed: ${(err as Error).message}`);
395
- return fail(`Upload failed: ${(err as Error).message}`);
396
- }
397
-
398
- logger.info(
399
- `[upload-tool] Upload success: fileRef=${result.fileRef} size=${result.sizeBytes}`,
400
- );
401
-
402
- return success({
403
- fileRef: result.fileRef,
404
- displayName: result.displayName,
405
- mimeType: result.mimeType,
406
- sizeBytes: result.sizeBytes,
407
- expiresAt: result.expiresAt,
408
- });
409
- },
410
- };
411
- }
@@ -1,190 +0,0 @@
1
- import type { OpenClawPluginApi } from 'openclaw/plugin-sdk/plugin-entry';
2
- import { logger } from '../util/logger.js';
3
-
4
- type AgentEvent = {
5
- runId?: unknown;
6
- stream?: unknown;
7
- data?: unknown;
8
- [key: string]: unknown;
9
- };
10
-
11
- type AgentRunContext = {
12
- verboseLevel?: string;
13
- [key: string]: unknown;
14
- };
15
-
16
- type AgentEventState = {
17
- listeners?: Set<(evt: AgentEvent) => void>;
18
- runContextById?: Map<string, AgentRunContext>;
19
- };
20
-
21
- const AGENT_EVENT_STATE_KEY = Symbol.for('openclaw.agentEvents.state');
22
- const REMOTE_TOOL_CALL_IDS = new Set<string>();
23
- const REMOTE_TOOL_CALL_IDS_BY_RUN = new Map<string, Set<string>>();
24
- const PROMOTED_RUNS = new Set<string>();
25
-
26
- let installed = false;
27
- let missingStateWarned = false;
28
- let missingRuntimeWarned = false;
29
-
30
- function asRecord(value: unknown): Record<string, unknown> {
31
- return value && typeof value === 'object' ? value as Record<string, unknown> : {};
32
- }
33
-
34
- function readString(value: unknown): string {
35
- return typeof value === 'string' ? value : '';
36
- }
37
-
38
- function readToolName(data: Record<string, unknown>): string {
39
- const call = asRecord(data.call);
40
- return (
41
- readString(data.name) ||
42
- readString(data.toolName) ||
43
- readString(data.tool_name) ||
44
- readString(call.name)
45
- ).toLowerCase();
46
- }
47
-
48
- function readToolCallId(data: Record<string, unknown>): string {
49
- const call = asRecord(data.call);
50
- return (
51
- readString(data.toolCallId) ||
52
- readString(data.tool_call_id) ||
53
- readString(data.id) ||
54
- readString(call.id)
55
- );
56
- }
57
-
58
- function isOpenagentRemoteToolName(name: string): boolean {
59
- return name.includes('call_remote_agent') || name.includes('openagent_delegate');
60
- }
61
-
62
- function rememberRemoteToolCall(runId: string, toolCallId: string): void {
63
- REMOTE_TOOL_CALL_IDS.add(toolCallId);
64
- let runToolCallIds = REMOTE_TOOL_CALL_IDS_BY_RUN.get(runId);
65
- if (!runToolCallIds) {
66
- runToolCallIds = new Set<string>();
67
- REMOTE_TOOL_CALL_IDS_BY_RUN.set(runId, runToolCallIds);
68
- }
69
- runToolCallIds.add(toolCallId);
70
- }
71
-
72
- function forgetRemoteToolCall(runId: string, toolCallId: string): void {
73
- REMOTE_TOOL_CALL_IDS.delete(toolCallId);
74
- const runToolCallIds = REMOTE_TOOL_CALL_IDS_BY_RUN.get(runId);
75
- if (!runToolCallIds) return;
76
- runToolCallIds.delete(toolCallId);
77
- if (runToolCallIds.size === 0) {
78
- REMOTE_TOOL_CALL_IDS_BY_RUN.delete(runId);
79
- }
80
- }
81
-
82
- function forgetRun(runId: string): void {
83
- PROMOTED_RUNS.delete(runId);
84
- const runToolCallIds = REMOTE_TOOL_CALL_IDS_BY_RUN.get(runId);
85
- if (!runToolCallIds) return;
86
- for (const toolCallId of runToolCallIds) {
87
- REMOTE_TOOL_CALL_IDS.delete(toolCallId);
88
- }
89
- REMOTE_TOOL_CALL_IDS_BY_RUN.delete(runId);
90
- }
91
-
92
- function getAgentEventState(): AgentEventState | null {
93
- const state = (globalThis as Record<PropertyKey, unknown>)[AGENT_EVENT_STATE_KEY];
94
- if (!state || typeof state !== 'object') return null;
95
- return state as AgentEventState;
96
- }
97
-
98
- function promoteRunVerboseLevel(runId: string, toolCallId: string, toolName: string): void {
99
- const state = getAgentEventState();
100
- const runContextById = state?.runContextById;
101
-
102
- if (!(runContextById instanceof Map)) {
103
- if (!missingStateWarned) {
104
- missingStateWarned = true;
105
- logger.warn('[verbose-preflight] OpenClaw agent event state is unavailable; OpenAgent SSE payload may be stripped');
106
- }
107
- return;
108
- }
109
-
110
- let ctx = runContextById.get(runId);
111
- if (!ctx) {
112
- ctx = {};
113
- runContextById.set(runId, ctx);
114
- }
115
-
116
- if (ctx.verboseLevel !== 'full') {
117
- ctx.verboseLevel = 'full';
118
- }
119
-
120
- if (!PROMOTED_RUNS.has(runId)) {
121
- PROMOTED_RUNS.add(runId);
122
- logger.info(`[verbose-preflight] promoted run verboseLevel=full (runId=${runId}, toolCallId=${toolCallId || 'unknown'}, tool=${toolName || 'unknown'})`);
123
- }
124
- }
125
-
126
- function handleAgentEvent(evt: AgentEvent): void {
127
- const runId = readString(evt.runId);
128
- if (!runId) return;
129
-
130
- const data = asRecord(evt.data);
131
-
132
- if (evt.stream === 'lifecycle') {
133
- const phase = readString(data.phase);
134
- if (phase === 'end' || phase === 'error') {
135
- forgetRun(runId);
136
- }
137
- return;
138
- }
139
-
140
- if (evt.stream !== 'tool') return;
141
- const toolName = readToolName(data);
142
- const toolCallId = readToolCallId(data);
143
-
144
- if (toolCallId && isOpenagentRemoteToolName(toolName)) {
145
- rememberRemoteToolCall(runId, toolCallId);
146
- }
147
-
148
- const shouldKeepPayload =
149
- isOpenagentRemoteToolName(toolName) ||
150
- (toolCallId !== '' && REMOTE_TOOL_CALL_IDS.has(toolCallId));
151
-
152
- if (!shouldKeepPayload) return;
153
-
154
- promoteRunVerboseLevel(runId, toolCallId, toolName);
155
-
156
- const phase = readString(data.phase);
157
- if (toolCallId && (phase === 'result' || phase === 'error' || phase === 'end')) {
158
- forgetRemoteToolCall(runId, toolCallId);
159
- }
160
- }
161
-
162
- function prioritizePreflightListener(listener: (evt: AgentEvent) => void): void {
163
- const listeners = getAgentEventState()?.listeners;
164
- if (!(listeners instanceof Set) || !listeners.has(listener)) return;
165
-
166
- const rest = Array.from(listeners).filter(item => item !== listener);
167
- listeners.clear();
168
- listeners.add(listener);
169
- for (const item of rest) listeners.add(item);
170
-
171
- logger.debug('[verbose-preflight] listener moved before gateway subscribers');
172
- }
173
-
174
- export function installOpenagentVerbosePreflight(api: OpenClawPluginApi): void {
175
- if (installed) return;
176
-
177
- const onAgentEvent = api.runtime?.events?.onAgentEvent;
178
- if (typeof onAgentEvent !== 'function') {
179
- if (!missingRuntimeWarned) {
180
- missingRuntimeWarned = true;
181
- logger.warn('[verbose-preflight] api.runtime.events.onAgentEvent is unavailable; OpenAgent SSE payload preflight disabled');
182
- }
183
- return;
184
- }
185
-
186
- onAgentEvent(handleAgentEvent);
187
- prioritizePreflightListener(handleAgentEvent);
188
- installed = true;
189
- logger.info('[verbose-preflight] installed');
190
- }