eve-lark 0.4.2 → 0.4.4
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.js +274 -66
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -531,6 +531,15 @@ function parseInbound(event, botOpenId) {
|
|
|
531
531
|
__name(parseInbound, "parseInbound");
|
|
532
532
|
|
|
533
533
|
// src/card.ts
|
|
534
|
+
function renderToolCalls(calls) {
|
|
535
|
+
if (calls.length === 0) return void 0;
|
|
536
|
+
return calls.map((c) => {
|
|
537
|
+
if (c.state === "running") return `<font color='blue'>\u23F3 ${c.name}</font>`;
|
|
538
|
+
if (c.state === "failed") return `<font color='red'>\u2717 ${c.name}</font>`;
|
|
539
|
+
return `<font color='green'>\u2713 ${c.name}</font>`;
|
|
540
|
+
}).join("\n");
|
|
541
|
+
}
|
|
542
|
+
__name(renderToolCalls, "renderToolCalls");
|
|
534
543
|
var BASE_CONFIG = {
|
|
535
544
|
wide_screen_mode: true,
|
|
536
545
|
update_multi: true
|
|
@@ -595,14 +604,35 @@ function buildTextCard(text) {
|
|
|
595
604
|
__name(buildTextCard, "buildTextCard");
|
|
596
605
|
function buildStreamingCard(opts) {
|
|
597
606
|
const lines = [];
|
|
607
|
+
const toolLine = renderToolCalls(opts.toolCalls ?? []);
|
|
608
|
+
if (toolLine) lines.push(toolLine);
|
|
598
609
|
if (opts.status) {
|
|
599
610
|
lines.push(`<font color='grey'>${opts.status}</font>`);
|
|
600
611
|
}
|
|
601
612
|
lines.push(opts.buffer.length > 0 ? opts.buffer : "_\u2026_");
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
613
|
+
if (opts.askRequest) {
|
|
614
|
+
lines.push(`**${opts.askRequest.prompt}**`);
|
|
615
|
+
if (opts.askRequest.allowFreeform && (opts.askRequest.options?.length ?? 0) === 0) {
|
|
616
|
+
lines.push(`<font color='grey'>_Reply to this chat with your answer_</font>`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
const elements = [
|
|
620
|
+
{ tag: "div", text: { tag: "lark_md", content: lines.join("\n\n") } }
|
|
621
|
+
];
|
|
622
|
+
if (opts.askRequest?.options && opts.askRequest.options.length > 0) {
|
|
623
|
+
const buttons = opts.askRequest.options.map((opt) => ({
|
|
624
|
+
tag: "button",
|
|
625
|
+
text: { tag: "plain_text", content: opt.label },
|
|
626
|
+
type: opt.style ?? "default",
|
|
627
|
+
value: {
|
|
628
|
+
[ASK_BUTTON_VALUE_MARKER]: true,
|
|
629
|
+
requestId: opts.askRequest.requestId,
|
|
630
|
+
optionId: opt.id
|
|
631
|
+
}
|
|
632
|
+
}));
|
|
633
|
+
elements.push({ tag: "action", actions: buttons });
|
|
634
|
+
}
|
|
635
|
+
return { config: { ...BASE_CONFIG }, elements };
|
|
606
636
|
}
|
|
607
637
|
__name(buildStreamingCard, "buildStreamingCard");
|
|
608
638
|
function buildErrorCard(message) {
|
|
@@ -670,10 +700,12 @@ function buildAskCard(request) {
|
|
|
670
700
|
return { config: { ...BASE_CONFIG }, elements };
|
|
671
701
|
}
|
|
672
702
|
__name(buildAskCard, "buildAskCard");
|
|
673
|
-
function buildAskAnsweredCard(request, selected) {
|
|
674
|
-
const elements = [
|
|
675
|
-
|
|
676
|
-
|
|
703
|
+
function buildAskAnsweredCard(request, selected, priorBuffer) {
|
|
704
|
+
const elements = [];
|
|
705
|
+
if (priorBuffer && priorBuffer.length > 0) {
|
|
706
|
+
elements.push({ tag: "div", text: { tag: "lark_md", content: priorBuffer } });
|
|
707
|
+
}
|
|
708
|
+
elements.push({ tag: "div", text: { tag: "lark_md", content: request.prompt } });
|
|
677
709
|
const summary = selected.kind === "option" ? `<font color='green'>\u2713 ${escapeMarkdown(selected.label)}</font>` : `<font color='green'>\u2713 ${escapeMarkdown(selected.text)}</font>`;
|
|
678
710
|
elements.push({ tag: "div", text: { tag: "lark_md", content: summary } });
|
|
679
711
|
return { config: { ...BASE_CONFIG }, elements };
|
|
@@ -685,10 +717,34 @@ function escapeMarkdown(s) {
|
|
|
685
717
|
__name(escapeMarkdown, "escapeMarkdown");
|
|
686
718
|
function buildCardKitStreamingCard(opts) {
|
|
687
719
|
const lines = [];
|
|
720
|
+
const toolLine = renderToolCalls(opts.toolCalls ?? []);
|
|
721
|
+
if (toolLine) lines.push(toolLine);
|
|
688
722
|
if (opts.status) {
|
|
689
723
|
lines.push(`<font color='grey'>${opts.status}</font>`);
|
|
690
724
|
}
|
|
691
725
|
lines.push(opts.buffer.length > 0 ? opts.buffer : "_\u2026_");
|
|
726
|
+
if (opts.askRequest) {
|
|
727
|
+
lines.push(`**${opts.askRequest.prompt}**`);
|
|
728
|
+
if (opts.askRequest.allowFreeform && (opts.askRequest.options?.length ?? 0) === 0) {
|
|
729
|
+
lines.push(`<font color='grey'>_Reply to this chat with your answer_</font>`);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
const elements = [
|
|
733
|
+
{ tag: "markdown", content: lines.join("\n\n") }
|
|
734
|
+
];
|
|
735
|
+
if (opts.askRequest?.options && opts.askRequest.options.length > 0) {
|
|
736
|
+
const buttons = opts.askRequest.options.map((opt) => ({
|
|
737
|
+
tag: "button",
|
|
738
|
+
text: { tag: "plain_text", content: opt.label },
|
|
739
|
+
type: opt.style ?? "default",
|
|
740
|
+
value: {
|
|
741
|
+
[ASK_BUTTON_VALUE_MARKER]: true,
|
|
742
|
+
requestId: opts.askRequest.requestId,
|
|
743
|
+
optionId: opt.id
|
|
744
|
+
}
|
|
745
|
+
}));
|
|
746
|
+
elements.push({ tag: "action", actions: buttons });
|
|
747
|
+
}
|
|
692
748
|
return {
|
|
693
749
|
schema: "2.0",
|
|
694
750
|
config: {
|
|
@@ -696,23 +752,38 @@ function buildCardKitStreamingCard(opts) {
|
|
|
696
752
|
wide_screen_mode: true,
|
|
697
753
|
update_multi: true
|
|
698
754
|
},
|
|
699
|
-
body: {
|
|
700
|
-
elements: [
|
|
701
|
-
{ tag: "div", text: { tag: "lark_md", content: lines.join("\n\n") } }
|
|
702
|
-
]
|
|
703
|
-
}
|
|
755
|
+
body: { elements }
|
|
704
756
|
};
|
|
705
757
|
}
|
|
706
758
|
__name(buildCardKitStreamingCard, "buildCardKitStreamingCard");
|
|
707
|
-
function buildCardKitFinalCard(text) {
|
|
759
|
+
function buildCardKitFinalCard(text, toolCalls, askRequest) {
|
|
760
|
+
const lines = [];
|
|
761
|
+
const toolLine = renderToolCalls(toolCalls ?? []);
|
|
762
|
+
if (toolLine) lines.push(toolLine);
|
|
763
|
+
lines.push(text);
|
|
764
|
+
if (askRequest) {
|
|
765
|
+
lines.push(`**${askRequest.prompt}**`);
|
|
766
|
+
}
|
|
767
|
+
const elements = [
|
|
768
|
+
{ tag: "markdown", content: lines.join("\n\n") }
|
|
769
|
+
];
|
|
770
|
+
if (askRequest?.options && askRequest.options.length > 0) {
|
|
771
|
+
const buttons = askRequest.options.map((opt) => ({
|
|
772
|
+
tag: "button",
|
|
773
|
+
text: { tag: "plain_text", content: opt.label },
|
|
774
|
+
type: opt.style ?? "default",
|
|
775
|
+
value: {
|
|
776
|
+
[ASK_BUTTON_VALUE_MARKER]: true,
|
|
777
|
+
requestId: askRequest.requestId,
|
|
778
|
+
optionId: opt.id
|
|
779
|
+
}
|
|
780
|
+
}));
|
|
781
|
+
elements.push({ tag: "action", actions: buttons });
|
|
782
|
+
}
|
|
708
783
|
return {
|
|
709
784
|
schema: "2.0",
|
|
710
785
|
config: { streaming_mode: false, wide_screen_mode: true, update_multi: true },
|
|
711
|
-
body: {
|
|
712
|
-
elements: [
|
|
713
|
-
{ tag: "div", text: { tag: "lark_md", content: text } }
|
|
714
|
-
]
|
|
715
|
-
}
|
|
786
|
+
body: { elements }
|
|
716
787
|
};
|
|
717
788
|
}
|
|
718
789
|
__name(buildCardKitFinalCard, "buildCardKitFinalCard");
|
|
@@ -729,6 +800,13 @@ var StreamingCardController = class {
|
|
|
729
800
|
status;
|
|
730
801
|
messageId;
|
|
731
802
|
fallbackToText = false;
|
|
803
|
+
/** Active HITL input request, when set via `setAskRequest`. The card
|
|
804
|
+
* builder appends prompt + buttons to the same streaming card so the
|
|
805
|
+
* user can answer inline instead of getting a separate ask-card. */
|
|
806
|
+
askRequest = null;
|
|
807
|
+
/** Tool calls made during this turn, in order. Rendered above the buffer
|
|
808
|
+
* so users can see what the agent is doing / has done, Claude-Code-style. */
|
|
809
|
+
toolCalls = [];
|
|
732
810
|
createTimer = null;
|
|
733
811
|
patchTimer = null;
|
|
734
812
|
patchInFlight = null;
|
|
@@ -754,6 +832,112 @@ var StreamingCardController = class {
|
|
|
754
832
|
this.schedulePatch();
|
|
755
833
|
}
|
|
756
834
|
}
|
|
835
|
+
/**
|
|
836
|
+
* Record the start of a tool call. Renders as `🔧 name` (or `⏳ name` while
|
|
837
|
+
* running). Persistent across patches — won't be lost if the tool completes
|
|
838
|
+
* between throttled patches.
|
|
839
|
+
*
|
|
840
|
+
* Schedules a patch regardless of state (idle/creating/streaming) so the
|
|
841
|
+
* user sees the call even when no text has streamed yet, which is the
|
|
842
|
+
* common case (model often calls a tool before any visible output).
|
|
843
|
+
*/
|
|
844
|
+
addToolCall(name) {
|
|
845
|
+
if (this.state === "completed" || this.state === "aborted") return;
|
|
846
|
+
if (this.toolCalls.some((t) => t.name === name && t.state === "running")) {
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
this.toolCalls.push({ name, state: "running" });
|
|
850
|
+
if (this.state === "idle") {
|
|
851
|
+
this.scheduleCreate();
|
|
852
|
+
} else if (this.state === "streaming") {
|
|
853
|
+
this.schedulePatch();
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Mark the most-recently-started running entry for `name` as done/failed.
|
|
858
|
+
* Rendered as `✓ name` (green) or `✗ name` (red). Stays visible — the
|
|
859
|
+
* entry is not removed, so the user sees the full tool history at the
|
|
860
|
+
* end of the turn.
|
|
861
|
+
*/
|
|
862
|
+
completeToolCall(name, failed = false) {
|
|
863
|
+
for (let i = this.toolCalls.length - 1; i >= 0; i--) {
|
|
864
|
+
const entry = this.toolCalls[i];
|
|
865
|
+
if (entry && entry.name === name && entry.state === "running") {
|
|
866
|
+
entry.state = failed ? "failed" : "done";
|
|
867
|
+
break;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
if (this.state === "streaming") {
|
|
871
|
+
this.schedulePatch();
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
/** Read-only view of tool calls (for card builders). */
|
|
875
|
+
getToolCalls() {
|
|
876
|
+
return this.toolCalls;
|
|
877
|
+
}
|
|
878
|
+
/** Current streaming buffer (for handleCardAction to preserve when patching
|
|
879
|
+
* the "answered" state of an inline ask card). */
|
|
880
|
+
getBuffer() {
|
|
881
|
+
return this.buffer;
|
|
882
|
+
}
|
|
883
|
+
/** Card message id (so input.requested can reuse the existing card instead
|
|
884
|
+
* of creating a separate ask-card). */
|
|
885
|
+
getMessageId() {
|
|
886
|
+
return this.messageId;
|
|
887
|
+
}
|
|
888
|
+
/** Active HITL request being rendered inline on this card, or null. */
|
|
889
|
+
getAskRequest() {
|
|
890
|
+
return this.askRequest;
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Render an `ask_question` request inline on this card by appending prompt
|
|
894
|
+
* + option buttons below the streaming text. Patches immediately so the
|
|
895
|
+
* user sees the buttons as soon as the agent asks.
|
|
896
|
+
*
|
|
897
|
+
* If the prior turn already finalized (state="completed"), transition back
|
|
898
|
+
* to "streaming" so the next patch can update the same card. The card
|
|
899
|
+
* keeps its messageId — no new card is sent.
|
|
900
|
+
*/
|
|
901
|
+
setAskRequest(req) {
|
|
902
|
+
if (this.state === "aborted") return;
|
|
903
|
+
this.askRequest = req;
|
|
904
|
+
if (this.state === "completed" && this.messageId) {
|
|
905
|
+
this.state = "streaming";
|
|
906
|
+
}
|
|
907
|
+
if (this.state === "streaming") {
|
|
908
|
+
this.schedulePatch();
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
/** Clear the inline ask request (e.g. after the user clicked an option). */
|
|
912
|
+
clearAskRequest() {
|
|
913
|
+
this.askRequest = null;
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* Reset per-turn state for the next turn within the same session. Clears
|
|
917
|
+
* the streaming buffer, tool-call history, status, and any inline ask —
|
|
918
|
+
* but keeps the card messageId and transitions back to "streaming" so the
|
|
919
|
+
* next message.appended patches the SAME card. Without this, the second
|
|
920
|
+
* turn's message.completed would create a brand-new card and the user
|
|
921
|
+
* would see N cards for N turns within one logical conversation.
|
|
922
|
+
*
|
|
923
|
+
* Called from the channel's `turn.started` event handler.
|
|
924
|
+
*/
|
|
925
|
+
resetForNewTurn() {
|
|
926
|
+
this.buffer = "";
|
|
927
|
+
this.status = void 0;
|
|
928
|
+
this.toolCalls = [];
|
|
929
|
+
this.askRequest = null;
|
|
930
|
+
this.fallbackToText = false;
|
|
931
|
+
this.cancelCreateTimer();
|
|
932
|
+
this.cancelPatchTimer();
|
|
933
|
+
this.patchInFlight = null;
|
|
934
|
+
this.patchScheduled = false;
|
|
935
|
+
if (this.messageId) {
|
|
936
|
+
this.state = "streaming";
|
|
937
|
+
} else {
|
|
938
|
+
this.state = "idle";
|
|
939
|
+
}
|
|
940
|
+
}
|
|
757
941
|
async finalize(fullText) {
|
|
758
942
|
if (this.state === "completed" || this.state === "aborted") return;
|
|
759
943
|
this.cancelCreateTimer();
|
|
@@ -773,7 +957,7 @@ var StreamingCardController = class {
|
|
|
773
957
|
try {
|
|
774
958
|
const res = await this.client.sendCard({
|
|
775
959
|
chatId: this.deps.chatId,
|
|
776
|
-
card: this.deps.useCardKitV2 ? buildCardKitFinalCard(fullText) : buildTextCard(fullText),
|
|
960
|
+
card: this.deps.useCardKitV2 ? buildCardKitFinalCard(fullText, this.toolCalls, this.askRequest) : buildTextCard(fullText),
|
|
777
961
|
rootId: this.deps.rootId,
|
|
778
962
|
parentId: this.deps.parentId
|
|
779
963
|
});
|
|
@@ -799,7 +983,7 @@ var StreamingCardController = class {
|
|
|
799
983
|
}
|
|
800
984
|
await this.client.patchCard({
|
|
801
985
|
messageId: this.messageId,
|
|
802
|
-
card: this.deps.useCardKitV2 ? buildCardKitStreamingCard({ buffer: fullText, streamingMode: false }) : buildStreamingCard({ buffer: fullText, status: void 0 })
|
|
986
|
+
card: this.deps.useCardKitV2 ? buildCardKitStreamingCard({ buffer: fullText, streamingMode: false, toolCalls: this.toolCalls, askRequest: this.askRequest }) : buildStreamingCard({ buffer: fullText, status: void 0, toolCalls: this.toolCalls, askRequest: this.askRequest })
|
|
803
987
|
});
|
|
804
988
|
this.state = "completed";
|
|
805
989
|
}
|
|
@@ -851,7 +1035,7 @@ var StreamingCardController = class {
|
|
|
851
1035
|
try {
|
|
852
1036
|
const res = await this.client.sendCard({
|
|
853
1037
|
chatId: this.deps.chatId,
|
|
854
|
-
card: this.deps.useCardKitV2 ? buildCardKitStreamingCard({ buffer: this.buffer, status: this.status, streamingMode: true }) : buildStreamingCard({ buffer: this.buffer, status: this.status }),
|
|
1038
|
+
card: this.deps.useCardKitV2 ? buildCardKitStreamingCard({ buffer: this.buffer, status: this.status, streamingMode: true, toolCalls: this.toolCalls, askRequest: this.askRequest }) : buildStreamingCard({ buffer: this.buffer, status: this.status, toolCalls: this.toolCalls, askRequest: this.askRequest }),
|
|
855
1039
|
rootId: this.deps.rootId,
|
|
856
1040
|
parentId: this.deps.parentId
|
|
857
1041
|
});
|
|
@@ -889,7 +1073,7 @@ var StreamingCardController = class {
|
|
|
889
1073
|
if (this.state !== "streaming") return;
|
|
890
1074
|
if (this.patchInFlight) return;
|
|
891
1075
|
if (this.messageId === void 0) return;
|
|
892
|
-
const card = this.deps.useCardKitV2 ? buildCardKitStreamingCard({ buffer: this.buffer, status: this.status, streamingMode: true }) : buildStreamingCard({ buffer: this.buffer, status: this.status });
|
|
1076
|
+
const card = this.deps.useCardKitV2 ? buildCardKitStreamingCard({ buffer: this.buffer, status: this.status, streamingMode: true, toolCalls: this.toolCalls, askRequest: this.askRequest }) : buildStreamingCard({ buffer: this.buffer, status: this.status, toolCalls: this.toolCalls, askRequest: this.askRequest });
|
|
893
1077
|
this.patchInFlight = this.client.patchCard({ messageId: this.messageId, card }).catch((e) => {
|
|
894
1078
|
console.warn(
|
|
895
1079
|
"[eve-lark] streaming card patch failed:",
|
|
@@ -1546,11 +1730,6 @@ function createLarkChannel(optionsInput) {
|
|
|
1546
1730
|
return ctrl;
|
|
1547
1731
|
}
|
|
1548
1732
|
__name(getController, "getController");
|
|
1549
|
-
function dropController(sessionId) {
|
|
1550
|
-
controllers.delete(sessionId);
|
|
1551
|
-
sessionMeta.delete(sessionId);
|
|
1552
|
-
}
|
|
1553
|
-
__name(dropController, "dropController");
|
|
1554
1733
|
async function cleanupAckReaction(sessionId) {
|
|
1555
1734
|
const meta = sessionMeta.get(sessionId);
|
|
1556
1735
|
if (!meta?.ackReactionId || !meta.messageId) return;
|
|
@@ -1920,14 +2099,18 @@ function createLarkChannel(optionsInput) {
|
|
|
1920
2099
|
helpers.waitUntil(
|
|
1921
2100
|
(async () => {
|
|
1922
2101
|
if (pending.cardMessageId && selectedOpt) {
|
|
2102
|
+
const ctrlForBuffer = controllers.get(pending.sessionId);
|
|
2103
|
+
const priorBuffer = ctrlForBuffer?.getBuffer() ?? void 0;
|
|
1923
2104
|
try {
|
|
1924
2105
|
await client.patchCard({
|
|
1925
2106
|
messageId: pending.cardMessageId,
|
|
1926
|
-
card: buildAskAnsweredCard(
|
|
1927
|
-
|
|
1928
|
-
label: selectedOpt.label
|
|
1929
|
-
|
|
2107
|
+
card: buildAskAnsweredCard(
|
|
2108
|
+
pending.request,
|
|
2109
|
+
{ kind: "option", label: selectedOpt.label },
|
|
2110
|
+
priorBuffer
|
|
2111
|
+
)
|
|
1930
2112
|
});
|
|
2113
|
+
ctrlForBuffer?.clearAskRequest();
|
|
1931
2114
|
} catch (e) {
|
|
1932
2115
|
console.warn(
|
|
1933
2116
|
"[eve-lark] patchCard after ask-answer failed:",
|
|
@@ -1987,30 +2170,39 @@ function createLarkChannel(optionsInput) {
|
|
|
1987
2170
|
const ctrl = getController(sessionId, info);
|
|
1988
2171
|
ctrl.appendDelta(d.messageDelta);
|
|
1989
2172
|
},
|
|
1990
|
-
// Model is about to call tools.
|
|
1991
|
-
//
|
|
1992
|
-
//
|
|
1993
|
-
//
|
|
2173
|
+
// Model is about to call tools. Record each call on the streaming
|
|
2174
|
+
// controller so it shows up in the card as ⏳ name. The controller
|
|
2175
|
+
// creates the card immediately if it doesn't exist yet, so the user
|
|
2176
|
+
// sees the tool call even before any text has streamed (which is the
|
|
2177
|
+
// common case — model often calls tools before producing visible
|
|
2178
|
+
// output). Only fires for streaming modes; post/static have no live
|
|
2179
|
+
// surface to update.
|
|
1994
2180
|
async "actions.requested"(data, _channel, ctx) {
|
|
1995
2181
|
if (options.replyMode !== "streaming" && options.replyMode !== "streaming-v2") return;
|
|
1996
2182
|
const sessionId = ctx.session.id;
|
|
1997
|
-
const
|
|
1998
|
-
if (!
|
|
2183
|
+
const info = sessionInfoFromCtx(ctx);
|
|
2184
|
+
if (!info) return;
|
|
1999
2185
|
const d = data;
|
|
2000
2186
|
const names = (d.actions ?? []).map((a) => a.toolName).filter((n) => typeof n === "string");
|
|
2001
2187
|
if (names.length === 0) return;
|
|
2002
|
-
const
|
|
2003
|
-
|
|
2188
|
+
const ctrl = getController(sessionId, info);
|
|
2189
|
+
for (const name of names) {
|
|
2190
|
+
ctrl.addToolCall(name);
|
|
2191
|
+
}
|
|
2004
2192
|
},
|
|
2005
|
-
// A tool finished.
|
|
2006
|
-
//
|
|
2007
|
-
//
|
|
2008
|
-
|
|
2193
|
+
// A tool finished. Mark its entry ✓ (or ✗ on failure). Stays visible —
|
|
2194
|
+
// the user keeps the tool history at the top of the card through end
|
|
2195
|
+
// of turn. Best-effort: if we can't find the controller (e.g. post
|
|
2196
|
+
// mode, or session already cleaned up), no-op.
|
|
2197
|
+
async "action.result"(data, _channel, ctx) {
|
|
2009
2198
|
if (options.replyMode !== "streaming" && options.replyMode !== "streaming-v2") return;
|
|
2010
2199
|
const sessionId = ctx.session.id;
|
|
2011
2200
|
const ctrl = controllers.get(sessionId);
|
|
2012
2201
|
if (!ctrl) return;
|
|
2013
|
-
|
|
2202
|
+
const d = data;
|
|
2203
|
+
const name = d.result?.toolName;
|
|
2204
|
+
if (!name) return;
|
|
2205
|
+
ctrl.completeToolCall(name, d.result?.status === "failed");
|
|
2014
2206
|
},
|
|
2015
2207
|
// eve's ask_question (and similar HITL tools) fire this event with a
|
|
2016
2208
|
// list of input requests. Each request becomes a Feishu card with
|
|
@@ -2029,22 +2221,29 @@ function createLarkChannel(optionsInput) {
|
|
|
2029
2221
|
`[eve-lark] input.requested sessionId=${sessionId} chatId=${info.chatId} count=${requests.length}`
|
|
2030
2222
|
);
|
|
2031
2223
|
for (const req of requests) {
|
|
2032
|
-
const
|
|
2224
|
+
const existingCtrl = controllers.get(sessionId);
|
|
2225
|
+
const canPatchExisting = existingCtrl && existingCtrl.getMessageId() && (options.replyMode === "streaming" || options.replyMode === "streaming-v2");
|
|
2033
2226
|
let cardMessageId;
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2227
|
+
if (canPatchExisting && existingCtrl) {
|
|
2228
|
+
existingCtrl.setAskRequest(req);
|
|
2229
|
+
cardMessageId = existingCtrl.getMessageId();
|
|
2230
|
+
} else {
|
|
2231
|
+
const card = buildAskCard(req);
|
|
2232
|
+
try {
|
|
2233
|
+
const res = await client.sendCard({
|
|
2234
|
+
chatId: info.chatId,
|
|
2235
|
+
card,
|
|
2236
|
+
rootId: info.rootId,
|
|
2237
|
+
parentId: info.parentId
|
|
2238
|
+
});
|
|
2239
|
+
cardMessageId = res.messageId;
|
|
2240
|
+
} catch (e) {
|
|
2241
|
+
console.error(
|
|
2242
|
+
`[eve-lark] ask card send failed (requestId=${req.requestId}):`,
|
|
2243
|
+
e instanceof Error ? e.message : e
|
|
2244
|
+
);
|
|
2245
|
+
continue;
|
|
2246
|
+
}
|
|
2048
2247
|
}
|
|
2049
2248
|
const pending = {
|
|
2050
2249
|
requestId: req.requestId,
|
|
@@ -2083,7 +2282,6 @@ function createLarkChannel(optionsInput) {
|
|
|
2083
2282
|
await deliverReply(sessionId, info, text);
|
|
2084
2283
|
} finally {
|
|
2085
2284
|
await cleanupAckReaction(sessionId);
|
|
2086
|
-
dropController(sessionId);
|
|
2087
2285
|
}
|
|
2088
2286
|
},
|
|
2089
2287
|
async "turn.failed"(data, _channel, ctx) {
|
|
@@ -2124,7 +2322,6 @@ function createLarkChannel(optionsInput) {
|
|
|
2124
2322
|
}
|
|
2125
2323
|
}
|
|
2126
2324
|
await cleanupAckReaction(sessionId);
|
|
2127
|
-
dropController(sessionId);
|
|
2128
2325
|
},
|
|
2129
2326
|
async "session.failed"(data) {
|
|
2130
2327
|
const userText = formatFailureMessage(data, "session failed", { sentence: "session" });
|
|
@@ -2133,11 +2330,23 @@ function createLarkChannel(optionsInput) {
|
|
|
2133
2330
|
`[eve-lark] session.failed: ${userText}` + (errorId ? ` (errorId=${errorId})` : "")
|
|
2134
2331
|
);
|
|
2135
2332
|
},
|
|
2333
|
+
// A new turn is starting within an existing session (e.g. user clicked
|
|
2334
|
+
// an inline ask button, eve resumed with their InputResponse). Reset
|
|
2335
|
+
// the controller's per-turn state so the new turn's text replaces the
|
|
2336
|
+
// prior turn's text on the SAME card — instead of creating a fresh
|
|
2337
|
+
// card per turn.
|
|
2338
|
+
async "turn.started"(_data, _channel, ctx) {
|
|
2339
|
+
const sessionId = ctx?.session?.id;
|
|
2340
|
+
if (!sessionId) return;
|
|
2341
|
+
const ctrl = controllers.get(sessionId);
|
|
2342
|
+
if (ctrl) {
|
|
2343
|
+
ctrl.resetForNewTurn();
|
|
2344
|
+
}
|
|
2345
|
+
},
|
|
2136
2346
|
// Turn ended cleanly. eve fires this after the final message.completed
|
|
2137
2347
|
// (or instead of it when the assistant step ended in tool-calls with no
|
|
2138
|
-
// visible text).
|
|
2139
|
-
//
|
|
2140
|
-
// never coming.
|
|
2348
|
+
// visible text). Just clean up the ack reaction — the controller stays
|
|
2349
|
+
// for the next turn (cleaned by stale-sweep if the session goes quiet).
|
|
2141
2350
|
async "turn.completed"(data, _channel, ctx) {
|
|
2142
2351
|
const sessionId = ctx?.session?.id;
|
|
2143
2352
|
if (!sessionId) return;
|
|
@@ -2145,7 +2354,6 @@ function createLarkChannel(optionsInput) {
|
|
|
2145
2354
|
await cleanupAckReaction(sessionId);
|
|
2146
2355
|
} catch {
|
|
2147
2356
|
}
|
|
2148
|
-
dropController(sessionId);
|
|
2149
2357
|
},
|
|
2150
2358
|
// The agent needs the user to sign in to an external service (e.g.
|
|
2151
2359
|
// GitHub, Slack, Linear). Render a card with a "Sign in with <X>"
|