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.
- package/dist/index.d.ts +25 -0
- package/dist/index.js +105 -0
- package/dist/src/app/channel-tools.d.ts +28 -0
- package/dist/src/app/channel-tools.js +251 -0
- package/dist/src/app/discovery-tools.d.ts +35 -0
- package/dist/src/app/discovery-tools.js +243 -0
- package/dist/src/app/download-file-tool.d.ts +10 -0
- package/dist/src/app/download-file-tool.js +104 -0
- package/dist/src/app/hooks.d.ts +14 -0
- package/dist/src/app/hooks.js +118 -0
- package/dist/src/app/index.d.ts +13 -0
- package/dist/src/app/index.js +44 -0
- package/dist/src/app/messaging-tools.d.ts +11 -0
- package/dist/src/app/messaging-tools.js +79 -0
- package/dist/src/app/ops-tools.d.ts +21 -0
- package/dist/src/app/ops-tools.js +158 -0
- package/dist/src/app/remote-agent-tool.d.ts +27 -0
- package/dist/src/app/remote-agent-tool.js +461 -0
- package/dist/src/app/types.d.ts +61 -0
- package/dist/src/app/types.js +11 -0
- package/dist/src/app/upload-file-tool.d.ts +16 -0
- package/dist/src/app/upload-file-tool.js +353 -0
- package/dist/src/app/verbose-preflight.d.ts +2 -0
- package/dist/src/app/verbose-preflight.js +145 -0
- package/dist/src/auth/config.d.ts +79 -0
- package/dist/src/auth/config.js +133 -0
- package/dist/src/auth/credential-manager.d.ts +65 -0
- package/dist/src/auth/credential-manager.js +122 -0
- package/dist/src/auth/index.d.ts +6 -0
- package/dist/src/auth/index.js +6 -0
- package/dist/src/auth/verify.d.ts +42 -0
- package/dist/src/auth/verify.js +60 -0
- package/dist/src/channel.d.ts +269 -0
- package/dist/src/channel.js +488 -0
- package/dist/src/compat.d.ts +37 -0
- package/dist/src/compat.js +70 -0
- package/dist/src/config/config-schema.d.ts +56 -0
- package/dist/src/config/config-schema.js +34 -0
- package/dist/src/messaging/aggregator.d.ts +25 -0
- package/dist/src/messaging/aggregator.js +90 -0
- package/dist/src/messaging/collector.d.ts +27 -0
- package/dist/src/messaging/collector.js +76 -0
- package/dist/src/messaging/executor.d.ts +14 -0
- package/dist/src/messaging/executor.js +59 -0
- package/dist/src/messaging/inbound.d.ts +97 -0
- package/dist/src/messaging/inbound.js +63 -0
- package/dist/src/messaging/index.d.ts +10 -0
- package/dist/src/messaging/index.js +9 -0
- package/dist/src/messaging/mention-protocol.d.ts +42 -0
- package/dist/src/messaging/mention-protocol.js +74 -0
- package/dist/src/messaging/process-c2c-request.d.ts +55 -0
- package/dist/src/messaging/process-c2c-request.js +445 -0
- package/dist/src/messaging/process-message.d.ts +62 -0
- package/dist/src/messaging/process-message.js +282 -0
- package/dist/src/messaging/scheduler.d.ts +17 -0
- package/dist/src/messaging/scheduler.js +47 -0
- package/dist/src/messaging/types.d.ts +34 -0
- package/dist/src/messaging/types.js +4 -0
- package/dist/src/plugin-ui/assets/openagent-override.js +9267 -0
- package/dist/src/plugin-ui/index.d.ts +13 -0
- package/dist/src/plugin-ui/index.js +16 -0
- package/dist/src/plugin-ui/ui-extension-loader/backup.d.ts +23 -0
- package/dist/src/plugin-ui/ui-extension-loader/backup.js +82 -0
- package/dist/src/plugin-ui/ui-extension-loader/index.d.ts +28 -0
- package/dist/src/plugin-ui/ui-extension-loader/index.js +240 -0
- package/dist/src/plugin-ui/ui-extension-loader/locator.d.ts +35 -0
- package/dist/src/plugin-ui/ui-extension-loader/locator.js +129 -0
- package/dist/src/plugin-ui/ui-extension-loader/manifest.d.ts +26 -0
- package/dist/src/plugin-ui/ui-extension-loader/manifest.js +45 -0
- package/dist/src/plugin-ui/ui-extension-loader/registry-regex.d.ts +9 -0
- package/dist/src/plugin-ui/ui-extension-loader/registry-regex.js +701 -0
- package/dist/src/plugin-ui/ui-extension-loader/removed-extensions.d.ts +8 -0
- package/dist/src/plugin-ui/ui-extension-loader/removed-extensions.js +58 -0
- package/dist/src/plugin-ui/ui-extension-loader/types.d.ts +55 -0
- package/dist/src/plugin-ui/ui-extension-loader/types.js +23 -0
- package/dist/src/proxy/auth-proxy.d.ts +20 -0
- package/dist/src/proxy/auth-proxy.js +337 -0
- package/dist/src/runtime/account.d.ts +156 -0
- package/dist/src/runtime/account.js +491 -0
- package/dist/src/runtime/index.d.ts +5 -0
- package/dist/src/runtime/index.js +6 -0
- package/dist/src/runtime/plugin-runtime.d.ts +40 -0
- package/dist/src/runtime/plugin-runtime.js +72 -0
- package/dist/src/runtime/registry.d.ts +41 -0
- package/dist/src/runtime/registry.js +60 -0
- package/dist/src/sdk/CLASS_MAP.md +143 -0
- package/dist/src/sdk/index.d.ts +126 -0
- package/dist/src/sdk/index.js +23990 -0
- package/dist/src/sdk/modules/cloud-search-module.js +1117 -0
- package/dist/src/sdk/modules/follow-module.js +1069 -0
- package/dist/src/sdk/modules/group-module.js +7397 -0
- package/dist/src/sdk/modules/relationship-module.js +2269 -0
- package/dist/src/sdk/modules/signaling-module.js +1468 -0
- package/dist/src/sdk/modules/tim-upload-plugin.js +730 -0
- package/dist/src/sdk/node-env/http-request.js +90 -0
- package/dist/src/sdk/node-env/index.js +57 -0
- package/dist/src/sdk/node-env/storage.js +114 -0
- package/dist/src/sdk/package.json +10 -0
- package/dist/src/sdk/sdk/CLASS_MAP.md +143 -0
- package/dist/src/sdk/sdk/index.d.ts +126 -0
- package/dist/src/sdk/sdk/index.js +23990 -0
- package/dist/src/sdk/sdk/modules/cloud-search-module.js +1117 -0
- package/dist/src/sdk/sdk/modules/follow-module.js +1069 -0
- package/dist/src/sdk/sdk/modules/group-module.js +7397 -0
- package/dist/src/sdk/sdk/modules/relationship-module.js +2269 -0
- package/dist/src/sdk/sdk/modules/signaling-module.js +1468 -0
- package/dist/src/sdk/sdk/modules/tim-upload-plugin.js +730 -0
- package/dist/src/sdk/sdk/node-env/http-request.js +90 -0
- package/dist/src/sdk/sdk/node-env/index.js +57 -0
- package/dist/src/sdk/sdk/node-env/storage.js +114 -0
- package/dist/src/sdk/sdk/package.json +10 -0
- package/dist/src/sdk/sdk/tsconfig.json +16 -0
- package/dist/src/sdk/tsconfig.json +16 -0
- package/dist/src/state/pending-invocation-store.d.ts +26 -0
- package/dist/src/state/pending-invocation-store.js +39 -0
- package/dist/src/state/store.d.ts +165 -0
- package/dist/src/state/store.js +535 -0
- package/dist/src/tim/c2c.d.ts +83 -0
- package/dist/src/tim/c2c.js +318 -0
- package/dist/src/tim/channels.d.ts +95 -0
- package/dist/src/tim/channels.js +279 -0
- package/dist/src/tim/client.d.ts +54 -0
- package/dist/src/tim/client.js +268 -0
- package/dist/src/tim/index.d.ts +6 -0
- package/dist/src/tim/index.js +6 -0
- package/dist/src/tim/messages.d.ts +50 -0
- package/dist/src/tim/messages.js +104 -0
- package/dist/src/tim/sdk-logger-init.d.ts +13 -0
- package/dist/src/tim/sdk-logger-init.js +46 -0
- package/dist/src/tools.d.ts +9 -0
- package/dist/src/tools.js +8 -0
- package/dist/src/transport/factory.d.ts +63 -0
- package/dist/src/transport/factory.js +54 -0
- package/dist/src/transport/oasn/index.d.ts +12 -0
- package/dist/src/transport/oasn/index.js +9 -0
- package/dist/src/transport/oasn/oasn-agent-card.d.ts +38 -0
- package/dist/src/transport/oasn/oasn-agent-card.js +102 -0
- package/dist/src/transport/oasn/oasn-discovery.d.ts +33 -0
- package/dist/src/transport/oasn/oasn-discovery.js +97 -0
- package/dist/src/transport/oasn/oasn-files.d.ts +64 -0
- package/dist/src/transport/oasn/oasn-files.js +174 -0
- package/dist/src/transport/oasn/oasn-http.d.ts +98 -0
- package/dist/src/transport/oasn/oasn-http.js +362 -0
- package/dist/src/transport/oasn/oasn-invocation.d.ts +154 -0
- package/dist/src/transport/oasn/oasn-invocation.js +432 -0
- package/dist/src/transport/oasn/oasn-normalize.d.ts +6 -0
- package/dist/src/transport/oasn/oasn-normalize.js +112 -0
- package/dist/src/transport/oasn/oasn-register.d.ts +19 -0
- package/dist/src/transport/oasn/oasn-register.js +24 -0
- package/dist/src/transport/oasn/oasn-transport.d.ts +114 -0
- package/dist/src/transport/oasn/oasn-transport.js +230 -0
- package/dist/src/transport/oasn/oasn-types.d.ts +331 -0
- package/dist/src/transport/oasn/oasn-types.js +44 -0
- package/dist/src/transport/tim/index.d.ts +7 -0
- package/dist/src/transport/tim/index.js +6 -0
- package/dist/src/transport/tim/tim-transport.d.ts +122 -0
- package/dist/src/transport/tim/tim-transport.js +402 -0
- package/dist/src/transport/types.d.ts +450 -0
- package/dist/src/transport/types.js +38 -0
- package/dist/src/util/http.d.ts +21 -0
- package/dist/src/util/http.js +93 -0
- package/dist/src/util/logger.d.ts +20 -0
- package/dist/src/util/logger.js +100 -0
- package/dist/src/util/url-resolver.d.ts +7 -0
- package/dist/src/util/url-resolver.js +20 -0
- package/index.ts +11 -0
- package/openclaw.plugin.json +9 -0
- package/package.json +7 -4
- package/src/app/download-file-tool.ts +133 -0
- package/src/app/hooks.ts +89 -5
- package/src/app/index.ts +6 -0
- package/src/app/remote-agent-tool.ts +46 -0
- package/src/app/types.ts +1 -0
- package/src/app/upload-file-tool.ts +411 -0
- package/src/plugin-ui/assets/openagent-override.js +1 -63
- package/src/plugin-ui/modules/agent-book/panel/agent-book.js +0 -61
- package/src/plugin-ui/modules/agent-book/panel/agent-data.js +0 -1
- package/src/plugin-ui/ui-extension-loader/registry-regex.ts +30 -13
- package/src/runtime/account.ts +0 -23
- package/src/transport/oasn/oasn-files.ts +5 -5
- package/src/transport/oasn/oasn-invocation.ts +44 -2
- package/src/transport/oasn/oasn-normalize.ts +0 -26
- package/src/transport/oasn/oasn-register.ts +8 -81
- package/src/transport/oasn/oasn-transport.ts +9 -32
- package/src/transport/oasn/oasn-types.ts +53 -6
- package/src/transport/types.ts +33 -36
- package/src/util/url-resolver.ts +17 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process Message — Core inbound dispatch pipeline
|
|
3
|
+
*
|
|
4
|
+
* ⚠️ TIM-only 路径(v3.9+ 双轨抽象)
|
|
5
|
+
* ---------------------------------------------------------
|
|
6
|
+
* 仅在 transport='tim' 下被 AccountRuntime._connectTIM() 通过
|
|
7
|
+
* `this.client.on('message', ...)` 注册。OASN 路径下没有 WebSocket 群聊消息,
|
|
8
|
+
* 该 pipeline 整体不参与运行。
|
|
9
|
+
*
|
|
10
|
+
* Replaces the old 4-module pipeline (Collector→Aggregator→Scheduler→Executor)
|
|
11
|
+
* with OpenClaw's standard channelRuntime dispatch:
|
|
12
|
+
*
|
|
13
|
+
* 1. Self-message filter
|
|
14
|
+
* 2. channelRuntime safety check
|
|
15
|
+
* 3. Append to channel history ring buffer
|
|
16
|
+
* 4. Fetch group context (name, skill)
|
|
17
|
+
* 5. TIM → MsgContext conversion (inbound.ts) with enriched context
|
|
18
|
+
* 6. Agent routing (channelRuntime.routing.resolveAgentRoute)
|
|
19
|
+
* 7. Finalize context (channelRuntime.reply.finalizeInboundContext)
|
|
20
|
+
* 8. Record session (channelRuntime.session.recordInboundSession)
|
|
21
|
+
* 9. Create reply dispatcher (channelRuntime.reply.createReplyDispatcherWithTyping)
|
|
22
|
+
* 10. Dispatch AI reply (channelRuntime.reply.dispatchReplyFromConfig)
|
|
23
|
+
*
|
|
24
|
+
* v3.4.0: Added InboundHistory ring buffer and GroupSystemPrompt/GroupSubject
|
|
25
|
+
* injection to fix LLM NO_REPLY conversation breaks.
|
|
26
|
+
* See: docs/audit/008-inbound-context-gap.md
|
|
27
|
+
*
|
|
28
|
+
* Reference: docs/reference/plugins/openclaw-weixin/src/messaging/process-message.ts
|
|
29
|
+
*/
|
|
30
|
+
import { timMessageToMsgContext } from './inbound.js';
|
|
31
|
+
import { buildMentionPrompt, extractAtTargets } from './mention-protocol.js';
|
|
32
|
+
// ── InboundHistory ring buffer ──
|
|
33
|
+
/**
|
|
34
|
+
* In-memory per-channel message history.
|
|
35
|
+
* Provides InboundHistory to LLM so it has conversation context.
|
|
36
|
+
*
|
|
37
|
+
* FIFO ring buffer: oldest messages are evicted when limit is reached.
|
|
38
|
+
* This is volatile (lost on restart), which is acceptable — LLM gets
|
|
39
|
+
* at least the recent conversation since last connection.
|
|
40
|
+
*/
|
|
41
|
+
const HISTORY_LIMIT = 20;
|
|
42
|
+
const channelHistories = new Map();
|
|
43
|
+
function appendHistory(channelId, entry) {
|
|
44
|
+
let arr = channelHistories.get(channelId);
|
|
45
|
+
if (!arr) {
|
|
46
|
+
arr = [];
|
|
47
|
+
channelHistories.set(channelId, arr);
|
|
48
|
+
}
|
|
49
|
+
arr.push(entry);
|
|
50
|
+
if (arr.length > HISTORY_LIMIT)
|
|
51
|
+
arr.shift(); // FIFO eviction
|
|
52
|
+
}
|
|
53
|
+
function getHistory(channelId) {
|
|
54
|
+
return [...(channelHistories.get(channelId) ?? [])];
|
|
55
|
+
}
|
|
56
|
+
// ── Group name cache ──
|
|
57
|
+
/**
|
|
58
|
+
* Simple in-memory cache for group names to avoid repeated SDK calls.
|
|
59
|
+
* No TTL — group names rarely change during a session.
|
|
60
|
+
*/
|
|
61
|
+
const groupNameCache = new Map();
|
|
62
|
+
/**
|
|
63
|
+
* Per-channel gm_req_mention cache.
|
|
64
|
+
* "1" = mention-only mode (only @ triggers LLM).
|
|
65
|
+
* "0" or absent = default mode (LLM receives all messages).
|
|
66
|
+
*/
|
|
67
|
+
const requireMentionCache = new Map();
|
|
68
|
+
// ── Per-channel promise queue ──
|
|
69
|
+
const channelQueues = new Map();
|
|
70
|
+
/**
|
|
71
|
+
* Enqueue message processing per channel to prevent concurrent AI turns
|
|
72
|
+
* on the same channel (which would cause interleaved replies).
|
|
73
|
+
*
|
|
74
|
+
* Each channel gets its own serial queue. Failures in one message
|
|
75
|
+
* do NOT block subsequent messages (.catch(() => undefined)).
|
|
76
|
+
*
|
|
77
|
+
* Reference: openclaw-weixin / yuanbao enqueueForConversation pattern.
|
|
78
|
+
*/
|
|
79
|
+
function enqueueForChannel(channelId, task) {
|
|
80
|
+
const prev = channelQueues.get(channelId) ?? Promise.resolve();
|
|
81
|
+
const next = prev
|
|
82
|
+
.catch(() => undefined)
|
|
83
|
+
.then(() => task())
|
|
84
|
+
.catch(() => undefined);
|
|
85
|
+
channelQueues.set(channelId, next);
|
|
86
|
+
}
|
|
87
|
+
// ── Core processing ──
|
|
88
|
+
/**
|
|
89
|
+
* Process a single inbound TIM push message through the standard
|
|
90
|
+
* OpenClaw channelRuntime pipeline.
|
|
91
|
+
*
|
|
92
|
+
* This function is fire-and-forget safe — errors are caught and logged,
|
|
93
|
+
* never propagated to crash the TIM event listener.
|
|
94
|
+
*/
|
|
95
|
+
export async function processOneMessage(raw, deps) {
|
|
96
|
+
// Step 1: Self-message filter
|
|
97
|
+
// Record self-messages in history (so LLM sees its own replies in context),
|
|
98
|
+
// but don't process them through the AI pipeline.
|
|
99
|
+
if (raw.from === deps.selfUserId) {
|
|
100
|
+
appendHistory(raw.channelId, {
|
|
101
|
+
sender: '(you)',
|
|
102
|
+
body: raw.text,
|
|
103
|
+
timestamp: raw.time,
|
|
104
|
+
});
|
|
105
|
+
deps.log.debug(`[process-message] skip self msg id=${raw.id} (recorded in history)`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// Step 2: channelRuntime safety check
|
|
109
|
+
if (!deps.channelRuntime) {
|
|
110
|
+
deps.log.error(`[process-message] channelRuntime is undefined, skip msg id=${raw.id}`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// Queue per channel to serialize AI turns
|
|
114
|
+
enqueueForChannel(raw.channelId, () => _processOneMessageInner(raw, deps));
|
|
115
|
+
}
|
|
116
|
+
async function _processOneMessageInner(raw, deps) {
|
|
117
|
+
const { channelRuntime, log } = deps;
|
|
118
|
+
// Step 3: Record this message in history BEFORE processing
|
|
119
|
+
// (so the LLM sees all prior messages, but not the current one which is Body)
|
|
120
|
+
const historySnapshot = getHistory(raw.channelId);
|
|
121
|
+
// Append current message to history for future turns
|
|
122
|
+
appendHistory(raw.channelId, {
|
|
123
|
+
sender: raw.nick || raw.from,
|
|
124
|
+
body: raw.text,
|
|
125
|
+
timestamp: raw.time,
|
|
126
|
+
});
|
|
127
|
+
// Step 4: Fetch group context (cached where possible)
|
|
128
|
+
let groupName;
|
|
129
|
+
let groupSystemPrompt;
|
|
130
|
+
// Group name (cached)
|
|
131
|
+
if (groupNameCache.has(raw.channelId)) {
|
|
132
|
+
groupName = groupNameCache.get(raw.channelId);
|
|
133
|
+
}
|
|
134
|
+
else if (deps.getGroupName) {
|
|
135
|
+
try {
|
|
136
|
+
const name = await deps.getGroupName(raw.channelId);
|
|
137
|
+
if (name) {
|
|
138
|
+
groupName = name;
|
|
139
|
+
groupNameCache.set(raw.channelId, name);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
log.warn(`[process-message] getGroupName failed: ${err.message}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Group system prompt (not cached — may change between messages)
|
|
147
|
+
if (deps.getChannelSkill) {
|
|
148
|
+
try {
|
|
149
|
+
const skill = await deps.getChannelSkill(raw.channelId);
|
|
150
|
+
if (skill)
|
|
151
|
+
groupSystemPrompt = skill;
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
log.warn(`[process-message] getChannelSkill failed: ${err.message}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// gm_req_mention (cached)
|
|
158
|
+
let requireMention = false;
|
|
159
|
+
if (requireMentionCache.has(raw.channelId)) {
|
|
160
|
+
requireMention = requireMentionCache.get(raw.channelId) === '1';
|
|
161
|
+
}
|
|
162
|
+
else if (deps.getGroupCustomField) {
|
|
163
|
+
try {
|
|
164
|
+
const val = await deps.getGroupCustomField(raw.channelId, 'gm_req_mention');
|
|
165
|
+
requireMentionCache.set(raw.channelId, val ?? '0');
|
|
166
|
+
requireMention = val === '1';
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
log.warn(`[process-message] gm_req_mention fetch failed: ${err.message}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Step 4.5: Mention protocol — fetch members + inject rules (only in mention mode)
|
|
173
|
+
// Always fetch fresh from TIM SDK (it hits the server each call).
|
|
174
|
+
// Do NOT cache: members can join/leave at any time.
|
|
175
|
+
// See: docs/audit/013-mention-at-delivery-failure-v3.4.5.md
|
|
176
|
+
let groupMembers;
|
|
177
|
+
if (requireMention && deps.getGroupMembers) {
|
|
178
|
+
try {
|
|
179
|
+
groupMembers = await deps.getGroupMembers(raw.channelId);
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
log.warn(`[process-message] getGroupMembers failed: ${err.message}`);
|
|
183
|
+
}
|
|
184
|
+
if (groupMembers && groupMembers.length > 0) {
|
|
185
|
+
const mentionRule = buildMentionPrompt(groupMembers, deps.selfUserId);
|
|
186
|
+
if (mentionRule) {
|
|
187
|
+
groupSystemPrompt = groupSystemPrompt
|
|
188
|
+
? `${groupSystemPrompt}\n\n${mentionRule}`
|
|
189
|
+
: mentionRule;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// Step 5: Convert TIM message to standard MsgContext with enriched context
|
|
194
|
+
const ctx = timMessageToMsgContext(raw, deps.accountId, {
|
|
195
|
+
groupName,
|
|
196
|
+
groupSystemPrompt,
|
|
197
|
+
inboundHistory: historySnapshot.length > 0 ? historySnapshot : undefined,
|
|
198
|
+
});
|
|
199
|
+
log.info(`[process-message] inbound from=${ctx.From} ch=${ctx.To} bodyLen=${ctx.Body.length} mention=${String(ctx.WasMentioned)} reqMention=${String(requireMention)} historyLen=${historySnapshot.length}`);
|
|
200
|
+
// Step 5.5: Mention-only gate
|
|
201
|
+
// When gm_req_mention="1", only @-mentioned messages trigger LLM.
|
|
202
|
+
// Non-mentioned messages are already recorded in history (Step 3),
|
|
203
|
+
// so context is preserved for when the agent IS mentioned.
|
|
204
|
+
if (requireMention && !ctx.WasMentioned) {
|
|
205
|
+
log.info(`[process-message] mention_only: skip LLM ch=${ctx.To} from=${ctx.From}`);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
// Step 6: Agent routing
|
|
209
|
+
const route = channelRuntime.routing.resolveAgentRoute({
|
|
210
|
+
cfg: deps.config,
|
|
211
|
+
channel: 'openagent',
|
|
212
|
+
accountId: deps.accountId,
|
|
213
|
+
peer: { kind: 'group', id: ctx.To },
|
|
214
|
+
});
|
|
215
|
+
log.debug(`[process-message] route: agentId=${route.agentId ?? '(none)'} sessionKey=${route.sessionKey ?? '(none)'}`);
|
|
216
|
+
if (!route.agentId) {
|
|
217
|
+
log.error(`[process-message] no agentId resolved for ch=${ctx.To}, skipping`);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
// Propagate resolved session key
|
|
221
|
+
ctx.SessionKey = route.sessionKey;
|
|
222
|
+
// Step 7: Finalize inbound context
|
|
223
|
+
const finalized = channelRuntime.reply.finalizeInboundContext(ctx);
|
|
224
|
+
const fin = finalized;
|
|
225
|
+
log.debug(`[process-message] finalized: From=${fin.From} To=${fin.To} GroupSubject=${fin.GroupSubject ?? '(none)'}`);
|
|
226
|
+
// Step 8: Record inbound session
|
|
227
|
+
const storePath = channelRuntime.session.resolveStorePath(deps.config.session?.store, { agentId: route.agentId });
|
|
228
|
+
await channelRuntime.session.recordInboundSession({
|
|
229
|
+
storePath,
|
|
230
|
+
sessionKey: route.sessionKey,
|
|
231
|
+
ctx: finalized,
|
|
232
|
+
updateLastRoute: {
|
|
233
|
+
sessionKey: route.mainSessionKey,
|
|
234
|
+
channel: 'openagent',
|
|
235
|
+
to: ctx.To,
|
|
236
|
+
accountId: deps.accountId,
|
|
237
|
+
},
|
|
238
|
+
onRecordError: (err) => log.error(`[process-message] recordInboundSession error: ${String(err)}`),
|
|
239
|
+
});
|
|
240
|
+
log.debug(`[process-message] session recorded, storePath=${storePath}`);
|
|
241
|
+
// Step 9: Create reply dispatcher with deliver callback
|
|
242
|
+
const { dispatcher, replyOptions, markDispatchIdle } = channelRuntime.reply.createReplyDispatcherWithTyping({
|
|
243
|
+
deliver: async (payload) => {
|
|
244
|
+
const text = payload.text ?? '';
|
|
245
|
+
if (!text) {
|
|
246
|
+
log.debug('[process-message] deliver: empty text, skip');
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
// Extract @-mention targets from AI output (mention mode only)
|
|
250
|
+
const atUserList = (requireMention && groupMembers)
|
|
251
|
+
? extractAtTargets(text, groupMembers, deps.selfUserId)
|
|
252
|
+
: undefined;
|
|
253
|
+
const effectiveAtList = atUserList && atUserList.length > 0 ? atUserList : undefined;
|
|
254
|
+
log.info(`[process-message] deliver: ch=${ctx.To} textLen=${text.length} at=${effectiveAtList?.join(',') ?? 'none'}`);
|
|
255
|
+
await deps.sendMessage(ctx.To, text, effectiveAtList);
|
|
256
|
+
log.debug(`[process-message] deliver: sent OK`);
|
|
257
|
+
},
|
|
258
|
+
onError: (err, info) => {
|
|
259
|
+
log.error(`[process-message] reply ${info.kind} error: ${String(err)}`);
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
// Step 10: Dispatch AI reply
|
|
263
|
+
log.debug(`[process-message] dispatching AI turn agentId=${route.agentId}`);
|
|
264
|
+
try {
|
|
265
|
+
await channelRuntime.reply.withReplyDispatcher({
|
|
266
|
+
dispatcher,
|
|
267
|
+
run: () => channelRuntime.reply.dispatchReplyFromConfig({
|
|
268
|
+
ctx: finalized,
|
|
269
|
+
cfg: deps.config,
|
|
270
|
+
dispatcher,
|
|
271
|
+
replyOptions: { ...replyOptions, disableBlockStreaming: true },
|
|
272
|
+
}),
|
|
273
|
+
});
|
|
274
|
+
log.info(`[process-message] AI turn completed for ch=${ctx.To}`);
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
log.error(`[process-message] dispatchReplyFromConfig error: ${err.message}`);
|
|
278
|
+
}
|
|
279
|
+
finally {
|
|
280
|
+
markDispatchIdle();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduler — Serial inbound queue with priority ordering
|
|
3
|
+
*
|
|
4
|
+
* Mention-containing deltas are pushed to the front of the queue.
|
|
5
|
+
* Processing is serial to avoid concurrent agent turns.
|
|
6
|
+
*/
|
|
7
|
+
import type { ChannelDelta } from './types.js';
|
|
8
|
+
import type { ActionExecutor } from './executor.js';
|
|
9
|
+
export declare class Scheduler {
|
|
10
|
+
private executor;
|
|
11
|
+
private queue;
|
|
12
|
+
private _processing;
|
|
13
|
+
constructor(executor: ActionExecutor);
|
|
14
|
+
enqueue(delta: ChannelDelta): void;
|
|
15
|
+
private _processNext;
|
|
16
|
+
get queueSize(): number;
|
|
17
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduler — Serial inbound queue with priority ordering
|
|
3
|
+
*
|
|
4
|
+
* Mention-containing deltas are pushed to the front of the queue.
|
|
5
|
+
* Processing is serial to avoid concurrent agent turns.
|
|
6
|
+
*/
|
|
7
|
+
import { logger } from '../util/logger.js';
|
|
8
|
+
export class Scheduler {
|
|
9
|
+
executor;
|
|
10
|
+
queue = [];
|
|
11
|
+
_processing = false;
|
|
12
|
+
constructor(executor) {
|
|
13
|
+
this.executor = executor;
|
|
14
|
+
}
|
|
15
|
+
enqueue(delta) {
|
|
16
|
+
if (delta.hasMention) {
|
|
17
|
+
this.queue.unshift(delta);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
this.queue.push(delta);
|
|
21
|
+
}
|
|
22
|
+
logger.debug(`[scheduler] ENQUEUE ch=${delta.channelId} pos=${delta.hasMention ? 'FRONT' : 'BACK'} queue=${this.queue.length}`);
|
|
23
|
+
void this._processNext();
|
|
24
|
+
}
|
|
25
|
+
async _processNext() {
|
|
26
|
+
if (this._processing || this.queue.length === 0)
|
|
27
|
+
return;
|
|
28
|
+
this._processing = true;
|
|
29
|
+
try {
|
|
30
|
+
const delta = this.queue.shift();
|
|
31
|
+
logger.debug(`[scheduler] PROCESS ch=${delta.channelId} msgs=${delta.messageCount} remaining=${this.queue.length}`);
|
|
32
|
+
await this.executor.handleDelta(delta);
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
logger.error(`[scheduler] ERROR processing delta: ${err.message}`);
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
this._processing = false;
|
|
39
|
+
if (this.queue.length > 0) {
|
|
40
|
+
setTimeout(() => void this._processNext(), 0);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
get queueSize() {
|
|
45
|
+
return this.queue.length;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Messaging — Shared type definitions
|
|
3
|
+
*/
|
|
4
|
+
import type { RawPushMessage } from '../tim/client.js';
|
|
5
|
+
/**
|
|
6
|
+
* A raw message from any source (push or history).
|
|
7
|
+
* Push messages come from TIM SDK events.
|
|
8
|
+
*/
|
|
9
|
+
export type RawMessage = RawPushMessage;
|
|
10
|
+
/**
|
|
11
|
+
* A batch of messages ready for agent processing.
|
|
12
|
+
* Built by the WindowAggregator when flush conditions are met.
|
|
13
|
+
*/
|
|
14
|
+
export interface ChannelDelta {
|
|
15
|
+
channelId: string;
|
|
16
|
+
channelName: string;
|
|
17
|
+
messageCount: number;
|
|
18
|
+
participants: string[];
|
|
19
|
+
hasMention: boolean;
|
|
20
|
+
urgency: number;
|
|
21
|
+
messages: RawMessage[];
|
|
22
|
+
source: string;
|
|
23
|
+
flushReason: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* SDK message format for fetchMessages (see tim/messages.ts).
|
|
27
|
+
*/
|
|
28
|
+
export interface HistoryMessage {
|
|
29
|
+
id: string;
|
|
30
|
+
from: string;
|
|
31
|
+
text: string;
|
|
32
|
+
time: number;
|
|
33
|
+
seq?: number;
|
|
34
|
+
}
|