@botcord/daemon 0.2.36 → 0.2.37
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/config.d.ts +29 -0
- package/dist/config.js +27 -0
- package/dist/daemon-config-map.d.ts +3 -0
- package/dist/daemon-config-map.js +30 -0
- package/dist/daemon.d.ts +15 -1
- package/dist/daemon.js +56 -11
- package/dist/gateway/channels/botcord.js +44 -0
- package/dist/gateway/channels/http-types.d.ts +19 -0
- package/dist/gateway/channels/http-types.js +1 -0
- package/dist/gateway/channels/index.d.ts +5 -0
- package/dist/gateway/channels/index.js +5 -0
- package/dist/gateway/channels/login-session.d.ts +83 -0
- package/dist/gateway/channels/login-session.js +99 -0
- package/dist/gateway/channels/secret-store.d.ts +21 -0
- package/dist/gateway/channels/secret-store.js +75 -0
- package/dist/gateway/channels/state-store.d.ts +60 -0
- package/dist/gateway/channels/state-store.js +173 -0
- package/dist/gateway/channels/telegram.d.ts +31 -0
- package/dist/gateway/channels/telegram.js +371 -0
- package/dist/gateway/channels/text-split.d.ts +13 -0
- package/dist/gateway/channels/text-split.js +33 -0
- package/dist/gateway/channels/url-guard.d.ts +18 -0
- package/dist/gateway/channels/url-guard.js +53 -0
- package/dist/gateway/channels/wechat-http.d.ts +18 -0
- package/dist/gateway/channels/wechat-http.js +28 -0
- package/dist/gateway/channels/wechat-login.d.ts +36 -0
- package/dist/gateway/channels/wechat-login.js +62 -0
- package/dist/gateway/channels/wechat.d.ts +40 -0
- package/dist/gateway/channels/wechat.js +472 -0
- package/dist/gateway/runtimes/openclaw-acp.js +211 -6
- package/dist/gateway/types.d.ts +10 -0
- package/dist/gateway-control.d.ts +53 -0
- package/dist/gateway-control.js +638 -0
- package/dist/provision.d.ts +7 -0
- package/dist/provision.js +255 -5
- package/package.json +1 -1
- package/src/__tests__/gateway-control.test.ts +499 -0
- package/src/__tests__/openclaw-acp.test.ts +63 -0
- package/src/__tests__/provision.test.ts +179 -0
- package/src/__tests__/secret-store.test.ts +70 -0
- package/src/__tests__/state-store.test.ts +119 -0
- package/src/__tests__/third-party-gateway.test.ts +126 -0
- package/src/__tests__/url-guard.test.ts +85 -0
- package/src/__tests__/wechat-channel.test.ts +1134 -0
- package/src/config.ts +71 -0
- package/src/daemon-config-map.ts +24 -0
- package/src/daemon.ts +70 -11
- package/src/gateway/__tests__/botcord-channel.test.ts +1 -1
- package/src/gateway/__tests__/telegram-channel.test.ts +555 -0
- package/src/gateway/channels/botcord.ts +39 -0
- package/src/gateway/channels/http-types.ts +22 -0
- package/src/gateway/channels/index.ts +22 -0
- package/src/gateway/channels/login-session.ts +135 -0
- package/src/gateway/channels/secret-store.ts +100 -0
- package/src/gateway/channels/state-store.ts +213 -0
- package/src/gateway/channels/telegram.ts +469 -0
- package/src/gateway/channels/text-split.ts +29 -0
- package/src/gateway/channels/url-guard.ts +55 -0
- package/src/gateway/channels/wechat-http.ts +35 -0
- package/src/gateway/channels/wechat-login.ts +90 -0
- package/src/gateway/channels/wechat.ts +572 -0
- package/src/gateway/runtimes/openclaw-acp.ts +211 -7
- package/src/gateway/types.ts +10 -0
- package/src/gateway-control.ts +709 -0
- package/src/provision.ts +336 -5
|
@@ -200,6 +200,7 @@ export class OpenclawAcpAdapter implements RuntimeAdapter {
|
|
|
200
200
|
let assistantBytes = 0;
|
|
201
201
|
let capped = false;
|
|
202
202
|
let finalText = "";
|
|
203
|
+
const assistantTextFilter = createAssistantTextFilter();
|
|
203
204
|
|
|
204
205
|
const emitBlock = (block: StreamBlock): void => {
|
|
205
206
|
try {
|
|
@@ -212,14 +213,9 @@ export class OpenclawAcpAdapter implements RuntimeAdapter {
|
|
|
212
213
|
};
|
|
213
214
|
|
|
214
215
|
const onNotification = (note: AcpNotification): void => {
|
|
215
|
-
seq += 1;
|
|
216
|
-
// Forward raw notification as a stream block for downstream visibility.
|
|
217
|
-
const kind = classifyAcpUpdate(note);
|
|
218
|
-
emitBlock({ raw: note, kind, seq });
|
|
219
|
-
|
|
220
216
|
const update = note.params?.update;
|
|
221
217
|
if (update?.sessionUpdate === "agent_message_chunk") {
|
|
222
|
-
const text = extractText(update.content);
|
|
218
|
+
const text = assistantTextFilter.push(extractText(update.content));
|
|
223
219
|
if (text && !capped) {
|
|
224
220
|
const bytes = Buffer.byteLength(text, "utf8");
|
|
225
221
|
if (assistantBytes + bytes > ASSISTANT_TEXT_CAP) {
|
|
@@ -229,7 +225,16 @@ export class OpenclawAcpAdapter implements RuntimeAdapter {
|
|
|
229
225
|
assistantBytes += bytes;
|
|
230
226
|
}
|
|
231
227
|
}
|
|
228
|
+
if (!text) return;
|
|
229
|
+
seq += 1;
|
|
230
|
+
emitBlock({ raw: sanitizeAssistantChunk(note, text), kind: "assistant_text", seq });
|
|
231
|
+
return;
|
|
232
232
|
}
|
|
233
|
+
|
|
234
|
+
seq += 1;
|
|
235
|
+
// Forward raw non-assistant notifications as stream blocks for downstream visibility.
|
|
236
|
+
const kind = classifyAcpUpdate(note);
|
|
237
|
+
emitBlock({ raw: note, kind, seq });
|
|
233
238
|
};
|
|
234
239
|
|
|
235
240
|
let abortListener: (() => void) | undefined;
|
|
@@ -322,7 +327,29 @@ export class OpenclawAcpAdapter implements RuntimeAdapter {
|
|
|
322
327
|
// OpenClaw's prompt response shape isn't strictly fixed; pull a final
|
|
323
328
|
// text out of common locations and otherwise fall back to the streamed
|
|
324
329
|
// chunks accumulated above.
|
|
325
|
-
|
|
330
|
+
const tailText = assistantTextFilter.flush();
|
|
331
|
+
if (tailText && !capped) {
|
|
332
|
+
const bytes = Buffer.byteLength(tailText, "utf8");
|
|
333
|
+
if (assistantBytes + bytes <= ASSISTANT_TEXT_CAP) {
|
|
334
|
+
assistantText += tailText;
|
|
335
|
+
assistantBytes += bytes;
|
|
336
|
+
seq += 1;
|
|
337
|
+
emitBlock({
|
|
338
|
+
raw: {
|
|
339
|
+
method: "session/update",
|
|
340
|
+
params: {
|
|
341
|
+
sessionId: acpSessionId,
|
|
342
|
+
update: { sessionUpdate: "agent_message_chunk", content: [{ type: "text", text: tailText }] },
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
kind: "assistant_text",
|
|
346
|
+
seq,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
const pickedText = normalizeAssistantText(pickFinalText(promptResult));
|
|
351
|
+
const streamedText = normalizeAssistantText(assistantText);
|
|
352
|
+
finalText = pickedText && !looksLikeReasoningLeak(pickedText) ? pickedText : streamedText;
|
|
326
353
|
|
|
327
354
|
if (capped) {
|
|
328
355
|
log.warn("openclaw-acp.assistant-text-capped", { sessionId: acpSessionId });
|
|
@@ -639,6 +666,8 @@ function extractText(content: unknown): string {
|
|
|
639
666
|
}
|
|
640
667
|
if (typeof content === "object") {
|
|
641
668
|
const c = content as Record<string, unknown>;
|
|
669
|
+
const type = typeof c.type === "string" ? c.type.toLowerCase() : "";
|
|
670
|
+
if (type === "thinking" || type === "reasoning" || type === "thought") return "";
|
|
642
671
|
if (typeof c.text === "string") return c.text;
|
|
643
672
|
if (typeof c.content === "string") return c.content;
|
|
644
673
|
if (Array.isArray(c.content)) return extractText(c.content);
|
|
@@ -646,14 +675,189 @@ function extractText(content: unknown): string {
|
|
|
646
675
|
return "";
|
|
647
676
|
}
|
|
648
677
|
|
|
678
|
+
function sanitizeAssistantChunk(note: AcpNotification, text: string): AcpNotification {
|
|
679
|
+
return {
|
|
680
|
+
...note,
|
|
681
|
+
params: {
|
|
682
|
+
...note.params,
|
|
683
|
+
update: {
|
|
684
|
+
...note.params?.update,
|
|
685
|
+
content: [{ type: "text", text }],
|
|
686
|
+
},
|
|
687
|
+
},
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
function normalizeAssistantText(text: string | undefined): string {
|
|
692
|
+
if (!text) return "";
|
|
693
|
+
const finalMatch = text.match(/<final>([\s\S]*?)<\/final>/i);
|
|
694
|
+
const selected = finalMatch ? finalMatch[1] : text;
|
|
695
|
+
if (!finalMatch && selected.trimStart().toLowerCase().startsWith("<think")) {
|
|
696
|
+
return "";
|
|
697
|
+
}
|
|
698
|
+
return selected
|
|
699
|
+
.replace(/<think[^>]*>[\s\S]*?<\/think>/gi, "")
|
|
700
|
+
.replace(/<\/?final>/gi, "")
|
|
701
|
+
.trim();
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
function createAssistantTextFilter(): {
|
|
705
|
+
push(text: string): string;
|
|
706
|
+
flush(): string;
|
|
707
|
+
} {
|
|
708
|
+
let pending = "";
|
|
709
|
+
let inThink = false;
|
|
710
|
+
let inFinal = false;
|
|
711
|
+
let seenFinal = false;
|
|
712
|
+
let fallback = "";
|
|
713
|
+
|
|
714
|
+
const consume = (flush: boolean): string => {
|
|
715
|
+
let out = "";
|
|
716
|
+
while (pending.length > 0) {
|
|
717
|
+
if (inThink) {
|
|
718
|
+
const close = pending.search(/<\/think>/i);
|
|
719
|
+
if (close === -1) {
|
|
720
|
+
if (flush) pending = "";
|
|
721
|
+
return out;
|
|
722
|
+
}
|
|
723
|
+
pending = pending.slice(close).replace(/^<\/think>/i, "");
|
|
724
|
+
inThink = false;
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
if (inFinal) {
|
|
728
|
+
const close = pending.search(/<\/final>/i);
|
|
729
|
+
if (close === -1) {
|
|
730
|
+
out += pending;
|
|
731
|
+
pending = "";
|
|
732
|
+
return out;
|
|
733
|
+
}
|
|
734
|
+
out += pending.slice(0, close);
|
|
735
|
+
pending = pending.slice(close).replace(/^<\/final>/i, "");
|
|
736
|
+
inFinal = false;
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const lt = pending.indexOf("<");
|
|
741
|
+
if (lt === -1) {
|
|
742
|
+
if (seenFinal) {
|
|
743
|
+
out += pending;
|
|
744
|
+
} else {
|
|
745
|
+
fallback += pending;
|
|
746
|
+
}
|
|
747
|
+
pending = "";
|
|
748
|
+
return out;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if (lt > 0) {
|
|
752
|
+
if (seenFinal) {
|
|
753
|
+
out += pending.slice(0, lt);
|
|
754
|
+
} else {
|
|
755
|
+
fallback += pending.slice(0, lt);
|
|
756
|
+
}
|
|
757
|
+
pending = pending.slice(lt);
|
|
758
|
+
continue;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
const lower = pending.toLowerCase();
|
|
762
|
+
if (lower.startsWith("<think")) {
|
|
763
|
+
const end = pending.indexOf(">");
|
|
764
|
+
if (end === -1) {
|
|
765
|
+
if (flush) pending = "";
|
|
766
|
+
return out;
|
|
767
|
+
}
|
|
768
|
+
pending = pending.slice(end + 1);
|
|
769
|
+
inThink = true;
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
if (lower.startsWith("</think")) {
|
|
773
|
+
const end = pending.indexOf(">");
|
|
774
|
+
if (end === -1) {
|
|
775
|
+
if (flush) pending = "";
|
|
776
|
+
return out;
|
|
777
|
+
}
|
|
778
|
+
pending = pending.slice(end + 1);
|
|
779
|
+
continue;
|
|
780
|
+
}
|
|
781
|
+
if (lower.startsWith("<final")) {
|
|
782
|
+
const end = pending.indexOf(">");
|
|
783
|
+
if (end === -1) {
|
|
784
|
+
if (flush) pending = "";
|
|
785
|
+
return out;
|
|
786
|
+
}
|
|
787
|
+
pending = pending.slice(end + 1);
|
|
788
|
+
seenFinal = true;
|
|
789
|
+
fallback = "";
|
|
790
|
+
inFinal = true;
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
if (lower.startsWith("</final")) {
|
|
794
|
+
const end = pending.indexOf(">");
|
|
795
|
+
if (end === -1) {
|
|
796
|
+
if (flush) pending = "";
|
|
797
|
+
return out;
|
|
798
|
+
}
|
|
799
|
+
pending = pending.slice(end + 1);
|
|
800
|
+
inFinal = false;
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
const knownPrefixes = ["<think", "</think", "<final", "</final"];
|
|
805
|
+
if (!flush && knownPrefixes.some((prefix) => prefix.startsWith(lower))) {
|
|
806
|
+
return out;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
out += "<";
|
|
810
|
+
pending = pending.slice(1);
|
|
811
|
+
}
|
|
812
|
+
if (flush && !seenFinal && fallback) {
|
|
813
|
+
const text = normalizeAssistantText(fallback);
|
|
814
|
+
fallback = "";
|
|
815
|
+
if (!looksLikeReasoningLeak(text)) return text;
|
|
816
|
+
}
|
|
817
|
+
return out;
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
return {
|
|
821
|
+
push(text: string): string {
|
|
822
|
+
if (!text) return "";
|
|
823
|
+
pending += text;
|
|
824
|
+
return consume(false);
|
|
825
|
+
},
|
|
826
|
+
flush(): string {
|
|
827
|
+
return consume(true);
|
|
828
|
+
},
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
|
|
649
832
|
function pickFinalText(result: unknown): string | undefined {
|
|
650
833
|
if (!result || typeof result !== "object") return undefined;
|
|
651
834
|
const r = result as Record<string, unknown>;
|
|
835
|
+
if (Array.isArray(r.assistantTexts)) {
|
|
836
|
+
const text = r.assistantTexts.filter((x): x is string => typeof x === "string").join("\n");
|
|
837
|
+
if (text.length > 0) return text;
|
|
838
|
+
}
|
|
839
|
+
const contentText = extractText(r.content);
|
|
840
|
+
if (contentText.length > 0) return contentText;
|
|
841
|
+
const outputText = extractText(r.output);
|
|
842
|
+
if (outputText.length > 0) return outputText;
|
|
843
|
+
const responseText = extractText(r.response);
|
|
844
|
+
if (responseText.length > 0) return responseText;
|
|
652
845
|
if (typeof r.text === "string" && r.text.length > 0) return r.text;
|
|
653
846
|
if (typeof r.message === "string" && r.message.length > 0) return r.message;
|
|
654
847
|
return undefined;
|
|
655
848
|
}
|
|
656
849
|
|
|
850
|
+
function looksLikeReasoningLeak(text: string): boolean {
|
|
851
|
+
const t = text.trim();
|
|
852
|
+
if (!t) return false;
|
|
853
|
+
return (
|
|
854
|
+
/^the user (said|asked|wants|is asking)\b/i.test(t) ||
|
|
855
|
+
/^i('|’)m .*\b(i('|’)ll|i will|need to|should|going to)\b/i.test(t) ||
|
|
856
|
+
/\bi('|’)ll respond\b/i.test(t) ||
|
|
857
|
+
/\bi need to\b/i.test(t)
|
|
858
|
+
);
|
|
859
|
+
}
|
|
860
|
+
|
|
657
861
|
function stringField(bag: Record<string, unknown> | undefined, key: string): string | undefined {
|
|
658
862
|
if (!bag) return undefined;
|
|
659
863
|
const v = bag[key];
|
package/src/gateway/types.ts
CHANGED
|
@@ -197,6 +197,16 @@ export interface ChannelStatusSnapshot {
|
|
|
197
197
|
lastStartAt?: number;
|
|
198
198
|
lastStopAt?: number;
|
|
199
199
|
lastError?: string | null;
|
|
200
|
+
/** Third-party provider id when this channel is not the built-in BotCord. */
|
|
201
|
+
provider?: "wechat" | "telegram";
|
|
202
|
+
/** Last time the adapter polled the upstream provider (ms epoch). */
|
|
203
|
+
lastPollAt?: number;
|
|
204
|
+
/** Last time the adapter accepted an inbound message (ms epoch). */
|
|
205
|
+
lastInboundAt?: number;
|
|
206
|
+
/** Last time the adapter successfully sent a reply (ms epoch). */
|
|
207
|
+
lastSendAt?: number;
|
|
208
|
+
/** Whether the adapter currently holds a usable provider credential. */
|
|
209
|
+
authorized?: boolean;
|
|
200
210
|
}
|
|
201
211
|
|
|
202
212
|
/** Per-turn status snapshot describing a currently-executing runtime invocation. */
|