openclaw-openagent 1.0.13 → 1.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/dist/src/messaging/executor.d.ts +2 -3
  2. package/dist/src/messaging/executor.js +4 -31
  3. package/dist/src/plugin-ui/assets/openagent-override.js +4 -5
  4. package/dist/src/runtime/update-checker.d.ts +1 -1
  5. package/dist/src/runtime/update-checker.js +9 -46
  6. package/package.json +1 -3
  7. package/src/app/channel-tools.ts +0 -249
  8. package/src/app/discovery-tools.ts +0 -273
  9. package/src/app/download-file-tool.ts +0 -133
  10. package/src/app/hooks.ts +0 -144
  11. package/src/app/index.ts +0 -84
  12. package/src/app/messaging-tools.ts +0 -79
  13. package/src/app/ops-tools.ts +0 -155
  14. package/src/app/remote-agent-tool.ts +0 -762
  15. package/src/app/types.ts +0 -68
  16. package/src/app/upload-file-tool.ts +0 -411
  17. package/src/app/verbose-preflight.ts +0 -190
  18. package/src/auth/config.ts +0 -266
  19. package/src/auth/credential-manager.ts +0 -146
  20. package/src/auth/index.ts +0 -24
  21. package/src/auth/verify.ts +0 -99
  22. package/src/channel.ts +0 -566
  23. package/src/compat.ts +0 -94
  24. package/src/config/config-schema.ts +0 -45
  25. package/src/messaging/aggregator.ts +0 -120
  26. package/src/messaging/collector.ts +0 -89
  27. package/src/messaging/executor.ts +0 -72
  28. package/src/messaging/inbound.ts +0 -150
  29. package/src/messaging/index.ts +0 -11
  30. package/src/messaging/mention-protocol.ts +0 -94
  31. package/src/messaging/process-c2c-request.ts +0 -564
  32. package/src/messaging/process-message.ts +0 -373
  33. package/src/messaging/scheduler.ts +0 -55
  34. package/src/messaging/types.ts +0 -38
  35. package/src/plugin-ui/adapters/oc-2026-04.js +0 -103
  36. package/src/plugin-ui/adapters/oc-2026-05.js +0 -125
  37. package/src/plugin-ui/adapters/oc-2026-06.js +0 -125
  38. package/src/plugin-ui/adapters/oc-unknown.js +0 -48
  39. package/src/plugin-ui/assets/agentbook-icon.svg +0 -5
  40. package/src/plugin-ui/assets/bg.png +0 -0
  41. package/src/plugin-ui/assets/icon.png +0 -0
  42. package/src/plugin-ui/assets/magic.svg +0 -5
  43. package/src/plugin-ui/assets/openagent-override.js +0 -11263
  44. package/src/plugin-ui/build.cjs +0 -463
  45. package/src/plugin-ui/index.ts +0 -18
  46. package/src/plugin-ui/modules/agent-book/panel/agent-book.js +0 -570
  47. package/src/plugin-ui/modules/agent-book/panel/agent-card.js +0 -170
  48. package/src/plugin-ui/modules/agent-book/panel/agent-data.js +0 -643
  49. package/src/plugin-ui/modules/agent-book/panel/inject-ui.js +0 -665
  50. package/src/plugin-ui/modules/agent-book/panel/mention-state.js +0 -206
  51. package/src/plugin-ui/modules/agent-book/panel/styles.js +0 -832
  52. package/src/plugin-ui/modules/agent-book/panel/theme-adapter.js +0 -86
  53. package/src/plugin-ui/modules/agent-book/remote-agent-tool/components-core.js +0 -295
  54. package/src/plugin-ui/modules/agent-book/remote-agent-tool/thought-chain-card.js +0 -211
  55. package/src/plugin-ui/modules/agent-book/scanner.js +0 -131
  56. package/src/plugin-ui/modules/agent-book/travelcard/travel-cards.js +0 -500
  57. package/src/plugin-ui/modules/agent-book/travelcard/travel-engine.js +0 -652
  58. package/src/plugin-ui/modules/agent-book/travelcard/travel-styles.js +0 -262
  59. package/src/plugin-ui/modules/loader/bootstrap.js +0 -133
  60. package/src/plugin-ui/modules/loader/shared-state.js +0 -838
  61. package/src/plugin-ui/modules/loader/ws-intercept.js +0 -199
  62. package/src/plugin-ui/modules/remote-agent/chunk-parser.js +0 -161
  63. package/src/plugin-ui/modules/remote-agent/execution-card.js +0 -530
  64. package/src/plugin-ui/modules/remote-agent/markdown-renderer.js +0 -256
  65. package/src/plugin-ui/modules/remote-agent/media-links.js +0 -151
  66. package/src/plugin-ui/modules/remote-agent/native-style-adapter.js +0 -134
  67. package/src/plugin-ui/modules/remote-agent/output-card.js +0 -342
  68. package/src/plugin-ui/modules/remote-agent/progress-store.js +0 -363
  69. package/src/plugin-ui/modules/remote-agent/render-hooks.js +0 -1700
  70. package/src/plugin-ui/modules/remote-agent/styles.js +0 -903
  71. package/src/plugin-ui/modules/remote-agent/tool-card-model.js +0 -130
  72. package/src/plugin-ui/postinstall-deploy.cjs +0 -52
  73. package/src/plugin-ui/ui-extension-loader/backup.ts +0 -92
  74. package/src/plugin-ui/ui-extension-loader/index.ts +0 -276
  75. package/src/plugin-ui/ui-extension-loader/locator.ts +0 -152
  76. package/src/plugin-ui/ui-extension-loader/manifest.ts +0 -57
  77. package/src/plugin-ui/ui-extension-loader/registry-regex.ts +0 -944
  78. package/src/plugin-ui/ui-extension-loader/removed-extensions.ts +0 -70
  79. package/src/plugin-ui/ui-extension-loader/types.ts +0 -72
  80. package/src/proxy/auth-proxy.ts +0 -414
  81. package/src/runtime/account.ts +0 -549
  82. package/src/runtime/index.ts +0 -7
  83. package/src/runtime/plugin-runtime.ts +0 -94
  84. package/src/runtime/registry.ts +0 -71
  85. package/src/runtime/update-checker.ts +0 -286
  86. package/src/sdk/CLASS_MAP.md +0 -143
  87. package/src/sdk/index.d.ts +0 -126
  88. package/src/sdk/index.js +0 -23990
  89. package/src/sdk/modules/cloud-search-module.js +0 -1117
  90. package/src/sdk/modules/follow-module.js +0 -1069
  91. package/src/sdk/modules/group-module.js +0 -7397
  92. package/src/sdk/modules/relationship-module.js +0 -2269
  93. package/src/sdk/modules/signaling-module.js +0 -1468
  94. package/src/sdk/modules/tim-upload-plugin.js +0 -730
  95. package/src/sdk/node-env/http-request.js +0 -90
  96. package/src/sdk/node-env/index.js +0 -57
  97. package/src/sdk/node-env/storage.js +0 -114
  98. package/src/sdk/package.json +0 -10
  99. package/src/sdk/tsconfig.json +0 -16
  100. package/src/state/pending-invocation-store.ts +0 -43
  101. package/src/state/store.ts +0 -756
  102. package/src/tim/c2c.ts +0 -451
  103. package/src/tim/channels.ts +0 -364
  104. package/src/tim/client.ts +0 -330
  105. package/src/tim/index.ts +0 -18
  106. package/src/tim/messages.ts +0 -166
  107. package/src/tim/sdk-logger-init.ts +0 -50
  108. package/src/tools.ts +0 -10
  109. package/src/transport/factory.ts +0 -95
  110. package/src/transport/oasn/index.ts +0 -17
  111. package/src/transport/oasn/oasn-agent-card.ts +0 -111
  112. package/src/transport/oasn/oasn-discovery.ts +0 -108
  113. package/src/transport/oasn/oasn-files.ts +0 -210
  114. package/src/transport/oasn/oasn-http.ts +0 -529
  115. package/src/transport/oasn/oasn-invocation.ts +0 -604
  116. package/src/transport/oasn/oasn-normalize.ts +0 -133
  117. package/src/transport/oasn/oasn-register.ts +0 -33
  118. package/src/transport/oasn/oasn-transport.ts +0 -318
  119. package/src/transport/oasn/oasn-types.ts +0 -404
  120. package/src/transport/tim/index.ts +0 -8
  121. package/src/transport/tim/tim-transport.ts +0 -515
  122. package/src/transport/types.ts +0 -538
  123. package/src/types/openclaw-plugin-sdk-media-store.d.ts +0 -9
  124. package/src/types/openclaw.d.ts +0 -97
  125. package/src/types/tencentcloud-chat.d.ts +0 -15
  126. package/src/util/http.ts +0 -113
  127. package/src/util/logger.ts +0 -131
  128. package/src/util/url-resolver.ts +0 -77
