openclaw-openagent 1.0.1 → 1.0.2

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 (187) hide show
  1. package/dist/index.d.ts +25 -0
  2. package/dist/index.js +105 -0
  3. package/dist/src/app/channel-tools.d.ts +28 -0
  4. package/dist/src/app/channel-tools.js +251 -0
  5. package/dist/src/app/discovery-tools.d.ts +35 -0
  6. package/dist/src/app/discovery-tools.js +243 -0
  7. package/dist/src/app/download-file-tool.d.ts +10 -0
  8. package/dist/src/app/download-file-tool.js +104 -0
  9. package/dist/src/app/hooks.d.ts +14 -0
  10. package/dist/src/app/hooks.js +118 -0
  11. package/dist/src/app/index.d.ts +13 -0
  12. package/dist/src/app/index.js +44 -0
  13. package/dist/src/app/messaging-tools.d.ts +11 -0
  14. package/dist/src/app/messaging-tools.js +79 -0
  15. package/dist/src/app/ops-tools.d.ts +21 -0
  16. package/dist/src/app/ops-tools.js +158 -0
  17. package/dist/src/app/remote-agent-tool.d.ts +27 -0
  18. package/dist/src/app/remote-agent-tool.js +461 -0
  19. package/dist/src/app/types.d.ts +61 -0
  20. package/dist/src/app/types.js +11 -0
  21. package/dist/src/app/upload-file-tool.d.ts +16 -0
  22. package/dist/src/app/upload-file-tool.js +353 -0
  23. package/dist/src/app/verbose-preflight.d.ts +2 -0
  24. package/dist/src/app/verbose-preflight.js +145 -0
  25. package/dist/src/auth/config.d.ts +79 -0
  26. package/dist/src/auth/config.js +133 -0
  27. package/dist/src/auth/credential-manager.d.ts +65 -0
  28. package/dist/src/auth/credential-manager.js +122 -0
  29. package/dist/src/auth/index.d.ts +6 -0
  30. package/dist/src/auth/index.js +6 -0
  31. package/dist/src/auth/verify.d.ts +42 -0
  32. package/dist/src/auth/verify.js +60 -0
  33. package/dist/src/channel.d.ts +269 -0
  34. package/dist/src/channel.js +488 -0
  35. package/dist/src/compat.d.ts +37 -0
  36. package/dist/src/compat.js +70 -0
  37. package/dist/src/config/config-schema.d.ts +56 -0
  38. package/dist/src/config/config-schema.js +34 -0
  39. package/dist/src/messaging/aggregator.d.ts +25 -0
  40. package/dist/src/messaging/aggregator.js +90 -0
  41. package/dist/src/messaging/collector.d.ts +27 -0
  42. package/dist/src/messaging/collector.js +76 -0
  43. package/dist/src/messaging/executor.d.ts +14 -0
  44. package/dist/src/messaging/executor.js +59 -0
  45. package/dist/src/messaging/inbound.d.ts +97 -0
  46. package/dist/src/messaging/inbound.js +63 -0
  47. package/dist/src/messaging/index.d.ts +10 -0
  48. package/dist/src/messaging/index.js +9 -0
  49. package/dist/src/messaging/mention-protocol.d.ts +42 -0
  50. package/dist/src/messaging/mention-protocol.js +74 -0
  51. package/dist/src/messaging/process-c2c-request.d.ts +55 -0
  52. package/dist/src/messaging/process-c2c-request.js +445 -0
  53. package/dist/src/messaging/process-message.d.ts +62 -0
  54. package/dist/src/messaging/process-message.js +282 -0
  55. package/dist/src/messaging/scheduler.d.ts +17 -0
  56. package/dist/src/messaging/scheduler.js +47 -0
  57. package/dist/src/messaging/types.d.ts +34 -0
  58. package/dist/src/messaging/types.js +4 -0
  59. package/dist/src/plugin-ui/assets/openagent-override.js +9267 -0
  60. package/dist/src/plugin-ui/index.d.ts +13 -0
  61. package/dist/src/plugin-ui/index.js +16 -0
  62. package/dist/src/plugin-ui/ui-extension-loader/backup.d.ts +23 -0
  63. package/dist/src/plugin-ui/ui-extension-loader/backup.js +82 -0
  64. package/dist/src/plugin-ui/ui-extension-loader/index.d.ts +28 -0
  65. package/dist/src/plugin-ui/ui-extension-loader/index.js +240 -0
  66. package/dist/src/plugin-ui/ui-extension-loader/locator.d.ts +35 -0
  67. package/dist/src/plugin-ui/ui-extension-loader/locator.js +129 -0
  68. package/dist/src/plugin-ui/ui-extension-loader/manifest.d.ts +26 -0
  69. package/dist/src/plugin-ui/ui-extension-loader/manifest.js +45 -0
  70. package/dist/src/plugin-ui/ui-extension-loader/registry-regex.d.ts +9 -0
  71. package/dist/src/plugin-ui/ui-extension-loader/registry-regex.js +701 -0
  72. package/dist/src/plugin-ui/ui-extension-loader/removed-extensions.d.ts +8 -0
  73. package/dist/src/plugin-ui/ui-extension-loader/removed-extensions.js +58 -0
  74. package/dist/src/plugin-ui/ui-extension-loader/types.d.ts +55 -0
  75. package/dist/src/plugin-ui/ui-extension-loader/types.js +23 -0
  76. package/dist/src/proxy/auth-proxy.d.ts +20 -0
  77. package/dist/src/proxy/auth-proxy.js +337 -0
  78. package/dist/src/runtime/account.d.ts +156 -0
  79. package/dist/src/runtime/account.js +491 -0
  80. package/dist/src/runtime/index.d.ts +5 -0
  81. package/dist/src/runtime/index.js +6 -0
  82. package/dist/src/runtime/plugin-runtime.d.ts +40 -0
  83. package/dist/src/runtime/plugin-runtime.js +72 -0
  84. package/dist/src/runtime/registry.d.ts +41 -0
  85. package/dist/src/runtime/registry.js +60 -0
  86. package/dist/src/sdk/CLASS_MAP.md +143 -0
  87. package/dist/src/sdk/index.d.ts +126 -0
  88. package/dist/src/sdk/index.js +23990 -0
  89. package/dist/src/sdk/modules/cloud-search-module.js +1117 -0
  90. package/dist/src/sdk/modules/follow-module.js +1069 -0
  91. package/dist/src/sdk/modules/group-module.js +7397 -0
  92. package/dist/src/sdk/modules/relationship-module.js +2269 -0
  93. package/dist/src/sdk/modules/signaling-module.js +1468 -0
  94. package/dist/src/sdk/modules/tim-upload-plugin.js +730 -0
  95. package/dist/src/sdk/node-env/http-request.js +90 -0
  96. package/dist/src/sdk/node-env/index.js +57 -0
  97. package/dist/src/sdk/node-env/storage.js +114 -0
  98. package/dist/src/sdk/package.json +10 -0
  99. package/dist/src/sdk/sdk/CLASS_MAP.md +143 -0
  100. package/dist/src/sdk/sdk/index.d.ts +126 -0
  101. package/dist/src/sdk/sdk/index.js +23990 -0
  102. package/dist/src/sdk/sdk/modules/cloud-search-module.js +1117 -0
  103. package/dist/src/sdk/sdk/modules/follow-module.js +1069 -0
  104. package/dist/src/sdk/sdk/modules/group-module.js +7397 -0
  105. package/dist/src/sdk/sdk/modules/relationship-module.js +2269 -0
  106. package/dist/src/sdk/sdk/modules/signaling-module.js +1468 -0
  107. package/dist/src/sdk/sdk/modules/tim-upload-plugin.js +730 -0
  108. package/dist/src/sdk/sdk/node-env/http-request.js +90 -0
  109. package/dist/src/sdk/sdk/node-env/index.js +57 -0
  110. package/dist/src/sdk/sdk/node-env/storage.js +114 -0
  111. package/dist/src/sdk/sdk/package.json +10 -0
  112. package/dist/src/sdk/sdk/tsconfig.json +16 -0
  113. package/dist/src/sdk/tsconfig.json +16 -0
  114. package/dist/src/state/pending-invocation-store.d.ts +26 -0
  115. package/dist/src/state/pending-invocation-store.js +39 -0
  116. package/dist/src/state/store.d.ts +165 -0
  117. package/dist/src/state/store.js +535 -0
  118. package/dist/src/tim/c2c.d.ts +83 -0
  119. package/dist/src/tim/c2c.js +318 -0
  120. package/dist/src/tim/channels.d.ts +95 -0
  121. package/dist/src/tim/channels.js +279 -0
  122. package/dist/src/tim/client.d.ts +54 -0
  123. package/dist/src/tim/client.js +268 -0
  124. package/dist/src/tim/index.d.ts +6 -0
  125. package/dist/src/tim/index.js +6 -0
  126. package/dist/src/tim/messages.d.ts +50 -0
  127. package/dist/src/tim/messages.js +104 -0
  128. package/dist/src/tim/sdk-logger-init.d.ts +13 -0
  129. package/dist/src/tim/sdk-logger-init.js +46 -0
  130. package/dist/src/tools.d.ts +9 -0
  131. package/dist/src/tools.js +8 -0
  132. package/dist/src/transport/factory.d.ts +63 -0
  133. package/dist/src/transport/factory.js +54 -0
  134. package/dist/src/transport/oasn/index.d.ts +12 -0
  135. package/dist/src/transport/oasn/index.js +9 -0
  136. package/dist/src/transport/oasn/oasn-agent-card.d.ts +38 -0
  137. package/dist/src/transport/oasn/oasn-agent-card.js +102 -0
  138. package/dist/src/transport/oasn/oasn-discovery.d.ts +33 -0
  139. package/dist/src/transport/oasn/oasn-discovery.js +97 -0
  140. package/dist/src/transport/oasn/oasn-files.d.ts +64 -0
  141. package/dist/src/transport/oasn/oasn-files.js +174 -0
  142. package/dist/src/transport/oasn/oasn-http.d.ts +98 -0
  143. package/dist/src/transport/oasn/oasn-http.js +362 -0
  144. package/dist/src/transport/oasn/oasn-invocation.d.ts +154 -0
  145. package/dist/src/transport/oasn/oasn-invocation.js +432 -0
  146. package/dist/src/transport/oasn/oasn-normalize.d.ts +6 -0
  147. package/dist/src/transport/oasn/oasn-normalize.js +112 -0
  148. package/dist/src/transport/oasn/oasn-register.d.ts +19 -0
  149. package/dist/src/transport/oasn/oasn-register.js +24 -0
  150. package/dist/src/transport/oasn/oasn-transport.d.ts +114 -0
  151. package/dist/src/transport/oasn/oasn-transport.js +230 -0
  152. package/dist/src/transport/oasn/oasn-types.d.ts +331 -0
  153. package/dist/src/transport/oasn/oasn-types.js +44 -0
  154. package/dist/src/transport/tim/index.d.ts +7 -0
  155. package/dist/src/transport/tim/index.js +6 -0
  156. package/dist/src/transport/tim/tim-transport.d.ts +122 -0
  157. package/dist/src/transport/tim/tim-transport.js +402 -0
  158. package/dist/src/transport/types.d.ts +450 -0
  159. package/dist/src/transport/types.js +38 -0
  160. package/dist/src/util/http.d.ts +21 -0
  161. package/dist/src/util/http.js +93 -0
  162. package/dist/src/util/logger.d.ts +20 -0
  163. package/dist/src/util/logger.js +100 -0
  164. package/dist/src/util/url-resolver.d.ts +7 -0
  165. package/dist/src/util/url-resolver.js +20 -0
  166. package/index.ts +11 -0
  167. package/openclaw.plugin.json +9 -0
  168. package/package.json +7 -4
  169. package/src/app/download-file-tool.ts +133 -0
  170. package/src/app/hooks.ts +89 -5
  171. package/src/app/index.ts +6 -0
  172. package/src/app/remote-agent-tool.ts +46 -0
  173. package/src/app/types.ts +1 -0
  174. package/src/app/upload-file-tool.ts +411 -0
  175. package/src/plugin-ui/assets/openagent-override.js +1 -63
  176. package/src/plugin-ui/modules/agent-book/panel/agent-book.js +0 -61
  177. package/src/plugin-ui/modules/agent-book/panel/agent-data.js +0 -1
  178. package/src/plugin-ui/ui-extension-loader/registry-regex.ts +30 -13
  179. package/src/runtime/account.ts +0 -23
  180. package/src/transport/oasn/oasn-files.ts +5 -5
  181. package/src/transport/oasn/oasn-invocation.ts +44 -2
  182. package/src/transport/oasn/oasn-normalize.ts +0 -26
  183. package/src/transport/oasn/oasn-register.ts +8 -81
  184. package/src/transport/oasn/oasn-transport.ts +9 -32
  185. package/src/transport/oasn/oasn-types.ts +53 -6
  186. package/src/transport/types.ts +33 -36
  187. package/src/util/url-resolver.ts +17 -0
