openclaw-openagent 1.0.12 → 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 (132) hide show
  1. package/dist/index.js +2 -0
  2. package/dist/src/config/config-schema.d.ts +9 -0
  3. package/dist/src/config/config-schema.js +6 -0
  4. package/dist/src/messaging/executor.d.ts +2 -3
  5. package/dist/src/messaging/executor.js +4 -31
  6. package/dist/src/plugin-ui/assets/openagent-override.js +124 -18
  7. package/dist/src/proxy/auth-proxy.js +5 -0
  8. package/dist/src/runtime/update-checker.d.ts +18 -0
  9. package/dist/src/runtime/update-checker.js +216 -0
  10. package/index.ts +3 -0
  11. package/package.json +1 -3
  12. package/src/app/channel-tools.ts +0 -249
  13. package/src/app/discovery-tools.ts +0 -273
  14. package/src/app/download-file-tool.ts +0 -133
  15. package/src/app/hooks.ts +0 -144
  16. package/src/app/index.ts +0 -84
  17. package/src/app/messaging-tools.ts +0 -79
  18. package/src/app/ops-tools.ts +0 -155
  19. package/src/app/remote-agent-tool.ts +0 -762
  20. package/src/app/types.ts +0 -68
  21. package/src/app/upload-file-tool.ts +0 -411
  22. package/src/app/verbose-preflight.ts +0 -190
  23. package/src/auth/config.ts +0 -266
  24. package/src/auth/credential-manager.ts +0 -146
  25. package/src/auth/index.ts +0 -24
  26. package/src/auth/verify.ts +0 -99
  27. package/src/channel.ts +0 -566
  28. package/src/compat.ts +0 -94
  29. package/src/config/config-schema.ts +0 -39
  30. package/src/messaging/aggregator.ts +0 -120
  31. package/src/messaging/collector.ts +0 -89
  32. package/src/messaging/executor.ts +0 -72
  33. package/src/messaging/inbound.ts +0 -150
  34. package/src/messaging/index.ts +0 -11
  35. package/src/messaging/mention-protocol.ts +0 -94
  36. package/src/messaging/process-c2c-request.ts +0 -564
  37. package/src/messaging/process-message.ts +0 -373
  38. package/src/messaging/scheduler.ts +0 -55
  39. package/src/messaging/types.ts +0 -38
  40. package/src/plugin-ui/adapters/oc-2026-04.js +0 -103
  41. package/src/plugin-ui/adapters/oc-2026-05.js +0 -125
  42. package/src/plugin-ui/adapters/oc-2026-06.js +0 -125
  43. package/src/plugin-ui/adapters/oc-unknown.js +0 -48
  44. package/src/plugin-ui/assets/agentbook-icon.svg +0 -5
  45. package/src/plugin-ui/assets/bg.png +0 -0
  46. package/src/plugin-ui/assets/icon.png +0 -0
  47. package/src/plugin-ui/assets/magic.svg +0 -5
  48. package/src/plugin-ui/assets/openagent-override.js +0 -11156
  49. package/src/plugin-ui/build.cjs +0 -463
  50. package/src/plugin-ui/index.ts +0 -18
  51. package/src/plugin-ui/modules/agent-book/panel/agent-book.js +0 -570
  52. package/src/plugin-ui/modules/agent-book/panel/agent-card.js +0 -170
  53. package/src/plugin-ui/modules/agent-book/panel/agent-data.js +0 -643
  54. package/src/plugin-ui/modules/agent-book/panel/inject-ui.js +0 -665
  55. package/src/plugin-ui/modules/agent-book/panel/mention-state.js +0 -206
  56. package/src/plugin-ui/modules/agent-book/panel/styles.js +0 -833
  57. package/src/plugin-ui/modules/agent-book/panel/theme-adapter.js +0 -86
  58. package/src/plugin-ui/modules/agent-book/remote-agent-tool/components-core.js +0 -295
  59. package/src/plugin-ui/modules/agent-book/remote-agent-tool/thought-chain-card.js +0 -211
  60. package/src/plugin-ui/modules/agent-book/scanner.js +0 -131
  61. package/src/plugin-ui/modules/agent-book/travelcard/travel-cards.js +0 -500
  62. package/src/plugin-ui/modules/agent-book/travelcard/travel-engine.js +0 -652
  63. package/src/plugin-ui/modules/agent-book/travelcard/travel-styles.js +0 -256
  64. package/src/plugin-ui/modules/loader/bootstrap.js +0 -38
  65. package/src/plugin-ui/modules/loader/shared-state.js +0 -838
  66. package/src/plugin-ui/modules/loader/ws-intercept.js +0 -199
  67. package/src/plugin-ui/modules/remote-agent/chunk-parser.js +0 -161
  68. package/src/plugin-ui/modules/remote-agent/execution-card.js +0 -530
  69. package/src/plugin-ui/modules/remote-agent/markdown-renderer.js +0 -256
  70. package/src/plugin-ui/modules/remote-agent/media-links.js +0 -151
  71. package/src/plugin-ui/modules/remote-agent/native-style-adapter.js +0 -134
  72. package/src/plugin-ui/modules/remote-agent/output-card.js +0 -342
  73. package/src/plugin-ui/modules/remote-agent/progress-store.js +0 -363
  74. package/src/plugin-ui/modules/remote-agent/render-hooks.js +0 -1700
  75. package/src/plugin-ui/modules/remote-agent/styles.js +0 -896
  76. package/src/plugin-ui/modules/remote-agent/tool-card-model.js +0 -130
  77. package/src/plugin-ui/postinstall-deploy.cjs +0 -52
  78. package/src/plugin-ui/ui-extension-loader/backup.ts +0 -92
  79. package/src/plugin-ui/ui-extension-loader/index.ts +0 -276
  80. package/src/plugin-ui/ui-extension-loader/locator.ts +0 -152
  81. package/src/plugin-ui/ui-extension-loader/manifest.ts +0 -57
  82. package/src/plugin-ui/ui-extension-loader/registry-regex.ts +0 -944
  83. package/src/plugin-ui/ui-extension-loader/removed-extensions.ts +0 -70
  84. package/src/plugin-ui/ui-extension-loader/types.ts +0 -72
  85. package/src/proxy/auth-proxy.ts +0 -409
  86. package/src/runtime/account.ts +0 -549
  87. package/src/runtime/index.ts +0 -7
  88. package/src/runtime/plugin-runtime.ts +0 -94
  89. package/src/runtime/registry.ts +0 -71
  90. package/src/sdk/CLASS_MAP.md +0 -143
  91. package/src/sdk/index.d.ts +0 -126
  92. package/src/sdk/index.js +0 -23990
  93. package/src/sdk/modules/cloud-search-module.js +0 -1117
  94. package/src/sdk/modules/follow-module.js +0 -1069
  95. package/src/sdk/modules/group-module.js +0 -7397
  96. package/src/sdk/modules/relationship-module.js +0 -2269
  97. package/src/sdk/modules/signaling-module.js +0 -1468
  98. package/src/sdk/modules/tim-upload-plugin.js +0 -730
  99. package/src/sdk/node-env/http-request.js +0 -90
  100. package/src/sdk/node-env/index.js +0 -57
  101. package/src/sdk/node-env/storage.js +0 -114
  102. package/src/sdk/package.json +0 -10
  103. package/src/sdk/tsconfig.json +0 -16
  104. package/src/state/pending-invocation-store.ts +0 -43
  105. package/src/state/store.ts +0 -756
  106. package/src/tim/c2c.ts +0 -451
  107. package/src/tim/channels.ts +0 -364
  108. package/src/tim/client.ts +0 -330
  109. package/src/tim/index.ts +0 -18
  110. package/src/tim/messages.ts +0 -166
  111. package/src/tim/sdk-logger-init.ts +0 -50
  112. package/src/tools.ts +0 -10
  113. package/src/transport/factory.ts +0 -95
  114. package/src/transport/oasn/index.ts +0 -17
  115. package/src/transport/oasn/oasn-agent-card.ts +0 -111
  116. package/src/transport/oasn/oasn-discovery.ts +0 -108
  117. package/src/transport/oasn/oasn-files.ts +0 -210
  118. package/src/transport/oasn/oasn-http.ts +0 -529
  119. package/src/transport/oasn/oasn-invocation.ts +0 -604
  120. package/src/transport/oasn/oasn-normalize.ts +0 -133
  121. package/src/transport/oasn/oasn-register.ts +0 -33
  122. package/src/transport/oasn/oasn-transport.ts +0 -318
  123. package/src/transport/oasn/oasn-types.ts +0 -404
  124. package/src/transport/tim/index.ts +0 -8
  125. package/src/transport/tim/tim-transport.ts +0 -515
  126. package/src/transport/types.ts +0 -538
  127. package/src/types/openclaw-plugin-sdk-media-store.d.ts +0 -9
  128. package/src/types/openclaw.d.ts +0 -97
  129. package/src/types/tencentcloud-chat.d.ts +0 -15
  130. package/src/util/http.ts +0 -113
  131. package/src/util/logger.ts +0 -131
  132. 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
- }