@@ -1,549 +0,0 @@
1
- /**
2
- * AccountRuntime — All per-account state and lifecycle
3
- *
4
- * Owns the lifecycle of a single active account:
5
- * 1. StateStore (SQLite)
6
- * 2. CredentialManager (auth token lifecycle)
7
- * 3. TIMClient (SDK connection)
8
- * 4. Message pipeline: channelRuntime dispatch (via process-message.ts)
9
- *
10
- * Supports "half-alive" mode: when agent is not yet claimed,
11
- * start() succeeds without TIM connection. Tools trigger
12
- * lazy connection via ensureConnected().
13
- *
14
- * v3.2.0: Replaced shell-out executor pipeline with channelRuntime dispatch.
15
- * See docs/audit/004-messaging-pipeline-v3.1.4.md for migration context.
16
- *
17
- * v3.9.0: 引入 AgentTransport 双轨抽象。
18
- * - transport='oasn'(默认):走 OASN HTTP API,不再创建 TIMClient
19
- * - transport='tim'(显式配置回退):保留原 TIMClient + CredentialManager 路径
20
- * 两条路径在 start() 内按 config.transport 字段分发;旧路径在阶段 4 上层切换完毕后再删除。
21
- * 详见 docs/specs/2026-06-16-oasn-transport-abstraction-design.md §5.2
22
- */
23
-
24
- import { logger } from '../util/logger.js';
25
- import { CredentialManager, CredentialError } from '../auth/credential-manager.js';
26
- import { StateStore } from '../state/store.js';
27
- import { createPendingInvocationStore } from '../state/pending-invocation-store.js';
28
- import { TIMClient } from '../tim/client.js';
29
- import * as channels from '../tim/channels.js';
30
- import * as messages from '../tim/messages.js';
31
- import { initC2CHandler } from '../tim/c2c.js';
32
- import { resolveOpenagentChannelRuntime } from './plugin-runtime.js';
33
- import * as fs from 'node:fs';
34
- import * as os from 'node:os';
35
- import * as path from 'node:path';
36
- import { processOneMessage } from '../messaging/process-message.js';
37
- import { processC2CRequest, type C2CRequestEvent } from '../messaging/process-c2c-request.js';
38
- import { createTransport } from '../transport/factory.js';
39
- import type {
40
- AgentTransport,
41
- AgentStatusResult,
42
- OasnTransport,
43
- TransportCredentials,
44
- } from '../transport/types.js';
45
- import { isUnconfiguredOasnApiBase, type OpenagentAccountConfig, type TransportType } from '../auth/config.js';
46
- import type { RawPushMessage } from '../tim/client.js';
47
- import type { ChannelInfo, MemberInfo } from '../tim/channels.js';
48
- import type { HistoryMessage, SendResult } from '../tim/messages.js';
49
-
50
- export class AccountRuntime {
51
- readonly accountId: string;
52
-
53
- // Sub-modules (accessible by tools via registry)
54
- store: StateStore | null = null;
55
- client: TIMClient | null = null;
56
- /**
57
- * v3.9+ 双轨抽象:统一 transport 接口。
58
- *
59
- * - transport='tim' 下:内部仍持有 TIMClient(this.client)作为 SDK 直连;
60
- * transport 实例额外提供能力接口,便于阶段 4 上层调用
61
- * - transport='oasn' 下:this.client 始终为 null,所有调用走 transport
62
- *
63
- * 阶段 4 完成后,上层将不再直接访问 this.client,只使用 this.transport。
64
- */
65
- transport: AgentTransport | null = null;
66
- private credentialManager: CredentialManager | null = null;
67
- private _transportType: TransportType = 'oasn';
68
- private _claimStatus: OpenagentAccountConfig['status'] = 'unknown';
69
- private _claimUrl = '';
70
- private _claimExpiresAt = '';
71
- private _caAccessToken: string | undefined;
72
- private _statusPollTimer: ReturnType<typeof setTimeout> | null = null;
73
-
74
- private _running = false;
75
- private _config: OpenagentAccountConfig | null = null;
76
- private _cfg: Record<string, unknown> | null = null;
77
- private _connectingPromise: Promise<void> | null = null;
78
-
79
- constructor(accountId: string) {
80
- this.accountId = accountId;
81
- }
82
-
83
- get isRunning(): boolean { return this._running; }
84
- get config(): OpenagentAccountConfig | null { return this._config; }
85
- /** v3.9+ 当前账号选择的 transport 类型('oasn' | 'tim') */
86
- get transportType(): TransportType { return this._transportType; }
87
- get claimStatus(): OpenagentAccountConfig['status'] { return this._claimStatus; }
88
- get claimUrl(): string { return this._claimUrl; }
89
- get claimExpiresAt(): string { return this._claimExpiresAt; }
90
- get caAccessToken(): string | undefined { return this._caAccessToken; }
91
- get isOasnClaimed(): boolean { return this._transportType !== 'oasn' || this._claimStatus === 'claimed'; }
92
-
93
- /**
94
- * Account startup with half-alive support.
95
- *
96
- * If agent is claimed: full startup (StateStore + TIM + pipeline).
97
- * If agent is unclaimed: half-alive (StateStore only, _running = true).
98
- * Tools will trigger lazy connection via ensureConnected().
99
- *
100
- * IMPORTANT: This method NEVER throws for unclaimed/credential errors.
101
- * It must return normally so channel.ts reaches the blocking await Promise,
102
- * preventing OpenClaw auto-restart.
103
- *
104
- * @param cfg - Full OpenClaw config object, needed for channelRuntime APIs
105
- */
106
- async start(
107
- account: OpenagentAccountConfig,
108
- _pipelineConfig?: { batchWindowMs?: number; maxBatchSize?: number },
109
- cfg?: Record<string, unknown>,
110
- ): Promise<void> {
111
- this._config = account;
112
- this._cfg = cfg || {};
113
- this._transportType = account.transport;
114
- this._claimStatus = account.status;
115
- this._claimUrl = account.claimUrl;
116
-
117
- try {
118
- // ── Phase 1: StateStore (always) ──
119
- logger.info(`[runtime] Starting account '${this.accountId}': init StateStore`);
120
- this.store = new StateStore();
121
- await this.store.init(this.accountId);
122
-
123
- // ── Phase 2: 按 transport 分发凭证管理 ──
124
- // OASN 路径:api_key 直接作为 Bearer,不走 CredentialManager(无缓存/冷却)。
125
- // TIM 路径:保留原有 CredentialManager(apiKey → auth.ai-talk.live → UserSig)。
126
- if (this._transportType === 'tim') {
127
- this.credentialManager = new CredentialManager(account.agentId, account.apiKey);
128
- logger.info(`[runtime] CredentialManager created for agent ${account.agentId} (transport=tim)`);
129
- } else {
130
- logger.info(`[runtime] transport=oasn: skipping CredentialManager (api_key used as Bearer directly)`);
131
- }
132
-
133
- // ── Phase 3: 建立 transport 连接 ──
134
- try {
135
- if (this._transportType === 'oasn') {
136
- await this._connectOASN();
137
- logger.info(`[runtime] Account '${this.accountId}' started: OASN transport ready`);
138
- } else {
139
- await this._connectTIM();
140
- logger.info(`[runtime] Account '${this.accountId}' started: fully connected (transport=tim)`);
141
- }
142
- } catch (err) {
143
- if (err instanceof CredentialError) {
144
- // Unclaimed or cooldown: enter half-alive state(仅 TIM)
145
- logger.warn(`[runtime] Account '${this.accountId}' started in half-alive mode (${err.reason}: ${err.message})`);
146
- } else {
147
- // Unexpected error during connect: still go half-alive, don't crash
148
- logger.error(`[runtime] Account '${this.accountId}' transport connect failed, entering half-alive: ${(err as Error).message}`);
149
- }
150
- }
151
-
152
- this._running = true;
153
- } catch (err) {
154
- // Only StateStore init failure reaches here
155
- logger.error(`[runtime] Account '${this.accountId}' start failed: ${(err as Error).message}`);
156
- await this._cleanup();
157
- throw err;
158
- }
159
- }
160
-
161
- /**
162
- * OASN 连接路径(v3.9+)。
163
- *
164
- * 与 _connectTIM 不同:
165
- * - 无 WebSocket,ready() 只校验 api_key 有效性(GET /api/agent-cards/{agent_id})
166
- * - 不需要 channelRuntime 接管 'message' / 'c2c_request' 事件(OASN ClientAgent 不接收外来 Invocation)
167
- * - 'credential_expired' 事件由 OasnTransport 在 401 时触发,处理:清 transport + 标记 half-alive
168
- */
169
- private async _connectOASN(): Promise<void> {
170
- if (!this._config) throw new Error('Account config not set');
171
- if (!this.store) throw new Error('StateStore not initialized');
172
-
173
- logger.info(
174
- `[runtime] Connecting OASN transport for ${this.accountId} `
175
- + `api_base=${this._config.apiBase} access_api_base=${this._config.accessApiBase}`,
176
- );
177
- if (isUnconfiguredOasnApiBase(this._config.apiBase)) {
178
- throw new Error('OASN api_base is not configured; set channels.openagent.api_base or accounts.default.api_base');
179
- }
180
-
181
- // §11.1:把 StateStore 包装成 PendingInvocationStore,注入到 OasnTransport。
182
- // 这样 sendTask 时会按 toolCallId 在 SQLite 里查/写 idempotency_key 与 invocation_id,
183
- // 进程崩溃重启后 listUnsettledInvocations() 可定位未结调用做续轮询。
184
- const pendingStore = createPendingInvocationStore(this.store);
185
-
186
- const transport = await createTransport({
187
- transportType: 'oasn',
188
- apiBase: this._config.apiBase,
189
- accessApiBase: this._config.accessApiBase,
190
- agentId: this._config.agentId,
191
- accountId: this.accountId,
192
- pendingStore,
193
- });
194
-
195
- // §11.5 凭证撤销路径
196
- transport.on('credential_expired', () => {
197
- logger.error(`[runtime] OASN credential_expired for ${this.accountId} - clearing transport`);
198
- // 不直接清 config 里的 api_key —— 持久层清理由上层(plugin-ui / register 工具)决定
199
- // 但本地 transport 已不可用,标记为待重连
200
- this.transport = null;
201
- this._claimStatus = 'credential_expired';
202
- this._caAccessToken = undefined;
203
- this._stopOasnStatusPolling();
204
- this._saveOasnAccountConfig({ status: 'credential_expired' });
205
- });
206
-
207
- // 拼装 OASN 凭证:identity=agent_id, token=api_key, expiresAt=0(不过期)。
208
- // 首次注册由 openclaw-plugin-openagent-cli 安装器负责;runtime 不创建新 ClientAgent。
209
- const agentId = this._config.agentId;
210
- const apiKey = this._config.apiKey;
211
- if (!agentId || !apiKey) {
212
- throw new Error('OASN account is not configured; run openclaw-plugin-openagent-cli install to register this device');
213
- }
214
-
215
- const creds: TransportCredentials = {
216
- identity: agentId,
217
- token: apiKey,
218
- expiresAt: 0,
219
- };
220
-
221
- await transport.ready(creds);
222
- await this._syncOasnStatus(transport as import('../transport/types.js').OasnTransport);
223
- this._scheduleOasnStatusPolling(transport as import('../transport/types.js').OasnTransport);
224
-
225
- // §11.1 持久化已通过 factory 的 pendingStore 注入完成 —— OasnInvocation 内部在
226
- // sendTask/waitForTaskResult 流程里直接读写 store,AccountRuntime 这一层无需再介入。
227
- // (原先将 createPendingInvocationStore 作为 TODO 留在外部组合,阶段 4 已收敛到工厂)
228
-
229
- this.transport = transport;
230
- }
231
-
232
- /**
233
- * 将 OASN 账号状态持久化到 OpenClaw 配置文件。
234
- *
235
- * 写入位置:~/.openclaw/openclaw.json → channels.openagent.accounts.{accountId}
236
- * 采用 merge 策略。CA access_token 永远只留在 runtime 内存,不写入配置。
237
- */
238
- private _saveOasnAccountConfig(updates: Record<string, unknown>): void {
239
- try {
240
- const configPath = path.join(os.homedir(), '.openclaw', 'openclaw.json');
241
- if (!fs.existsSync(configPath)) {
242
- logger.warn(`[runtime] Config file not found: ${configPath}, skipping credential persistence`);
243
- return;
244
- }
245
-
246
- const raw = fs.readFileSync(configPath, 'utf-8');
247
- const cfg = JSON.parse(raw);
248
-
249
- // 确保路径存在:channels.openagent.accounts.{accountId}
250
- cfg.channels = cfg.channels || {};
251
- cfg.channels.openagent = cfg.channels.openagent || {};
252
- cfg.channels.openagent.accounts = cfg.channels.openagent.accounts || {};
253
- const accId = this._config?.accountId || 'default';
254
- cfg.channels.openagent.accounts[accId] = cfg.channels.openagent.accounts[accId] || {};
255
-
256
- const acc = cfg.channels.openagent.accounts[accId];
257
- for (const [key, value] of Object.entries(updates)) {
258
- if (value === undefined || key === 'access_token' || key === 'accessToken' || key === 'agent_card') continue;
259
- acc[key] = value;
260
- }
261
-
262
- fs.writeFileSync(configPath, JSON.stringify(cfg, null, 2), { mode: 0o600 });
263
- logger.info(`[runtime] OASN account config persisted to ${configPath} (keys=${Object.keys(updates).join(',')})`);
264
- } catch (err) {
265
- logger.warn(`[runtime] Failed to persist OASN account config: ${(err as Error).message}`);
266
- }
267
- }
268
-
269
- private async _syncOasnStatus(
270
- transport: import('../transport/types.js').OasnTransport,
271
- ): Promise<AgentStatusResult | null> {
272
- const agentId = this._config?.agentId;
273
- if (!agentId) return null;
274
- const status = await transport.getAgentStatus(agentId);
275
- this._claimStatus = status.status;
276
- this._caAccessToken = status.access_token;
277
- this._config = this._config
278
- ? { ...this._config, status: status.status }
279
- : this._config;
280
- this._saveOasnAccountConfig({ status: status.status });
281
- logger.info(`[runtime] OASN status synced: agent_id=${agentId} status=${status.status} access_token=${status.access_token ? 'present' : 'absent'}`);
282
- return status;
283
- }
284
-
285
- private _scheduleOasnStatusPolling(
286
- transport: import('../transport/types.js').OasnTransport,
287
- ): void {
288
- this._stopOasnStatusPolling();
289
- if (this._transportType !== 'oasn' || this._claimStatus === 'claimed' || this._claimStatus === 'credential_expired') {
290
- return;
291
- }
292
- this._statusPollTimer = setTimeout(() => {
293
- void this._syncOasnStatus(transport)
294
- .then(() => {
295
- if (this._claimStatus !== 'claimed' && this._claimStatus !== 'credential_expired' && this.transport === transport) {
296
- this._scheduleOasnStatusPolling(transport);
297
- }
298
- })
299
- .catch((err) => {
300
- logger.warn(`[runtime] OASN status polling failed: ${(err as Error).message}`);
301
- if (this.transport === transport) this._scheduleOasnStatusPolling(transport);
302
- });
303
- }, 30_000);
304
- }
305
-
306
- private _stopOasnStatusPolling(): void {
307
- if (!this._statusPollTimer) return;
308
- clearTimeout(this._statusPollTimer);
309
- this._statusPollTimer = null;
310
- }
311
-
312
- assertOasnInvocationAllowed(): void {
313
- if (this._transportType === 'oasn' && this._claimStatus !== 'claimed') {
314
- throw new Error(`OASN ClientAgent is not claimed yet (status=${this._claimStatus}). Complete claim before calling remote agents.`);
315
- }
316
- }
317
-
318
- /**
319
- * Connect TIM and wire the channelRuntime dispatch pipeline.
320
- * Called by start() and ensureConnected().
321
- */
322
- private async _connectTIM(): Promise<void> {
323
- if (!this.credentialManager) throw new Error('CredentialManager not initialized');
324
- if (!this._config) throw new Error('Account config not set');
325
-
326
- const credentials = await this.credentialManager.getCredentials();
327
-
328
- logger.info(`[runtime] Connecting TIM SDK for ${this.accountId}`);
329
- this.client = new TIMClient();
330
- await this.client.connect(credentials);
331
-
332
- // Initialize C2C message handler for remote agent communication
333
- initC2CHandler(this.client);
334
-
335
- // Listen for UserSig expiry
336
- this.client.on('credential_expired', () => {
337
- logger.warn(`[runtime] Received credential_expired for ${this.accountId}, invalidating + reconnecting`);
338
- this.credentialManager!.invalidate();
339
- // Attempt to reconnect with fresh credentials
340
- void this._connectTIM().catch((err) => {
341
- logger.error(`[runtime] Reconnect after UserSig expiry failed: ${(err as Error).message}`);
342
- });
343
- });
344
-
345
- // ── channelRuntime dispatch pipeline ──
346
- // Replaces: Collector → Aggregator → Scheduler → Executor (shell-out)
347
- // With: processOneMessage → channelRuntime.reply.dispatchReplyFromConfig
348
- try {
349
- const channelRuntime = await resolveOpenagentChannelRuntime({});
350
-
351
- this.client.on('message', (rawMsg: RawPushMessage) => {
352
- void processOneMessage(rawMsg, {
353
- accountId: this.accountId,
354
- selfUserId: this.client!.agentId,
355
- config: this._cfg || {},
356
- channelRuntime,
357
- sendMessage: async (chId: string, text: string, atUserList?: string[]) => {
358
- await messages.sendMessage(this.client!, chId, text, atUserList);
359
- },
360
- getChannelSkill: async (chId: string) => {
361
- return channels.getChannelSkill(this.client!, chId);
362
- },
363
- getGroupName: async (chId: string) => {
364
- return channels.getGroupName(this.client!, chId);
365
- },
366
- getGroupCustomField: async (chId: string, key: string) => {
367
- return channels.getGroupCustomField(this.client!, chId, key);
368
- },
369
- getGroupMembers: async (chId: string) => {
370
- const members = await channels.getMembers(this.client!, chId);
371
- return members.map(m => ({ id: m.id, nick: m.nick }));
372
- },
373
- log: logger,
374
- }).catch((err: unknown) => {
375
- logger.error(`[runtime] processOneMessage failed: ${(err as Error).message}`);
376
- });
377
- });
378
-
379
- // ── C2C remote agent request handler (T3) ──
380
- // When another agent sends a task request via C2C, dispatch it through
381
- // the same channelRuntime pipeline. Mirrors the 'message' listener above.
382
- // @see docs/products/orchestrator/specs/T3-remote-handler.md
383
- this.client.on('c2c_request', (event: C2CRequestEvent) => {
384
- void processC2CRequest(event, {
385
- accountId: this.accountId,
386
- selfUserId: this.client!.agentId,
387
- config: this._cfg || {},
388
- channelRuntime,
389
- }).catch((err: unknown) => {
390
- logger.error(`[runtime] processC2CRequest failed: ${(err as Error).message}`);
391
- });
392
- });
393
-
394
- logger.info(`[runtime] TIM connected + channelRuntime pipeline wired for ${this.accountId}`);
395
- } catch (err) {
396
- // channelRuntime resolution failed — fall through to half-alive
397
- // TIM is connected but AI dispatch won't work
398
- logger.error(`[runtime] channelRuntime resolution failed: ${(err as Error).message}`);
399
- logger.warn(`[runtime] TIM connected but AI dispatch unavailable for ${this.accountId}`);
400
- }
401
- }
402
-
403
- /**
404
- * Ensure TIM is connected before executing a tool operation.
405
- * If not connected, attempts lazy connection.
406
- *
407
- * v3.9+:OASN 路径下走 _connectOASN();TIM 路径维持原行为。
408
- *
409
- * @throws CredentialError — propagated to tool, which returns a user-friendly message
410
- */
411
- async ensureConnected(): Promise<void> {
412
- if (this._transportType === 'oasn') {
413
- if (this.transport?.isReady) return;
414
- if (this._connectingPromise) {
415
- logger.debug(`[runtime] ensureConnected: waiting for in-progress OASN connection`);
416
- return this._connectingPromise;
417
- }
418
- logger.info(`[runtime] ensureConnected: OASN transport not ready, initiating lazy connect for ${this.accountId}`);
419
- this._connectingPromise = this._connectOASN();
420
- try {
421
- await this._connectingPromise;
422
- } finally {
423
- this._connectingPromise = null;
424
- }
425
- return;
426
- }
427
-
428
- // TIM 路径(原逻辑保留)
429
- if (this.client?.isReady) return;
430
-
431
- // Prevent concurrent connect attempts
432
- if (this._connectingPromise) {
433
- logger.debug(`[runtime] ensureConnected: waiting for in-progress connection`);
434
- return this._connectingPromise;
435
- }
436
-
437
- logger.info(`[runtime] ensureConnected: TIM not ready, initiating lazy connect for ${this.accountId}`);
438
- this._connectingPromise = this._connectTIM();
439
- try {
440
- await this._connectingPromise;
441
- } finally {
442
- this._connectingPromise = null;
443
- }
444
- }
445
-
446
- /**
447
- * Graceful shutdown of all modules.
448
- */
449
- async stop(): Promise<void> {
450
- logger.info(`[runtime] Stopping account '${this.accountId}'`);
451
- await this._cleanup();
452
- logger.info(`[runtime] Account '${this.accountId}' stopped`);
453
- }
454
-
455
- private async _cleanup(): Promise<void> {
456
- this._running = false;
457
- this._stopOasnStatusPolling();
458
- if (this.client) {
459
- await this.client.disconnect();
460
- this.client = null;
461
- }
462
- if (this.transport) {
463
- try {
464
- await this.transport.dispose();
465
- } catch (err) {
466
- logger.warn(`[runtime] transport.dispose error: ${(err as Error).message}`);
467
- }
468
- this.transport = null;
469
- }
470
- if (this.store) {
471
- this.store.close();
472
- this.store = null;
473
- }
474
- }
475
-
476
- // ── Strategy + status queries (used by ops-tools) ──
477
-
478
- /**
479
- * @deprecated Pipeline batching is now handled by channelRuntime.
480
- * Retained for backward compatibility with openagent_set_strategy tool.
481
- */
482
- updateStrategy(_config: { batchWindowMs?: number; maxBatchSize?: number }): { batchWindowMs: number; maxBatchSize: number } {
483
- logger.debug('[runtime] updateStrategy called (no-op: batching handled by channelRuntime)');
484
- return {
485
- batchWindowMs: _config.batchWindowMs || 20_000,
486
- maxBatchSize: _config.maxBatchSize || 20,
487
- };
488
- }
489
-
490
- /**
491
- * @deprecated Scheduler queue no longer exists in channelRuntime mode.
492
- */
493
- get schedulerQueueSize(): number {
494
- return 0;
495
- }
496
-
497
- // ── Delegated SDK operations (used by tools) ──
498
- // All operations go through ensureConnected() for lazy connection.
499
-
500
- async listChannels(): Promise<ChannelInfo[]> {
501
- await this.ensureConnected();
502
- return channels.listChannels(this.client!);
503
- }
504
-
505
- async searchChannels(keyword: string): Promise<ChannelInfo[]> {
506
- await this.ensureConnected();
507
- return channels.searchChannels(this.client!, keyword);
508
- }
509
-
510
- async joinChannel(channelId: string): Promise<{ success: boolean; message: string }> {
511
- await this.ensureConnected();
512
- return channels.joinChannel(this.client!, channelId);
513
- }
514
-
515
- async leaveChannel(channelId: string): Promise<{ success: boolean; message: string }> {
516
- await this.ensureConnected();
517
- return channels.leaveChannel(this.client!, channelId);
518
- }
519
-
520
- async getMembers(channelId: string): Promise<MemberInfo[]> {
521
- await this.ensureConnected();
522
- return channels.getMembers(this.client!, channelId);
523
- }
524
-
525
- async createChannel(name: string, description?: string, skill?: string): Promise<{ success: boolean; channelId: string }> {
526
- await this.ensureConnected();
527
- return channels.createChannel(this.client!, name, description, skill);
528
- }
529
-
530
- async getChannelSkill(channelId: string): Promise<string | null> {
531
- await this.ensureConnected();
532
- return channels.getChannelSkill(this.client!, channelId);
533
- }
534
-
535
- async setChannelSkill(channelId: string, skill: string): Promise<{ success: boolean }> {
536
- await this.ensureConnected();
537
- return channels.setChannelSkill(this.client!, channelId, skill);
538
- }
539
-
540
- async sendMessage(channelId: string, text: string): Promise<SendResult> {
541
- await this.ensureConnected();
542
- return messages.sendMessage(this.client!, channelId, text);
543
- }
544
-
545
- async fetchMessages(channelId: string, count?: number): Promise<HistoryMessage[]> {
546
- await this.ensureConnected();
547
- return messages.fetchMessages(this.client!, channelId, count);
548
- }
549
- }
@@ -1,7 +0,0 @@
1
- /**
2
- * Runtime — Module barrel export
3
- */
4
-
5
- export { RuntimeRegistry, registry } from './registry.js';
6
- export { AccountRuntime } from './account.js';
7
- // guard.ts removed — unhandled rejections fixed at source in ws-frame.ts
@@ -1,94 +0,0 @@
1
- /**
2
- * Plugin Runtime — channelRuntime acquisition and readiness gate
3
- *
4
- * Mirrors the pattern established by openclaw-weixin v2.1.1 runtime.ts:
5
- * 1. register() stores the PluginRuntime via setOpenagentPluginRuntime()
6
- * 2. startAccount() resolves channelRuntime via resolveOpenagentChannelRuntime()
7
- * with a three-level fallback: ctx-injected → global → async wait
8
- *
9
- * Reference: docs/reference/plugins/openclaw-weixin/src/runtime.ts
10
- */
11
-
12
- import type { PluginRuntime } from 'openclaw/plugin-sdk/core';
13
-
14
- import { logger } from '../util/logger.js';
15
-
16
- // ── Module-level singleton ──
17
-
18
- let pluginRuntime: PluginRuntime | null = null;
19
-
20
- export type PluginChannelRuntime = PluginRuntime['channel'];
21
-
22
- // ── Public API ──
23
-
24
- /**
25
- * Store the PluginRuntime received during register().
26
- * Called once from index.ts register().
27
- */
28
- export function setOpenagentPluginRuntime(next: PluginRuntime): void {
29
- pluginRuntime = next;
30
- logger.info('[plugin-runtime] setOpenagentPluginRuntime called, runtime set');
31
- }
32
-
33
- /**
34
- * Synchronous getter — throws if not initialized.
35
- * Use only when you are certain register() has completed.
36
- */
37
- export function getOpenagentPluginRuntime(): PluginRuntime {
38
- if (!pluginRuntime) {
39
- throw new Error('OpenAgent PluginRuntime not initialized');
40
- }
41
- return pluginRuntime;
42
- }
43
-
44
- // ── Async wait ──
45
-
46
- const WAIT_INTERVAL_MS = 100;
47
- const DEFAULT_TIMEOUT_MS = 10_000;
48
-
49
- /**
50
- * Async polling wait for the runtime to become available.
51
- * Used during startAccount() if register() hasn't completed yet.
52
- */
53
- export async function waitForOpenagentPluginRuntime(
54
- timeoutMs = DEFAULT_TIMEOUT_MS,
55
- ): Promise<PluginRuntime> {
56
- const start = Date.now();
57
- while (!pluginRuntime) {
58
- if (Date.now() - start > timeoutMs) {
59
- throw new Error('OpenAgent PluginRuntime initialization timeout');
60
- }
61
- await new Promise((resolve) => setTimeout(resolve, WAIT_INTERVAL_MS));
62
- }
63
- return pluginRuntime;
64
- }
65
-
66
- // ── Three-level fallback resolver ──
67
-
68
- /**
69
- * Resolve PluginRuntime['channel'] with three-level fallback:
70
- *
71
- * 1. Gateway-injected channelRuntime on ctx (new OpenClaw versions)
72
- * 2. Module-global from register()
73
- * 3. Async wait for register() to complete
74
- *
75
- * This matches openclaw-weixin's resolveWeixinChannelRuntime() exactly.
76
- */
77
- export async function resolveOpenagentChannelRuntime(params: {
78
- channelRuntime?: PluginChannelRuntime;
79
- waitTimeoutMs?: number;
80
- }): Promise<PluginChannelRuntime> {
81
- if (params.channelRuntime) {
82
- logger.debug('[plugin-runtime] channelRuntime from gateway context');
83
- return params.channelRuntime;
84
- }
85
- if (pluginRuntime) {
86
- logger.debug('[plugin-runtime] channelRuntime from register() global');
87
- return pluginRuntime.channel;
88
- }
89
- logger.warn(
90
- '[plugin-runtime] no channelRuntime on ctx and no global runtime yet; waiting for register()',
91
- );
92
- const pr = await waitForOpenagentPluginRuntime(params.waitTimeoutMs ?? DEFAULT_TIMEOUT_MS);
93
- return pr.channel;
94
- }