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
@@ -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
- }