@alfe.ai/openclaw-chat 0.4.0 → 0.5.1
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.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin.cjs +4 -0
- package/dist/plugin.d.cts +30 -1
- package/dist/plugin.d.ts +30 -1
- package/dist/plugin.js +2 -2
- package/dist/plugin2.cjs +220 -39
- package/dist/plugin2.d.cts +2 -2
- package/dist/plugin2.d.ts +2 -2
- package/dist/plugin2.js +197 -40
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _ as AlfeResolvedAccount, g as AlfePluginConfig, h as AlfeChannelConfig, m as createAlfeChannelPlugin, u as plugin } from "./plugin.cjs";
|
|
2
2
|
|
|
3
3
|
//#region src/session-store.d.ts
|
|
4
4
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _ as AlfeResolvedAccount, g as AlfePluginConfig, h as AlfeChannelConfig, m as createAlfeChannelPlugin, u as plugin } from "./plugin.js";
|
|
2
2
|
|
|
3
3
|
//#region src/session-store.d.ts
|
|
4
4
|
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as plugin, f as createAlfeChannelPlugin } from "./plugin2.js";
|
|
2
2
|
export { createAlfeChannelPlugin, plugin as default };
|
package/dist/plugin.cjs
CHANGED
|
@@ -3,10 +3,14 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_plugin = require("./plugin2.cjs");
|
|
6
|
+
exports.__agentTurnQueueForTest = require_plugin.__agentTurnQueueForTest;
|
|
7
|
+
exports.__resetAgentTurnQueueForTest = require_plugin.__resetAgentTurnQueueForTest;
|
|
8
|
+
exports.__setActiveRunForTest = require_plugin.__setActiveRunForTest;
|
|
6
9
|
exports.admitAgentEvent = require_plugin.admitAgentEvent;
|
|
7
10
|
exports.buildA2ACompletePayload = require_plugin.buildA2ACompletePayload;
|
|
8
11
|
exports.buildToolActivity = require_plugin.buildToolActivity;
|
|
9
12
|
exports.default = require_plugin.plugin;
|
|
10
13
|
exports.joinAssistantText = require_plugin.joinAssistantText;
|
|
14
|
+
exports.resolveAbortTargetKeys = require_plugin.resolveAbortTargetKeys;
|
|
11
15
|
exports.rewriteAssistantText = require_plugin.rewriteAssistantText;
|
|
12
16
|
exports.stripLeakedToolSummary = require_plugin.stripLeakedToolSummary;
|
package/dist/plugin.d.cts
CHANGED
|
@@ -402,6 +402,35 @@ interface PluginApi {
|
|
|
402
402
|
priority?: number;
|
|
403
403
|
}): void;
|
|
404
404
|
}
|
|
405
|
+
declare function enqueueAgentTurn(task: () => Promise<void>, opts?: {
|
|
406
|
+
coalesceKey?: string;
|
|
407
|
+
onSuperseded?: () => void;
|
|
408
|
+
}): Promise<void>;
|
|
409
|
+
/**
|
|
410
|
+
* Test-only: reset the agent-turn backlog between unit tests. Not used at
|
|
411
|
+
* runtime (the backlog lives for the process lifetime of one daemon).
|
|
412
|
+
*/
|
|
413
|
+
declare function __resetAgentTurnQueueForTest(): void;
|
|
414
|
+
/** Test-only accessor for the coalescing queue (see __tests__/queue.test.ts). */
|
|
415
|
+
declare const __agentTurnQueueForTest: {
|
|
416
|
+
enqueue: typeof enqueueAgentTurn;
|
|
417
|
+
backlogLength: () => number;
|
|
418
|
+
};
|
|
419
|
+
interface ActiveRunRef {
|
|
420
|
+
requestSessionKey: string;
|
|
421
|
+
routeSessionKey: string | null;
|
|
422
|
+
}
|
|
423
|
+
/** Test-only: set/clear the active-run ref for handleChatAbort unit tests. */
|
|
424
|
+
declare function __setActiveRunForTest(ref: ActiveRunRef | null): void;
|
|
425
|
+
/**
|
|
426
|
+
* Ordered session keys to try when aborting for `abortSessionKey`. Pure, so it
|
|
427
|
+
* unit-tests without the OpenClaw SDK. The resolved route key (what the run
|
|
428
|
+
* registered under) is preferred; the raw request key is a best-effort
|
|
429
|
+
* fallback. An abort with NO sessionKey targets the single active run. A
|
|
430
|
+
* sessionKey that does NOT match the running turn yields no candidates — we
|
|
431
|
+
* never abort another session's run on a shared daemon.
|
|
432
|
+
*/
|
|
433
|
+
declare function resolveAbortTargetKeys(active: ActiveRunRef | null, abortSessionKey: string | undefined): string[];
|
|
405
434
|
declare const plugin: {
|
|
406
435
|
id: string;
|
|
407
436
|
name: string;
|
|
@@ -411,4 +440,4 @@ declare const plugin: {
|
|
|
411
440
|
deactivate(api: PluginApi): Promise<void>;
|
|
412
441
|
};
|
|
413
442
|
//#endregion
|
|
414
|
-
export {
|
|
443
|
+
export { AlfeResolvedAccount as _, __setActiveRunForTest as a, buildToolActivity as c, resolveAbortTargetKeys as d, rewriteAssistantText as f, AlfePluginConfig as g, AlfeChannelConfig as h, __resetAgentTurnQueueForTest as i, joinAssistantText as l, createAlfeChannelPlugin as m, RunEventGate as n, admitAgentEvent as o, stripLeakedToolSummary as p, __agentTurnQueueForTest as r, buildA2ACompletePayload as s, ActiveRunRef as t, plugin as u };
|
package/dist/plugin.d.ts
CHANGED
|
@@ -402,6 +402,35 @@ interface PluginApi {
|
|
|
402
402
|
priority?: number;
|
|
403
403
|
}): void;
|
|
404
404
|
}
|
|
405
|
+
declare function enqueueAgentTurn(task: () => Promise<void>, opts?: {
|
|
406
|
+
coalesceKey?: string;
|
|
407
|
+
onSuperseded?: () => void;
|
|
408
|
+
}): Promise<void>;
|
|
409
|
+
/**
|
|
410
|
+
* Test-only: reset the agent-turn backlog between unit tests. Not used at
|
|
411
|
+
* runtime (the backlog lives for the process lifetime of one daemon).
|
|
412
|
+
*/
|
|
413
|
+
declare function __resetAgentTurnQueueForTest(): void;
|
|
414
|
+
/** Test-only accessor for the coalescing queue (see __tests__/queue.test.ts). */
|
|
415
|
+
declare const __agentTurnQueueForTest: {
|
|
416
|
+
enqueue: typeof enqueueAgentTurn;
|
|
417
|
+
backlogLength: () => number;
|
|
418
|
+
};
|
|
419
|
+
interface ActiveRunRef {
|
|
420
|
+
requestSessionKey: string;
|
|
421
|
+
routeSessionKey: string | null;
|
|
422
|
+
}
|
|
423
|
+
/** Test-only: set/clear the active-run ref for handleChatAbort unit tests. */
|
|
424
|
+
declare function __setActiveRunForTest(ref: ActiveRunRef | null): void;
|
|
425
|
+
/**
|
|
426
|
+
* Ordered session keys to try when aborting for `abortSessionKey`. Pure, so it
|
|
427
|
+
* unit-tests without the OpenClaw SDK. The resolved route key (what the run
|
|
428
|
+
* registered under) is preferred; the raw request key is a best-effort
|
|
429
|
+
* fallback. An abort with NO sessionKey targets the single active run. A
|
|
430
|
+
* sessionKey that does NOT match the running turn yields no candidates — we
|
|
431
|
+
* never abort another session's run on a shared daemon.
|
|
432
|
+
*/
|
|
433
|
+
declare function resolveAbortTargetKeys(active: ActiveRunRef | null, abortSessionKey: string | undefined): string[];
|
|
405
434
|
declare const plugin: {
|
|
406
435
|
id: string;
|
|
407
436
|
name: string;
|
|
@@ -411,4 +440,4 @@ declare const plugin: {
|
|
|
411
440
|
deactivate(api: PluginApi): Promise<void>;
|
|
412
441
|
};
|
|
413
442
|
//#endregion
|
|
414
|
-
export {
|
|
443
|
+
export { AlfeResolvedAccount as _, __setActiveRunForTest as a, buildToolActivity as c, resolveAbortTargetKeys as d, rewriteAssistantText as f, AlfePluginConfig as g, AlfeChannelConfig as h, __resetAgentTurnQueueForTest as i, joinAssistantText as l, createAlfeChannelPlugin as m, RunEventGate as n, admitAgentEvent as o, stripLeakedToolSummary as p, __agentTurnQueueForTest as r, buildA2ACompletePayload as s, ActiveRunRef as t, plugin as u };
|
package/dist/plugin.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as plugin, i as
|
|
2
|
-
export { admitAgentEvent, buildA2ACompletePayload, buildToolActivity, plugin as default, joinAssistantText, rewriteAssistantText, stripLeakedToolSummary };
|
|
1
|
+
import { a as buildA2ACompletePayload, c as plugin, d as stripLeakedToolSummary, i as admitAgentEvent, l as resolveAbortTargetKeys, n as __resetAgentTurnQueueForTest, o as buildToolActivity, r as __setActiveRunForTest, s as joinAssistantText, t as __agentTurnQueueForTest, u as rewriteAssistantText } from "./plugin2.js";
|
|
2
|
+
export { __agentTurnQueueForTest, __resetAgentTurnQueueForTest, __setActiveRunForTest, admitAgentEvent, buildA2ACompletePayload, buildToolActivity, plugin as default, joinAssistantText, resolveAbortTargetKeys, rewriteAssistantText, stripLeakedToolSummary };
|
package/dist/plugin2.cjs
CHANGED
|
@@ -1186,6 +1186,37 @@ function buildA2ACompletePayload(params) {
|
|
|
1186
1186
|
};
|
|
1187
1187
|
}
|
|
1188
1188
|
let dispatchInbound = null;
|
|
1189
|
+
let abortEmbeddedAgentRun = null;
|
|
1190
|
+
let resolveActiveRunSessionId = null;
|
|
1191
|
+
let resolveRouteEnvelope = null;
|
|
1192
|
+
/**
|
|
1193
|
+
* Load the OpenClaw SDK surfaces from a single resolver anchor. Returns true
|
|
1194
|
+
* once the critical `dispatchInboundDirectDmWithRuntime` is resolved. The abort
|
|
1195
|
+
* primitives + route resolver are best-effort — an older openclaw without them
|
|
1196
|
+
* degrades `chat.abort` to a logged no-op (Stop still seals the UI) rather than
|
|
1197
|
+
* a hard failure.
|
|
1198
|
+
*/
|
|
1199
|
+
function loadOpenClawSdk(req, log, anchor) {
|
|
1200
|
+
let resolvedDispatch = false;
|
|
1201
|
+
try {
|
|
1202
|
+
const channelInbound = req("openclaw/plugin-sdk/channel-inbound");
|
|
1203
|
+
if (channelInbound.dispatchInboundDirectDmWithRuntime) {
|
|
1204
|
+
dispatchInbound = channelInbound.dispatchInboundDirectDmWithRuntime;
|
|
1205
|
+
resolvedDispatch = true;
|
|
1206
|
+
}
|
|
1207
|
+
} catch {}
|
|
1208
|
+
try {
|
|
1209
|
+
const harness = req("openclaw/plugin-sdk/agent-harness");
|
|
1210
|
+
if (harness.abortEmbeddedAgentRun) abortEmbeddedAgentRun = harness.abortEmbeddedAgentRun;
|
|
1211
|
+
if (harness.resolveActiveEmbeddedRunSessionId) resolveActiveRunSessionId = harness.resolveActiveEmbeddedRunSessionId;
|
|
1212
|
+
} catch {}
|
|
1213
|
+
try {
|
|
1214
|
+
const envelope = req("openclaw/plugin-sdk/inbound-envelope");
|
|
1215
|
+
if (envelope.resolveInboundRouteEnvelopeBuilderWithRuntime) resolveRouteEnvelope = envelope.resolveInboundRouteEnvelopeBuilderWithRuntime;
|
|
1216
|
+
} catch {}
|
|
1217
|
+
if (resolvedDispatch) log.info(`Resolved OpenClaw SDK from ${anchor} (hard-abort=${abortEmbeddedAgentRun && resolveActiveRunSessionId ? "available" : "unavailable"})`);
|
|
1218
|
+
return resolvedDispatch;
|
|
1219
|
+
}
|
|
1189
1220
|
/**
|
|
1190
1221
|
* Resolve OpenClaw SDK functions from the running process.
|
|
1191
1222
|
*
|
|
@@ -1197,21 +1228,11 @@ let dispatchInbound = null;
|
|
|
1197
1228
|
function resolveOpenClawSdk(log) {
|
|
1198
1229
|
const anchors = [require$1.main?.filename, process.argv[1]].filter(Boolean);
|
|
1199
1230
|
for (const anchor of anchors) try {
|
|
1200
|
-
|
|
1201
|
-
if (channelInbound.dispatchInboundDirectDmWithRuntime) {
|
|
1202
|
-
dispatchInbound = channelInbound.dispatchInboundDirectDmWithRuntime;
|
|
1203
|
-
log.info(`Resolved OpenClaw SDK from ${anchor}`);
|
|
1204
|
-
return;
|
|
1205
|
-
}
|
|
1231
|
+
if (loadOpenClawSdk((0, node_module.createRequire)(anchor), log, anchor)) return;
|
|
1206
1232
|
} catch {}
|
|
1207
1233
|
try {
|
|
1208
1234
|
const derivedPath = (0, node_path.join)((0, node_path.resolve)((0, node_path.dirname)(process.execPath), ".."), "lib", "node_modules", "openclaw", "package.json");
|
|
1209
|
-
|
|
1210
|
-
if (channelInbound.dispatchInboundDirectDmWithRuntime) {
|
|
1211
|
-
dispatchInbound = channelInbound.dispatchInboundDirectDmWithRuntime;
|
|
1212
|
-
log.info(`Resolved OpenClaw SDK from ${derivedPath}`);
|
|
1213
|
-
return;
|
|
1214
|
-
}
|
|
1235
|
+
if (loadOpenClawSdk((0, node_module.createRequire)(derivedPath), log, derivedPath)) return;
|
|
1215
1236
|
} catch {}
|
|
1216
1237
|
log.warn("OpenClaw SDK not resolvable — chat dispatch will not work");
|
|
1217
1238
|
}
|
|
@@ -1288,11 +1309,111 @@ async function downloadAttachments(attachments, log) {
|
|
|
1288
1309
|
}
|
|
1289
1310
|
return results;
|
|
1290
1311
|
}
|
|
1291
|
-
let
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1312
|
+
let agentTurnBacklog = [];
|
|
1313
|
+
let agentTurnDraining = false;
|
|
1314
|
+
function enqueueAgentTurn(task, opts) {
|
|
1315
|
+
return new Promise((resolve, reject) => {
|
|
1316
|
+
const entry = {
|
|
1317
|
+
run: task,
|
|
1318
|
+
coalesceKey: opts?.coalesceKey,
|
|
1319
|
+
onSuperseded: opts?.onSuperseded ?? (() => void 0),
|
|
1320
|
+
resolve,
|
|
1321
|
+
reject
|
|
1322
|
+
};
|
|
1323
|
+
if (entry.coalesceKey) {
|
|
1324
|
+
const survivors = [];
|
|
1325
|
+
for (const queued of agentTurnBacklog) if (queued.coalesceKey === entry.coalesceKey) {
|
|
1326
|
+
try {
|
|
1327
|
+
queued.onSuperseded();
|
|
1328
|
+
} catch {}
|
|
1329
|
+
queued.resolve();
|
|
1330
|
+
} else survivors.push(queued);
|
|
1331
|
+
agentTurnBacklog = survivors;
|
|
1332
|
+
}
|
|
1333
|
+
agentTurnBacklog.push(entry);
|
|
1334
|
+
drainAgentTurns();
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
async function drainAgentTurns() {
|
|
1338
|
+
if (agentTurnDraining) return;
|
|
1339
|
+
agentTurnDraining = true;
|
|
1340
|
+
try {
|
|
1341
|
+
while (agentTurnBacklog.length > 0) {
|
|
1342
|
+
const entry = agentTurnBacklog.shift();
|
|
1343
|
+
if (!entry) break;
|
|
1344
|
+
try {
|
|
1345
|
+
await entry.run();
|
|
1346
|
+
entry.resolve();
|
|
1347
|
+
} catch (err) {
|
|
1348
|
+
entry.reject(err);
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
} finally {
|
|
1352
|
+
agentTurnDraining = false;
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Test-only: reset the agent-turn backlog between unit tests. Not used at
|
|
1357
|
+
* runtime (the backlog lives for the process lifetime of one daemon).
|
|
1358
|
+
*/
|
|
1359
|
+
function __resetAgentTurnQueueForTest() {
|
|
1360
|
+
agentTurnBacklog = [];
|
|
1361
|
+
agentTurnDraining = false;
|
|
1362
|
+
}
|
|
1363
|
+
/** Test-only accessor for the coalescing queue (see __tests__/queue.test.ts). */
|
|
1364
|
+
const __agentTurnQueueForTest = {
|
|
1365
|
+
enqueue: enqueueAgentTurn,
|
|
1366
|
+
backlogLength: () => agentTurnBacklog.length
|
|
1367
|
+
};
|
|
1368
|
+
let activeRun = null;
|
|
1369
|
+
/** Test-only: set/clear the active-run ref for handleChatAbort unit tests. */
|
|
1370
|
+
function __setActiveRunForTest(ref) {
|
|
1371
|
+
activeRun = ref;
|
|
1372
|
+
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Ordered session keys to try when aborting for `abortSessionKey`. Pure, so it
|
|
1375
|
+
* unit-tests without the OpenClaw SDK. The resolved route key (what the run
|
|
1376
|
+
* registered under) is preferred; the raw request key is a best-effort
|
|
1377
|
+
* fallback. An abort with NO sessionKey targets the single active run. A
|
|
1378
|
+
* sessionKey that does NOT match the running turn yields no candidates — we
|
|
1379
|
+
* never abort another session's run on a shared daemon.
|
|
1380
|
+
*/
|
|
1381
|
+
function resolveAbortTargetKeys(active, abortSessionKey) {
|
|
1382
|
+
const keys = [];
|
|
1383
|
+
if (active && (!abortSessionKey || active.requestSessionKey === abortSessionKey)) {
|
|
1384
|
+
if (active.routeSessionKey) keys.push(active.routeSessionKey);
|
|
1385
|
+
if (!keys.includes(active.requestSessionKey)) keys.push(active.requestSessionKey);
|
|
1386
|
+
} else if (abortSessionKey) keys.push(abortSessionKey);
|
|
1387
|
+
return keys;
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* `chat.abort` handler — genuinely cancels the RUNNING turn on the daemon.
|
|
1391
|
+
*
|
|
1392
|
+
* Invoked OUT-OF-BAND (never through the serialised agent-turn queue) so it
|
|
1393
|
+
* executes immediately while a turn is mid-flight. Resolves the live run's
|
|
1394
|
+
* sessionId from the target session key and fires the run handle's
|
|
1395
|
+
* AbortController via `abortEmbeddedAgentRun`. The in-flight dispatch then
|
|
1396
|
+
* settles (AbortError), so `handleAgentRequest` sends its RPC response and the
|
|
1397
|
+
* chat service seals the UI. When the abort primitives are unavailable (older
|
|
1398
|
+
* openclaw), this degrades to a logged no-op: the UI is still sealed by the
|
|
1399
|
+
* chat service's optimistic idle broadcast, but the turn completes server-side.
|
|
1400
|
+
*/
|
|
1401
|
+
function handleChatAbort(request, log) {
|
|
1402
|
+
const { sessionKey } = request.params;
|
|
1403
|
+
const targetKeys = resolveAbortTargetKeys(activeRun, sessionKey);
|
|
1404
|
+
let aborted = false;
|
|
1405
|
+
if (abortEmbeddedAgentRun && resolveActiveRunSessionId) {
|
|
1406
|
+
for (const key of targetKeys) {
|
|
1407
|
+
const sessionId = resolveActiveRunSessionId(key);
|
|
1408
|
+
if (sessionId && abortEmbeddedAgentRun(sessionId)) {
|
|
1409
|
+
aborted = true;
|
|
1410
|
+
log.info(`chat.abort: hard-aborted running turn (sessionId=${sessionId}, key=${key})`);
|
|
1411
|
+
break;
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
if (!aborted) log.info(`chat.abort: no active run to abort (candidates=${String(targetKeys.length)})`);
|
|
1415
|
+
} else log.warn("chat.abort: OpenClaw abort primitives unavailable — cannot hard-abort the running turn");
|
|
1416
|
+
chatClient?.sendResponse(request.id, true, { aborted });
|
|
1296
1417
|
}
|
|
1297
1418
|
async function handleAgentRequest(request, log) {
|
|
1298
1419
|
const runtime = pluginRuntime;
|
|
@@ -1320,6 +1441,7 @@ async function handleAgentRequest(request, log) {
|
|
|
1320
1441
|
sessionKey: null
|
|
1321
1442
|
};
|
|
1322
1443
|
let lastThinkingLen = 0;
|
|
1444
|
+
let sawStructuredThinking = false;
|
|
1323
1445
|
const turnThinking = {
|
|
1324
1446
|
text: "",
|
|
1325
1447
|
ts: 0
|
|
@@ -1376,20 +1498,22 @@ async function handleAgentRequest(request, log) {
|
|
|
1376
1498
|
const { visible, thinking } = splitThinkTags(raw);
|
|
1377
1499
|
if (thinking.length > lastThinkingLen) {
|
|
1378
1500
|
const thinkingDelta = thinking.slice(lastThinkingLen);
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1501
|
+
if (!sawStructuredThinking) {
|
|
1502
|
+
chatClient?.sendEvent("chat-activity", {
|
|
1503
|
+
runId: evt.runId,
|
|
1504
|
+
sessionKey: legacySessionKey,
|
|
1505
|
+
seq: evt.seq,
|
|
1506
|
+
state: "activity",
|
|
1507
|
+
activity: {
|
|
1508
|
+
kind: "thinking",
|
|
1509
|
+
text: thinkingDelta,
|
|
1510
|
+
delta: true,
|
|
1511
|
+
ts: evt.ts,
|
|
1512
|
+
seq: evt.seq
|
|
1513
|
+
}
|
|
1514
|
+
});
|
|
1515
|
+
recordThinking(thinkingDelta, evt.ts);
|
|
1516
|
+
}
|
|
1393
1517
|
lastThinkingLen = thinking.length;
|
|
1394
1518
|
}
|
|
1395
1519
|
const message = visible === raw && thinking.length === 0 ? evt.data : rewriteAssistantText(evt.data, visible);
|
|
@@ -1403,6 +1527,7 @@ async function handleAgentRequest(request, log) {
|
|
|
1403
1527
|
return;
|
|
1404
1528
|
}
|
|
1405
1529
|
if (evt.stream === "thinking") {
|
|
1530
|
+
sawStructuredThinking = true;
|
|
1406
1531
|
const activity = buildThinkingActivity(evt.data);
|
|
1407
1532
|
if (activity) {
|
|
1408
1533
|
chatClient?.sendEvent("chat-activity", {
|
|
@@ -1473,19 +1598,37 @@ async function handleAgentRequest(request, log) {
|
|
|
1473
1598
|
const conversationLabel = conversationType === "group" ? shortConvId ? `[${channelLabel}] Group (${shortConvId})` : `[${channelLabel}] Group` : shortConvId ? `[${channelLabel}] ${userLabel} (${shortConvId})` : `[${channelLabel}] ${userLabel}`;
|
|
1474
1599
|
let a2aResponseBuffer = "";
|
|
1475
1600
|
if (isA2A) resetA2AEndSignal();
|
|
1601
|
+
const peer = conversationType === "group" ? {
|
|
1602
|
+
kind: "group",
|
|
1603
|
+
id: conversationId ?? senderId
|
|
1604
|
+
} : {
|
|
1605
|
+
kind: "direct",
|
|
1606
|
+
id: conversationId ? `${senderId}:conv:${conversationId}` : senderId
|
|
1607
|
+
};
|
|
1608
|
+
let routeSessionKey = null;
|
|
1609
|
+
if (resolveRouteEnvelope) try {
|
|
1610
|
+
routeSessionKey = resolveRouteEnvelope({
|
|
1611
|
+
cfg,
|
|
1612
|
+
channel: "alfe",
|
|
1613
|
+
accountId: "default",
|
|
1614
|
+
peer,
|
|
1615
|
+
runtime: runtime.channel,
|
|
1616
|
+
sessionStore: cfg.session?.store
|
|
1617
|
+
}).route.sessionKey;
|
|
1618
|
+
} catch (err) {
|
|
1619
|
+
log.debug(`Route precompute failed (abort will use raw key): ${err instanceof Error ? err.message : String(err)}`);
|
|
1620
|
+
}
|
|
1621
|
+
activeRun = {
|
|
1622
|
+
requestSessionKey: legacySessionKey,
|
|
1623
|
+
routeSessionKey
|
|
1624
|
+
};
|
|
1476
1625
|
runGate.sessionKey = (await dispatchInbound({
|
|
1477
1626
|
cfg,
|
|
1478
1627
|
runtime: { channel: runtime.channel },
|
|
1479
1628
|
channel: "alfe",
|
|
1480
1629
|
channelLabel,
|
|
1481
1630
|
accountId: "default",
|
|
1482
|
-
peer
|
|
1483
|
-
kind: "group",
|
|
1484
|
-
id: conversationId ?? senderId
|
|
1485
|
-
} : {
|
|
1486
|
-
kind: "direct",
|
|
1487
|
-
id: conversationId ? `${senderId}:conv:${conversationId}` : senderId
|
|
1488
|
-
},
|
|
1631
|
+
peer,
|
|
1489
1632
|
senderId,
|
|
1490
1633
|
senderAddress: senderId,
|
|
1491
1634
|
recipientAddress: "agent",
|
|
@@ -1554,6 +1697,7 @@ async function handleAgentRequest(request, log) {
|
|
|
1554
1697
|
log.error(`Agent dispatch failed: ${errMsg}`);
|
|
1555
1698
|
chatClient?.sendResponse(request.id, false, { message: errMsg });
|
|
1556
1699
|
} finally {
|
|
1700
|
+
activeRun = null;
|
|
1557
1701
|
unsubscribe();
|
|
1558
1702
|
clearAllToolUpdates();
|
|
1559
1703
|
try {
|
|
@@ -1662,7 +1806,20 @@ const plugin = {
|
|
|
1662
1806
|
apiKey,
|
|
1663
1807
|
onRequest: (request) => {
|
|
1664
1808
|
const handle = async () => {
|
|
1665
|
-
if (request.method === "agent")
|
|
1809
|
+
if (request.method === "agent") {
|
|
1810
|
+
const params = request.params;
|
|
1811
|
+
const coalesceKey = params.coalesce === "replace" && typeof params.sessionKey === "string" ? params.sessionKey : void 0;
|
|
1812
|
+
await enqueueAgentTurn(() => handleAgentRequest(request, log), coalesceKey ? {
|
|
1813
|
+
coalesceKey,
|
|
1814
|
+
onSuperseded: () => {
|
|
1815
|
+
log.info(`Agent turn superseded before start (coalesce=replace): ${coalesceKey}`);
|
|
1816
|
+
chatClient?.sendResponse(request.id, true, {
|
|
1817
|
+
superseded: true,
|
|
1818
|
+
sessionKey: coalesceKey
|
|
1819
|
+
});
|
|
1820
|
+
}
|
|
1821
|
+
} : void 0);
|
|
1822
|
+
} else if (request.method === "chat.abort") handleChatAbort(request, log);
|
|
1666
1823
|
else if (request.method === "sessions.list") await handleSessionsList(request, log);
|
|
1667
1824
|
else if (request.method === "sessions.get") await handleSessionsGet(request, log);
|
|
1668
1825
|
else chatClient?.sendResponse(request.id, false, { message: `Unknown method: ${request.method}` });
|
|
@@ -1795,6 +1952,24 @@ const plugin = {
|
|
|
1795
1952
|
}
|
|
1796
1953
|
};
|
|
1797
1954
|
//#endregion
|
|
1955
|
+
Object.defineProperty(exports, "__agentTurnQueueForTest", {
|
|
1956
|
+
enumerable: true,
|
|
1957
|
+
get: function() {
|
|
1958
|
+
return __agentTurnQueueForTest;
|
|
1959
|
+
}
|
|
1960
|
+
});
|
|
1961
|
+
Object.defineProperty(exports, "__resetAgentTurnQueueForTest", {
|
|
1962
|
+
enumerable: true,
|
|
1963
|
+
get: function() {
|
|
1964
|
+
return __resetAgentTurnQueueForTest;
|
|
1965
|
+
}
|
|
1966
|
+
});
|
|
1967
|
+
Object.defineProperty(exports, "__setActiveRunForTest", {
|
|
1968
|
+
enumerable: true,
|
|
1969
|
+
get: function() {
|
|
1970
|
+
return __setActiveRunForTest;
|
|
1971
|
+
}
|
|
1972
|
+
});
|
|
1798
1973
|
Object.defineProperty(exports, "admitAgentEvent", {
|
|
1799
1974
|
enumerable: true,
|
|
1800
1975
|
get: function() {
|
|
@@ -1831,6 +2006,12 @@ Object.defineProperty(exports, "plugin", {
|
|
|
1831
2006
|
return plugin;
|
|
1832
2007
|
}
|
|
1833
2008
|
});
|
|
2009
|
+
Object.defineProperty(exports, "resolveAbortTargetKeys", {
|
|
2010
|
+
enumerable: true,
|
|
2011
|
+
get: function() {
|
|
2012
|
+
return resolveAbortTargetKeys;
|
|
2013
|
+
}
|
|
2014
|
+
});
|
|
1834
2015
|
Object.defineProperty(exports, "rewriteAssistantText", {
|
|
1835
2016
|
enumerable: true,
|
|
1836
2017
|
get: function() {
|
package/dist/plugin2.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { RunEventGate, admitAgentEvent, buildA2ACompletePayload, buildToolActivity, plugin as default, joinAssistantText, rewriteAssistantText, stripLeakedToolSummary };
|
|
1
|
+
import { a as __setActiveRunForTest, c as buildToolActivity, d as resolveAbortTargetKeys, f as rewriteAssistantText, i as __resetAgentTurnQueueForTest, l as joinAssistantText, n as RunEventGate, o as admitAgentEvent, p as stripLeakedToolSummary, r as __agentTurnQueueForTest, s as buildA2ACompletePayload, t as ActiveRunRef, u as plugin } from "./plugin.cjs";
|
|
2
|
+
export { ActiveRunRef, RunEventGate, __agentTurnQueueForTest, __resetAgentTurnQueueForTest, __setActiveRunForTest, admitAgentEvent, buildA2ACompletePayload, buildToolActivity, plugin as default, joinAssistantText, resolveAbortTargetKeys, rewriteAssistantText, stripLeakedToolSummary };
|
package/dist/plugin2.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { RunEventGate, admitAgentEvent, buildA2ACompletePayload, buildToolActivity, plugin as default, joinAssistantText, rewriteAssistantText, stripLeakedToolSummary };
|
|
1
|
+
import { a as __setActiveRunForTest, c as buildToolActivity, d as resolveAbortTargetKeys, f as rewriteAssistantText, i as __resetAgentTurnQueueForTest, l as joinAssistantText, n as RunEventGate, o as admitAgentEvent, p as stripLeakedToolSummary, r as __agentTurnQueueForTest, s as buildA2ACompletePayload, t as ActiveRunRef, u as plugin } from "./plugin.js";
|
|
2
|
+
export { ActiveRunRef, RunEventGate, __agentTurnQueueForTest, __resetAgentTurnQueueForTest, __setActiveRunForTest, admitAgentEvent, buildA2ACompletePayload, buildToolActivity, plugin as default, joinAssistantText, resolveAbortTargetKeys, rewriteAssistantText, stripLeakedToolSummary };
|
package/dist/plugin2.js
CHANGED
|
@@ -1186,6 +1186,37 @@ function buildA2ACompletePayload(params) {
|
|
|
1186
1186
|
};
|
|
1187
1187
|
}
|
|
1188
1188
|
let dispatchInbound = null;
|
|
1189
|
+
let abortEmbeddedAgentRun = null;
|
|
1190
|
+
let resolveActiveRunSessionId = null;
|
|
1191
|
+
let resolveRouteEnvelope = null;
|
|
1192
|
+
/**
|
|
1193
|
+
* Load the OpenClaw SDK surfaces from a single resolver anchor. Returns true
|
|
1194
|
+
* once the critical `dispatchInboundDirectDmWithRuntime` is resolved. The abort
|
|
1195
|
+
* primitives + route resolver are best-effort — an older openclaw without them
|
|
1196
|
+
* degrades `chat.abort` to a logged no-op (Stop still seals the UI) rather than
|
|
1197
|
+
* a hard failure.
|
|
1198
|
+
*/
|
|
1199
|
+
function loadOpenClawSdk(req, log, anchor) {
|
|
1200
|
+
let resolvedDispatch = false;
|
|
1201
|
+
try {
|
|
1202
|
+
const channelInbound = req("openclaw/plugin-sdk/channel-inbound");
|
|
1203
|
+
if (channelInbound.dispatchInboundDirectDmWithRuntime) {
|
|
1204
|
+
dispatchInbound = channelInbound.dispatchInboundDirectDmWithRuntime;
|
|
1205
|
+
resolvedDispatch = true;
|
|
1206
|
+
}
|
|
1207
|
+
} catch {}
|
|
1208
|
+
try {
|
|
1209
|
+
const harness = req("openclaw/plugin-sdk/agent-harness");
|
|
1210
|
+
if (harness.abortEmbeddedAgentRun) abortEmbeddedAgentRun = harness.abortEmbeddedAgentRun;
|
|
1211
|
+
if (harness.resolveActiveEmbeddedRunSessionId) resolveActiveRunSessionId = harness.resolveActiveEmbeddedRunSessionId;
|
|
1212
|
+
} catch {}
|
|
1213
|
+
try {
|
|
1214
|
+
const envelope = req("openclaw/plugin-sdk/inbound-envelope");
|
|
1215
|
+
if (envelope.resolveInboundRouteEnvelopeBuilderWithRuntime) resolveRouteEnvelope = envelope.resolveInboundRouteEnvelopeBuilderWithRuntime;
|
|
1216
|
+
} catch {}
|
|
1217
|
+
if (resolvedDispatch) log.info(`Resolved OpenClaw SDK from ${anchor} (hard-abort=${abortEmbeddedAgentRun && resolveActiveRunSessionId ? "available" : "unavailable"})`);
|
|
1218
|
+
return resolvedDispatch;
|
|
1219
|
+
}
|
|
1189
1220
|
/**
|
|
1190
1221
|
* Resolve OpenClaw SDK functions from the running process.
|
|
1191
1222
|
*
|
|
@@ -1197,21 +1228,11 @@ let dispatchInbound = null;
|
|
|
1197
1228
|
function resolveOpenClawSdk(log) {
|
|
1198
1229
|
const anchors = [require.main?.filename, process.argv[1]].filter(Boolean);
|
|
1199
1230
|
for (const anchor of anchors) try {
|
|
1200
|
-
|
|
1201
|
-
if (channelInbound.dispatchInboundDirectDmWithRuntime) {
|
|
1202
|
-
dispatchInbound = channelInbound.dispatchInboundDirectDmWithRuntime;
|
|
1203
|
-
log.info(`Resolved OpenClaw SDK from ${anchor}`);
|
|
1204
|
-
return;
|
|
1205
|
-
}
|
|
1231
|
+
if (loadOpenClawSdk(createRequire(anchor), log, anchor)) return;
|
|
1206
1232
|
} catch {}
|
|
1207
1233
|
try {
|
|
1208
1234
|
const derivedPath = join(resolve(dirname(process.execPath), ".."), "lib", "node_modules", "openclaw", "package.json");
|
|
1209
|
-
|
|
1210
|
-
if (channelInbound.dispatchInboundDirectDmWithRuntime) {
|
|
1211
|
-
dispatchInbound = channelInbound.dispatchInboundDirectDmWithRuntime;
|
|
1212
|
-
log.info(`Resolved OpenClaw SDK from ${derivedPath}`);
|
|
1213
|
-
return;
|
|
1214
|
-
}
|
|
1235
|
+
if (loadOpenClawSdk(createRequire(derivedPath), log, derivedPath)) return;
|
|
1215
1236
|
} catch {}
|
|
1216
1237
|
log.warn("OpenClaw SDK not resolvable — chat dispatch will not work");
|
|
1217
1238
|
}
|
|
@@ -1288,11 +1309,111 @@ async function downloadAttachments(attachments, log) {
|
|
|
1288
1309
|
}
|
|
1289
1310
|
return results;
|
|
1290
1311
|
}
|
|
1291
|
-
let
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1312
|
+
let agentTurnBacklog = [];
|
|
1313
|
+
let agentTurnDraining = false;
|
|
1314
|
+
function enqueueAgentTurn(task, opts) {
|
|
1315
|
+
return new Promise((resolve, reject) => {
|
|
1316
|
+
const entry = {
|
|
1317
|
+
run: task,
|
|
1318
|
+
coalesceKey: opts?.coalesceKey,
|
|
1319
|
+
onSuperseded: opts?.onSuperseded ?? (() => void 0),
|
|
1320
|
+
resolve,
|
|
1321
|
+
reject
|
|
1322
|
+
};
|
|
1323
|
+
if (entry.coalesceKey) {
|
|
1324
|
+
const survivors = [];
|
|
1325
|
+
for (const queued of agentTurnBacklog) if (queued.coalesceKey === entry.coalesceKey) {
|
|
1326
|
+
try {
|
|
1327
|
+
queued.onSuperseded();
|
|
1328
|
+
} catch {}
|
|
1329
|
+
queued.resolve();
|
|
1330
|
+
} else survivors.push(queued);
|
|
1331
|
+
agentTurnBacklog = survivors;
|
|
1332
|
+
}
|
|
1333
|
+
agentTurnBacklog.push(entry);
|
|
1334
|
+
drainAgentTurns();
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
async function drainAgentTurns() {
|
|
1338
|
+
if (agentTurnDraining) return;
|
|
1339
|
+
agentTurnDraining = true;
|
|
1340
|
+
try {
|
|
1341
|
+
while (agentTurnBacklog.length > 0) {
|
|
1342
|
+
const entry = agentTurnBacklog.shift();
|
|
1343
|
+
if (!entry) break;
|
|
1344
|
+
try {
|
|
1345
|
+
await entry.run();
|
|
1346
|
+
entry.resolve();
|
|
1347
|
+
} catch (err) {
|
|
1348
|
+
entry.reject(err);
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
} finally {
|
|
1352
|
+
agentTurnDraining = false;
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Test-only: reset the agent-turn backlog between unit tests. Not used at
|
|
1357
|
+
* runtime (the backlog lives for the process lifetime of one daemon).
|
|
1358
|
+
*/
|
|
1359
|
+
function __resetAgentTurnQueueForTest() {
|
|
1360
|
+
agentTurnBacklog = [];
|
|
1361
|
+
agentTurnDraining = false;
|
|
1362
|
+
}
|
|
1363
|
+
/** Test-only accessor for the coalescing queue (see __tests__/queue.test.ts). */
|
|
1364
|
+
const __agentTurnQueueForTest = {
|
|
1365
|
+
enqueue: enqueueAgentTurn,
|
|
1366
|
+
backlogLength: () => agentTurnBacklog.length
|
|
1367
|
+
};
|
|
1368
|
+
let activeRun = null;
|
|
1369
|
+
/** Test-only: set/clear the active-run ref for handleChatAbort unit tests. */
|
|
1370
|
+
function __setActiveRunForTest(ref) {
|
|
1371
|
+
activeRun = ref;
|
|
1372
|
+
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Ordered session keys to try when aborting for `abortSessionKey`. Pure, so it
|
|
1375
|
+
* unit-tests without the OpenClaw SDK. The resolved route key (what the run
|
|
1376
|
+
* registered under) is preferred; the raw request key is a best-effort
|
|
1377
|
+
* fallback. An abort with NO sessionKey targets the single active run. A
|
|
1378
|
+
* sessionKey that does NOT match the running turn yields no candidates — we
|
|
1379
|
+
* never abort another session's run on a shared daemon.
|
|
1380
|
+
*/
|
|
1381
|
+
function resolveAbortTargetKeys(active, abortSessionKey) {
|
|
1382
|
+
const keys = [];
|
|
1383
|
+
if (active && (!abortSessionKey || active.requestSessionKey === abortSessionKey)) {
|
|
1384
|
+
if (active.routeSessionKey) keys.push(active.routeSessionKey);
|
|
1385
|
+
if (!keys.includes(active.requestSessionKey)) keys.push(active.requestSessionKey);
|
|
1386
|
+
} else if (abortSessionKey) keys.push(abortSessionKey);
|
|
1387
|
+
return keys;
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* `chat.abort` handler — genuinely cancels the RUNNING turn on the daemon.
|
|
1391
|
+
*
|
|
1392
|
+
* Invoked OUT-OF-BAND (never through the serialised agent-turn queue) so it
|
|
1393
|
+
* executes immediately while a turn is mid-flight. Resolves the live run's
|
|
1394
|
+
* sessionId from the target session key and fires the run handle's
|
|
1395
|
+
* AbortController via `abortEmbeddedAgentRun`. The in-flight dispatch then
|
|
1396
|
+
* settles (AbortError), so `handleAgentRequest` sends its RPC response and the
|
|
1397
|
+
* chat service seals the UI. When the abort primitives are unavailable (older
|
|
1398
|
+
* openclaw), this degrades to a logged no-op: the UI is still sealed by the
|
|
1399
|
+
* chat service's optimistic idle broadcast, but the turn completes server-side.
|
|
1400
|
+
*/
|
|
1401
|
+
function handleChatAbort(request, log) {
|
|
1402
|
+
const { sessionKey } = request.params;
|
|
1403
|
+
const targetKeys = resolveAbortTargetKeys(activeRun, sessionKey);
|
|
1404
|
+
let aborted = false;
|
|
1405
|
+
if (abortEmbeddedAgentRun && resolveActiveRunSessionId) {
|
|
1406
|
+
for (const key of targetKeys) {
|
|
1407
|
+
const sessionId = resolveActiveRunSessionId(key);
|
|
1408
|
+
if (sessionId && abortEmbeddedAgentRun(sessionId)) {
|
|
1409
|
+
aborted = true;
|
|
1410
|
+
log.info(`chat.abort: hard-aborted running turn (sessionId=${sessionId}, key=${key})`);
|
|
1411
|
+
break;
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
if (!aborted) log.info(`chat.abort: no active run to abort (candidates=${String(targetKeys.length)})`);
|
|
1415
|
+
} else log.warn("chat.abort: OpenClaw abort primitives unavailable — cannot hard-abort the running turn");
|
|
1416
|
+
chatClient?.sendResponse(request.id, true, { aborted });
|
|
1296
1417
|
}
|
|
1297
1418
|
async function handleAgentRequest(request, log) {
|
|
1298
1419
|
const runtime = pluginRuntime;
|
|
@@ -1320,6 +1441,7 @@ async function handleAgentRequest(request, log) {
|
|
|
1320
1441
|
sessionKey: null
|
|
1321
1442
|
};
|
|
1322
1443
|
let lastThinkingLen = 0;
|
|
1444
|
+
let sawStructuredThinking = false;
|
|
1323
1445
|
const turnThinking = {
|
|
1324
1446
|
text: "",
|
|
1325
1447
|
ts: 0
|
|
@@ -1376,20 +1498,22 @@ async function handleAgentRequest(request, log) {
|
|
|
1376
1498
|
const { visible, thinking } = splitThinkTags(raw);
|
|
1377
1499
|
if (thinking.length > lastThinkingLen) {
|
|
1378
1500
|
const thinkingDelta = thinking.slice(lastThinkingLen);
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1501
|
+
if (!sawStructuredThinking) {
|
|
1502
|
+
chatClient?.sendEvent("chat-activity", {
|
|
1503
|
+
runId: evt.runId,
|
|
1504
|
+
sessionKey: legacySessionKey,
|
|
1505
|
+
seq: evt.seq,
|
|
1506
|
+
state: "activity",
|
|
1507
|
+
activity: {
|
|
1508
|
+
kind: "thinking",
|
|
1509
|
+
text: thinkingDelta,
|
|
1510
|
+
delta: true,
|
|
1511
|
+
ts: evt.ts,
|
|
1512
|
+
seq: evt.seq
|
|
1513
|
+
}
|
|
1514
|
+
});
|
|
1515
|
+
recordThinking(thinkingDelta, evt.ts);
|
|
1516
|
+
}
|
|
1393
1517
|
lastThinkingLen = thinking.length;
|
|
1394
1518
|
}
|
|
1395
1519
|
const message = visible === raw && thinking.length === 0 ? evt.data : rewriteAssistantText(evt.data, visible);
|
|
@@ -1403,6 +1527,7 @@ async function handleAgentRequest(request, log) {
|
|
|
1403
1527
|
return;
|
|
1404
1528
|
}
|
|
1405
1529
|
if (evt.stream === "thinking") {
|
|
1530
|
+
sawStructuredThinking = true;
|
|
1406
1531
|
const activity = buildThinkingActivity(evt.data);
|
|
1407
1532
|
if (activity) {
|
|
1408
1533
|
chatClient?.sendEvent("chat-activity", {
|
|
@@ -1473,19 +1598,37 @@ async function handleAgentRequest(request, log) {
|
|
|
1473
1598
|
const conversationLabel = conversationType === "group" ? shortConvId ? `[${channelLabel}] Group (${shortConvId})` : `[${channelLabel}] Group` : shortConvId ? `[${channelLabel}] ${userLabel} (${shortConvId})` : `[${channelLabel}] ${userLabel}`;
|
|
1474
1599
|
let a2aResponseBuffer = "";
|
|
1475
1600
|
if (isA2A) resetA2AEndSignal();
|
|
1601
|
+
const peer = conversationType === "group" ? {
|
|
1602
|
+
kind: "group",
|
|
1603
|
+
id: conversationId ?? senderId
|
|
1604
|
+
} : {
|
|
1605
|
+
kind: "direct",
|
|
1606
|
+
id: conversationId ? `${senderId}:conv:${conversationId}` : senderId
|
|
1607
|
+
};
|
|
1608
|
+
let routeSessionKey = null;
|
|
1609
|
+
if (resolveRouteEnvelope) try {
|
|
1610
|
+
routeSessionKey = resolveRouteEnvelope({
|
|
1611
|
+
cfg,
|
|
1612
|
+
channel: "alfe",
|
|
1613
|
+
accountId: "default",
|
|
1614
|
+
peer,
|
|
1615
|
+
runtime: runtime.channel,
|
|
1616
|
+
sessionStore: cfg.session?.store
|
|
1617
|
+
}).route.sessionKey;
|
|
1618
|
+
} catch (err) {
|
|
1619
|
+
log.debug(`Route precompute failed (abort will use raw key): ${err instanceof Error ? err.message : String(err)}`);
|
|
1620
|
+
}
|
|
1621
|
+
activeRun = {
|
|
1622
|
+
requestSessionKey: legacySessionKey,
|
|
1623
|
+
routeSessionKey
|
|
1624
|
+
};
|
|
1476
1625
|
runGate.sessionKey = (await dispatchInbound({
|
|
1477
1626
|
cfg,
|
|
1478
1627
|
runtime: { channel: runtime.channel },
|
|
1479
1628
|
channel: "alfe",
|
|
1480
1629
|
channelLabel,
|
|
1481
1630
|
accountId: "default",
|
|
1482
|
-
peer
|
|
1483
|
-
kind: "group",
|
|
1484
|
-
id: conversationId ?? senderId
|
|
1485
|
-
} : {
|
|
1486
|
-
kind: "direct",
|
|
1487
|
-
id: conversationId ? `${senderId}:conv:${conversationId}` : senderId
|
|
1488
|
-
},
|
|
1631
|
+
peer,
|
|
1489
1632
|
senderId,
|
|
1490
1633
|
senderAddress: senderId,
|
|
1491
1634
|
recipientAddress: "agent",
|
|
@@ -1554,6 +1697,7 @@ async function handleAgentRequest(request, log) {
|
|
|
1554
1697
|
log.error(`Agent dispatch failed: ${errMsg}`);
|
|
1555
1698
|
chatClient?.sendResponse(request.id, false, { message: errMsg });
|
|
1556
1699
|
} finally {
|
|
1700
|
+
activeRun = null;
|
|
1557
1701
|
unsubscribe();
|
|
1558
1702
|
clearAllToolUpdates();
|
|
1559
1703
|
try {
|
|
@@ -1662,7 +1806,20 @@ const plugin = {
|
|
|
1662
1806
|
apiKey,
|
|
1663
1807
|
onRequest: (request) => {
|
|
1664
1808
|
const handle = async () => {
|
|
1665
|
-
if (request.method === "agent")
|
|
1809
|
+
if (request.method === "agent") {
|
|
1810
|
+
const params = request.params;
|
|
1811
|
+
const coalesceKey = params.coalesce === "replace" && typeof params.sessionKey === "string" ? params.sessionKey : void 0;
|
|
1812
|
+
await enqueueAgentTurn(() => handleAgentRequest(request, log), coalesceKey ? {
|
|
1813
|
+
coalesceKey,
|
|
1814
|
+
onSuperseded: () => {
|
|
1815
|
+
log.info(`Agent turn superseded before start (coalesce=replace): ${coalesceKey}`);
|
|
1816
|
+
chatClient?.sendResponse(request.id, true, {
|
|
1817
|
+
superseded: true,
|
|
1818
|
+
sessionKey: coalesceKey
|
|
1819
|
+
});
|
|
1820
|
+
}
|
|
1821
|
+
} : void 0);
|
|
1822
|
+
} else if (request.method === "chat.abort") handleChatAbort(request, log);
|
|
1666
1823
|
else if (request.method === "sessions.list") await handleSessionsList(request, log);
|
|
1667
1824
|
else if (request.method === "sessions.get") await handleSessionsGet(request, log);
|
|
1668
1825
|
else chatClient?.sendResponse(request.id, false, { message: `Unknown method: ${request.method}` });
|
|
@@ -1795,4 +1952,4 @@ const plugin = {
|
|
|
1795
1952
|
}
|
|
1796
1953
|
};
|
|
1797
1954
|
//#endregion
|
|
1798
|
-
export {
|
|
1955
|
+
export { buildA2ACompletePayload as a, plugin as c, stripLeakedToolSummary as d, createAlfeChannelPlugin as f, admitAgentEvent as i, resolveAbortTargetKeys as l, __resetAgentTurnQueueForTest as n, buildToolActivity as o, __setActiveRunForTest as r, joinAssistantText as s, __agentTurnQueueForTest as t, rewriteAssistantText as u };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfe.ai/openclaw-chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "OpenClaw chat plugin for Alfe — web widget and mobile app channels",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/plugin.js",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"openclaw.plugin.json"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@alfe.ai/chat": "^0.0.14",
|
|
31
30
|
"@alfe.ai/agent-api-client": "^0.5.0",
|
|
31
|
+
"@alfe.ai/chat": "^0.0.14",
|
|
32
32
|
"@alfe.ai/config": "^0.3.0"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|