@@ -0,0 +1,54 @@
1
+ /**
2
+ * TIM Client — SDK connection core
3
+ *
4
+ * Manages TIM SDK lifecycle: connect, disconnect, auto-reconnect.
5
+ * Emits 'message' events for incoming group chat messages.
6
+ * Emits 'reconnected' when auto-reconnect succeeds.
7
+ *
8
+ * Internal noise containment: all SDK calls wrapped in try/catch,
9
+ * known TIM noise errors logged at debug level.
10
+ */
11
+ import './sdk-logger-init.js';
12
+ import '../sdk/node-env/index.js';
13
+ import TencentCloudChat from '../sdk/index.js';
14
+ import { EventEmitter } from 'node:events';
15
+ import type { TIMCredentials } from '../auth/verify.js';
16
+ export interface RawPushMessage {
17
+ id: string;
18
+ channelId: string;
19
+ seq: number;
20
+ from: string;
21
+ nick: string;
22
+ text: string;
23
+ time: number;
24
+ mentionsMe: boolean;
25
+ source: 'push';
26
+ }
27
+ export declare class TIMClient extends EventEmitter {
28
+ private chat;
29
+ private _agentId;
30
+ private _credentials;
31
+ private _reconnecting;
32
+ private _ready;
33
+ get agentId(): string;
34
+ get isReady(): boolean;
35
+ get userSig(): string | null;
36
+ /**
37
+ * Connect to TIM SDK using verified credentials.
38
+ */
39
+ connect(credentials: TIMCredentials): Promise<void>;
40
+ /**
41
+ * Gracefully disconnect from TIM SDK.
42
+ */
43
+ disconnect(): Promise<void>;
44
+ private _messageHandler;
45
+ private _notReadyHandler;
46
+ private _kickedHandler;
47
+ private _registerListeners;
48
+ private _removeListeners;
49
+ private _autoReconnect;
50
+ /** @internal */
51
+ get _chat(): ReturnType<typeof TencentCloudChat.create> | null;
52
+ /** @internal */
53
+ get _types(): typeof TencentCloudChat.TYPES;
54
+ }
@@ -0,0 +1,268 @@
1
+ /**
2
+ * TIM Client — SDK connection core
3
+ *
4
+ * Manages TIM SDK lifecycle: connect, disconnect, auto-reconnect.
5
+ * Emits 'message' events for incoming group chat messages.
6
+ * Emits 'reconnected' when auto-reconnect succeeds.
7
+ *
8
+ * Internal noise containment: all SDK calls wrapped in try/catch,
9
+ * known TIM noise errors logged at debug level.
10
+ */
11
+ import './sdk-logger-init.js'; // Must be before @tencentcloud/chat — redirects SDK logs to file
12
+ // @ts-ignore — JS side-effect module, no types needed
13
+ import '../sdk/node-env/index.js'; // Must be before @tencentcloud/chat — installs ws (no permessage-deflate), localStorage, rejection handler
14
+ import TencentCloudChat from '../sdk/index.js';
15
+ // @ts-ignore — vendor JS module, typed as unknown
16
+ import GroupModule from '../sdk/modules/group-module.js';
17
+ // @ts-ignore — vendor JS module, patched for Node.js
18
+ import TIMUploadPlugin from '../sdk/modules/tim-upload-plugin.js';
19
+ import { EventEmitter } from 'node:events';
20
+ import { logger } from '../util/logger.js';
21
+ // ── TIMClient ──
22
+ export class TIMClient extends EventEmitter {
23
+ chat = null;
24
+ _agentId = '';
25
+ _credentials = null;
26
+ _reconnecting = false;
27
+ _ready = false;
28
+ get agentId() { return this._agentId; }
29
+ get isReady() { return this._ready; }
30
+ get userSig() { return this._credentials?.userSig ?? null; }
31
+ /**
32
+ * Connect to TIM SDK using verified credentials.
33
+ */
34
+ async connect(credentials) {
35
+ this._agentId = credentials.userId;
36
+ this._credentials = credentials;
37
+ logger.info(`[tim/client] Connecting agentId=${this._agentId}`);
38
+ // Create SDK instance
39
+ this.chat = TencentCloudChat.create({
40
+ SDKAppID: credentials.sdkAppId,
41
+ modules: { 'group-module': GroupModule },
42
+ });
43
+ this.chat.setLogLevel(0); // ALL logs — WebSocket heartbeat, transport, message push (P0-A diagnosis)
44
+ // Register tim-upload-plugin — enables createFileMessage/sendMessage for T4 file transfer
45
+ // Must be after create(), before login(). SDK_READY triggers UploadModule._init() which looks up this plugin.
46
+ // @see docs/audit/023-tim-upload-plugin-v1.md §4.7
47
+ this.chat.registerPlugin({ 'tim-upload-plugin': TIMUploadPlugin });
48
+ logger.info('[tim/client] tim-upload-plugin registered');
49
+ // Wait for SDK_READY with guaranteed timer cleanup
50
+ let readyTimeout;
51
+ const sdkReadyPromise = new Promise((resolve, reject) => {
52
+ readyTimeout = setTimeout(() => reject(new Error('SDK_READY timeout (15s)')), 15_000);
53
+ this.chat.on(TencentCloudChat.EVENT.SDK_READY, () => {
54
+ clearTimeout(readyTimeout);
55
+ this._ready = true;
56
+ resolve();
57
+ });
58
+ });
59
+ // Register event listeners
60
+ this._registerListeners();
61
+ logger.info('[tim/client] Calling TIM SDK login...');
62
+ try {
63
+ await this.chat.login({
64
+ userID: credentials.userId,
65
+ userSig: credentials.userSig,
66
+ });
67
+ await sdkReadyPromise;
68
+ }
69
+ catch (err) {
70
+ clearTimeout(readyTimeout);
71
+ this._ready = false;
72
+ throw err;
73
+ }
74
+ logger.info('[tim/client] SDK_READY — connected');
75
+ }
76
+ /**
77
+ * Gracefully disconnect from TIM SDK.
78
+ */
79
+ async disconnect() {
80
+ this._reconnecting = false;
81
+ if (this.chat) {
82
+ try {
83
+ await this.chat.logout();
84
+ }
85
+ catch { /* ignore */ }
86
+ this._ready = false;
87
+ this._removeListeners();
88
+ this.chat = null;
89
+ }
90
+ }
91
+ // ── Event Listeners ──
92
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
93
+ _messageHandler = null;
94
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
95
+ _notReadyHandler = null;
96
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
97
+ _kickedHandler = null;
98
+ _registerListeners() {
99
+ if (!this.chat) {
100
+ logger.error('[tim/client] _registerListeners: chat instance is null, cannot register');
101
+ return;
102
+ }
103
+ logger.info('[tim/client] _registerListeners: installing SDK event handlers');
104
+ // Disconnect detection
105
+ this._notReadyHandler = () => {
106
+ logger.warn('[tim/client] SDK_NOT_READY — connection lost');
107
+ this._ready = false;
108
+ void this._autoReconnect();
109
+ };
110
+ this.chat.on(TencentCloudChat.EVENT.SDK_NOT_READY, this._notReadyHandler);
111
+ this._kickedHandler = (event) => {
112
+ const ev = event;
113
+ const reason = ev?.data?.type || 'unknown';
114
+ logger.warn(`[tim/client] KICKED_OUT (reason=${reason})`);
115
+ this._ready = false;
116
+ switch (reason) {
117
+ case 'multipleDevice':
118
+ case 'multipleAccount':
119
+ // Multi-device/page kick: do NOT reconnect to avoid kick loop
120
+ logger.warn('[tim/client] Kicked by multi-device/account login, will not reconnect');
121
+ break;
122
+ case 'userSigExpired':
123
+ // UserSig expired: old credentials are useless, notify upper layer
124
+ logger.warn('[tim/client] UserSig expired, emitting credential_expired event');
125
+ this.emit('credential_expired');
126
+ break;
127
+ case 'REST_API_Kick':
128
+ default:
129
+ // REST API kick or unknown: reconnect with existing credentials
130
+ void this._autoReconnect();
131
+ break;
132
+ }
133
+ };
134
+ this.chat.on(TencentCloudChat.EVENT.KICKED_OUT, this._kickedHandler);
135
+ // Push message listener
136
+ this._messageHandler = (event) => {
137
+ const ev = event;
138
+ const messages = ev.data;
139
+ logger.info(`[tim/client] MESSAGE_RECEIVED fired: ${messages?.length ?? 0} message(s)`);
140
+ if (!messages || messages.length === 0) {
141
+ logger.warn('[tim/client] MESSAGE_RECEIVED: empty data array, nothing to process');
142
+ return;
143
+ }
144
+ for (const msg of messages) {
145
+ try {
146
+ // ── C2C messages → emit for c2c.ts handler, skip group logic ──
147
+ if (msg.conversationType === TencentCloudChat.TYPES.CONV_C2C) {
148
+ logger.info(`[tim/client] C2C msg from=${msg.from}`);
149
+ this.emit('c2c_message', {
150
+ from: msg.from,
151
+ payload: msg.payload,
152
+ type: msg.type,
153
+ time: msg.time,
154
+ });
155
+ continue;
156
+ }
157
+ // Only group messages
158
+ if (msg.conversationType !== TencentCloudChat.TYPES.CONV_GROUP) {
159
+ logger.debug(`[tim/client] skip non-group msg type=${msg.conversationType} from=${msg.from}`);
160
+ continue;
161
+ }
162
+ // Skip own messages
163
+ if (msg.from === this._agentId) {
164
+ logger.debug(`[tim/client] skip self msg from=${msg.from} to=${msg.to}`);
165
+ continue;
166
+ }
167
+ const text = msg.payload?.text || '';
168
+ if (!text) {
169
+ logger.debug(`[tim/client] skip empty text msg from=${msg.from} to=${msg.to}`);
170
+ continue;
171
+ }
172
+ const channelId = msg.to;
173
+ const seq = msg.sequence;
174
+ const rawMessage = {
175
+ id: `${channelId}:${seq}`,
176
+ channelId,
177
+ seq,
178
+ from: msg.from,
179
+ nick: msg.nick || msg.from,
180
+ text,
181
+ time: msg.time * 1000,
182
+ mentionsMe: Array.isArray(msg.atUserList)
183
+ && (msg.atUserList.includes(this._agentId)
184
+ || msg.atUserList.includes(TencentCloudChat.TYPES.MSG_AT_ALL)),
185
+ source: 'push',
186
+ };
187
+ logger.debug(`[tim/client] PUSH msg id=${rawMessage.id} ch=${channelId} from=${rawMessage.nick}`);
188
+ this.emit('message', rawMessage);
189
+ }
190
+ catch (err) {
191
+ logger.debug(`[tim/client] Error processing push message: ${err.message}`);
192
+ }
193
+ }
194
+ };
195
+ this.chat.on(TencentCloudChat.EVENT.MESSAGE_RECEIVED, this._messageHandler);
196
+ }
197
+ _removeListeners() {
198
+ if (!this.chat)
199
+ return;
200
+ if (this._notReadyHandler) {
201
+ this.chat.off(TencentCloudChat.EVENT.SDK_NOT_READY, this._notReadyHandler);
202
+ this._notReadyHandler = null;
203
+ }
204
+ if (this._kickedHandler) {
205
+ this.chat.off(TencentCloudChat.EVENT.KICKED_OUT, this._kickedHandler);
206
+ this._kickedHandler = null;
207
+ }
208
+ if (this._messageHandler) {
209
+ this.chat.off(TencentCloudChat.EVENT.MESSAGE_RECEIVED, this._messageHandler);
210
+ this._messageHandler = null;
211
+ }
212
+ }
213
+ // ── Auto-reconnect ──
214
+ async _autoReconnect(maxRetries = 5) {
215
+ if (this._reconnecting) {
216
+ logger.debug('[tim/client] Reconnect already in progress');
217
+ return;
218
+ }
219
+ if (!this._credentials) {
220
+ logger.error('[tim/client] Cannot reconnect: missing credentials');
221
+ return;
222
+ }
223
+ this._reconnecting = true;
224
+ logger.warn('[tim/client] Starting auto-reconnect...');
225
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
226
+ if (!this._reconnecting) {
227
+ logger.info('[tim/client] Reconnect cancelled');
228
+ return;
229
+ }
230
+ const delay = Math.min(5000 * attempt, 25_000);
231
+ logger.info(`[tim/client] Reconnect attempt ${attempt}/${maxRetries} in ${delay / 1000}s...`);
232
+ await new Promise(r => setTimeout(r, delay));
233
+ try {
234
+ // Teardown old SDK instance
235
+ if (this.chat) {
236
+ this._removeListeners();
237
+ try {
238
+ await this.chat.logout();
239
+ }
240
+ catch { /* ignore */ }
241
+ this.chat = null;
242
+ }
243
+ this._ready = false;
244
+ await this.connect(this._credentials);
245
+ if (this._ready) {
246
+ logger.info(`[tim/client] Auto-reconnect successful on attempt ${attempt}`);
247
+ this._reconnecting = false;
248
+ this.emit('reconnected');
249
+ return;
250
+ }
251
+ }
252
+ catch (err) {
253
+ logger.error(`[tim/client] Reconnect attempt ${attempt}/${maxRetries} failed: ${err.message}`);
254
+ }
255
+ }
256
+ this._reconnecting = false;
257
+ logger.error(`[tim/client] All ${maxRetries} reconnect attempts failed`);
258
+ }
259
+ // ── Internal SDK access (for messages.ts and channels.ts) ──
260
+ /** @internal */
261
+ get _chat() {
262
+ return this.chat;
263
+ }
264
+ /** @internal */
265
+ get _types() {
266
+ return TencentCloudChat.TYPES;
267
+ }
268
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * TIM — Module barrel export
3
+ */
4
+ export { TIMClient, type RawPushMessage } from './client.js';
5
+ export { sendMessage, fetchMessages, mapToHistoryMessage, type HistoryMessage, type SendResult } from './messages.js';
6
+ export { listChannels, searchChannels, joinChannel, leaveChannel, getMembers, createChannel, getChannelSkill, setChannelSkill, type ChannelInfo, type MemberInfo, } from './channels.js';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * TIM — Module barrel export
3
+ */
4
+ export { TIMClient } from './client.js';
5
+ export { sendMessage, fetchMessages, mapToHistoryMessage } from './messages.js';
6
+ export { listChannels, searchChannels, joinChannel, leaveChannel, getMembers, createChannel, getChannelSkill, setChannelSkill, } from './channels.js';
@@ -0,0 +1,50 @@
1
+ /**
2
+ * TIM Messages — Send and fetch messages via SDK
3
+ *
4
+ * v3: fetchMessages uses SDK getMessageList() instead of REST API.
5
+ * This fixes the v2 bug where non-admin accounts got empty results.
6
+ */
7
+ import type { TIMClient } from './client.js';
8
+ export interface HistoryMessage {
9
+ id: string;
10
+ from: string;
11
+ text: string;
12
+ time: number;
13
+ seq?: number;
14
+ }
15
+ export interface SendResult {
16
+ ok: boolean;
17
+ messageId: string;
18
+ }
19
+ /**
20
+ * Send a text message to a group channel.
21
+ * Includes a 10s local timeout to catch dead SDK connections.
22
+ *
23
+ * When atUserList is provided, uses TIM createTextAtMessage to set
24
+ * protocol-level @mentions (visible to receivers' atUserList detection).
25
+ * See: docs/audit/012-mention-mode-bot-to-bot-v3.4.4.md
26
+ */
27
+ export declare function sendMessage(client: TIMClient, channelId: string, text: string, atUserList?: string[]): Promise<SendResult>;
28
+ /**
29
+ * Fetch message history for a group channel using SDK getMessageList().
30
+ *
31
+ * v3 change: REST API → SDK getMessageList()
32
+ * - Filters non-text messages (system msgs, join/leave notifications)
33
+ * - Maps to HistoryMessage interface
34
+ * - seq from message.sequence (optional)
35
+ * - Works with normal user credentials (fixes v2 empty array bug)
36
+ */
37
+ export declare function fetchMessages(client: TIMClient, channelId: string, count?: number): Promise<HistoryMessage[]>;
38
+ /**
39
+ * Map a single SDK message to HistoryMessage interface.
40
+ * Exported for contract testing.
41
+ */
42
+ export declare function mapToHistoryMessage(msg: {
43
+ ID?: string;
44
+ from?: string;
45
+ payload?: {
46
+ text?: string;
47
+ };
48
+ time?: number;
49
+ sequence?: number;
50
+ }, channelId: string): HistoryMessage;
@@ -0,0 +1,104 @@
1
+ /**
2
+ * TIM Messages — Send and fetch messages via SDK
3
+ *
4
+ * v3: fetchMessages uses SDK getMessageList() instead of REST API.
5
+ * This fixes the v2 bug where non-admin accounts got empty results.
6
+ */
7
+ import { logger } from '../util/logger.js';
8
+ // ── Functions ──
9
+ /**
10
+ * Send a text message to a group channel.
11
+ * Includes a 10s local timeout to catch dead SDK connections.
12
+ *
13
+ * When atUserList is provided, uses TIM createTextAtMessage to set
14
+ * protocol-level @mentions (visible to receivers' atUserList detection).
15
+ * See: docs/audit/012-mention-mode-bot-to-bot-v3.4.4.md
16
+ */
17
+ export async function sendMessage(client, channelId, text, atUserList) {
18
+ const chat = client._chat;
19
+ if (!chat || !client.isReady) {
20
+ throw new Error('Not connected');
21
+ }
22
+ logger.info(`[tim/messages] sendMessage: ch=${channelId} len=${text.length} at=${atUserList?.join(',') ?? 'none'}`);
23
+ const message = (atUserList && atUserList.length > 0)
24
+ ? chat.createTextAtMessage({
25
+ to: channelId,
26
+ conversationType: client._types.CONV_GROUP,
27
+ payload: { text, atUserList },
28
+ })
29
+ : chat.createTextMessage({
30
+ to: channelId,
31
+ conversationType: client._types.CONV_GROUP,
32
+ payload: { text },
33
+ });
34
+ // CRITICAL: Timer MUST be cleared after race settles.
35
+ // Without clearTimeout, a successful send leaves an orphan reject()
36
+ // firing 10s later as an unhandled rejection → gateway crash.
37
+ let timeoutHandle;
38
+ const timeoutPromise = new Promise((_, reject) => {
39
+ timeoutHandle = setTimeout(() => reject(new Error(`sendMessage timeout (10s) for channel ${channelId}`)), 10_000);
40
+ });
41
+ try {
42
+ const res = await Promise.race([chat.sendMessage(message), timeoutPromise]);
43
+ return {
44
+ ok: true,
45
+ messageId: res.data?.message?.ID || '',
46
+ };
47
+ }
48
+ catch (err) {
49
+ const code = err?.code;
50
+ logger.error(`[tim/messages] sendMessage failed: code=${code} msg=${err.message} ch=${channelId}`);
51
+ throw err;
52
+ }
53
+ finally {
54
+ clearTimeout(timeoutHandle);
55
+ }
56
+ }
57
+ /**
58
+ * Fetch message history for a group channel using SDK getMessageList().
59
+ *
60
+ * v3 change: REST API → SDK getMessageList()
61
+ * - Filters non-text messages (system msgs, join/leave notifications)
62
+ * - Maps to HistoryMessage interface
63
+ * - seq from message.sequence (optional)
64
+ * - Works with normal user credentials (fixes v2 empty array bug)
65
+ */
66
+ export async function fetchMessages(client, channelId, count = 20) {
67
+ const chat = client._chat;
68
+ if (!chat || !client.isReady) {
69
+ throw new Error('Not connected');
70
+ }
71
+ try {
72
+ logger.info(`[tim/messages] fetchMessages: ch=${channelId} count=${count}`);
73
+ const conversationID = `GROUP${channelId}`;
74
+ const res = await chat.getMessageList({
75
+ conversationID,
76
+ count,
77
+ });
78
+ const messageList = res.data?.messageList || [];
79
+ // Filter to text messages only (exclude system messages, join/leave, etc.)
80
+ return messageList
81
+ .filter(msg => msg.type === client._types.MSG_TEXT)
82
+ .map(msg => mapToHistoryMessage(msg, channelId));
83
+ }
84
+ catch (err) {
85
+ logger.error(`[tim/messages] fetchMessages error for ${channelId}: ${err.message}`);
86
+ throw err;
87
+ }
88
+ }
89
+ /**
90
+ * Map a single SDK message to HistoryMessage interface.
91
+ * Exported for contract testing.
92
+ */
93
+ export function mapToHistoryMessage(msg, channelId) {
94
+ const result = {
95
+ id: msg.ID || `${channelId}:${msg.sequence || Date.now()}`,
96
+ from: msg.from || 'unknown',
97
+ text: msg.payload?.text || '',
98
+ time: (msg.time || 0) * 1000,
99
+ };
100
+ if (msg.sequence != null) {
101
+ result.seq = msg.sequence;
102
+ }
103
+ return result;
104
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * TIM SDK Logger Bridge — 副作用模块
3
+ *
4
+ * 将腾讯云 IM SDK 的内部日志输出重定向到 OpenAgent 的 logger 组件。
5
+ * SDK 内部原本直接调用 console.log/warn/error,此模块通过
6
+ * globalThis.__OPENAGENT_SDK_LOGGER 注入自定义 logger 适配器,
7
+ * 使 SDK 日志走文件输出而非 stdout,避免污染 Gateway JSON 通道。
8
+ *
9
+ * 必须在 @tencentcloud/chat 之前 import(副作用 import):
10
+ * import './sdk-logger-init.js';
11
+ * import TencentCloudChat from '@tencentcloud/chat';
12
+ */
13
+ export {};
@@ -0,0 +1,46 @@
1
+ /**
2
+ * TIM SDK Logger Bridge — 副作用模块
3
+ *
4
+ * 将腾讯云 IM SDK 的内部日志输出重定向到 OpenAgent 的 logger 组件。
5
+ * SDK 内部原本直接调用 console.log/warn/error,此模块通过
6
+ * globalThis.__OPENAGENT_SDK_LOGGER 注入自定义 logger 适配器,
7
+ * 使 SDK 日志走文件输出而非 stdout,避免污染 Gateway JSON 通道。
8
+ *
9
+ * 必须在 @tencentcloud/chat 之前 import(副作用 import):
10
+ * import './sdk-logger-init.js';
11
+ * import TencentCloudChat from '@tencentcloud/chat';
12
+ */
13
+ // node-polyfill.js removed — SDK now uses native Node.js APIs directly
14
+ import { logger } from '../util/logger.js';
15
+ function formatArgs(...args) {
16
+ return args
17
+ .map((a) => (typeof a === 'string' ? a : JSON.stringify(a)))
18
+ .join(' ');
19
+ }
20
+ /**
21
+ * SDK 内部 logger(变量 qe/Ye)期望的接口:
22
+ * qe.log(...) — 普通日志
23
+ * qe.warn(...) — 警告
24
+ * qe.error(...) — 错误
25
+ * qe.info(...) — 信息
26
+ * qe.debug(...) — 调试
27
+ */
28
+ const sdkLoggerAdapter = {
29
+ log(...args) {
30
+ logger.debug(`[tim-sdk] ${formatArgs(...args)}`);
31
+ },
32
+ warn(...args) {
33
+ logger.warn(`[tim-sdk] ${formatArgs(...args)}`);
34
+ },
35
+ error(...args) {
36
+ logger.error(`[tim-sdk] ${formatArgs(...args)}`);
37
+ },
38
+ info(...args) {
39
+ logger.info(`[tim-sdk] ${formatArgs(...args)}`);
40
+ },
41
+ debug(...args) {
42
+ logger.debug(`[tim-sdk] ${formatArgs(...args)}`);
43
+ },
44
+ };
45
+ // 注入到 globalThis,SDK 加载时会检查此变量
46
+ globalThis.__OPENAGENT_SDK_LOGGER = sdkLoggerAdapter;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * OpenAgent Tools — re-export from app/ module
3
+ *
4
+ * This file exists for backward compatibility.
5
+ * The actual tool implementations live in app/channel-tools.ts,
6
+ * app/messaging-tools.ts, and app/ops-tools.ts.
7
+ */
8
+ export { registerTools } from './app/index.js';
9
+ export type { ToolDescriptor, ToolFactory } from './app/types.js';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * OpenAgent Tools — re-export from app/ module
3
+ *
4
+ * This file exists for backward compatibility.
5
+ * The actual tool implementations live in app/channel-tools.ts,
6
+ * app/messaging-tools.ts, and app/ops-tools.ts.
7
+ */
8
+ export { registerTools } from './app/index.js';
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Transport 工厂 —— 按配置创建 AgentTransport 实例
3
+ *
4
+ * 设计依据:docs/specs/2026-06-16-oasn-transport-abstraction-design.md §3.1
5
+ *
6
+ * 调用方(AccountRuntime)通过 transportType 字段决定走 OASN 还是 TIM 路径,
7
+ * 工厂内部 lazy-import 各自的实现模块,确保「未启用的 transport 不进入运行时」。
8
+ * - 选 OASN 时不加载 TIM SDK(节省启动内存 & 避免无意义的 WebSocket 探测)
9
+ * - 选 TIM 时不加载 OASN HTTP 客户端(更小的攻击面)
10
+ *
11
+ * 注意:transport 实例创建后必须显式调用 ready(credentials) 才能使用,
12
+ * 工厂只负责实例化,不负责凭证校验和连接。
13
+ */
14
+ import type { AgentTransport } from './types.js';
15
+ import type { PendingInvocationStore } from './oasn/index.js';
16
+ /** 工厂入参 */
17
+ export interface CreateTransportOptions {
18
+ /** 选择的 transport 类型 */
19
+ transportType: 'oasn' | 'tim';
20
+ /**
21
+ * OASN API base URL(仅 OASN 路径必填)。
22
+ * 形如:`https://api.oasn.ai`,不含尾斜杠和 `/api/*` 前缀。
23
+ */
24
+ apiBase?: string;
25
+ /** Optional Access-layer API base URL for invocation/file/artifact endpoints. */
26
+ accessApiBase?: string;
27
+ /**
28
+ * Agent id(仅 OASN 路径使用,TIM 路径从 credentials.userId 拿)。
29
+ * 用于实例标识 + 401 撤销时定位本地存储。
30
+ */
31
+ agentId?: string;
32
+ /**
33
+ * 多账户场景下的逻辑 id。
34
+ * 默认与 agentId 相同;当一个 OpenClaw 实例跑多个 OASN/TIM 账号时由调用方传入。
35
+ */
36
+ accountId?: string;
37
+ /**
38
+ * §11.1 OASN-only:把 SQLite-backed PendingInvocationStore 注入 OasnInvocation。
39
+ *
40
+ * - 不传:OasnInvocation 退化为「无持久化」模式(idempotency 仍然在内存生效,但跨进程重启丢失)
41
+ * - 传入:sendTask 时按 toolCallId 复用 idempotency_key,崩溃恢复时可由
42
+ * StateStore.listUnsettledInvocations 续轮询
43
+ *
44
+ * 仅当 transportType='oasn' 时有意义;TIM 路径忽略此字段。
45
+ */
46
+ pendingStore?: PendingInvocationStore;
47
+ }
48
+ /**
49
+ * 创建 transport 实例。
50
+ *
51
+ * 关键设计:返回类型是 union `AgentTransport`,调用方需用 `transportType` 字段 narrow:
52
+ *
53
+ * ```ts
54
+ * const t = await createTransport({ transportType: 'oasn', apiBase, agentId });
55
+ * if (t.transportType === 'oasn') {
56
+ * // 这里 TypeScript 知道 t 是 OasnTransport —— 可以调 t.registerAgent()
57
+ * await t.registerAgent('openclaw');
58
+ * }
59
+ * ```
60
+ *
61
+ * @throws 当 OASN 路径但缺少 apiBase 时
62
+ */
63
+ export declare function createTransport(options: CreateTransportOptions): Promise<AgentTransport>;