@alfe.ai/openclaw-chat 0.2.0 → 0.2.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 +1 -0
- package/dist/plugin.d.cts +22 -1
- package/dist/plugin.d.ts +22 -1
- package/dist/plugin.js +2 -2
- package/dist/plugin2.cjs +75 -13
- package/dist/plugin2.d.cts +2 -2
- package/dist/plugin2.d.ts +2 -2
- package/dist/plugin2.js +70 -14
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as AlfeChannelConfig, i as createAlfeChannelPlugin, n as plugin, o as AlfePluginConfig, s as AlfeResolvedAccount } 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 { a as
|
|
1
|
+
import { a as AlfeChannelConfig, i as createAlfeChannelPlugin, n as plugin, o as AlfePluginConfig, s as AlfeResolvedAccount } 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 { i as createAlfeChannelPlugin, n as plugin } from "./plugin2.js";
|
|
2
2
|
export { createAlfeChannelPlugin, plugin as default };
|
package/dist/plugin.cjs
CHANGED
|
@@ -3,5 +3,6 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_plugin = require("./plugin2.cjs");
|
|
6
|
+
exports.buildA2ACompletePayload = require_plugin.buildA2ACompletePayload;
|
|
6
7
|
exports.default = require_plugin.plugin;
|
|
7
8
|
exports.stripLeakedToolSummary = require_plugin.stripLeakedToolSummary;
|
package/dist/plugin.d.cts
CHANGED
|
@@ -275,6 +275,27 @@ interface PluginServiceContext {
|
|
|
275
275
|
logger: PluginLogger;
|
|
276
276
|
}
|
|
277
277
|
declare function stripLeakedToolSummary(text: string): string;
|
|
278
|
+
/**
|
|
279
|
+
* Build the `a2a-complete` notify payload. ALWAYS fired for an A2A turn —
|
|
280
|
+
* even when the dispatch produced no relayable text — so the chat service can
|
|
281
|
+
* emit an explicit terminal/stop frame instead of the thread dead-ending
|
|
282
|
+
* silently. `terminated`/`reason` are set when there's nothing to relay
|
|
283
|
+
* (`empty_turn`) or the agent ended the thread via end_conversation
|
|
284
|
+
* (`resolved`).
|
|
285
|
+
*/
|
|
286
|
+
declare function buildA2ACompletePayload<A>(params: {
|
|
287
|
+
conversationId: string;
|
|
288
|
+
fullText: string;
|
|
289
|
+
a2a: A;
|
|
290
|
+
resolved: boolean;
|
|
291
|
+
}): {
|
|
292
|
+
conversationId: string;
|
|
293
|
+
fullText: string;
|
|
294
|
+
a2a: A;
|
|
295
|
+
resolved: boolean;
|
|
296
|
+
terminated?: boolean;
|
|
297
|
+
reason?: string;
|
|
298
|
+
};
|
|
278
299
|
interface PluginApi {
|
|
279
300
|
logger: PluginLogger;
|
|
280
301
|
registrationMode?: 'full' | 'setup-only' | 'setup-runtime' | 'cli-metadata';
|
|
@@ -307,4 +328,4 @@ declare const plugin: {
|
|
|
307
328
|
deactivate(api: PluginApi): Promise<void>;
|
|
308
329
|
};
|
|
309
330
|
//#endregion
|
|
310
|
-
export {
|
|
331
|
+
export { AlfeChannelConfig as a, createAlfeChannelPlugin as i, plugin as n, AlfePluginConfig as o, stripLeakedToolSummary as r, AlfeResolvedAccount as s, buildA2ACompletePayload as t };
|
package/dist/plugin.d.ts
CHANGED
|
@@ -275,6 +275,27 @@ interface PluginServiceContext {
|
|
|
275
275
|
logger: PluginLogger;
|
|
276
276
|
}
|
|
277
277
|
declare function stripLeakedToolSummary(text: string): string;
|
|
278
|
+
/**
|
|
279
|
+
* Build the `a2a-complete` notify payload. ALWAYS fired for an A2A turn —
|
|
280
|
+
* even when the dispatch produced no relayable text — so the chat service can
|
|
281
|
+
* emit an explicit terminal/stop frame instead of the thread dead-ending
|
|
282
|
+
* silently. `terminated`/`reason` are set when there's nothing to relay
|
|
283
|
+
* (`empty_turn`) or the agent ended the thread via end_conversation
|
|
284
|
+
* (`resolved`).
|
|
285
|
+
*/
|
|
286
|
+
declare function buildA2ACompletePayload<A>(params: {
|
|
287
|
+
conversationId: string;
|
|
288
|
+
fullText: string;
|
|
289
|
+
a2a: A;
|
|
290
|
+
resolved: boolean;
|
|
291
|
+
}): {
|
|
292
|
+
conversationId: string;
|
|
293
|
+
fullText: string;
|
|
294
|
+
a2a: A;
|
|
295
|
+
resolved: boolean;
|
|
296
|
+
terminated?: boolean;
|
|
297
|
+
reason?: string;
|
|
298
|
+
};
|
|
278
299
|
interface PluginApi {
|
|
279
300
|
logger: PluginLogger;
|
|
280
301
|
registrationMode?: 'full' | 'setup-only' | 'setup-runtime' | 'cli-metadata';
|
|
@@ -307,4 +328,4 @@ declare const plugin: {
|
|
|
307
328
|
deactivate(api: PluginApi): Promise<void>;
|
|
308
329
|
};
|
|
309
330
|
//#endregion
|
|
310
|
-
export {
|
|
331
|
+
export { AlfeChannelConfig as a, createAlfeChannelPlugin as i, plugin as n, AlfePluginConfig as o, stripLeakedToolSummary as r, AlfeResolvedAccount as s, buildA2ACompletePayload as t };
|
package/dist/plugin.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as stripLeakedToolSummary, t as
|
|
2
|
-
export { plugin as default, stripLeakedToolSummary };
|
|
1
|
+
import { n as plugin, r as stripLeakedToolSummary, t as buildA2ACompletePayload } from "./plugin2.js";
|
|
2
|
+
export { buildA2ACompletePayload, plugin as default, stripLeakedToolSummary };
|
package/dist/plugin2.cjs
CHANGED
|
@@ -382,6 +382,31 @@ async function listSessions(filters, limit = 50) {
|
|
|
382
382
|
}
|
|
383
383
|
//#endregion
|
|
384
384
|
//#region src/a2a-tools.ts
|
|
385
|
+
let conversationEndRequested = false;
|
|
386
|
+
let a2aTurnArmed = false;
|
|
387
|
+
/**
|
|
388
|
+
* Clear the end signal AND arm the gate. Call immediately before an A2A
|
|
389
|
+
* dispatch. Arming is what permits `end_conversation` to set the flag for the
|
|
390
|
+
* duration of this turn.
|
|
391
|
+
*/
|
|
392
|
+
function resetA2AEndSignal() {
|
|
393
|
+
conversationEndRequested = false;
|
|
394
|
+
a2aTurnArmed = true;
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Clear the end signal AND disarm the gate. Call after an A2A dispatch (in a
|
|
398
|
+
* `finally`, so it also runs on error). Disarming closes the gate so a later
|
|
399
|
+
* NON-A2A turn invoking `end_conversation` can't set a flag the next A2A turn
|
|
400
|
+
* would read.
|
|
401
|
+
*/
|
|
402
|
+
function disarmA2AEndSignal() {
|
|
403
|
+
conversationEndRequested = false;
|
|
404
|
+
a2aTurnArmed = false;
|
|
405
|
+
}
|
|
406
|
+
/** True if `end_conversation` was invoked since the last reset. */
|
|
407
|
+
function isA2AEndSignalled() {
|
|
408
|
+
return conversationEndRequested;
|
|
409
|
+
}
|
|
385
410
|
function ok(result) {
|
|
386
411
|
return { content: [{
|
|
387
412
|
type: "text",
|
|
@@ -478,6 +503,7 @@ function buildA2ATools(chatClient, log) {
|
|
|
478
503
|
handler: (params) => {
|
|
479
504
|
const summary = params.summary;
|
|
480
505
|
log.info(`end_conversation tool called${summary ? `: ${summary}` : ""}`);
|
|
506
|
+
if (a2aTurnArmed) conversationEndRequested = true;
|
|
481
507
|
return Promise.resolve({
|
|
482
508
|
status: "conversation_ended",
|
|
483
509
|
summary
|
|
@@ -675,6 +701,28 @@ function stripLeakedToolSummary(text) {
|
|
|
675
701
|
if (!text.includes("step")) return text;
|
|
676
702
|
return text.split("\n").filter((line) => !LEAKED_TOOL_SUMMARY_LINE.test(line)).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
677
703
|
}
|
|
704
|
+
/**
|
|
705
|
+
* Build the `a2a-complete` notify payload. ALWAYS fired for an A2A turn —
|
|
706
|
+
* even when the dispatch produced no relayable text — so the chat service can
|
|
707
|
+
* emit an explicit terminal/stop frame instead of the thread dead-ending
|
|
708
|
+
* silently. `terminated`/`reason` are set when there's nothing to relay
|
|
709
|
+
* (`empty_turn`) or the agent ended the thread via end_conversation
|
|
710
|
+
* (`resolved`).
|
|
711
|
+
*/
|
|
712
|
+
function buildA2ACompletePayload(params) {
|
|
713
|
+
const hasText = params.fullText.trim().length > 0;
|
|
714
|
+
const terminal = params.resolved || !hasText;
|
|
715
|
+
return {
|
|
716
|
+
conversationId: params.conversationId,
|
|
717
|
+
fullText: params.fullText,
|
|
718
|
+
a2a: params.a2a,
|
|
719
|
+
resolved: params.resolved,
|
|
720
|
+
...terminal ? {
|
|
721
|
+
terminated: true,
|
|
722
|
+
reason: params.resolved ? "resolved" : "empty_turn"
|
|
723
|
+
} : {}
|
|
724
|
+
};
|
|
725
|
+
}
|
|
678
726
|
let dispatchInbound = null;
|
|
679
727
|
/**
|
|
680
728
|
* Resolve OpenClaw SDK functions from the running process.
|
|
@@ -778,6 +826,12 @@ async function downloadAttachments(attachments, log) {
|
|
|
778
826
|
}
|
|
779
827
|
return results;
|
|
780
828
|
}
|
|
829
|
+
let agentTurnQueue = Promise.resolve();
|
|
830
|
+
function enqueueAgentTurn(task) {
|
|
831
|
+
const run = agentTurnQueue.then(task, task);
|
|
832
|
+
agentTurnQueue = run.then(() => void 0, () => void 0);
|
|
833
|
+
return run;
|
|
834
|
+
}
|
|
781
835
|
async function handleAgentRequest(request, log) {
|
|
782
836
|
const runtime = pluginRuntime;
|
|
783
837
|
if (!runtime) {
|
|
@@ -859,7 +913,7 @@ async function handleAgentRequest(request, log) {
|
|
|
859
913
|
const userLabel = displayName ?? userId ?? senderId;
|
|
860
914
|
const conversationLabel = conversationType === "group" ? shortConvId ? `[${channelLabel}] Group (${shortConvId})` : `[${channelLabel}] Group` : shortConvId ? `[${channelLabel}] ${userLabel} (${shortConvId})` : `[${channelLabel}] ${userLabel}`;
|
|
861
915
|
let a2aResponseBuffer = "";
|
|
862
|
-
|
|
916
|
+
if (isA2A) resetA2AEndSignal();
|
|
863
917
|
resolvedOpenClawKey = (await dispatchInbound({
|
|
864
918
|
cfg,
|
|
865
919
|
runtime: { channel: runtime.channel },
|
|
@@ -899,7 +953,8 @@ async function handleAgentRequest(request, log) {
|
|
|
899
953
|
`This is an agent-to-agent conversation with ${a2a.sourceAgentName}.`,
|
|
900
954
|
"Rules:",
|
|
901
955
|
"- Only respond if you have new information, a question, or an action to coordinate.",
|
|
902
|
-
"-
|
|
956
|
+
"- When the discussion is complete, call the end_conversation() tool AND end your final message with the literal token [RESOLVED] (uppercase, in square brackets, as the last thing in the message).",
|
|
957
|
+
"- Do NOT write the word \"resolved\" in prose unless you mean to end — only the exact token [RESOLVED] ends the thread.",
|
|
903
958
|
"- Do NOT respond just to acknowledge — that creates infinite loops."
|
|
904
959
|
].join("\n")
|
|
905
960
|
} : {}
|
|
@@ -908,10 +963,7 @@ async function handleAgentRequest(request, log) {
|
|
|
908
963
|
const responseText = stripLeakedToolSummary(payload.text ?? "");
|
|
909
964
|
const mediaUrls = [...payload.mediaUrl ? [payload.mediaUrl] : [], ...payload.mediaUrls ?? []];
|
|
910
965
|
await addMessage(sessionId, "assistant", responseText);
|
|
911
|
-
if (isA2A)
|
|
912
|
-
a2aResponseBuffer += responseText;
|
|
913
|
-
if (responseText.includes("\"status\":\"conversation_ended\"") || responseText.includes("\"status\": \"conversation_ended\"")) a2aResolved = true;
|
|
914
|
-
}
|
|
966
|
+
if (isA2A) a2aResponseBuffer += responseText;
|
|
915
967
|
chatClient?.notify("agent-message", {
|
|
916
968
|
conversationId: conversationId ?? legacySessionKey,
|
|
917
969
|
text: responseText,
|
|
@@ -927,12 +979,15 @@ async function handleAgentRequest(request, log) {
|
|
|
927
979
|
log.error(`Dispatch error (${info.kind}): ${err instanceof Error ? err.message : String(err)}`);
|
|
928
980
|
}
|
|
929
981
|
})).route.sessionKey;
|
|
930
|
-
if (isA2A
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
982
|
+
if (isA2A) {
|
|
983
|
+
const a2aResolved = isA2AEndSignalled();
|
|
984
|
+
chatClient?.notify("a2a-complete", buildA2ACompletePayload({
|
|
985
|
+
conversationId: conversationId ?? legacySessionKey,
|
|
986
|
+
fullText: a2aResponseBuffer,
|
|
987
|
+
a2a,
|
|
988
|
+
resolved: a2aResolved
|
|
989
|
+
}));
|
|
990
|
+
}
|
|
936
991
|
chatClient?.sendResponse(request.id, true, { sessionKey: resolvedOpenClawKey });
|
|
937
992
|
log.info(`Agent dispatch complete: sessionKey=${resolvedOpenClawKey}`);
|
|
938
993
|
} catch (err) {
|
|
@@ -941,6 +996,7 @@ async function handleAgentRequest(request, log) {
|
|
|
941
996
|
chatClient?.sendResponse(request.id, false, { message: errMsg });
|
|
942
997
|
} finally {
|
|
943
998
|
unsubscribe();
|
|
999
|
+
if (isA2A) disarmA2AEndSignal();
|
|
944
1000
|
}
|
|
945
1001
|
}
|
|
946
1002
|
async function handleSessionsList(request, log) {
|
|
@@ -1033,7 +1089,7 @@ const plugin = {
|
|
|
1033
1089
|
apiKey,
|
|
1034
1090
|
onRequest: (request) => {
|
|
1035
1091
|
const handle = async () => {
|
|
1036
|
-
if (request.method === "agent") await handleAgentRequest(request, log);
|
|
1092
|
+
if (request.method === "agent") await enqueueAgentTurn(() => handleAgentRequest(request, log));
|
|
1037
1093
|
else if (request.method === "sessions.list") await handleSessionsList(request, log);
|
|
1038
1094
|
else if (request.method === "sessions.get") await handleSessionsGet(request, log);
|
|
1039
1095
|
else chatClient?.sendResponse(request.id, false, { message: `Unknown method: ${request.method}` });
|
|
@@ -1168,6 +1224,12 @@ const plugin = {
|
|
|
1168
1224
|
}
|
|
1169
1225
|
};
|
|
1170
1226
|
//#endregion
|
|
1227
|
+
Object.defineProperty(exports, "buildA2ACompletePayload", {
|
|
1228
|
+
enumerable: true,
|
|
1229
|
+
get: function() {
|
|
1230
|
+
return buildA2ACompletePayload;
|
|
1231
|
+
}
|
|
1232
|
+
});
|
|
1171
1233
|
Object.defineProperty(exports, "createAlfeChannelPlugin", {
|
|
1172
1234
|
enumerable: true,
|
|
1173
1235
|
get: function() {
|
package/dist/plugin2.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as stripLeakedToolSummary, t as
|
|
2
|
-
export { plugin as default, stripLeakedToolSummary };
|
|
1
|
+
import { n as plugin, r as stripLeakedToolSummary, t as buildA2ACompletePayload } from "./plugin.cjs";
|
|
2
|
+
export { buildA2ACompletePayload, plugin as default, stripLeakedToolSummary };
|
package/dist/plugin2.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as stripLeakedToolSummary, t as
|
|
2
|
-
export { plugin as default, stripLeakedToolSummary };
|
|
1
|
+
import { n as plugin, r as stripLeakedToolSummary, t as buildA2ACompletePayload } from "./plugin.js";
|
|
2
|
+
export { buildA2ACompletePayload, plugin as default, stripLeakedToolSummary };
|
package/dist/plugin2.js
CHANGED
|
@@ -382,6 +382,31 @@ async function listSessions(filters, limit = 50) {
|
|
|
382
382
|
}
|
|
383
383
|
//#endregion
|
|
384
384
|
//#region src/a2a-tools.ts
|
|
385
|
+
let conversationEndRequested = false;
|
|
386
|
+
let a2aTurnArmed = false;
|
|
387
|
+
/**
|
|
388
|
+
* Clear the end signal AND arm the gate. Call immediately before an A2A
|
|
389
|
+
* dispatch. Arming is what permits `end_conversation` to set the flag for the
|
|
390
|
+
* duration of this turn.
|
|
391
|
+
*/
|
|
392
|
+
function resetA2AEndSignal() {
|
|
393
|
+
conversationEndRequested = false;
|
|
394
|
+
a2aTurnArmed = true;
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Clear the end signal AND disarm the gate. Call after an A2A dispatch (in a
|
|
398
|
+
* `finally`, so it also runs on error). Disarming closes the gate so a later
|
|
399
|
+
* NON-A2A turn invoking `end_conversation` can't set a flag the next A2A turn
|
|
400
|
+
* would read.
|
|
401
|
+
*/
|
|
402
|
+
function disarmA2AEndSignal() {
|
|
403
|
+
conversationEndRequested = false;
|
|
404
|
+
a2aTurnArmed = false;
|
|
405
|
+
}
|
|
406
|
+
/** True if `end_conversation` was invoked since the last reset. */
|
|
407
|
+
function isA2AEndSignalled() {
|
|
408
|
+
return conversationEndRequested;
|
|
409
|
+
}
|
|
385
410
|
function ok(result) {
|
|
386
411
|
return { content: [{
|
|
387
412
|
type: "text",
|
|
@@ -478,6 +503,7 @@ function buildA2ATools(chatClient, log) {
|
|
|
478
503
|
handler: (params) => {
|
|
479
504
|
const summary = params.summary;
|
|
480
505
|
log.info(`end_conversation tool called${summary ? `: ${summary}` : ""}`);
|
|
506
|
+
if (a2aTurnArmed) conversationEndRequested = true;
|
|
481
507
|
return Promise.resolve({
|
|
482
508
|
status: "conversation_ended",
|
|
483
509
|
summary
|
|
@@ -675,6 +701,28 @@ function stripLeakedToolSummary(text) {
|
|
|
675
701
|
if (!text.includes("step")) return text;
|
|
676
702
|
return text.split("\n").filter((line) => !LEAKED_TOOL_SUMMARY_LINE.test(line)).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
677
703
|
}
|
|
704
|
+
/**
|
|
705
|
+
* Build the `a2a-complete` notify payload. ALWAYS fired for an A2A turn —
|
|
706
|
+
* even when the dispatch produced no relayable text — so the chat service can
|
|
707
|
+
* emit an explicit terminal/stop frame instead of the thread dead-ending
|
|
708
|
+
* silently. `terminated`/`reason` are set when there's nothing to relay
|
|
709
|
+
* (`empty_turn`) or the agent ended the thread via end_conversation
|
|
710
|
+
* (`resolved`).
|
|
711
|
+
*/
|
|
712
|
+
function buildA2ACompletePayload(params) {
|
|
713
|
+
const hasText = params.fullText.trim().length > 0;
|
|
714
|
+
const terminal = params.resolved || !hasText;
|
|
715
|
+
return {
|
|
716
|
+
conversationId: params.conversationId,
|
|
717
|
+
fullText: params.fullText,
|
|
718
|
+
a2a: params.a2a,
|
|
719
|
+
resolved: params.resolved,
|
|
720
|
+
...terminal ? {
|
|
721
|
+
terminated: true,
|
|
722
|
+
reason: params.resolved ? "resolved" : "empty_turn"
|
|
723
|
+
} : {}
|
|
724
|
+
};
|
|
725
|
+
}
|
|
678
726
|
let dispatchInbound = null;
|
|
679
727
|
/**
|
|
680
728
|
* Resolve OpenClaw SDK functions from the running process.
|
|
@@ -778,6 +826,12 @@ async function downloadAttachments(attachments, log) {
|
|
|
778
826
|
}
|
|
779
827
|
return results;
|
|
780
828
|
}
|
|
829
|
+
let agentTurnQueue = Promise.resolve();
|
|
830
|
+
function enqueueAgentTurn(task) {
|
|
831
|
+
const run = agentTurnQueue.then(task, task);
|
|
832
|
+
agentTurnQueue = run.then(() => void 0, () => void 0);
|
|
833
|
+
return run;
|
|
834
|
+
}
|
|
781
835
|
async function handleAgentRequest(request, log) {
|
|
782
836
|
const runtime = pluginRuntime;
|
|
783
837
|
if (!runtime) {
|
|
@@ -859,7 +913,7 @@ async function handleAgentRequest(request, log) {
|
|
|
859
913
|
const userLabel = displayName ?? userId ?? senderId;
|
|
860
914
|
const conversationLabel = conversationType === "group" ? shortConvId ? `[${channelLabel}] Group (${shortConvId})` : `[${channelLabel}] Group` : shortConvId ? `[${channelLabel}] ${userLabel} (${shortConvId})` : `[${channelLabel}] ${userLabel}`;
|
|
861
915
|
let a2aResponseBuffer = "";
|
|
862
|
-
|
|
916
|
+
if (isA2A) resetA2AEndSignal();
|
|
863
917
|
resolvedOpenClawKey = (await dispatchInbound({
|
|
864
918
|
cfg,
|
|
865
919
|
runtime: { channel: runtime.channel },
|
|
@@ -899,7 +953,8 @@ async function handleAgentRequest(request, log) {
|
|
|
899
953
|
`This is an agent-to-agent conversation with ${a2a.sourceAgentName}.`,
|
|
900
954
|
"Rules:",
|
|
901
955
|
"- Only respond if you have new information, a question, or an action to coordinate.",
|
|
902
|
-
"-
|
|
956
|
+
"- When the discussion is complete, call the end_conversation() tool AND end your final message with the literal token [RESOLVED] (uppercase, in square brackets, as the last thing in the message).",
|
|
957
|
+
"- Do NOT write the word \"resolved\" in prose unless you mean to end — only the exact token [RESOLVED] ends the thread.",
|
|
903
958
|
"- Do NOT respond just to acknowledge — that creates infinite loops."
|
|
904
959
|
].join("\n")
|
|
905
960
|
} : {}
|
|
@@ -908,10 +963,7 @@ async function handleAgentRequest(request, log) {
|
|
|
908
963
|
const responseText = stripLeakedToolSummary(payload.text ?? "");
|
|
909
964
|
const mediaUrls = [...payload.mediaUrl ? [payload.mediaUrl] : [], ...payload.mediaUrls ?? []];
|
|
910
965
|
await addMessage(sessionId, "assistant", responseText);
|
|
911
|
-
if (isA2A)
|
|
912
|
-
a2aResponseBuffer += responseText;
|
|
913
|
-
if (responseText.includes("\"status\":\"conversation_ended\"") || responseText.includes("\"status\": \"conversation_ended\"")) a2aResolved = true;
|
|
914
|
-
}
|
|
966
|
+
if (isA2A) a2aResponseBuffer += responseText;
|
|
915
967
|
chatClient?.notify("agent-message", {
|
|
916
968
|
conversationId: conversationId ?? legacySessionKey,
|
|
917
969
|
text: responseText,
|
|
@@ -927,12 +979,15 @@ async function handleAgentRequest(request, log) {
|
|
|
927
979
|
log.error(`Dispatch error (${info.kind}): ${err instanceof Error ? err.message : String(err)}`);
|
|
928
980
|
}
|
|
929
981
|
})).route.sessionKey;
|
|
930
|
-
if (isA2A
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
982
|
+
if (isA2A) {
|
|
983
|
+
const a2aResolved = isA2AEndSignalled();
|
|
984
|
+
chatClient?.notify("a2a-complete", buildA2ACompletePayload({
|
|
985
|
+
conversationId: conversationId ?? legacySessionKey,
|
|
986
|
+
fullText: a2aResponseBuffer,
|
|
987
|
+
a2a,
|
|
988
|
+
resolved: a2aResolved
|
|
989
|
+
}));
|
|
990
|
+
}
|
|
936
991
|
chatClient?.sendResponse(request.id, true, { sessionKey: resolvedOpenClawKey });
|
|
937
992
|
log.info(`Agent dispatch complete: sessionKey=${resolvedOpenClawKey}`);
|
|
938
993
|
} catch (err) {
|
|
@@ -941,6 +996,7 @@ async function handleAgentRequest(request, log) {
|
|
|
941
996
|
chatClient?.sendResponse(request.id, false, { message: errMsg });
|
|
942
997
|
} finally {
|
|
943
998
|
unsubscribe();
|
|
999
|
+
if (isA2A) disarmA2AEndSignal();
|
|
944
1000
|
}
|
|
945
1001
|
}
|
|
946
1002
|
async function handleSessionsList(request, log) {
|
|
@@ -1033,7 +1089,7 @@ const plugin = {
|
|
|
1033
1089
|
apiKey,
|
|
1034
1090
|
onRequest: (request) => {
|
|
1035
1091
|
const handle = async () => {
|
|
1036
|
-
if (request.method === "agent") await handleAgentRequest(request, log);
|
|
1092
|
+
if (request.method === "agent") await enqueueAgentTurn(() => handleAgentRequest(request, log));
|
|
1037
1093
|
else if (request.method === "sessions.list") await handleSessionsList(request, log);
|
|
1038
1094
|
else if (request.method === "sessions.get") await handleSessionsGet(request, log);
|
|
1039
1095
|
else chatClient?.sendResponse(request.id, false, { message: `Unknown method: ${request.method}` });
|
|
@@ -1168,4 +1224,4 @@ const plugin = {
|
|
|
1168
1224
|
}
|
|
1169
1225
|
};
|
|
1170
1226
|
//#endregion
|
|
1171
|
-
export {
|
|
1227
|
+
export { createAlfeChannelPlugin as i, plugin as n, stripLeakedToolSummary as r, buildA2ACompletePayload as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfe.ai/openclaw-chat",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.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",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@alfe.ai/agent-api-client": "^0.2.2",
|
|
31
|
-
"@alfe.ai/chat": "^0.0.
|
|
31
|
+
"@alfe.ai/chat": "^0.0.10",
|
|
32
32
|
"@alfe.ai/config": "^0.0.9"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|